diff options
Diffstat (limited to 'iptables/nft-shared.c')
-rw-r--r-- | iptables/nft-shared.c | 1045 |
1 files changed, 43 insertions, 1002 deletions
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 74e19cca..6775578b 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -10,6 +10,7 @@ * This code has been sponsored by Sophos Astaro <http://www.sophos.com> */ +#include <assert.h> #include <string.h> #include <stdio.h> #include <stdlib.h> @@ -20,12 +21,6 @@ #include <xtables.h> -#include <linux/netfilter/nf_log.h> -#include <linux/netfilter/xt_comment.h> -#include <linux/netfilter/xt_limit.h> -#include <linux/netfilter/xt_NFLOG.h> -#include <linux/netfilter/xt_mark.h> - #include <libmnl/libmnl.h> #include <libnftnl/rule.h> #include <libnftnl/expr.h> @@ -152,38 +147,29 @@ void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op, uint8_t sreg) add_cmp_ptr(r, op, &val, sizeof(val), sreg); } -void add_iniface(struct nft_handle *h, struct nftnl_rule *r, - char *iface, uint32_t op) +void add_iface(struct nft_handle *h, struct nftnl_rule *r, + char *iface, uint32_t key, uint32_t op) { - int iface_len; + int iface_len = strlen(iface); uint8_t reg; - iface_len = strlen(iface); - add_meta(h, r, NFT_META_IIFNAME, ®); if (iface[iface_len - 1] == '+') { - if (iface_len > 1) - add_cmp_ptr(r, op, iface, iface_len - 1, reg); + if (iface_len > 1) { + iface_len -= 1; + } else if (op != NFT_CMP_EQ) { + op = NFT_CMP_EQ; + iface = "INVAL/D"; + iface_len = strlen(iface) + 1; + } else { + return; /* -o + */ + } } else { - add_cmp_ptr(r, op, iface, iface_len + 1, reg); + iface_len += 1; } -} - -void add_outiface(struct nft_handle *h, struct nftnl_rule *r, - char *iface, uint32_t op) -{ - int iface_len; - uint8_t reg; - iface_len = strlen(iface); - - add_meta(h, r, NFT_META_OIFNAME, ®); - if (iface[iface_len - 1] == '+') { - if (iface_len > 1) - add_cmp_ptr(r, op, iface, iface_len - 1, reg); - } else { - add_cmp_ptr(r, op, iface, iface_len + 1, reg); - } + add_meta(h, r, key, ®); + add_cmp_ptr(r, op, iface, iface_len, reg); } void add_addr(struct nft_handle *h, struct nftnl_rule *r, @@ -267,978 +253,20 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface, return true; } -static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned char *mask) -{ - if (len == 0) - return; - - memcpy(dst, name, len); - if (name[len - 1] == '\0') { - if (mask) - memset(mask, 0xff, len); - return; - } - - if (len >= IFNAMSIZ) - return; - - /* wildcard */ - dst[len++] = '+'; - if (len >= IFNAMSIZ) - return; - dst[len++] = 0; - if (mask) - memset(mask, 0xff, len - 2); -} - -static struct xtables_match * -nft_create_match(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - const char *name); - -static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - struct xt_mark_mtinfo1 *mark; - struct xtables_match *match; - uint32_t value; - - match = nft_create_match(ctx, ctx->cs, "mark"); - if (!match) - return -1; - - mark = (void*)match->m->data; - - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - mark->invert = 1; - - value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA); - mark->mark = value; - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(&mark->mask, &ctx->bitwise.mask, sizeof(mark->mask)); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } else { - mark->mask = 0xffffffff; - } - - return 0; -} - -int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key, - char *iniface, unsigned char *iniface_mask, - char *outiface, unsigned char *outiface_mask, uint8_t *invflags) +void __get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, uint8_t *op) { - uint32_t value; - const void *ifname; uint32_t len; - switch(key) { - case NFT_META_IIF: - value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA); - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_IN; - - if_indextoname(value, iniface); - - memset(iniface_mask, 0xff, strlen(iniface)+1); - break; - case NFT_META_OIF: - value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA); - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_OUT; - - if_indextoname(value, outiface); - - memset(outiface_mask, 0xff, strlen(outiface)+1); - break; - case NFT_META_BRI_IIFNAME: - case NFT_META_IIFNAME: - ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len); - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_IN; - - parse_ifname(ifname, len, iniface, iniface_mask); - break; - case NFT_META_BRI_OIFNAME: - case NFT_META_OIFNAME: - ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len); - if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ) - *invflags |= IPT_INV_VIA_OUT; - - parse_ifname(ifname, len, outiface, outiface_mask); - break; - case NFT_META_MARK: - parse_meta_mark(ctx, e); - break; - default: - return -1; - } - - return 0; -} - -static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - uint32_t tg_len; - const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME); - const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len); - struct xtables_target *target; - struct xt_entry_target *t; - size_t size; - - target = xtables_find_target(targname, XTF_TRY_LOAD); - if (target == NULL) - return; - - size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len; - - t = xtables_calloc(1, size); - memcpy(&t->data, targinfo, tg_len); - t->u.target_size = size; - t->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV); - strcpy(t->u.user.name, target->name); - - target->t = t; - - ctx->h->ops->parse_target(target, ctx->cs); -} - -static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - uint32_t mt_len; - const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME); - const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len); - struct xtables_match *match; - struct xtables_rule_match **matches; - struct xt_entry_match *m; - - switch (ctx->h->family) { - case NFPROTO_IPV4: - case NFPROTO_IPV6: - case NFPROTO_BRIDGE: - matches = &ctx->cs->matches; - break; - default: - fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n", - ctx->h->family); - exit(EXIT_FAILURE); - } - - match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches); - if (match == NULL) - return; - - m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len); - memcpy(&m->data, mt_info, mt_len); - m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match)); - m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV); - strcpy(m->u.user.name, match->name); - - match->m = m; - - if (ctx->h->ops->parse_match != NULL) - ctx->h->ops->parse_match(match, ctx->cs); + memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen); + *op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); } void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv) { - uint32_t len; uint8_t op; - memcpy(data, nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len), dlen); - op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); - if (op == NFT_CMP_NEQ) - *inv = true; - else - *inv = false; -} - -static void nft_meta_set_to_target(struct nft_xt_ctx *ctx) -{ - struct xtables_target *target; - struct xt_entry_target *t; - unsigned int size; - const char *targname; - - switch (ctx->meta.key) { - case NFT_META_NFTRACE: - if (ctx->immediate.data[0] == 0) - return; - targname = "TRACE"; - break; - default: - return; - } - - target = xtables_find_target(targname, XTF_TRY_LOAD); - if (target == NULL) - return; - - size = XT_ALIGN(sizeof(struct xt_entry_target)) + target->size; - - t = xtables_calloc(1, size); - t->u.target_size = size; - t->u.user.revision = target->revision; - strcpy(t->u.user.name, targname); - - target->t = t; - - ctx->h->ops->parse_target(target, ctx->cs); -} - -static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - ctx->meta.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY); - - if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG) && - (ctx->flags & NFT_XT_CTX_IMMEDIATE) && - nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG) == ctx->immediate.reg) { - ctx->flags &= ~NFT_XT_CTX_IMMEDIATE; - nft_meta_set_to_target(ctx); - return; - } - - ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG); - ctx->flags |= NFT_XT_CTX_META; -} - -static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - memcpy(&ctx->prev_payload, &ctx->payload, - sizeof(ctx->prev_payload)); - ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD; - } - - ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG); - ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE); - ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET); - ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN); - ctx->flags |= NFT_XT_CTX_PAYLOAD; -} - -static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - uint32_t reg, len; - const void *data; - - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG); - if (ctx->reg && reg != ctx->reg) - return; - - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG); - ctx->reg = reg; - data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len); - memcpy(ctx->bitwise.xor, data, len); - data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len); - memcpy(ctx->bitwise.mask, data, len); - ctx->flags |= NFT_XT_CTX_BITWISE; -} - -static struct xtables_match * -nft_create_match(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - const char *name) -{ - struct xtables_match *match; - struct xt_entry_match *m; - unsigned int size; - - match = xtables_find_match(name, XTF_TRY_LOAD, - &cs->matches); - if (!match) - return NULL; - - size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size; - m = xtables_calloc(1, size); - m->u.match_size = size; - m->u.user.revision = match->revision; - - strcpy(m->u.user.name, match->name); - match->m = m; - - xs_init_match(match); - - return match; -} - -static struct xt_udp *nft_udp_match(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs) -{ - struct xt_udp *udp = ctx->tcpudp.udp; - struct xtables_match *match; - - if (!udp) { - match = nft_create_match(ctx, cs, "udp"); - if (!match) - return NULL; - - udp = (void*)match->m->data; - ctx->tcpudp.udp = udp; - } - - return udp; -} - -static struct xt_tcp *nft_tcp_match(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs) -{ - struct xt_tcp *tcp = ctx->tcpudp.tcp; - struct xtables_match *match; - - if (!tcp) { - match = nft_create_match(ctx, cs, "tcp"); - if (!match) - return NULL; - - tcp = (void*)match->m->data; - ctx->tcpudp.tcp = tcp; - } - - return tcp; -} - -static void nft_parse_udp_range(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - int sport_from, int sport_to, - int dport_from, int dport_to, - uint8_t op) -{ - struct xt_udp *udp = nft_udp_match(ctx, cs); - - if (!udp) - return; - - if (sport_from >= 0) { - switch (op) { - case NFT_RANGE_NEQ: - udp->invflags |= XT_UDP_INV_SRCPT; - /* fallthrough */ - case NFT_RANGE_EQ: - udp->spts[0] = sport_from; - udp->spts[1] = sport_to; - break; - } - } - - if (dport_to >= 0) { - switch (op) { - case NFT_CMP_NEQ: - udp->invflags |= XT_UDP_INV_DSTPT; - /* fallthrough */ - case NFT_CMP_EQ: - udp->dpts[0] = dport_from; - udp->dpts[1] = dport_to; - break; - } - } -} - -static void nft_parse_tcp_range(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - int sport_from, int sport_to, - int dport_from, int dport_to, - uint8_t op) -{ - struct xt_tcp *tcp = nft_tcp_match(ctx, cs); - - if (!tcp) - return; - - if (sport_from >= 0) { - switch (op) { - case NFT_RANGE_NEQ: - tcp->invflags |= XT_TCP_INV_SRCPT; - /* fallthrough */ - case NFT_RANGE_EQ: - tcp->spts[0] = sport_from; - tcp->spts[1] = sport_to; - break; - } - } - - if (dport_to >= 0) { - switch (op) { - case NFT_CMP_NEQ: - tcp->invflags |= XT_TCP_INV_DSTPT; - /* fallthrough */ - case NFT_CMP_EQ: - tcp->dpts[0] = dport_from; - tcp->dpts[1] = dport_to; - break; - } - } -} - -static void nft_parse_udp(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - int sport, int dport, - uint8_t op) -{ - struct xt_udp *udp = nft_udp_match(ctx, cs); - - if (!udp) - return; - - if (sport >= 0) { - switch (op) { - case NFT_CMP_NEQ: - udp->invflags |= XT_UDP_INV_SRCPT; - /* fallthrough */ - case NFT_CMP_EQ: - udp->spts[0] = sport; - udp->spts[1] = sport; - break; - case NFT_CMP_LT: - udp->spts[1] = sport > 1 ? sport - 1 : 1; - break; - case NFT_CMP_LTE: - udp->spts[1] = sport; - break; - case NFT_CMP_GT: - udp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff; - break; - case NFT_CMP_GTE: - udp->spts[0] = sport; - break; - } - } - if (dport >= 0) { - switch (op) { - case NFT_CMP_NEQ: - udp->invflags |= XT_UDP_INV_DSTPT; - /* fallthrough */ - case NFT_CMP_EQ: - udp->dpts[0] = dport; - udp->dpts[1] = dport; - break; - case NFT_CMP_LT: - udp->dpts[1] = dport > 1 ? dport - 1 : 1; - break; - case NFT_CMP_LTE: - udp->dpts[1] = dport; - break; - case NFT_CMP_GT: - udp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff; - break; - case NFT_CMP_GTE: - udp->dpts[0] = dport; - break; - } - } -} - -static void nft_parse_tcp(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - int sport, int dport, - uint8_t op) -{ - struct xt_tcp *tcp = nft_tcp_match(ctx, cs); - - if (!tcp) - return; - - if (sport >= 0) { - switch (op) { - case NFT_CMP_NEQ: - tcp->invflags |= XT_TCP_INV_SRCPT; - /* fallthrough */ - case NFT_CMP_EQ: - tcp->spts[0] = sport; - tcp->spts[1] = sport; - break; - case NFT_CMP_LT: - tcp->spts[1] = sport > 1 ? sport - 1 : 1; - break; - case NFT_CMP_LTE: - tcp->spts[1] = sport; - break; - case NFT_CMP_GT: - tcp->spts[0] = sport < 0xffff ? sport + 1 : 0xffff; - break; - case NFT_CMP_GTE: - tcp->spts[0] = sport; - break; - } - } - - if (dport >= 0) { - switch (op) { - case NFT_CMP_NEQ: - tcp->invflags |= XT_TCP_INV_DSTPT; - /* fallthrough */ - case NFT_CMP_EQ: - tcp->dpts[0] = dport; - tcp->dpts[1] = dport; - break; - case NFT_CMP_LT: - tcp->dpts[1] = dport > 1 ? dport - 1 : 1; - break; - case NFT_CMP_LTE: - tcp->dpts[1] = dport; - break; - case NFT_CMP_GT: - tcp->dpts[0] = dport < 0xffff ? dport + 1 : 0xffff; - break; - case NFT_CMP_GTE: - tcp->dpts[0] = dport; - break; - } - } -} - -static void nft_parse_th_port(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - uint8_t proto, - int sport, int dport, uint8_t op) -{ - switch (proto) { - case IPPROTO_UDP: - nft_parse_udp(ctx, cs, sport, dport, op); - break; - case IPPROTO_TCP: - nft_parse_tcp(ctx, cs, sport, dport, op); - break; - } -} - -static void nft_parse_th_port_range(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - uint8_t proto, - int sport_from, int sport_to, - int dport_from, int dport_to, uint8_t op) -{ - switch (proto) { - case IPPROTO_UDP: - nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op); - break; - case IPPROTO_TCP: - nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op); - break; - } -} - -static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx, - struct iptables_command_state *cs, - uint8_t op, uint8_t flags, uint8_t mask) -{ - struct xt_tcp *tcp = nft_tcp_match(ctx, cs); - - if (!tcp) - return; - - if (op == NFT_CMP_NEQ) - tcp->invflags |= XT_TCP_INV_FLAGS; - tcp->flg_cmp = flags; - tcp->flg_mask = mask; -} - -static void nft_parse_transport(struct nft_xt_ctx *ctx, - struct nftnl_expr *e, - struct iptables_command_state *cs) -{ - uint32_t sdport; - uint16_t port; - uint8_t proto, op; - unsigned int len; - - switch (ctx->h->family) { - case NFPROTO_IPV4: - proto = ctx->cs->fw.ip.proto; - break; - case NFPROTO_IPV6: - proto = ctx->cs->fw6.ipv6.proto; - break; - default: - proto = 0; - break; - } - - nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len); - op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP); - - switch(ctx->payload.offset) { - case 0: /* th->sport */ - switch (len) { - case 2: /* load sport only */ - port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA)); - nft_parse_th_port(ctx, cs, proto, port, -1, op); - return; - case 4: /* load both src and dst port */ - sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA)); - nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op); - return; - } - break; - case 2: /* th->dport */ - switch (len) { - case 2: - port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA)); - nft_parse_th_port(ctx, cs, proto, -1, port, op); - return; - } - break; - case 13: /* th->flags */ - if (len == 1 && proto == IPPROTO_TCP) { - uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA); - uint8_t mask = ~0; - - if (ctx->flags & NFT_XT_CTX_BITWISE) { - memcpy(&mask, &ctx->bitwise.mask, sizeof(mask)); - ctx->flags &= ~NFT_XT_CTX_BITWISE; - } - nft_parse_tcp_flags(ctx, cs, op, flags, mask); - } - return; - } -} - -static void nft_parse_transport_range(struct nft_xt_ctx *ctx, - struct nftnl_expr *e, - struct iptables_command_state *cs) -{ - unsigned int len_from, len_to; - uint8_t proto, op; - uint16_t from, to; - - switch (ctx->h->family) { - case NFPROTO_IPV4: - proto = ctx->cs->fw.ip.proto; - break; - case NFPROTO_IPV6: - proto = ctx->cs->fw6.ipv6.proto; - break; - default: - proto = 0; - break; - } - - nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from); - nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to); - if (len_to != len_from || len_to != 2) - return; - - op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP); - - from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA)); - to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA)); - - switch(ctx->payload.offset) { - case 0: - nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op); - return; - case 2: - to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA)); - nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op); - return; - } -} - -static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - uint32_t reg; - - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG); - if (ctx->reg && reg != ctx->reg) - return; - - if (ctx->flags & NFT_XT_CTX_META) { - ctx->h->ops->parse_meta(ctx, e, ctx->cs); - ctx->flags &= ~NFT_XT_CTX_META; - } - /* bitwise context is interpreted from payload */ - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - switch (ctx->payload.base) { - case NFT_PAYLOAD_LL_HEADER: - if (ctx->h->family == NFPROTO_BRIDGE) - ctx->h->ops->parse_payload(ctx, e, ctx->cs); - break; - case NFT_PAYLOAD_NETWORK_HEADER: - ctx->h->ops->parse_payload(ctx, e, ctx->cs); - break; - case NFT_PAYLOAD_TRANSPORT_HEADER: - nft_parse_transport(ctx, e, ctx->cs); - break; - } - } -} - -static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters) -{ - counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS); - counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES); -} - -static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); - struct iptables_command_state *cs = ctx->cs; - struct xt_entry_target *t; - uint32_t size; - int verdict; - - if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) { - const void *imm_data; - uint32_t len; - - imm_data = nftnl_expr_get_data(e, NFTNL_EXPR_IMM_DATA, &len); - - if (len > sizeof(ctx->immediate.data)) - return; - - memcpy(ctx->immediate.data, imm_data, len); - ctx->immediate.len = len; - ctx->immediate.reg = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG); - ctx->flags |= NFT_XT_CTX_IMMEDIATE; - return; - } - - verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT); - /* Standard target? */ - switch(verdict) { - case NF_ACCEPT: - cs->jumpto = "ACCEPT"; - break; - case NF_DROP: - cs->jumpto = "DROP"; - break; - case NFT_RETURN: - cs->jumpto = "RETURN"; - break;; - case NFT_GOTO: - if (ctx->h->ops->set_goto_flag) - ctx->h->ops->set_goto_flag(cs); - /* fall through */ - case NFT_JUMP: - cs->jumpto = chain; - /* fall through */ - default: - return; - } - - cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); - if (!cs->target) - return; - - size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size; - t = xtables_calloc(1, size); - t->u.target_size = size; - t->u.user.revision = cs->target->revision; - strcpy(t->u.user.name, cs->jumpto); - cs->target->t = t; -} - -static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - __u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST); - __u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT); - __u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE); - struct xtables_rule_match **matches; - struct xtables_match *match; - struct xt_rateinfo *rinfo; - size_t size; - - switch (ctx->h->family) { - case NFPROTO_IPV4: - case NFPROTO_IPV6: - case NFPROTO_BRIDGE: - matches = &ctx->cs->matches; - break; - default: - fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n", - ctx->h->family); - exit(EXIT_FAILURE); - } - - match = xtables_find_match("limit", XTF_TRY_LOAD, matches); - if (match == NULL) - return; - - size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size; - match->m = xtables_calloc(1, size); - match->m->u.match_size = size; - strcpy(match->m->u.user.name, match->name); - match->m->u.user.revision = match->revision; - xs_init_match(match); - - rinfo = (void *)match->m->data; - rinfo->avg = XT_LIMIT_SCALE * unit / rate; - rinfo->burst = burst; - - if (ctx->h->ops->parse_match != NULL) - ctx->h->ops->parse_match(match, ctx->cs); -} - -static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - struct xtables_target *target; - struct xt_entry_target *t; - size_t target_size; - /* - * In order to handle the longer log-prefix supported by nft, instead of - * using struct xt_nflog_info, we use a struct with a compatible layout, but - * a larger buffer for the prefix. - */ - struct xt_nflog_info_nft { - __u32 len; - __u16 group; - __u16 threshold; - __u16 flags; - __u16 pad; - char prefix[NF_LOG_PREFIXLEN]; - } info = { - .group = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP), - .threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD), - }; - if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) { - info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN); - info.flags = XT_NFLOG_F_COPY_LEN; - } - if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX)) - snprintf(info.prefix, sizeof(info.prefix), "%s", - nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX)); - - target = xtables_find_target("NFLOG", XTF_TRY_LOAD); - if (target == NULL) - return; - - target_size = XT_ALIGN(sizeof(struct xt_entry_target)) + - XT_ALIGN(sizeof(struct xt_nflog_info_nft)); - - t = xtables_calloc(1, target_size); - t->u.target_size = target_size; - strcpy(t->u.user.name, target->name); - t->u.user.revision = target->revision; - - target->t = t; - - memcpy(&target->t->data, &info, sizeof(info)); - - ctx->h->ops->parse_target(target, ctx->cs); -} - -static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h, - struct nftnl_expr *e) -{ - if (ctx->h->ops->parse_lookup) - ctx->h->ops->parse_lookup(ctx, e); -} - -static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e) -{ - uint32_t reg; - - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG); - if (reg != ctx->reg) - return; - - if (ctx->flags & NFT_XT_CTX_PAYLOAD) { - switch (ctx->payload.base) { - case NFT_PAYLOAD_TRANSPORT_HEADER: - nft_parse_transport_range(ctx, e, ctx->cs); - break; - default: - break; - } - } -} - -void nft_rule_to_iptables_command_state(struct nft_handle *h, - const struct nftnl_rule *r, - struct iptables_command_state *cs) -{ - struct nftnl_expr_iter *iter; - struct nftnl_expr *expr; - struct nft_xt_ctx ctx = { - .cs = cs, - .h = h, - .table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE), - }; - - iter = nftnl_expr_iter_create(r); - if (iter == NULL) - return; - - ctx.iter = iter; - expr = nftnl_expr_iter_next(iter); - while (expr != NULL) { - const char *name = - nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); - - if (strcmp(name, "counter") == 0) - nft_parse_counter(expr, &ctx.cs->counters); - else if (strcmp(name, "payload") == 0) - nft_parse_payload(&ctx, expr); - else if (strcmp(name, "meta") == 0) - nft_parse_meta(&ctx, expr); - else if (strcmp(name, "bitwise") == 0) - nft_parse_bitwise(&ctx, expr); - else if (strcmp(name, "cmp") == 0) - nft_parse_cmp(&ctx, expr); - else if (strcmp(name, "immediate") == 0) - nft_parse_immediate(&ctx, expr); - else if (strcmp(name, "match") == 0) - nft_parse_match(&ctx, expr); - else if (strcmp(name, "target") == 0) - nft_parse_target(&ctx, expr); - else if (strcmp(name, "limit") == 0) - nft_parse_limit(&ctx, expr); - else if (strcmp(name, "lookup") == 0) - nft_parse_lookup(&ctx, h, expr); - else if (strcmp(name, "log") == 0) - nft_parse_log(&ctx, expr); - else if (strcmp(name, "range") == 0) - nft_parse_range(&ctx, expr); - - expr = nftnl_expr_iter_next(iter); - } - - nftnl_expr_iter_destroy(iter); - - if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) { - const void *data; - uint32_t len, size; - const char *comment; - - data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len); - comment = get_comment(data, len); - if (comment) { - struct xtables_match *match; - struct xt_entry_match *m; - - match = xtables_find_match("comment", XTF_TRY_LOAD, - &cs->matches); - if (match == NULL) - return; - - size = XT_ALIGN(sizeof(struct xt_entry_match)) - + match->size; - m = xtables_calloc(1, size); - - strncpy((char *)m->data, comment, match->size - 1); - m->u.match_size = size; - m->u.user.revision = 0; - strcpy(m->u.user.name, match->name); - - match->m = m; - } - } - - if (!cs->jumpto) - cs->jumpto = ""; -} - -void nft_clear_iptables_command_state(struct iptables_command_state *cs) -{ - xtables_rule_matches_free(&cs->matches); - if (cs->target) { - free(cs->target->t); - cs->target->t = NULL; - - if (cs->target == cs->target->next) { - free(cs->target); - cs->target = NULL; - } - } + __get_cmp_data(e, data, dlen, &op); + *inv = (op == NFT_CMP_NEQ); } void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy) @@ -1338,6 +366,7 @@ bool compare_matches(struct xtables_rule_match *mt1, for (mp1 = mt1, mp2 = mt2; mp1 && mp2; mp1 = mp1->next, mp2 = mp2->next) { struct xt_entry_match *m1 = mp1->match->m; struct xt_entry_match *m2 = mp2->match->m; + size_t cmplen = mp1->match->userspacesize; if (strcmp(m1->u.user.name, m2->u.user.name) != 0) { DEBUGP("mismatching match name\n"); @@ -1349,9 +378,13 @@ bool compare_matches(struct xtables_rule_match *mt1, return false; } - if (memcmp(m1->data, m2->data, - mp1->match->userspacesize) != 0) { + if (!strcmp(m1->u.user.name, "among")) + cmplen = m1->u.match_size - sizeof(*m1); + + if (memcmp(m1->data, m2->data, cmplen) != 0) { DEBUGP("mismatch match data\n"); + DEBUG_HEXDUMP("m1->data", m1->data, cmplen); + DEBUG_HEXDUMP("m2->data", m2->data, cmplen); return false; } } @@ -1384,13 +417,6 @@ bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2) return true; } -void nft_ipv46_parse_target(struct xtables_target *t, - struct iptables_command_state *cs) -{ - cs->target = t; - cs->jumpto = t->name; -} - void nft_check_xt_legacy(int family, bool is_ipt_save) { static const char tables6[] = "/proc/net/ip6_tables_names"; @@ -1424,3 +450,18 @@ void nft_check_xt_legacy(int family, bool is_ipt_save) prefix, prefix, is_ipt_save ? "-save" : ""); fclose(fp); } + +enum nft_registers nft_get_next_reg(enum nft_registers reg, size_t size) +{ + /* convert size to NETLINK_ALIGN-sized chunks */ + size = (size + NETLINK_ALIGN - 1) / NETLINK_ALIGN; + + /* map 16byte reg to 4byte one */ + if (reg < __NFT_REG_MAX) + reg = NFT_REG32_00 + (reg - 1) * NFT_REG_SIZE / NFT_REG32_SIZE; + + reg += size; + assert(reg <= NFT_REG32_15); + + return reg; +} |