From 0391677c1a0b28c14d01febd9628a543e8e5fd62 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Sun, 13 Jan 2013 16:42:11 +0100 Subject: xtables: add IPv6 support Summary of changes to add IPv6 support to the xtables utility: * modify all commands (add, delete, replace, check and listing) to support IPv6 addresses. And for the internal nft library: * add family to struct nft_handle and modify all caller to use this family instead of the hardcoded AF_INET. * move code that we can re-use for IPv4 and IPv6 into helper functions. * add IPv6 rule printing support. * add support to parse IPv6 address. Pablo added several improvements to this patch: * added basic xtables-save and xtables-restore support (so it defaults to IPv4) * fixed a couple of bugs found while testing * added reference when -f is used to point to -m frag (until we can make this consistent with IPv4). Note that we use one single xtables binary utility for IPv4 and IPv6. Signed-off-by: Tomasz Bursztyka Signed-off-by: Pablo Neira Ayuso --- iptables/nft.c | 655 +++++++++++++++++++++++++++++++++------------ iptables/nft.h | 1 + iptables/xtables-restore.c | 4 +- iptables/xtables-save.c | 4 +- iptables/xtables.c | 400 +++++++++++++++++++-------- 5 files changed, 772 insertions(+), 292 deletions(-) diff --git a/iptables/nft.c b/iptables/nft.c index 46c8c948..43b13deb 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -34,7 +34,9 @@ #include #include -#include /* FIXME: only IPV4 by now */ +#include +#include +#include #include #include @@ -248,7 +250,7 @@ nft_table_builtin_add(struct nft_handle *h, struct builtin_table *_t, NFT_TABLE_F_DORMANT); } - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); @@ -294,7 +296,7 @@ nft_chain_builtin_add(struct nft_handle *h, struct builtin_table *table, return; /* NLM_F_CREATE requests module autoloading */ - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -410,7 +412,7 @@ int nft_table_add(struct nft_handle *h, const struct nft_table *t) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_table_nlmsg_build_payload(nlh, t); @@ -422,7 +424,7 @@ int nft_chain_add(struct nft_handle *h, const struct nft_chain *c) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -464,7 +466,7 @@ int nft_table_wake_dormant(struct nft_handle *h, const char *table) nft_table_attr_set(t, NFT_TABLE_ATTR_NAME, (char *)table); nft_table_attr_set_u32(t, NFT_TABLE_ATTR_FLAGS, 0); - nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, AF_INET, + nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE, h->family, NLM_F_ACK, h->seq); nft_table_nlmsg_build_payload(nlh, t); nft_table_free(t); @@ -525,7 +527,7 @@ __nft_chain_set(struct nft_handle *h, const char *table, counters->pcnt); } - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -741,6 +743,78 @@ static void add_counters(struct nft_rule *r, uint64_t packets, uint64_t bytes) nft_rule_add_expr(r, expr); } +static void add_iniface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_IN) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_IIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_IIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +static void add_outiface(struct nft_rule *r, char *iface, int invflags) +{ + int iface_len; + uint32_t op; + + iface_len = strlen(iface); + + if (invflags & IPT_INV_VIA_OUT) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + if (iface[iface_len - 1] == '+') { + add_meta(r, NFT_META_OIFNAME); + add_cmp_ptr(r, op, iface, iface_len - 1); + } else { + add_meta(r, NFT_META_OIF); + add_cmp_u32(r, if_nametoindex(iface), op); + } +} + +static void add_addr(struct nft_rule *r, int offset, + void *data, size_t len, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & IPT_INV_SRCIP || invflags & IPT_INV_DSTIP) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_ptr(r, op, data, len); +} + +static void add_proto(struct nft_rule *r, int offset, size_t len, + uint32_t proto, int invflags) +{ + uint32_t op; + + add_payload(r, offset, len); + + if (invflags & XT_INV_PROTO) + op = NFT_CMP_NEQ; + else + op = NFT_CMP_EQ; + + add_cmp_u32(r, proto, op); +} + int nft_rule_add(struct nft_handle *h, const char *chain, const char *table, struct iptables_command_state *cs, @@ -753,6 +827,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, int ret = 1; uint32_t op; int flags = append ? NLM_F_APPEND : 0; + int ip_flags = 0; /* If built-in chains don't exist for this table, create them */ nft_chain_builtin_init(h, table, chain, NF_ACCEPT); @@ -768,77 +843,72 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, nft_rule_attr_set(r, NFT_RULE_ATTR_TABLE, (char *)table); nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); - if (cs->fw.ip.iniface[0] != '\0') { - int iface_len = strlen(cs->fw.ip.iniface); - - if (cs->fw.ip.invflags & IPT_INV_VIA_IN) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; - - if (cs->fw.ip.iniface[iface_len - 1] == '+') { - add_meta(r, NFT_META_IIFNAME); - add_cmp_ptr(r, op, cs->fw.ip.iniface, iface_len - 1); - } else { - add_meta(r, NFT_META_IIF); - add_cmp_u32(r, if_nametoindex(cs->fw.ip.iniface), op); + switch (h->family) { + case AF_INET: + if (cs->fw.ip.iniface[0] != '\0') + add_iniface(r, cs->fw.ip.iniface, cs->fw.ip.invflags); + + if (cs->fw.ip.outiface[0] != '\0') + add_outiface(r, cs->fw.ip.outiface, + cs->fw.ip.invflags); + + if (cs->fw.ip.src.s_addr != 0) + add_addr(r, offsetof(struct iphdr, saddr), + &cs->fw.ip.src.s_addr, 4, + cs->fw.ip.invflags); + + if (cs->fw.ip.dst.s_addr != 0) + add_addr(r, offsetof(struct iphdr, daddr), + &cs->fw.ip.dst.s_addr, 4, + cs->fw.ip.invflags); + + if (cs->fw.ip.proto != 0) + add_proto(r, offsetof(struct iphdr, protocol), 1, + cs->fw.ip.proto, cs->fw.ip.invflags); + + if (cs->fw.ip.flags & IPT_F_FRAG) { + add_payload(r, offsetof(struct iphdr, frag_off), 2); + /* get the 13 bits that contain the fragment offset */ + add_bitwise_u16(r, 0x1fff, !0x1fff); + + /* if offset is non-zero, this is a fragment */ + if (cs->fw.ip.invflags & IPT_INV_FRAG) + op = NFT_CMP_EQ; + else + op = NFT_CMP_NEQ; + + add_cmp_u16(r, 0, op); } - } - if (cs->fw.ip.outiface[0] != '\0') { - int iface_len = strlen(cs->fw.ip.outiface); - if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + ip_flags = cs->fw.ip.flags; - if (cs->fw.ip.outiface[iface_len - 1] == '+') { - add_meta(r, NFT_META_OIFNAME); - add_cmp_ptr(r, op, cs->fw.ip.outiface, iface_len - 1); - } else { - add_meta(r, NFT_META_OIF); - add_cmp_u32(r, if_nametoindex(cs->fw.ip.outiface), op); - } - } - if (cs->fw.ip.src.s_addr != 0) { - add_payload(r, offsetof(struct iphdr, saddr), 4); - if (cs->fw.ip.invflags & IPT_INV_SRCIP) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + break; + case AF_INET6: + if (cs->fw6.ipv6.iniface[0] != '\0') + add_iniface(r, cs->fw6.ipv6.iniface, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.src.s_addr, op); - } - if (cs->fw.ip.dst.s_addr != 0) { - add_payload(r, offsetof(struct iphdr, daddr), 4); - if (cs->fw.ip.invflags & IPT_INV_DSTIP) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + if (cs->fw6.ipv6.outiface[0] != '\0') + add_outiface(r, cs->fw6.ipv6.outiface, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.dst.s_addr, op); - } - if (cs->fw.ip.proto != 0) { - add_payload(r, offsetof(struct iphdr, protocol), 1); - if (cs->fw.ip.invflags & XT_INV_PROTO) - op = NFT_CMP_NEQ; - else - op = NFT_CMP_EQ; + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src)) + add_addr(r, offsetof(struct ip6_hdr, ip6_src), + &cs->fw6.ipv6.src, 16, + cs->fw6.ipv6.invflags); - add_cmp_u32(r, cs->fw.ip.proto, op); - } - if (cs->fw.ip.flags & IPT_F_FRAG) { - add_payload(r, offsetof(struct iphdr, frag_off), 2); - /* get the 13 bits that contain the fragment offset */ - add_bitwise_u16(r, 0x1fff, !0x1fff); + if (!IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst)) + add_addr(r, offsetof(struct ip6_hdr, ip6_dst), + &cs->fw6.ipv6.dst, 16, + cs->fw6.ipv6.invflags); - /* if offset is non-zero, this is a fragment */ - if (cs->fw.ip.invflags & IPT_INV_FRAG) - op = NFT_CMP_EQ; - else - op = NFT_CMP_NEQ; + if (cs->fw6.ipv6.proto != 0) + add_proto(r, offsetof(struct ip6_hdr, ip6_nxt), 1, + cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags); + + ip_flags = cs->fw6.ipv6.flags; - add_cmp_u16(r, 0, op); + break; } for (matchp = cs->matches; matchp; matchp = matchp->next) @@ -862,7 +932,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, add_target(r, cs->target->t); } else if (strlen(cs->jumpto) > 0) { /* Not standard, then it's a go / jump to chain */ - if (cs->fw.ip.flags & IPT_F_GOTO) + if (ip_flags & IPT_F_GOTO) add_jumpto(r, cs->jumpto, NFT_GOTO); else add_jumpto(r, cs->jumpto, NFT_JUMP); @@ -877,7 +947,7 @@ nft_rule_add(struct nft_handle *h, const char *chain, const char *table, } nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE, - AF_INET, flags, h->seq); + h->family, flags, h->seq); nft_rule_nlmsg_build_payload(nlh, r); @@ -1205,7 +1275,7 @@ static const char *mask_to_str(uint32_t mask) } static void -nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +nft_print_payload_ipv4(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) { uint32_t offset; bool inv; @@ -1248,6 +1318,48 @@ nft_print_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) } } +static void +nft_print_payload_ipv6(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter) +{ + uint32_t offset; + bool inv; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch (offset) { + char addr_str[INET6_ADDRSTRLEN]; + struct in6_addr addr; + uint8_t proto; + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -s %s ", addr_str); + else + printf("-s %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN); + + if (inv) + printf("! -d %s ", addr_str); + else + printf("-d %s ", addr_str); + + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + print_proto(proto, inv); + break; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + static void nft_print_counters(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, bool counters) @@ -1279,7 +1391,10 @@ static void nft_rule_print_save(struct nft_rule *r, bool counters) if (strcmp(name, "counter") == 0) { nft_print_counters(expr, iter, counters); } else if (strcmp(name, "payload") == 0) { - nft_print_payload(expr, iter); + if (nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY) == AF_INET) + nft_print_payload_ipv4(expr, iter); + else + nft_print_payload_ipv6(expr, iter); } else if (strcmp(name, "meta") == 0) { nft_print_meta(expr, iter); } else if (strcmp(name, "match") == 0) { @@ -1334,7 +1449,7 @@ static struct nft_chain_list *nft_chain_list_get(struct nft_handle *h) return 0; } - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_chain_list_cb, list); @@ -1446,7 +1561,7 @@ static struct nft_rule_list *nft_rule_list_get(struct nft_handle *h) return 0; } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_rule_list_cb, list); @@ -1512,7 +1627,7 @@ __nft_rule_flush(struct nft_handle *h, const char *table, const char *chain) nft_rule_attr_set(r, NFT_RULE_ATTR_CHAIN, (char *)chain); /* Delete all rules in this table + chain */ - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); nft_rule_nlmsg_build_payload(nlh, r); nft_rule_free(r); @@ -1589,7 +1704,7 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl nft_chain_attr_set(c, NFT_CHAIN_ATTR_TABLE, (char *)table); nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)chain); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK|NLM_F_EXCL, h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); @@ -1610,7 +1725,7 @@ static int __nft_chain_del(struct nft_handle *h, struct nft_chain *c) struct nlmsghdr *nlh; int ret; - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); @@ -1756,7 +1871,7 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, nft_chain_attr_set(c, NFT_CHAIN_ATTR_NAME, (char *)newname); nft_chain_attr_set_u64(c, NFT_CHAIN_ATTR_HANDLE, handle); - nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, AF_INET, + nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN, h->family, NLM_F_ACK, h->seq); nft_chain_nlmsg_build_payload(nlh, c); nft_chain_free(c); @@ -1809,7 +1924,7 @@ static struct nft_table_list *nft_table_list_get(struct nft_handle *h) return 0; } - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family, NLM_F_DUMP, h->seq); ret = mnl_talk(h, nlh, nft_table_list_cb, list); @@ -1919,39 +2034,86 @@ match_different(const struct xt_entry_match *a, } static bool -is_same(const struct iptables_command_state *a, const struct iptables_command_state *b) +is_same(int family, const struct iptables_command_state *a, + const struct iptables_command_state *b) { unsigned int i; + const char *a_outiface, *a_iniface; + unsigned const char *a_iniface_mask, *a_outiface_mask; + const char *b_outiface, *b_iniface; + unsigned const char *b_iniface_mask, *b_outiface_mask; /* Always compare head structures: ignore mask here. */ - 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 - || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr - || a->fw.ip.proto != b->fw.ip.proto - || a->fw.ip.flags != b->fw.ip.flags - || a->fw.ip.invflags != b->fw.ip.invflags) { - DEBUGP("different src/dst/proto/flags/invflags\n"); + switch (family) { + case AF_INET: + 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 + || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr + || a->fw.ip.proto != b->fw.ip.proto + || a->fw.ip.flags != b->fw.ip.flags + || a->fw.ip.invflags != b->fw.ip.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + a_iniface_mask = a->fw.ip.iniface_mask; + a_iniface = a->fw.ip.iniface; + a_outiface_mask = a->fw.ip.outiface_mask; + a_outiface = a->fw.ip.outiface; + + b_iniface_mask = b->fw.ip.iniface_mask; + b_iniface = b->fw.ip.iniface; + b_outiface_mask = b->fw.ip.outiface_mask; + b_outiface = b->fw.ip.outiface; + + break; + case AF_INET6: + if (memcmp(a->fw6.ipv6.src.s6_addr, + b->fw6.ipv6.src.s6_addr, + sizeof(struct in6_addr)) != 0 || + memcmp(a->fw6.ipv6.dst.s6_addr, + b->fw6.ipv6.dst.s6_addr, + sizeof(struct in6_addr)) != 0 || + a->fw6.ipv6.proto != b->fw6.ipv6.proto || + a->fw6.ipv6.flags != b->fw6.ipv6.flags || + a->fw6.ipv6.invflags != b->fw6.ipv6.invflags) { + DEBUGP("different src/dst/proto/flags/invflags\n"); + return false; + } + + a_iniface_mask = a->fw6.ipv6.iniface_mask; + a_iniface = a->fw6.ipv6.iniface; + a_outiface_mask = a->fw6.ipv6.outiface_mask; + a_outiface = a->fw6.ipv6.outiface; + + b_iniface_mask = b->fw6.ipv6.iniface_mask; + b_iniface = b->fw6.ipv6.iniface; + b_outiface_mask = b->fw6.ipv6.outiface_mask; + b_outiface = b->fw6.ipv6.outiface; + + break; + default: return false; } for (i = 0; i < IFNAMSIZ; i++) { - if (a->fw.ip.iniface_mask[i] != b->fw.ip.iniface_mask[i]) { + if (a_iniface_mask[i] != b_iniface_mask[i]) { DEBUGP("different iniface mask %x, %x (%d)\n", - a->fw.ip.iniface_mask[i] & 0xff, b->fw.ip.iniface_mask[i] & 0xff, i); + a_iniface_mask[i] & 0xff, b_iniface_mask[i] & 0xff, i); return false; } - if ((a->fw.ip.iniface[i] & a->fw.ip.iniface_mask[i]) - != (b->fw.ip.iniface[i] & b->fw.ip.iniface_mask[i])) { + if ((a_iniface[i] & a_iniface_mask[i]) + != (b_iniface[i] & b_iniface_mask[i])) { DEBUGP("different iniface\n"); return false; } - if (a->fw.ip.outiface_mask[i] != b->fw.ip.outiface_mask[i]) { + if (a_outiface_mask[i] != b_outiface_mask[i]) { DEBUGP("different outiface mask\n"); return false; } - if ((a->fw.ip.outiface[i] & a->fw.ip.outiface_mask[i]) - != (b->fw.ip.outiface[i] & b->fw.ip.outiface_mask[i])) { + if ((a_outiface[i] & a_outiface_mask[i]) + != (b_outiface[i] & b_outiface_mask[i])) { DEBUGP("different outiface\n"); return false; } @@ -1962,13 +2124,16 @@ is_same(const struct iptables_command_state *a, const struct iptables_command_st static void nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) + int family, struct iptables_command_state *cs) { uint8_t key = nft_rule_expr_get_u8(e, NFT_EXPR_META_KEY); uint32_t value; const char *name; const void *ifname; + char *iniface, *outiface; + unsigned char *iniface_mask, *outiface_mask; size_t len; + uint8_t *invflags; e = nft_rule_expr_iter_next(iter); if (e == NULL) @@ -1980,58 +2145,75 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, return; } + switch (family) { + case AF_INET: + iniface = cs->fw.ip.iniface; + outiface = cs->fw.ip.outiface; + iniface_mask = cs->fw.ip.iniface_mask; + outiface_mask = cs->fw.ip.outiface_mask; + invflags = &cs->fw.ip.invflags; + break; + case AF_INET6: + iniface = cs->fw6.ipv6.iniface; + outiface = cs->fw6.ipv6.outiface; + iniface_mask = cs->fw6.ipv6.iniface_mask; + outiface_mask = cs->fw6.ipv6.outiface_mask; + invflags = &cs->fw6.ipv6.invflags; + break; + default: + return; + } + switch(key) { case NFT_META_IIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_IN; + *invflags |= IPT_INV_VIA_IN; - if_indextoname(value, cs->fw.ip.iniface); + if_indextoname(value, iniface); - memset(cs->fw.ip.iniface_mask, 0xff, - strlen(cs->fw.ip.iniface)+1); + memset(iniface_mask, 0xff, strlen(iniface)+1); break; case NFT_META_OIF: value = nft_rule_expr_get_u32(e, NFT_EXPR_CMP_DATA); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + *invflags |= IPT_INV_VIA_OUT; - if_indextoname(value, cs->fw.ip.outiface); + if_indextoname(value, outiface); - memset(cs->fw.ip.outiface_mask, 0xff, - strlen(cs->fw.ip.outiface)+1); + memset(outiface_mask, 0xff, strlen(outiface)+1); break; case NFT_META_IIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_IN; + *invflags |= IPT_INV_VIA_IN; - memcpy(cs->fw.ip.iniface, ifname, len); - cs->fw.ip.iniface[len] = '\0'; + memcpy(iniface, ifname, len); + iniface[len] = '\0'; /* If zero, then this is an interface mask */ - if (if_nametoindex(cs->fw.ip.iniface) == 0) { - cs->fw.ip.iniface[len] = '+'; - cs->fw.ip.iniface[len+1] = '\0'; + if (if_nametoindex(iniface) == 0) { + iniface[len] = '+'; + iniface[len+1] = '\0'; } - memset(cs->fw.ip.iniface_mask, 0xff, len); + memset(iniface_mask, 0xff, len); break; case NFT_META_OIFNAME: ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len); if (nft_rule_expr_get_u8(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ) - cs->fw.ip.invflags |= IPT_INV_VIA_OUT; + *invflags |= IPT_INV_VIA_OUT; - memcpy(cs->fw.ip.outiface, ifname, len); - cs->fw.ip.outiface[len] = '\0'; + memcpy(outiface, ifname, len); + outiface[len] = '\0'; /* If zero, then this is an interface mask */ - if (if_nametoindex(cs->fw.ip.outiface) == 0) { - cs->fw.ip.outiface[len] = '+'; - cs->fw.ip.outiface[len+1] = '\0'; + if (if_nametoindex(outiface) == 0) { + outiface[len] = '+'; + outiface[len+1] = '\0'; } - memset(cs->fw.ip.outiface_mask, 0xff, len); + memset(outiface_mask, 0xff, len); break; default: DEBUGP("unknown meta key %d\n", key); @@ -2040,17 +2222,13 @@ nft_parse_meta(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, } static void -nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) +nft_parse_payload_ipv4(uint32_t offset, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) { - uint32_t offset; - bool inv; - - offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); - switch(offset) { struct in_addr addr; uint8_t proto; + bool inv; case offsetof(struct iphdr, saddr): get_cmp_data(iter, &addr, sizeof(addr), &inv); @@ -2084,6 +2262,56 @@ nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, } } +static void +nft_parse_payload_ipv6(uint32_t offset, struct nft_rule_expr_iter *iter, + struct iptables_command_state *cs) +{ + switch (offset) { + struct in6_addr addr; + uint8_t proto; + bool inv; + + case offsetof(struct ip6_hdr, ip6_src): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.src.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_SRCIP; + break; + case offsetof(struct ip6_hdr, ip6_dst): + get_cmp_data(iter, &addr, sizeof(addr), &inv); + memcpy(cs->fw6.ipv6.dst.s6_addr, &addr, sizeof(addr)); + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_DSTIP; + break; + case offsetof(struct ip6_hdr, ip6_nxt): + get_cmp_data(iter, &proto, sizeof(proto), &inv); + cs->fw6.ipv6.proto = proto; + if (inv) + cs->fw6.ipv6.invflags |= IPT_INV_PROTO; + default: + DEBUGP("unknown payload offset %d\n", offset); + break; + } +} + +static void +nft_parse_payload(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, + int family, struct iptables_command_state *cs) +{ + uint32_t offset; + + offset = nft_rule_expr_get_u32(e, NFT_EXPR_PAYLOAD_OFFSET); + + switch (family) { + case AF_INET: + nft_parse_payload_ipv4(offset, iter, cs); + break; + case AF_INET6: + nft_parse_payload_ipv6(offset, iter, cs); + break; + } +} + static void nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, struct xt_counters *counters) @@ -2094,7 +2322,7 @@ nft_parse_counter(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, static void nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, - struct iptables_command_state *cs) + int family, struct iptables_command_state *cs) { int verdict = nft_rule_expr_get_u32(e, NFT_EXPR_IMM_VERDICT); const char *chain = nft_rule_expr_get_str(e, NFT_EXPR_IMM_CHAIN); @@ -2111,7 +2339,10 @@ nft_parse_immediate(struct nft_rule_expr *e, struct nft_rule_expr_iter *iter, cs->jumpto = "RETURN"; return; case NFT_GOTO: - cs->fw.ip.flags |= IPT_F_GOTO; + if (family == AF_INET) + cs->fw.ip.flags |= IPT_F_GOTO; + else + cs->fw6.ipv6.flags |= IPT_F_GOTO; case NFT_JUMP: cs->jumpto = chain; return; @@ -2124,6 +2355,7 @@ nft_rule_to_iptables_command_state(struct nft_rule *r, { struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; + int family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); iter = nft_rule_expr_iter_create(r); if (iter == NULL) @@ -2137,11 +2369,11 @@ nft_rule_to_iptables_command_state(struct nft_rule *r, if (strcmp(name, "counter") == 0) { nft_parse_counter(expr, iter, &cs->counters); } else if (strcmp(name, "payload") == 0) { - nft_parse_payload(expr, iter, cs); + nft_parse_payload(expr, iter, family, cs); } else if (strcmp(name, "meta") == 0) { - nft_parse_meta(expr, iter, cs); + nft_parse_meta(expr, iter, family, cs); } else if (strcmp(name, "immediate") == 0) { - nft_parse_immediate(expr, iter, cs); + nft_parse_immediate(expr, iter, family, cs); } expr = nft_rule_expr_iter_next(iter); @@ -2348,7 +2580,7 @@ __nft_rule_del(struct nft_handle *h, struct nft_rule *r) struct nlmsghdr *nlh; int ret; - nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, AF_INET, + nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_DELRULE, h->family, NLM_F_ACK, h->seq); nft_rule_nlmsg_build_payload(nlh, r); @@ -2421,7 +2653,7 @@ nft_rule_find(struct nft_rule_list *list, const char *chain, const char *table, nft_rule_to_iptables_command_state(r, &this); - if (!is_same(cs, &this)) + if (!is_same(nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY), cs, &this)) goto next; if (!find_matches(cs->matches, r)) { @@ -2683,6 +2915,73 @@ print_match(struct nft_rule_expr *expr, int numeric) free(m); } +static void +print_ipv4_addr(const struct iptables_command_state *cs, unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); + else + strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); + strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + +static void +print_ipv6_addr(const struct iptables_command_state *cs, unsigned int format) +{ + char buf[BUFSIZ]; + + fputc(cs->fw6.ipv6.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.src) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.src)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.src)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.smsk)); + printf(FMT("%-19s ","%s "), buf); + } + + + fputc(cs->fw6.ipv6.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dst) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + strcpy(buf, + xtables_ip6addr_to_numeric(&cs->fw6.ipv6.dst)); + else + strcpy(buf, + xtables_ip6addr_to_anyname(&cs->fw6.ipv6.dst)); + strcat(buf, xtables_ip6mask_to_numeric(&cs->fw6.ipv6.dmsk)); + printf(FMT("%-19s ","-> %s"), buf); + } +} + static void print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, unsigned int num, unsigned int format) @@ -2690,8 +2989,11 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, const struct xtables_target *target = NULL; const char *targname = NULL; const void *targinfo = NULL; - uint8_t flags; - char buf[BUFSIZ]; + int family; + uint8_t flags = 0; + uint8_t invflags = 0; + uint8_t proto = 0; + const char *iniface = NULL, *outiface = NULL; struct nft_rule_expr_iter *iter; struct nft_rule_expr *expr; struct xt_entry_target *t; @@ -2709,8 +3011,10 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME); if (strcmp(name, "target") == 0) { - targname = nft_rule_expr_get_str(expr, NFT_EXPR_TG_NAME); - targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, &target_len); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_TG_NAME); + targinfo = nft_rule_expr_get(expr, NFT_EXPR_TG_INFO, + &target_len); break; } else if (strcmp(name, "immediate") == 0) { uint32_t verdict = @@ -2727,10 +3031,12 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, targname = "RETURN"; break; case NFT_GOTO: - targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); break; case NFT_JUMP: - targname = nft_rule_expr_get_str(expr, NFT_EXPR_IMM_CHAIN); + targname = nft_rule_expr_get_str(expr, + NFT_EXPR_IMM_CHAIN); break; } } @@ -2738,7 +3044,24 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, } nft_rule_expr_iter_destroy(iter); - flags = cs->fw.ip.flags; + family = nft_rule_attr_get_u8(r, NFT_RULE_ATTR_FAMILY); + + switch (family) { + case AF_INET: + flags = cs->fw.ip.flags; + invflags = flags = cs->fw.ip.invflags; + proto = cs->fw.ip.proto; + iniface = cs->fw.ip.iniface; + outiface = cs->fw.ip.outiface; + break; + case AF_INET6: + flags = cs->fw6.ipv6.flags; + invflags = cs->fw6.ipv6.invflags; + proto = cs->fw6.ipv6.proto; + iniface = cs->fw6.ipv6.iniface; + outiface = cs->fw6.ipv6.outiface; + break; + } if (format & FMT_LINENUMBERS) printf(FMT("%-4u ", "%u "), num); @@ -2751,82 +3074,68 @@ print_firewall(const struct iptables_command_state *cs, struct nft_rule *r, if (!(format & FMT_NOTARGET)) printf(FMT("%-9s ", "%s "), targname ? targname : ""); - fputc(cs->fw.ip.invflags & XT_INV_PROTO ? '!' : ' ', stdout); + fputc(invflags & XT_INV_PROTO ? '!' : ' ', stdout); { const char *pname = - proto_to_name(cs->fw.ip.proto, format&FMT_NUMERIC); + proto_to_name(proto, format&FMT_NUMERIC); if (pname) printf(FMT("%-5s", "%s "), pname); else - printf(FMT("%-5hu", "%hu "), cs->fw.ip.proto); + printf(FMT("%-5hu", "%hu "), proto); } if (format & FMT_OPTIONS) { if (format & FMT_NOTABLE) fputs("opt ", stdout); - fputc(cs->fw.ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(invflags & IPT_INV_FRAG ? '!' : '-', stdout); fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); fputc(' ', stdout); } if (format & FMT_VIA) { char iface[IFNAMSIZ+2]; - if (cs->fw.ip.invflags & IPT_INV_VIA_IN) { + if (invflags & IPT_INV_VIA_IN) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; - if (cs->fw.ip.iniface[0] != '\0') { - strcat(iface, cs->fw.ip.iniface); + if (iniface[0] != '\0') { + strcat(iface, iniface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT(" %-6s ","in %s "), iface); - if (cs->fw.ip.invflags & IPT_INV_VIA_OUT) { + if (invflags & IPT_INV_VIA_OUT) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; - if (cs->fw.ip.outiface[0] != '\0') { - strcat(iface, cs->fw.ip.outiface); + if (outiface[0] != '\0') { + strcat(iface, outiface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf(FMT("%-6s ","out %s "), iface); } - fputc(cs->fw.ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); - if (cs->fw.ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","%s "), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.src)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.src)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.smsk)); - printf(FMT("%-19s ","%s "), buf); - } - fputc(cs->fw.ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); - if (cs->fw.ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) - printf(FMT("%-19s ","-> %s"), "anywhere"); - else { - if (format & FMT_NUMERIC) - strcpy(buf, xtables_ipaddr_to_numeric(&cs->fw.ip.dst)); - else - strcpy(buf, xtables_ipaddr_to_anyname(&cs->fw.ip.dst)); - strcat(buf, xtables_ipmask_to_numeric(&cs->fw.ip.dmsk)); - printf(FMT("%-19s ","-> %s"), buf); + switch (family) { + case AF_INET: + print_ipv4_addr(cs, format); + break; + case AF_INET6: + print_ipv6_addr(cs, format); + break; } if (format & FMT_NOTABLE) fputs(" ", stdout); #ifdef IPT_F_GOTO - if(cs->fw.ip.flags & IPT_F_GOTO) + if(flags & IPT_F_GOTO) printf("[goto] "); #endif diff --git a/iptables/nft.h b/iptables/nft.h index aed2498b..1bd9ccce 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -4,6 +4,7 @@ #include "xshared.h" struct nft_handle { + int family; struct mnl_socket *nl; uint32_t portid; uint32_t seq; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index 30ea813c..e83eacc3 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -161,7 +161,9 @@ static void add_param_to_argv(char *parsestart) int xtables_restore_main(int argc, char *argv[]) { - struct nft_handle h; + struct nft_handle h = { + .family = AF_INET, /* default to IPv4 */ + }; char buffer[10240]; int c; char curtable[XT_TABLE_MAXNAMELEN + 1]; diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index 046c948d..05d06b1e 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -76,7 +76,9 @@ int xtables_save_main(int argc, char *argv[]) { const char *tablename = NULL; - struct nft_handle h; + struct nft_handle h = { + .family = AF_INET, /* default to AF_INET */ + }; int c; xtables_globals.program_name = "xtables-save"; diff --git a/iptables/xtables.c b/iptables/xtables.c index d6045906..9c59b7d8 100644 --- a/iptables/xtables.c +++ b/iptables/xtables.c @@ -204,6 +204,20 @@ enum { IPT_DOTTED_MASK }; +struct addr_mask { + union { + struct in_addr *v4; + struct in6_addr *v6; + } addr; + + unsigned int naddrs; + + union { + struct in_addr *v4; + struct in6_addr *v6; + } mask; +}; + static void __attribute__((noreturn)) exit_tryhelp(int status) { @@ -370,6 +384,15 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds, * return global static data. */ +/* These are invalid numbers as upper layer protocol */ +static int is_exthdr(uint16_t proto) +{ + return (proto == IPPROTO_ROUTING || + proto == IPPROTO_FRAGMENT || + proto == IPPROTO_AH || + proto == IPPROTO_DSTOPTS); +} + /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ /* Can't be zero. */ static int @@ -430,26 +453,38 @@ static int add_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, - const struct in_addr saddrs[], - const struct in_addr smasks[], - unsigned int ndaddrs, - const struct in_addr daddrs[], - const struct in_addr dmasks[], + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h, bool append) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; - ret = nft_rule_add(h, chain, table, - cs, append, 0, verbose); + ret = nft_rule_add(h, chain, table, + cs, append, 0, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_add(h, chain, table, + cs, append, 0, verbose); + } } } @@ -460,14 +495,23 @@ static int replace_entry(const char *chain, const char *table, struct iptables_command_state *cs, unsigned int rulenum, - const struct in_addr *saddr, const struct in_addr *smask, - const struct in_addr *daddr, const struct in_addr *dmask, + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { - cs->fw.ip.src.s_addr = saddr->s_addr; - cs->fw.ip.dst.s_addr = daddr->s_addr; - cs->fw.ip.smsk.s_addr = smask->s_addr; - cs->fw.ip.dmsk.s_addr = dmask->s_addr; + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4->s_addr; + cs->fw.ip.dst.s_addr = d.addr.v4->s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4->s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4->s_addr; + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, s.addr.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dst, d.addr.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, s.mask.v6, sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, d.mask.v6, sizeof(struct in6_addr)); + } else + return 1; return nft_rule_replace(h, chain, table, cs, rulenum, verbose); } @@ -475,25 +519,38 @@ replace_entry(const char *chain, const char *table, static int delete_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, - const struct in_addr saddrs[], - const struct in_addr smasks[], - unsigned int ndaddrs, - const struct in_addr daddrs[], - const struct in_addr dmasks[], + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; - ret = nft_rule_delete(h, chain, table, cs, verbose); + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; + ret = nft_rule_delete(h, chain, + table, cs, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_delete(h, chain, + table, cs, verbose); + } } } @@ -503,21 +560,37 @@ delete_entry(const char *chain, const char *table, static int check_entry(const char *chain, const char *table, struct iptables_command_state *cs, - unsigned int nsaddrs, const struct in_addr *saddrs, - const struct in_addr *smasks, unsigned int ndaddrs, - const struct in_addr *daddrs, const struct in_addr *dmasks, + int family, + const struct addr_mask s, + const struct addr_mask d, bool verbose, struct nft_handle *h) { unsigned int i, j; int ret = 1; - for (i = 0; i < nsaddrs; i++) { - cs->fw.ip.src.s_addr = saddrs[i].s_addr; - cs->fw.ip.smsk.s_addr = smasks[i].s_addr; - for (j = 0; j < ndaddrs; j++) { - cs->fw.ip.dst.s_addr = daddrs[j].s_addr; - cs->fw.ip.dmsk.s_addr = dmasks[j].s_addr; - ret = nft_rule_check(h, chain, table, cs, verbose); + for (i = 0; i < s.naddrs; i++) { + if (family == AF_INET) { + cs->fw.ip.src.s_addr = s.addr.v4[i].s_addr; + cs->fw.ip.smsk.s_addr = s.mask.v4[i].s_addr; + for (j = 0; j < d.naddrs; j++) { + cs->fw.ip.dst.s_addr = d.addr.v4[j].s_addr; + cs->fw.ip.dmsk.s_addr = d.mask.v4[j].s_addr; + ret = nft_rule_check(h, chain, + table, cs, verbose); + } + } else if (family == AF_INET6) { + memcpy(&cs->fw6.ipv6.src, + &s.addr.v6[i], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.smsk, + &s.mask.v6[i], sizeof(struct in6_addr)); + for (j = 0; j < d.naddrs; j++) { + memcpy(&cs->fw6.ipv6.dst, + &d.addr.v6[j], sizeof(struct in6_addr)); + memcpy(&cs->fw6.ipv6.dmsk, + &d.mask.v6[j], sizeof(struct in6_addr)); + ret = nft_rule_check(h, chain, + table, cs, verbose); + } } } @@ -673,9 +746,8 @@ static void command_match(struct iptables_command_state *cs) int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) { struct iptables_command_state cs; - unsigned int nsaddrs = 0, ndaddrs = 0; - struct in_addr *saddrs = NULL, *smasks = NULL; - struct in_addr *daddrs = NULL, *dmasks = NULL; + struct addr_mask s; + struct addr_mask d; int verbose = 0; const char *chain = NULL; @@ -687,7 +759,21 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) struct xtables_match *m; struct xtables_rule_match *matchp; struct xtables_target *t; - unsigned long long cnt; + unsigned long long pcnt_cnt = 0, bcnt_cnt = 0; + + int family = AF_INET; + u_int16_t proto = 0; + u_int8_t flags = 0, invflags = 0; + char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; + unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; + bool goto_set = false; + + memset(&s, 0, sizeof(s)); + memset(&d, 0, sizeof(d)); + memset(iniface, 0, sizeof(iniface)); + memset(outiface, 0, sizeof(outiface)); + memset(iniface_mask, 0, sizeof(iniface_mask)); + memset(outiface_mask, 0, sizeof(outiface_mask)); memset(&cs, 0, sizeof(cs)); cs.jumpto = ""; @@ -877,7 +963,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) * Option selection */ case 'p': - set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_PROTOCOL, &invflags, cs.invert); /* Canonicalize into lower case */ @@ -885,31 +971,30 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) *cs.protocol = tolower(*cs.protocol); cs.protocol = optarg; - cs.fw.ip.proto = xtables_parse_protocol(cs.protocol); + proto = xtables_parse_protocol(cs.protocol); - if (cs.fw.ip.proto == 0 - && (cs.fw.ip.invflags & XT_INV_PROTO)) + if (proto == 0 && (invflags & XT_INV_PROTO)) xtables_error(PARAMETER_PROBLEM, "rule would never match protocol"); break; case 's': - set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_SOURCE, &invflags, cs.invert); shostnetworkmask = optarg; break; case 'd': - set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_DESTINATION, &invflags, cs.invert); dhostnetworkmask = optarg; break; #ifdef IPT_F_GOTO case 'g': - set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_JUMP, &invflags, cs.invert); - cs.fw.ip.flags |= IPT_F_GOTO; + goto_set = true; cs.jumpto = parse_target(optarg); break; #endif @@ -924,11 +1009,11 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_VIANAMEIN, &invflags, cs.invert); xtables_parse_interface(optarg, - cs.fw.ip.iniface, - cs.fw.ip.iniface_mask); + iniface, + iniface_mask); break; case 'o': @@ -936,23 +1021,28 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "Empty interface is likely to be " "undesired"); - set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_VIANAMEOUT, &invflags, cs.invert); xtables_parse_interface(optarg, - cs.fw.ip.outiface, - cs.fw.ip.outiface_mask); + outiface, + outiface_mask); break; case 'f': - set_option(&cs.options, OPT_FRAGMENT, &cs.fw.ip.invflags, + if (family == AF_INET6) { + xtables_error(PARAMETER_PROBLEM, + "`-f' is not supported in IPv6, " + "use -m frag instead"); + } + set_option(&cs.options, OPT_FRAGMENT, &invflags, cs.invert); - cs.fw.ip.flags |= IPT_F_FRAG; + flags |= IPT_F_FRAG; break; case 'v': if (!verbose) set_option(&cs.options, OPT_VERBOSE, - &cs.fw.ip.invflags, cs.invert); + &invflags, cs.invert); verbose++; break; @@ -961,7 +1051,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'n': - set_option(&cs.options, OPT_NUMERIC, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_NUMERIC, &invflags, cs.invert); break; @@ -973,7 +1063,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) break; case 'x': - set_option(&cs.options, OPT_EXPANDED, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_EXPANDED, &invflags, cs.invert); break; @@ -986,7 +1076,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) exit(0); case '0': - set_option(&cs.options, OPT_LINENUMBERS, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_LINENUMBERS, &invflags, cs.invert); break; @@ -996,7 +1086,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) case 'c': - set_option(&cs.options, OPT_COUNTERS, &cs.fw.ip.invflags, + set_option(&cs.options, OPT_COUNTERS, &invflags, cs.invert); pcnt = optarg; bcnt = strchr(pcnt + 1, ','); @@ -1010,29 +1100,25 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); - if (sscanf(pcnt, "%llu", &cnt) != 1) + if (sscanf(pcnt, "%llu", &pcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); - cs.counters.pcnt = cnt; - if (sscanf(bcnt, "%llu", &cnt) != 1) + if (sscanf(bcnt, "%llu", &bcnt_cnt) != 1) xtables_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); - cs.counters.bcnt = cnt; break; case '4': - /* This is indeed the IPv4 iptables */ + if (family != AF_INET) + exit_tryhelp(2); break; case '6': - /* This is not the IPv6 ip6tables */ - if (line != -1) - return 1; /* success: line ignored */ - fprintf(stderr, "This is the IPv4 version of iptables.\n"); - exit_tryhelp(2); + family = AF_INET6; + break; case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { @@ -1079,27 +1165,109 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) xtables_error(PARAMETER_PROBLEM, "nothing appropriate following !"); - if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) { - if (!(cs.options & OPT_DESTINATION)) - dhostnetworkmask = "0.0.0.0/0"; - if (!(cs.options & OPT_SOURCE)) - shostnetworkmask = "0.0.0.0/0"; - } + switch (family) { + case AF_INET: + cs.fw.ip.proto = proto; + cs.fw.ip.invflags = invflags; + cs.fw.ip.flags = flags; - if (shostnetworkmask) - xtables_ipparse_multiple(shostnetworkmask, &saddrs, - &smasks, &nsaddrs); + strncpy(cs.fw.ip.iniface, iniface, IFNAMSIZ); + memcpy(cs.fw.ip.iniface_mask, + iniface_mask, IFNAMSIZ*sizeof(unsigned char)); - if (dhostnetworkmask) - xtables_ipparse_multiple(dhostnetworkmask, &daddrs, - &dmasks, &ndaddrs); + strncpy(cs.fw.ip.outiface, outiface, IFNAMSIZ); + memcpy(cs.fw.ip.outiface_mask, + outiface_mask, IFNAMSIZ*sizeof(unsigned char)); - if ((nsaddrs > 1 || ndaddrs > 1) && - (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) - xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple" - " source or destination IP addresses"); + if (goto_set) + cs.fw.ip.flags |= IPT_F_GOTO; - if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + cs.counters.pcnt = pcnt_cnt; + cs.counters.bcnt = bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs.options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(cs.options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + xtables_ipparse_multiple(shostnetworkmask, &s.addr.v4, + &s.mask.v4, &s.naddrs); + if (dhostnetworkmask) + xtables_ipparse_multiple(dhostnetworkmask, &d.addr.v4, + &d.mask.v4, &d.naddrs); + + if ((s.naddrs > 1 || 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"); + break; + case AF_INET6: + if (proto != 0) + flags |= IP6T_F_PROTO; + + cs.fw6.ipv6.proto = proto; + cs.fw6.ipv6.invflags = invflags; + cs.fw6.ipv6.flags = flags; + + if (flags & IPT_F_FRAG) + xtables_error(PARAMETER_PROBLEM, + "-f is not valid on IPv6"); + + if (is_exthdr(cs.fw6.ipv6.proto) + && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0) + fprintf(stderr, + "Warning: never matched protocol: %s. " + "use extension match instead.\n", + cs.protocol); + + strncpy(cs.fw6.ipv6.iniface, iniface, IFNAMSIZ); + memcpy(cs.fw6.ipv6.iniface_mask, + iniface_mask, IFNAMSIZ*sizeof(unsigned char)); + + strncpy(cs.fw6.ipv6.outiface, outiface, IFNAMSIZ); + memcpy(cs.fw6.ipv6.outiface_mask, + outiface_mask, IFNAMSIZ*sizeof(unsigned char)); + + if (goto_set) + cs.fw6.ipv6.flags |= IP6T_F_GOTO; + + cs.fw6.counters.pcnt = pcnt_cnt; + cs.fw6.counters.bcnt = bcnt_cnt; + + if (command & (CMD_REPLACE | CMD_INSERT | + CMD_DELETE | CMD_APPEND | CMD_CHECK)) { + if (!(cs.options & OPT_DESTINATION)) + dhostnetworkmask = "::0/0"; + if (!(cs.options & OPT_SOURCE)) + shostnetworkmask = "::0/0"; + } + + if (shostnetworkmask) + xtables_ip6parse_multiple(shostnetworkmask, &s.addr.v6, + &s.mask.v6, &s.naddrs); + if (dhostnetworkmask) + xtables_ip6parse_multiple(dhostnetworkmask, &d.addr.v6, + &d.mask.v6, &d.naddrs); + + if ((s.naddrs > 1 || d.naddrs > 1) && + (cs.fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) + xtables_error(PARAMETER_PROBLEM, + "! not allowed with multiple" + " source or destination IP addresses"); + break; + default: + exit_tryhelp(2); + break; + } + + h->family = family; + + if (command == CMD_REPLACE && (s.naddrs != 1 || d.naddrs != 1)) xtables_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); @@ -1144,39 +1312,30 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) switch (command) { case CMD_APPEND: - ret = add_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, + ret = add_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h, true); break; case CMD_DELETE: - ret = delete_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + ret = delete_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h); break; case CMD_DELETE_NUM: ret = nft_rule_delete_num(h, chain, *table, rulenum - 1, verbose); break; case CMD_CHECK: - ret = check_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + ret = check_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h); break; case CMD_REPLACE: /* FIXME replace at rulenum */ ret = replace_entry(chain, *table, &cs, rulenum - 1, - saddrs, smasks, daddrs, dmasks, - cs.options&OPT_VERBOSE, h); + family, s, d, cs.options&OPT_VERBOSE, h); break; case CMD_INSERT: /* FIXME insert at rulenum */ - ret = add_entry(chain, *table, &cs, - nsaddrs, saddrs, smasks, - ndaddrs, daddrs, dmasks, - cs.options&OPT_VERBOSE, h, false); + ret = add_entry(chain, *table, &cs, family, + s, d, cs.options&OPT_VERBOSE, h, false); break; case CMD_FLUSH: ret = nft_rule_flush(h, chain, *table); @@ -1242,10 +1401,17 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table) clear_rule_matches(&cs.matches); - free(saddrs); - free(smasks); - free(daddrs); - free(dmasks); + if (family == AF_INET) { + free(s.addr.v4); + free(s.mask.v4); + free(d.addr.v4); + free(d.mask.v4); + } else if (family == AF_INET6) { + free(s.addr.v6); + free(s.mask.v6); + free(d.addr.v6); + free(d.mask.v6); + } xtables_free_opts(1); return ret; -- cgit v1.2.3