diff options
Diffstat (limited to 'iptables/nft-ipv4.c')
-rw-r--r-- | iptables/nft-ipv4.c | 470 |
1 files changed, 186 insertions, 284 deletions
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c index afdecf97..74092875 100644 --- a/iptables/nft-ipv4.c +++ b/iptables/nft-ipv4.c @@ -26,58 +26,65 @@ #include "nft.h" #include "nft-shared.h" -static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data) +static int nft_ipv4_add(struct nft_handle *h, struct nft_rule_ctx *ctx, + struct nftnl_rule *r, struct iptables_command_state *cs) { - struct iptables_command_state *cs = data; struct xtables_rule_match *matchp; uint32_t op; int ret; + if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) { + op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + offsetof(struct iphdr, saddr), + &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr, + sizeof(struct in_addr), op); + } + + if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) { + op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP); + add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER, + offsetof(struct iphdr, daddr), + &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr, + sizeof(struct in_addr), op); + } + if (cs->fw.ip.iniface[0] != '\0') { op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN); - add_iniface(r, cs->fw.ip.iniface, op); + add_iface(h, r, cs->fw.ip.iniface, NFT_META_IIFNAME, op); } if (cs->fw.ip.outiface[0] != '\0') { op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT); - add_outiface(r, cs->fw.ip.outiface, op); + add_iface(h, r, cs->fw.ip.outiface, NFT_META_OIFNAME, op); } if (cs->fw.ip.proto != 0) { op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO); - add_l4proto(r, cs->fw.ip.proto, op); + add_proto(h, r, offsetof(struct iphdr, protocol), + sizeof(uint8_t), cs->fw.ip.proto, op); } - if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) { - op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP); - add_addr(r, offsetof(struct iphdr, saddr), - &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr, - sizeof(struct in_addr), op); - } - if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) { - op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP); - add_addr(r, offsetof(struct iphdr, daddr), - &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr, - sizeof(struct in_addr), op); - } if (cs->fw.ip.flags & IPT_F_FRAG) { - add_payload(r, offsetof(struct iphdr, frag_off), 2, - NFT_PAYLOAD_NETWORK_HEADER); + uint8_t reg; + + add_payload(h, r, offsetof(struct iphdr, frag_off), 2, + NFT_PAYLOAD_NETWORK_HEADER, ®); /* get the 13 bits that contain the fragment offset */ - add_bitwise_u16(r, htons(0x1fff), 0); + add_bitwise_u16(h, r, htons(0x1fff), 0, reg, ®); /* if offset is non-zero, this is a fragment */ op = NFT_CMP_NEQ; if (cs->fw.ip.invflags & IPT_INV_FRAG) op = NFT_CMP_EQ; - add_cmp_u16(r, 0, op); + add_cmp_u16(r, 0, op, reg); } add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO); for (matchp = cs->matches; matchp; matchp = matchp->next) { - ret = add_match(h, r, matchp->match->m); + ret = add_match(h, ctx, r, matchp->match->m); if (ret < 0) return ret; } @@ -91,12 +98,9 @@ static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data) return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO)); } -static bool nft_ipv4_is_same(const void *data_a, - const void *data_b) +static bool nft_ipv4_is_same(const struct iptables_command_state *a, + const struct iptables_command_state *b) { - const struct iptables_command_state *a = data_a; - const struct iptables_command_state *b = data_b; - if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr @@ -114,151 +118,9 @@ static bool nft_ipv4_is_same(const void *data_a, b->fw.ip.iniface_mask, b->fw.ip.outiface_mask); } -static void get_frag(struct nft_xt_ctx *ctx, struct nftnl_expr *e, bool *inv) -{ - uint8_t op; - - /* we assume correct mask and xor */ - if (!(ctx->flags & NFT_XT_CTX_BITWISE)) - return; - - /* we assume correct data */ - op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); - if (op == NFT_CMP_EQ) - *inv = true; - else - *inv = false; - - ctx->flags &= ~NFT_XT_CTX_BITWISE; -} - -static const char *mask_to_str(uint32_t mask) -{ - static char mask_str[sizeof("255.255.255.255")]; - uint32_t bits, hmask = ntohl(mask); - struct in_addr mask_addr = { - .s_addr = mask, - }; - int i; - - if (mask == 0xFFFFFFFFU) { - sprintf(mask_str, "32"); - return mask_str; - } - - i = 32; - bits = 0xFFFFFFFEU; - while (--i >= 0 && hmask != bits) - bits <<= 1; - if (i >= 0) - sprintf(mask_str, "%u", i); - else - sprintf(mask_str, "%s", inet_ntoa(mask_addr)); - - return mask_str; -} - -static void nft_ipv4_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, - void *data) -{ - struct iptables_command_state *cs = data; - - switch (ctx->meta.key) { - case NFT_META_L4PROTO: - cs->fw.ip.proto = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA); - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= XT_INV_PROTO; - return; - default: - break; - } - - parse_meta(e, ctx->meta.key, cs->fw.ip.iniface, cs->fw.ip.iniface_mask, - cs->fw.ip.outiface, cs->fw.ip.outiface_mask, - &cs->fw.ip.invflags); -} - -static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) -{ - mask->s_addr = ctx->bitwise.mask[0]; -} - -static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, - struct nftnl_expr *e, void *data) -{ - struct iptables_command_state *cs = data; - struct in_addr addr; - uint8_t proto; - bool inv; - - switch(ctx->payload.offset) { - case offsetof(struct iphdr, saddr): - get_cmp_data(e, &addr, sizeof(addr), &inv); - cs->fw.ip.src.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &cs->fw.ip.smsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { - cs->fw.ip.smsk.s_addr = 0xffffffff; - } - - if (inv) - cs->fw.ip.invflags |= IPT_INV_SRCIP; - break; - case offsetof(struct iphdr, daddr): - get_cmp_data(e, &addr, sizeof(addr), &inv); - cs->fw.ip.dst.s_addr = addr.s_addr; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - parse_mask_ipv4(ctx, &cs->fw.ip.dmsk); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { - cs->fw.ip.dmsk.s_addr = 0xffffffff; - } - - if (inv) - cs->fw.ip.invflags |= IPT_INV_DSTIP; - break; - case offsetof(struct iphdr, protocol): - get_cmp_data(e, &proto, sizeof(proto), &inv); - cs->fw.ip.proto = proto; - if (inv) - cs->fw.ip.invflags |= IPT_INV_PROTO; - break; - case offsetof(struct iphdr, frag_off): - cs->fw.ip.flags |= IPT_F_FRAG; - inv = false; - get_frag(ctx, e, &inv); - if (inv) - cs->fw.ip.invflags |= IPT_INV_FRAG; - break; - default: - DEBUGP("unknown payload offset %d\n", ctx->payload.offset); - break; - } -} - -static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto, - void *data) -{ - struct iptables_command_state *cs = data; - - cs->jumpto = jumpto; - - if (nft_goto) - cs->fw.ip.flags |= IPT_F_GOTO; -} - -static void print_fragment(unsigned int flags, unsigned int invflags, - unsigned int format) +static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs) { - if (!(format & FMT_OPTIONS)) - return; - - if (format & FMT_NOTABLE) - fputs("opt ", stdout); - fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); - fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); - fputc(' ', stdout); + cs->fw.ip.flags |= IPT_F_GOTO; } static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r, @@ -268,9 +130,9 @@ static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r, nft_rule_to_iptables_command_state(h, r, &cs); - print_rule_details(&cs, cs.jumpto, cs.fw.ip.flags, - cs.fw.ip.invflags, cs.fw.ip.proto, num, format); - print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format); + print_rule_details(num, &cs.counters, cs.jumpto, cs.fw.ip.proto, + cs.fw.ip.flags, cs.fw.ip.invflags, format); + print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format, false); print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags, format); print_ipv4_addresses(&cs.fw, format); @@ -288,100 +150,57 @@ static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r, if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); - nft_clear_iptables_command_state(&cs); -} - -static void save_ipv4_addr(char letter, const struct in_addr *addr, - uint32_t mask, int invert) -{ - if (!mask && !invert && !addr->s_addr) - return; - - printf("%s-%c %s/%s ", invert ? "! " : "", letter, inet_ntoa(*addr), - mask_to_str(mask)); + xtables_clear_iptables_command_state(&cs); } -static void nft_ipv4_save_rule(const void *data, unsigned int format) +static void nft_ipv4_save_rule(const struct iptables_command_state *cs, + unsigned int format) { - const struct iptables_command_state *cs = data; - - save_ipv4_addr('s', &cs->fw.ip.src, cs->fw.ip.smsk.s_addr, + save_ipv4_addr('s', &cs->fw.ip.src, &cs->fw.ip.smsk, cs->fw.ip.invflags & IPT_INV_SRCIP); - save_ipv4_addr('d', &cs->fw.ip.dst, cs->fw.ip.dmsk.s_addr, + save_ipv4_addr('d', &cs->fw.ip.dst, &cs->fw.ip.dmsk, cs->fw.ip.invflags & IPT_INV_DSTIP); - save_rule_details(cs, cs->fw.ip.invflags, cs->fw.ip.proto, - cs->fw.ip.iniface, cs->fw.ip.iniface_mask, - cs->fw.ip.outiface, cs->fw.ip.outiface_mask); - - if (cs->fw.ip.flags & IPT_F_FRAG) { - if (cs->fw.ip.invflags & IPT_INV_FRAG) - printf("! "); - printf("-f "); - } + save_rule_details(cs->fw.ip.iniface, cs->fw.ip.outiface, + cs->fw.ip.proto, cs->fw.ip.flags & IPT_F_FRAG, + cs->fw.ip.invflags); save_matches_and_target(cs, cs->fw.ip.flags & IPT_F_GOTO, &cs->fw, format); } -static void nft_ipv4_proto_parse(struct iptables_command_state *cs, - struct xtables_args *args) +static void xlate_ipv4_addr(const char *selector, const struct in_addr *addr, + const struct in_addr *mask, + bool inv, struct xt_xlate *xl) { - cs->fw.ip.proto = args->proto; - cs->fw.ip.invflags = args->invflags; -} - -static void nft_ipv4_post_parse(int command, - struct iptables_command_state *cs, - struct xtables_args *args) -{ - cs->fw.ip.flags = args->flags; - /* We already set invflags in proto_parse, but we need to refresh it - * to include new parsed options. - */ - cs->fw.ip.invflags = args->invflags; + char mbuf[INET_ADDRSTRLEN], abuf[INET_ADDRSTRLEN]; + const char *op = inv ? "!= " : ""; + int cidr; - strncpy(cs->fw.ip.iniface, args->iniface, IFNAMSIZ); - memcpy(cs->fw.ip.iniface_mask, - args->iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - - strncpy(cs->fw.ip.outiface, args->outiface, IFNAMSIZ); - memcpy(cs->fw.ip.outiface_mask, - args->outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - - if (args->goto_set) - cs->fw.ip.flags |= IPT_F_GOTO; + if (!inv && !addr->s_addr && !mask->s_addr) + return; - cs->counters.pcnt = args->pcnt_cnt; - cs->counters.bcnt = args->bcnt_cnt; + inet_ntop(AF_INET, addr, abuf, sizeof(abuf)); - if (command & (CMD_REPLACE | CMD_INSERT | - CMD_DELETE | CMD_APPEND | CMD_CHECK)) { - if (!(cs->options & OPT_DESTINATION)) - args->dhostnetworkmask = "0.0.0.0/0"; - if (!(cs->options & OPT_SOURCE)) - args->shostnetworkmask = "0.0.0.0/0"; + cidr = xtables_ipmask_to_cidr(mask); + switch (cidr) { + case -1: + xt_xlate_add(xl, "%s & %s %s %s ", selector, + inet_ntop(AF_INET, mask, mbuf, sizeof(mbuf)), + inv ? "!=" : "==", abuf); + break; + case 32: + xt_xlate_add(xl, "%s %s%s ", selector, op, abuf); + break; + default: + xt_xlate_add(xl, "%s %s%s/%d ", selector, op, abuf, cidr); } - - if (args->shostnetworkmask) - xtables_ipparse_multiple(args->shostnetworkmask, - &args->s.addr.v4, &args->s.mask.v4, - &args->s.naddrs); - if (args->dhostnetworkmask) - xtables_ipparse_multiple(args->dhostnetworkmask, - &args->d.addr.v4, &args->d.mask.v4, - &args->d.naddrs); - - if ((args->s.naddrs > 1 || args->d.naddrs > 1) && - (cs->fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) - xtables_error(PARAMETER_PROBLEM, - "! not allowed with multiple" - " source or destination IP addresses"); } -static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl) +static int nft_ipv4_xlate(const struct iptables_command_state *cs, + struct xt_xlate *xl) { - const struct iptables_command_state *cs = data; + uint16_t proto = cs->fw.ip.proto; const char *comment; int ret; @@ -395,37 +214,23 @@ static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl) cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0); } - if (cs->fw.ip.proto != 0) { - const struct protoent *pent = - getprotobynumber(cs->fw.ip.proto); - char protonum[sizeof("65535")]; - const char *name = protonum; - - snprintf(protonum, sizeof(protonum), "%u", - cs->fw.ip.proto); - - if (!pent || !xlate_find_match(cs, pent->p_name)) { - if (pent) - name = pent->p_name; - xt_xlate_add(xl, "ip protocol %s%s ", - cs->fw.ip.invflags & IPT_INV_PROTO ? - "!= " : "", name); - } - } + if (proto != 0 && !xlate_find_protomatch(cs, proto)) { + const char *pname = proto_to_name(proto, 0); - if (cs->fw.ip.src.s_addr != 0) { - xt_xlate_add(xl, "ip saddr %s%s%s ", - cs->fw.ip.invflags & IPT_INV_SRCIP ? "!= " : "", - inet_ntoa(cs->fw.ip.src), - xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); - } - if (cs->fw.ip.dst.s_addr != 0) { - xt_xlate_add(xl, "ip daddr %s%s%s ", - cs->fw.ip.invflags & IPT_INV_DSTIP ? "!= " : "", - inet_ntoa(cs->fw.ip.dst), - xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); + xt_xlate_add(xl, "ip protocol"); + if (cs->fw.ip.invflags & IPT_INV_PROTO) + xt_xlate_add(xl, " !="); + if (pname) + xt_xlate_add(xl, "%s", pname); + else + xt_xlate_add(xl, "%hu", proto); } + xlate_ipv4_addr("ip saddr", &cs->fw.ip.src, &cs->fw.ip.smsk, + cs->fw.ip.invflags & IPT_INV_SRCIP, xl); + xlate_ipv4_addr("ip daddr", &cs->fw.ip.dst, &cs->fw.ip.dmsk, + cs->fw.ip.invflags & IPT_INV_DSTIP, xl); + ret = xlate_matches(cs, xl); if (!ret) return ret; @@ -441,20 +246,117 @@ static int nft_ipv4_xlate(const void *data, struct xt_xlate *xl) return ret; } +static int +nft_ipv4_add_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose, + bool append, int rulenum) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr; + + if (append) { + ret = nft_cmd_rule_append(h, chain, table, + cs, verbose); + } else { + ret = nft_cmd_rule_insert(h, chain, table, + cs, rulenum, verbose); + } + } + } + + return ret; +} + +static int +nft_ipv4_delete_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr; + ret = nft_cmd_rule_delete(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +nft_ipv4_check_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < args->s.naddrs; i++) { + cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr; + for (j = 0; j < args->d.naddrs; j++) { + cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr; + ret = nft_cmd_rule_check(h, chain, table, cs, verbose); + } + } + + return ret; +} + +static int +nft_ipv4_replace_entry(struct nft_handle *h, + const char *chain, const char *table, + struct iptables_command_state *cs, + struct xtables_args *args, bool verbose, + int rulenum) +{ + cs->fw.ip.src.s_addr = args->s.addr.v4->s_addr; + cs->fw.ip.dst.s_addr = args->d.addr.v4->s_addr; + cs->fw.ip.smsk.s_addr = args->s.mask.v4->s_addr; + cs->fw.ip.dmsk.s_addr = args->d.mask.v4->s_addr; + + return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose); +} + struct nft_family_ops nft_family_ops_ipv4 = { .add = nft_ipv4_add, .is_same = nft_ipv4_is_same, - .parse_meta = nft_ipv4_parse_meta, - .parse_payload = nft_ipv4_parse_payload, - .parse_immediate = nft_ipv4_parse_immediate, + .set_goto_flag = nft_ipv4_set_goto_flag, .print_header = print_header, .print_rule = nft_ipv4_print_rule, .save_rule = nft_ipv4_save_rule, .save_chain = nft_ipv46_save_chain, - .proto_parse = nft_ipv4_proto_parse, - .post_parse = nft_ipv4_post_parse, - .parse_target = nft_ipv46_parse_target, + .rule_parse = &nft_ruleparse_ops_ipv4, + .cmd_parse = { + .proto_parse = ipv4_proto_parse, + .post_parse = ipv4_post_parse, + .option_name = ip46t_option_name, + .option_invert = ip46t_option_invert, + .command_default = command_default, + .print_help = xtables_printhelp, + }, .rule_to_cs = nft_rule_to_iptables_command_state, - .clear_cs = nft_clear_iptables_command_state, + .clear_cs = xtables_clear_iptables_command_state, .xlate = nft_ipv4_xlate, + .add_entry = nft_ipv4_add_entry, + .delete_entry = nft_ipv4_delete_entry, + .check_entry = nft_ipv4_check_entry, + .replace_entry = nft_ipv4_replace_entry, }; |