1 /* 2 * (C) 2012-2014 by Pablo Neira Ayuso <pablo (at) netfilter.org> 3 * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka (at) linux.intel.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com> 11 */ 12 13 #include <string.h> 14 #include <stdio.h> 15 16 #include <sys/socket.h> 17 #include <netinet/in.h> 18 #include <arpa/inet.h> 19 #include <netinet/ip.h> 20 #include <netdb.h> 21 22 #include <xtables.h> 23 24 #include <linux/netfilter/nf_tables.h> 25 26 #include "nft.h" 27 #include "nft-shared.h" 28 29 static int nft_ipv4_add(struct nftnl_rule *r, void *data) 30 { 31 struct iptables_command_state *cs = data; 32 struct xtables_rule_match *matchp; 33 uint32_t op; 34 int ret; 35 36 if (cs->fw.ip.iniface[0] != '\0') { 37 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN); 38 add_iniface(r, cs->fw.ip.iniface, op); 39 } 40 41 if (cs->fw.ip.outiface[0] != '\0') { 42 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT); 43 add_outiface(r, cs->fw.ip.outiface, op); 44 } 45 46 if (cs->fw.ip.proto != 0) { 47 op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO); 48 add_proto(r, offsetof(struct iphdr, protocol), 1, 49 cs->fw.ip.proto, op); 50 } 51 52 if (cs->fw.ip.src.s_addr != 0) { 53 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP); 54 add_addr(r, offsetof(struct iphdr, saddr), 55 &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr, 56 sizeof(struct in_addr), op); 57 } 58 if (cs->fw.ip.dst.s_addr != 0) { 59 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP); 60 add_addr(r, offsetof(struct iphdr, daddr), 61 &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr, 62 sizeof(struct in_addr), op); 63 } 64 if (cs->fw.ip.flags & IPT_F_FRAG) { 65 add_payload(r, offsetof(struct iphdr, frag_off), 2, 66 NFT_PAYLOAD_NETWORK_HEADER); 67 /* get the 13 bits that contain the fragment offset */ 68 add_bitwise_u16(r, 0x1fff, !0x1fff); 69 70 /* if offset is non-zero, this is a fragment */ 71 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_FRAG); 72 add_cmp_u16(r, 0, op); 73 } 74 75 add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags); 76 77 for (matchp = cs->matches; matchp; matchp = matchp->next) { 78 /* Use nft built-in comments support instead of comment match */ 79 if (strcmp(matchp->match->name, "comment") == 0) { 80 ret = add_comment(r, (char *)matchp->match->m->data); 81 if (ret < 0) 82 return ret; 83 } else { 84 ret = add_match(r, matchp->match->m); 85 if (ret < 0) 86 return ret; 87 } 88 } 89 90 /* Counters need to me added before the target, otherwise they are 91 * increased for each rule because of the way nf_tables works. 92 */ 93 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0) 94 return -1; 95 96 return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO)); 97 } 98 99 static bool nft_ipv4_is_same(const void *data_a, 100 const void *data_b) 101 { 102 const struct iptables_command_state *a = data_a; 103 const struct iptables_command_state *b = data_b; 104 105 if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr 106 || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr 107 || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr 108 || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr 109 || a->fw.ip.proto != b->fw.ip.proto 110 || a->fw.ip.flags != b->fw.ip.flags 111 || a->fw.ip.invflags != b->fw.ip.invflags) { 112 DEBUGP("different src/dst/proto/flags/invflags\n"); 113 return false; 114 } 115 116 return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface, 117 a->fw.ip.iniface_mask, a->fw.ip.outiface_mask, 118 b->fw.ip.iniface, b->fw.ip.outiface, 119 b->fw.ip.iniface_mask, b->fw.ip.outiface_mask); 120 } 121 122 static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_expr *e, bool *inv) 123 { 124 uint8_t op; 125 126 /* we assume correct mask and xor */ 127 if (!(ctx->flags & NFT_XT_CTX_BITWISE)) 128 return; 129 130 /* we assume correct data */ 131 op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); 132 if (op == NFT_CMP_EQ) 133 *inv = true; 134 else 135 *inv = false; 136 137 ctx->flags &= ~NFT_XT_CTX_BITWISE; 138 } 139 140 static const char *mask_to_str(uint32_t mask) 141 { 142 static char mask_str[sizeof("255.255.255.255")]; 143 uint32_t bits, hmask = ntohl(mask); 144 struct in_addr mask_addr = { 145 .s_addr = mask, 146 }; 147 int i; 148 149 if (mask == 0xFFFFFFFFU) { 150 sprintf(mask_str, "32"); 151 return mask_str; 152 } 153 154 i = 32; 155 bits = 0xFFFFFFFEU; 156 while (--i >= 0 && hmask != bits) 157 bits <<= 1; 158 if (i >= 0) 159 sprintf(mask_str, "%u", i); 160 else 161 sprintf(mask_str, "%s", inet_ntoa(mask_addr)); 162 163 return mask_str; 164 } 165 166 static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, 167 void *data) 168 { 169 struct iptables_command_state *cs = data; 170 171 parse_meta(e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, 172 cs->fw.ip.outiface, cs->fw.ip.outiface_mask, 173 &cs->fw.ip.invflags); 174 } 175 176 static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) 177 { 178 mask->s_addr = ctx->bitwise.mask[0]; 179 } 180 181 static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, 182 struct nftnl_expr *e, void *data) 183 { 184 struct iptables_command_state *cs = data; 185 struct in_addr addr; 186 uint8_t proto; 187 bool inv; 188 189 switch(ctx->payload.offset) { 190 case offsetof(struct iphdr, saddr): 191 get_cmp_data(e, &addr, sizeof(addr), &inv); 192 cs->fw.ip.src.s_addr = addr.s_addr; 193 if (ctx->flags & NFT_XT_CTX_BITWISE) { 194 parse_mask_ipv4(ctx, &cs->fw.ip.smsk); 195 ctx->flags &= ~NFT_XT_CTX_BITWISE; 196 } else { 197 cs->fw.ip.smsk.s_addr = 0xffffffff; 198 } 199 200 if (inv) 201 cs->fw.ip.invflags |= IPT_INV_SRCIP; 202 break; 203 case offsetof(struct iphdr, daddr): 204 get_cmp_data(e, &addr, sizeof(addr), &inv); 205 cs->fw.ip.dst.s_addr = addr.s_addr; 206 if (ctx->flags & NFT_XT_CTX_BITWISE) { 207 parse_mask_ipv4(ctx, &cs->fw.ip.dmsk); 208 ctx->flags &= ~NFT_XT_CTX_BITWISE; 209 } else { 210 cs->fw.ip.dmsk.s_addr = 0xffffffff; 211 } 212 213 if (inv) 214 cs->fw.ip.invflags |= IPT_INV_DSTIP; 215 break; 216 case offsetof(struct iphdr, protocol): 217 get_cmp_data(e, &proto, sizeof(proto), &inv); 218 cs->fw.ip.proto = proto; 219 if (inv) 220 cs->fw.ip.invflags |= IPT_INV_PROTO; 221 break; 222 case offsetof(struct iphdr, frag_off): 223 cs->fw.ip.flags |= IPT_F_FRAG; 224 get_frag(ctx, e, &inv); 225 if (inv) 226 cs->fw.ip.invflags |= IPT_INV_FRAG; 227 break; 228 default: 229 DEBUGP("unknown payload offset %d\n", ctx->payload.offset); 230 break; 231 } 232 } 233 234 static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto, 235 void *data) 236 { 237 struct iptables_command_state *cs = data; 238 239 cs->jumpto = jumpto; 240 241 if (nft_goto) 242 cs->fw.ip.flags |= IPT_F_GOTO; 243 } 244 245 static void nft_ipv4_print_header(unsigned int format, const char *chain, 246 const char *pol, 247 const struct xt_counters *counters, 248 bool basechain, uint32_t refs) 249 { 250 print_header(format, chain, pol, counters, basechain, refs); 251 } 252 253 static void print_ipv4_addr(const struct iptables_command_state *cs, 254 unsigned int format) 255 { 256 char buf[BUFSIZ]; 257 258 fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); 259 if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) 260 printf(FMT("%-19s ","%s "), "anywhere"); 261 else { 262 if (format & FMT_NUMERIC) 263 strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); 264 else 265 strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); 266 strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); 267 printf(FMT("%-19s ","%s "), buf); 268 } 269 270 fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); 271 if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) 272 printf(FMT("%-19s ","-> %s"), "anywhere"); 273 else { 274 if (format & FMT_NUMERIC) 275 strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); 276 else 277 strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); 278 strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); 279 printf(FMT("%-19s ","-> %s"), buf); 280 } 281 } 282 283 static void print_fragment(unsigned int flags, unsigned int invflags, 284 unsigned int format) 285 { 286 if (!(format & FMT_OPTIONS)) 287 return; 288 289 if (format & FMT_NOTABLE) 290 fputs("opt ", stdout); 291 fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); 292 fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); 293 fputc(' ', stdout); 294 } 295 296 static void nft_ipv4_print_firewall(struct nftnl_rule *r, unsigned int num, 297 unsigned int format) 298 { 299 struct iptables_command_state cs = {}; 300 301 nft_rule_to_iptables_command_state(r, &cs); 302 303 print_firewall_details(&cs, cs.jumpto, cs.fw.ip.flags, 304 cs.fw.ip.invflags, cs.fw.ip.proto, 305 num, format); 306 print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format); 307 print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags, 308 format); 309 print_ipv4_addr(&cs, format); 310 311 if (format & FMT_NOTABLE) 312 fputs(" ", stdout); 313 314 #ifdef IPT_F_GOTO 315 if (cs.fw.ip.flags & IPT_F_GOTO) 316 printf("[goto] "); 317 #endif 318 319 print_matches_and_target(&cs, format); 320 321 if (!(format & FMT_NONEWLINE)) 322 fputc('\n', stdout); 323 } 324 325 static void save_ipv4_addr(char letter, const struct in_addr *addr, 326 uint32_t mask, int invert) 327 { 328 if (!mask && !invert && !addr->s_addr) 329 return; 330 331 printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr), 332 mask_to_str(mask)); 333 } 334 335 static void nft_ipv4_save_firewall(const void *data, unsigned int format) 336 { 337 const struct iptables_command_state *cs = data; 338 339 save_firewall_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto, 340 cs->fw.ip.iniface, cs->fw.ip.iniface_mask, 341 cs->fw.ip.outiface, cs->fw.ip.outiface_mask); 342 343 if (cs->fw.ip.flags & IPT_F_FRAG) { 344 if (cs->fw.ip.invflags & IPT_INV_FRAG) 345 printf("! "); 346 printf("-f "); 347 } 348 349 save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr, 350 cs->fw.ip.invflags & IPT_INV_SRCIP); 351 save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr, 352 cs->fw.ip.invflags & IPT_INV_DSTIP); 353 354 save_matches_and_target(cs->matches, cs->target, 355 cs->jumpto, cs->fw.ip.flags, &cs->fw); 356 357 if (cs->target == NULL && strlen(cs->jumpto) > 0) { 358 printf("-%c %s", cs->fw.ip.flags & IPT_F_GOTO ? 'g' : 'j', 359 cs->jumpto); 360 } 361 printf("\n"); 362 } 363 364 static void nft_ipv4_proto_parse(struct iptables_command_state *cs, 365 struct xtables_args *args) 366 { 367 cs->fw.ip.proto = args->proto; 368 cs->fw.ip.invflags = args->invflags; 369 } 370 371 static void nft_ipv4_post_parse(int command, 372 struct iptables_command_state *cs, 373 struct xtables_args *args) 374 { 375 cs->fw.ip.flags = args->flags; 376 /* We already set invflags in proto_parse, but we need to refresh it 377 * to include new parsed options. 378 */ 379 cs->fw.ip.invflags = args->invflags; 380 381 strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); 382 memcpy(cs->fw.ip.iniface_mask, 383 args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); 384 385 strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ); 386 memcpy(cs->fw.ip.outiface_mask, 387 args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); 388 389 if (args->goto_set) 390 cs->fw.ip.flags |= IPT_F_GOTO; 391 392 cs->counters.pcnt = args->pcnt_cnt; 393 cs->counters.bcnt = args->bcnt_cnt; 394 395 if (command & (CMD_REPLACE | CMD_INSERT | 396 CMD_DELETE | CMD_APPEND | CMD_CHECK)) { 397 if (!(cs->options & OPT_DESTINATION)) 398 args->dhostnetworkmask = "0.0.0.0/0"; 399 if (!(cs->options & OPT_SOURCE)) 400 args->shostnetworkmask = "0.0.0.0/0"; 401 } 402 403 if (args->shostnetworkmask) 404 xtables_ipparse_multiple(args->shostnetworkmask, 405 &args->s.addr.v4, &args->s.mask.v4, 406 &args->s.naddrs); 407 if (args->dhostnetworkmask) 408 xtables_ipparse_multiple(args->dhostnetworkmask, 409 &args->d.addr.v4, &args->d.mask.v4, 410 &args->d.naddrs); 411 412 if ((args->s.naddrs > 1 || args->d.naddrs > 1) && 413 (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) 414 xtables_error(PARAMETER_PROBLEM, 415 "! not allowed with multiple" 416 " source or destination IP addresses"); 417 } 418 419 static void nft_ipv4_parse_target(struct xtables_target *t, void *data) 420 { 421 struct iptables_command_state *cs = data; 422 423 cs->target = t; 424 } 425 426 static bool nft_ipv4_rule_find(struct nft_family_ops *ops, 427 struct nftnl_rule *r, void *data) 428 { 429 struct iptables_command_state *cs = data; 430 431 return nft_ipv46_rule_find(ops, r, cs); 432 } 433 434 static void nft_ipv4_save_counters(const void *data) 435 { 436 const struct iptables_command_state *cs = data; 437 438 save_counters(cs->counters.pcnt, cs->counters.bcnt); 439 } 440 441 static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl) 442 { 443 const struct iptables_command_state *cs = data; 444 const char *comment; 445 int ret; 446 447 xlate_ifname(xl, "iifname", cs->fw.ip.iniface, 448 cs->fw.ip.invflags & IPT_INV_VIA_IN); 449 xlate_ifname(xl, "oifname", cs->fw.ip.outiface, 450 cs->fw.ip.invflags & IPT_INV_VIA_OUT); 451 452 if (cs->fw.ip.flags & IPT_F_FRAG) { 453 xt_xlate_add(xl, "ip frag-off %s%x ", 454 cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0); 455 } 456 457 if (cs->fw.ip.proto != 0) { 458 const struct protoent *pent = 459 getprotobynumber(cs->fw.ip.proto); 460 char protonum[strlen("255") + 1]; 461 462 if (!xlate_find_match(cs, pent->p_name)) { 463 snprintf(protonum, sizeof(protonum), "%u", 464 cs->fw.ip.proto); 465 protonum[sizeof(protonum) - 1] = '\0'; 466 xt_xlate_add(xl, "ip protocol %s%s ", 467 cs->fw.ip.invflags & IPT_INV_PROTO ? 468 "!= " : "", 469 pent ? pent->p_name : protonum); 470 } 471 } 472 473 if (cs->fw.ip.src.s_addr != 0) { 474 xt_xlate_add(xl, "ip saddr %s%s%s ", 475 cs->fw.ip.invflags & IPT_INV_SRCIP ? "!= " : "", 476 inet_ntoa(cs->fw.ip.src), 477 xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); 478 } 479 if (cs->fw.ip.dst.s_addr != 0) { 480 xt_xlate_add(xl, "ip daddr %s%s%s ", 481 cs->fw.ip.invflags & IPT_INV_DSTIP ? "!= " : "", 482 inet_ntoa(cs->fw.ip.dst), 483 xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); 484 } 485 486 ret = xlate_matches(cs, xl); 487 if (!ret) 488 return ret; 489 490 /* Always add counters per rule, as in iptables */ 491 xt_xlate_add(xl, "counter "); 492 493 comment = xt_xlate_get_comment(xl); 494 if (comment) 495 xt_xlate_add(xl, "comment %s", comment); 496 497 ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl); 498 499 return ret; 500 } 501 502 struct nft_family_ops nft_family_ops_ipv4 = { 503 .add = nft_ipv4_add, 504 .is_same = nft_ipv4_is_same, 505 .parse_meta = nft_ipv4_parse_meta, 506 .parse_payload = nft_ipv4_parse_payload, 507 .parse_immediate = nft_ipv4_parse_immediate, 508 .print_header = nft_ipv4_print_header, 509 .print_firewall = nft_ipv4_print_firewall, 510 .save_firewall = nft_ipv4_save_firewall, 511 .save_counters = nft_ipv4_save_counters, 512 .proto_parse = nft_ipv4_proto_parse, 513 .post_parse = nft_ipv4_post_parse, 514 .parse_target = nft_ipv4_parse_target, 515 .rule_find = nft_ipv4_rule_find, 516 .xlate = nft_ipv4_xlate, 517 }; 518