diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml index 1d163130241a..56c13705243b 100644 --- a/Documentation/netlink/specs/conntrack.yaml +++ b/Documentation/netlink/specs/conntrack.yaml @@ -393,6 +393,12 @@ attribute-sets: name: reply-flags type: u32 doc: bitmask of tuple fields to filter on, reply direction + - + name: zone + type: flag + doc: >- + Filter on conntrack zone id; requires the top-level zone + (``CTA_ZONE``) attribute. - name: conntrack-attrs attributes: diff --git a/Documentation/networking/nf_conntrack-sysctl.rst b/Documentation/networking/nf_conntrack-sysctl.rst index 35f889259fcd..4426cce2a73f 100644 --- a/Documentation/networking/nf_conntrack-sysctl.rst +++ b/Documentation/networking/nf_conntrack-sysctl.rst @@ -44,7 +44,7 @@ nf_conntrack_events - BOOLEAN nf_conntrack_expect_max - INTEGER Maximum size of expectation table. Default value is - nf_conntrack_buckets / 256. Minimum is 1. + nf_conntrack_buckets / 64. Minimum is 1. nf_conntrack_frag6_high_thresh - INTEGER default 262144 diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h index 883c414b768e..0f5bbb14a25a 100644 --- a/include/net/netfilter/nf_conntrack_seqadj.h +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -33,6 +33,7 @@ static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct) int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, s32 off); +void nf_ct_seqadj_reset(struct nf_conn *ct, enum ip_conntrack_info ctinfo); int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, __be32 seq, s32 off); void nf_ct_tcp_seqadj_set(struct sk_buff *skb, struct nf_conn *ct, diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h index a6a0bf4a247e..7fd78394d1e7 100644 --- a/include/net/netns/netfilter.h +++ b/include/net/netns/netfilter.h @@ -33,5 +33,7 @@ struct netns_nf { #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) unsigned int defrag_ipv6_users; #endif + unsigned int hook_base_seq; + unsigned int nat_hook_base_seq; }; #endif diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 43233af75b9d..985f9c08d3e9 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -285,6 +285,7 @@ enum ctattr_filter { CTA_FILTER_UNSPEC, CTA_FILTER_ORIG_FLAGS, CTA_FILTER_REPLY_FLAGS, + CTA_FILTER_ZONE, __CTA_FILTER_MAX }; #define CTA_FILTER_MAX (__CTA_FILTER_MAX - 1) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index db307fa49f3f..cf747e1e17a3 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -58,7 +58,7 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap, /* * Unfortunately, _b and _mask are not aligned to an int (or long int) * Some arches dont care, unrolling the loop is a win on them. - * For other arches, we only have a 16bit alignement. + * For other arches, we only have a 16bit alignment. */ static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask) { diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 675a1034b340..940dea2663e9 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -386,6 +386,15 @@ static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf) #endif } +static void bump_hook_base_seq(struct net *net) +{ + unsigned int base_seq = READ_ONCE(net->nf.hook_base_seq); + + while (++base_seq == 0) + ; + smp_store_release(&net->nf.hook_base_seq, base_seq); +} + static int __nf_register_net_hook(struct net *net, int pf, const struct nf_hook_ops *reg) { @@ -430,6 +439,7 @@ static int __nf_register_net_hook(struct net *net, int pf, if (!IS_ERR(new_hooks)) { hooks_validate(new_hooks); rcu_assign_pointer(*pp, new_hooks); + bump_hook_base_seq(net); } mutex_unlock(&nf_hook_mutex); @@ -483,6 +493,7 @@ static void __nf_unregister_net_hook(struct net *net, int pf, { struct nf_hook_entries __rcu **pp; struct nf_hook_entries *p; + bool found; pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); if (!pp) @@ -496,7 +507,8 @@ static void __nf_unregister_net_hook(struct net *net, int pf, return; } - if (nf_remove_net_hook(p, reg)) { + found = nf_remove_net_hook(p, reg); + if (found) { #ifdef CONFIG_NETFILTER_INGRESS if (nf_ingress_hook(reg, pf)) net_dec_ingress_queue(); @@ -511,6 +523,8 @@ static void __nf_unregister_net_hook(struct net *net, int pf, } p = __nf_hook_entries_try_shrink(p, pp); + if (found) + bump_hook_base_seq(net); mutex_unlock(&nf_hook_mutex); if (!p) return; @@ -784,6 +798,8 @@ static int __net_init netfilter_net_init(struct net *net) return -ENOMEM; } #endif + net->nf.hook_base_seq = 1; + net->nf.nat_hook_base_seq = 1; return 0; } diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 0a86a170ba90..4aeb767088cf 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1426,7 +1426,7 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, return -IPSET_ERR_EXIST_SETNAME2; /* Features must not change. - * Not an artifical restriction anymore, as we must prevent + * Not an artificial restriction anymore, as we must prevent * possible loops created by swapping in setlist type of sets. */ if (!(from->type->features == to->type->features && diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 5383aeafb0ae..4dacd5af65db 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1344,7 +1344,7 @@ static void set_mcast_pmtudisc(struct sock *sk, int val) } /* - * Specifiy default interface for outgoing multicasts + * Specify default interface for outgoing multicasts */ static int set_mcast_if(struct sock *sk, struct net_device *dev) { diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index d0d9e5ea84a0..f9b8927327ba 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1169,7 +1169,6 @@ nf_ct_resolve_clash(struct sk_buff *skb, struct nf_conntrack_tuple_hash *h, return ret; drop: - NF_CT_STAT_INC(net, drop); NF_CT_STAT_INC(net, insert_failed); return NF_DROP; } @@ -1259,7 +1258,6 @@ __nf_conntrack_confirm(struct sk_buff *skb) if (chainlen++ > max_chainlen) { chaintoolong: NF_CT_STAT_INC(net, chaintoolong); - NF_CT_STAT_INC(net, insert_failed); ret = NF_DROP; goto dying; } @@ -2067,9 +2065,10 @@ nf_conntrack_in(struct sk_buff *skb, const struct nf_hook_state *state) if (ret == -NF_REPEAT) goto repeat; - NF_CT_STAT_INC_ATOMIC(state->net, invalid); if (ret == NF_DROP) NF_CT_STAT_INC_ATOMIC(state->net, drop); + else + NF_CT_STAT_INC_ATOMIC(state->net, invalid); ret = -ret; goto out; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4e5d7c701436..4b6abe36028e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -911,6 +911,7 @@ struct ctnetlink_filter { static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = { [CTA_FILTER_ORIG_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL), [CTA_FILTER_REPLY_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL), + [CTA_FILTER_ZONE] = { .type = NLA_FLAG }, }; static int ctnetlink_parse_filter(const struct nlattr *attr, @@ -930,6 +931,9 @@ static int ctnetlink_parse_filter(const struct nlattr *attr, if (tb[CTA_FILTER_REPLY_FLAGS]) filter->reply_flags = nla_get_u32(tb[CTA_FILTER_REPLY_FLAGS]); + if (tb[CTA_FILTER_ZONE]) + filter->zone_filter = true; + return 0; } @@ -1006,6 +1010,7 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) if (err) goto err_filter; + /* CTA_ZONE is allowed without CTA_FILTER_ZONE. */ if (cda[CTA_ZONE]) { err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone); if (err < 0) @@ -1020,6 +1025,12 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) if (err < 0) goto err_filter; + /* CTA_FILTER_ZONE cannot be set without CTA_ZONE. */ + if (filter->zone_filter && !cda[CTA_ZONE]) { + err = -EINVAL; + goto err_filter; + } + if (filter->orig_flags) { if (!cda[CTA_TUPLE_ORIG]) { err = -EINVAL; @@ -1098,13 +1109,13 @@ static int ctnetlink_filter_match_tuple(struct nf_conntrack_tuple *filter_tuple, break; case NFPROTO_IPV6: if ((flags & CTA_FILTER_FLAG(CTA_IP_SRC)) && - !ipv6_addr_cmp(&filter_tuple->src.u3.in6, - &ct_tuple->src.u3.in6)) + !ipv6_addr_equal(&filter_tuple->src.u3.in6, + &ct_tuple->src.u3.in6)) return 0; if ((flags & CTA_FILTER_FLAG(CTA_IP_DST)) && - !ipv6_addr_cmp(&filter_tuple->dst.u3.in6, - &ct_tuple->dst.u3.in6)) + !ipv6_addr_equal(&filter_tuple->dst.u3.in6, + &ct_tuple->dst.u3.in6)) return 0; break; } diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index d75e8dafb189..b7b166a8ad58 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -31,6 +31,23 @@ int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, } EXPORT_SYMBOL_GPL(nf_ct_seqadj_init); +void nf_ct_seqadj_reset(struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_seqadj *this_way; + + if (unlikely(!seqadj)) + return; + + spin_lock_bh(&ct->lock); + this_way = &seqadj->seq[dir]; + this_way->offset_before = 0; + this_way->offset_after = 0; + spin_unlock_bh(&ct->lock); +} +EXPORT_SYMBOL_GPL(nf_ct_seqadj_reset); + int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, __be32 seq, s32 off) { diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 934c6151f558..bd8f9cf394ff 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -737,14 +737,9 @@ static void nf_flow_table_do_cleanup(struct nf_flowtable *flow_table, { struct net_device *dev = data; - if (!dev) { - flow_offload_teardown(flow); - return; - } - - if (net_eq(nf_ct_net(flow->ct), dev_net(dev)) && - (flow->tuplehash[0].tuple.iifidx == dev->ifindex || - flow->tuplehash[1].tuple.iifidx == dev->ifindex)) + if (!dev || + flow->tuplehash[0].tuple.iifidx == dev->ifindex || + flow->tuplehash[1].tuple.iifidx == dev->ifindex) flow_offload_teardown(flow); } @@ -761,8 +756,10 @@ void nf_flow_table_cleanup(struct net_device *dev) struct nf_flowtable *flowtable; mutex_lock(&flowtable_lock); - list_for_each_entry(flowtable, &flowtables, list) - nf_flow_table_gc_cleanup(flowtable, dev); + list_for_each_entry(flowtable, &flowtables, list) { + if (net_eq(read_pnet(&flowtable->net), dev_net(dev))) + nf_flow_table_gc_cleanup(flowtable, dev); + } mutex_unlock(&flowtable_lock); } EXPORT_SYMBOL_GPL(nf_flow_table_cleanup); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 84f82957e66b..5ddd5fc95b24 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -1160,6 +1160,15 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct, } #endif +static void bump_nat_hook_base_seq(struct net *net) +{ + unsigned int base_seq = READ_ONCE(net->nf.nat_hook_base_seq); + + while (++base_seq == 0) + ; + smp_store_release(&net->nf.nat_hook_base_seq, base_seq); +} + static struct nf_ct_helper_expectfn follow_master_nat = { .name = "nat-follow-master", .expectfn = nf_nat_follow_master, @@ -1245,6 +1254,7 @@ int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, nat_proto_net->nat_hook_ops = nat_ops; nat_proto_net->users++; + bump_nat_hook_base_seq(net); mutex_unlock(&nf_nat_proto_mutex); @@ -1299,6 +1309,7 @@ void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops, goto unlock; priv = nat_ops[hooknum].priv; nf_hook_entries_delete_raw(&priv->entries, ops); + bump_nat_hook_base_seq(net); if (nat_proto_net->users == 0) { nf_unregister_net_hooks(net, nat_ops, ops_count); diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 9fc959ba2030..401ce5a13f3f 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -686,7 +686,7 @@ ipv4_synproxy_hook(void *priv, struct sk_buff *skb, * adjustments, they will get initialized once the connection is * reestablished. */ - nf_ct_seqadj_init(ct, ctinfo, 0); + nf_ct_seqadj_reset(ct, ctinfo); synproxy->tsoff = 0; this_cpu_inc(snet->stats->conn_reopened); fallthrough; @@ -1116,7 +1116,7 @@ ipv6_synproxy_hook(void *priv, struct sk_buff *skb, * adjustments, they will get initialized once the connection is * reestablished. */ - nf_ct_seqadj_init(ct, ctinfo, 0); + nf_ct_seqadj_reset(ct, ctinfo); synproxy->tsoff = 0; this_cpu_inc(snet->stats->conn_reopened); fallthrough; diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c index 95005e9a6066..b05fd79397c5 100644 --- a/net/netfilter/nfnetlink_hook.c +++ b/net/netfilter/nfnetlink_hook.c @@ -54,7 +54,6 @@ static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb, struct nfnl_dump_hook_data { char devname[IFNAMSIZ]; - unsigned long headv; u8 hook; }; @@ -338,27 +337,47 @@ nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *de } static int nfnl_hook_dump_nat(struct sk_buff *nlskb, - const struct nfnl_dump_hook_data *ctx, - const struct nf_hook_ops *ops, - int family, unsigned int seq) + struct netlink_callback *cb, + const struct nf_hook_ops *ops, int family) { struct nf_nat_lookup_hook_priv *priv = ops->priv; - struct nf_hook_entries *e = rcu_dereference(priv->entries); + struct nfnl_dump_hook_data *ctx = cb->data; + struct net *net = sock_net(nlskb->sk); struct nf_hook_ops **nat_ops; - int i, err; + unsigned int i = cb->args[1]; + struct nf_hook_entries *e; + unsigned int base_seq; + int err = 0; + base_seq = smp_load_acquire(&net->nf.nat_hook_base_seq); + + e = rcu_dereference(priv->entries); if (!e) - return 0; + goto out; nat_ops = nf_hook_entries_get_hook_ops(e); - for (i = 0; i < e->num_hook_entries; i++) { - err = nfnl_hook_dump_one(nlskb, ctx, nat_ops[i], - ops->priority, family, seq); + for (; i < e->num_hook_entries; i++) { + err = nfnl_hook_dump_one(nlskb, ctx, + READ_ONCE(nat_ops[i]), + ops->priority, family, + cb->nlh->nlmsg_seq); if (err) - return err; + break; + } - return 0; +out: + if (!err) + i = 0; + cb->args[1] = i; + + if (cb->args[2] && base_seq != cb->args[2]) { + cb->seq++; + err = -EINTR; + } + cb->args[2] = base_seq; + + return err; } static int nfnl_hook_dump(struct sk_buff *nlskb, @@ -373,35 +392,31 @@ static int nfnl_hook_dump(struct sk_buff *nlskb, unsigned int i = cb->args[0]; rcu_read_lock(); + cb->seq = smp_load_acquire(&net->nf.hook_base_seq); e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname); - if (!e) + if (!e || IS_ERR(e)) goto done; - if (IS_ERR(e)) { - cb->seq++; - goto done; - } - - if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries) - cb->seq++; - ops = nf_hook_entries_get_hook_ops(e); for (; i < e->num_hook_entries; i++) { - if (ops[i]->hook_ops_type == NF_HOOK_OP_NAT) - err = nfnl_hook_dump_nat(nlskb, ctx, ops[i], family, - cb->nlh->nlmsg_seq); - else - err = nfnl_hook_dump_one(nlskb, ctx, ops[i], - ops[i]->priority, family, + const struct nf_hook_ops *cur = READ_ONCE(ops[i]); + + if (cur->hook_ops_type == NF_HOOK_OP_NAT) { + err = nfnl_hook_dump_nat(nlskb, cb, cur, family); + } else { + err = nfnl_hook_dump_one(nlskb, ctx, cur, + cur->priority, family, cb->nlh->nlmsg_seq); + } if (err) break; } done: - nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); + if (nlskb->len > 0) + nl_dump_check_consistent(cb, nlmsg_hdr(nlskb)); rcu_read_unlock(); cb->args[0] = i; return nlskb->len; @@ -442,10 +457,7 @@ static int nfnl_hook_dump_start(struct netlink_callback *cb) return -ENOMEM; strscpy(ctx->devname, name, sizeof(ctx->devname)); - ctx->headv = (unsigned long)head; ctx->hook = hooknum; - - cb->seq = 1; cb->data = ctx; return 0; diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index e4835b0f4bb1..c8cd6af95db4 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -36,7 +36,6 @@ static inline int nf_osf_ttl(const struct sk_buff *skb, switch (ttl_check) { case NF_OSF_TTL_TRUE: return ip->ttl == f_ttl; - break; case NF_OSF_TTL_NOCHECK: return 1; case NF_OSF_TTL_LESS: diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 411e3dd92d07..e26c0d5b2176 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -349,10 +349,10 @@ static int tcf_ct_flow_table_get(struct net *net, struct tcf_ct_params *params) ct_ft->nf_ft.type = &flowtable_ct; ct_ft->nf_ft.flags |= NF_FLOWTABLE_HW_OFFLOAD | NF_FLOWTABLE_COUNTER; + write_pnet(&ct_ft->nf_ft.net, net); err = nf_flow_table_init(&ct_ft->nf_ft); if (err) goto err_init; - write_pnet(&ct_ft->nf_ft.net, net); __module_get(THIS_MODULE); out_unlock: diff --git a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c index 31b8250ddc53..0c777323f4ba 100644 --- a/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c +++ b/tools/testing/selftests/net/netfilter/conntrack_dump_flush.c @@ -215,7 +215,22 @@ static int count_entries(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } -static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) +static void put_zone_attr(struct nlmsghdr *nlh, uint16_t zone, + bool use_cta_filter) +{ + struct nlattr *nest; + + mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + + if (use_cta_filter) { + nest = mnl_attr_nest_start(nlh, CTA_FILTER); + mnl_attr_put(nlh, CTA_FILTER_ZONE, 0, NULL); + mnl_attr_nest_end(nlh, nest); + } +} + +static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone, + bool use_cta_filter) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -235,7 +250,7 @@ static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) nfh->version = NFNETLINK_V0; nfh->res_id = 0; - mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + put_zone_attr(nlh, zone, use_cta_filter); ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); if (ret < 0) { @@ -261,7 +276,8 @@ static int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone) return reply_counter; } -static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) +static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone, + bool use_cta_filter) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -281,7 +297,7 @@ static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) nfh->version = NFNETLINK_V0; nfh->res_id = 0; - mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone)); + put_zone_attr(nlh, zone, use_cta_filter); ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len); if (ret < 0) { @@ -304,43 +320,40 @@ static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone) return 0; } -FIXTURE(conntrack_dump_flush) -{ - struct mnl_socket *sock; -}; - -FIXTURE_SETUP(conntrack_dump_flush) +static int conntrack_zone_setup(struct __test_metadata *_metadata, + struct mnl_socket **sock) { struct in6_addr src, dst; int ret; - self->sock = mnl_socket_open(NETLINK_NETFILTER); - if (!self->sock) { + *sock = mnl_socket_open(NETLINK_NETFILTER); + if (!*sock) { perror("mnl_socket_open"); - SKIP(return, "cannot open netlink_netfilter socket"); + SKIP(return -1, "cannot open netlink_netfilter socket"); } - ret = mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID); - EXPECT_EQ(ret, 0); + ret = mnl_socket_bind(*sock, 0, MNL_SOCKET_AUTOPID); + ASSERT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(*sock, TEST_ZONE_ID, false); if (ret < 0 && errno == EPERM) - SKIP(return, "Needs to be run as root"); + SKIP(return -1, "Needs to be run as root"); else if (ret < 0 && errno == EOPNOTSUPP) - SKIP(return, "Kernel does not seem to support conntrack zones"); + SKIP(return -1, + "Kernel does not seem to support conntrack zones"); - ret = conntrack_data_generate_v4(self->sock, 0xf0f0f0f0, 0xf1f1f1f1, + ret = conntrack_data_generate_v4(*sock, 0xf0f0f0f0, 0xf1f1f1f1, TEST_ZONE_ID); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf2f2f2f2, 0xf3f3f3f3, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf2f2f2f2, 0xf3f3f3f3, TEST_ZONE_ID + 1); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf4f4f4f4, 0xf5f5f5f5, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf4f4f4f4, 0xf5f5f5f5, TEST_ZONE_ID + 2); - EXPECT_EQ(ret, 0); - ret = conntrack_data_generate_v4(self->sock, 0xf6f6f6f6, 0xf7f7f7f7, + ASSERT_EQ(ret, 0); + ret = conntrack_data_generate_v4(*sock, 0xf6f6f6f6, 0xf7f7f7f7, NF_CT_DEFAULT_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { @@ -358,9 +371,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x02000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { 0xb80d0120, @@ -377,9 +390,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x04000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID + 1); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { 0xb80d0120, @@ -396,9 +409,9 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x06000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, TEST_ZONE_ID + 2); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); src = (struct in6_addr) {{ .__u6_addr32 = { @@ -416,14 +429,51 @@ FIXTURE_SETUP(conntrack_dump_flush) 0x08000000 } }}; - ret = conntrack_data_generate_v6(self->sock, src, dst, + ret = conntrack_data_generate_v6(*sock, src, dst, NF_CT_DEFAULT_ZONE_ID); - EXPECT_EQ(ret, 0); + ASSERT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + return 0; +} + +FIXTURE(conntrack_dump_flush) +{ + struct mnl_socket *sock; +}; + +FIXTURE_VARIANT(conntrack_dump_flush) +{ + bool use_cta_filter; +}; + +FIXTURE_VARIANT_ADD(conntrack_dump_flush, cta_zone) +{ + .use_cta_filter = false, +}; + +FIXTURE_VARIANT_ADD(conntrack_dump_flush, cta_filter) +{ + .use_cta_filter = true, +}; + +FIXTURE_SETUP(conntrack_dump_flush) +{ + int ret; + + if (conntrack_zone_setup(_metadata, &self->sock)) + return; + + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, false); EXPECT_GE(ret, 2); if (ret > 2) SKIP(return, "kernel does not support filtering by zone"); + + if (variant->use_cta_filter) { + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, true); + if (ret < 0 && errno == EINVAL) + SKIP(return, "kernel does not support CTA_FILTER_ZONE"); + ASSERT_GE(ret, 0); + } } FIXTURE_TEARDOWN(conntrack_dump_flush) @@ -434,39 +484,42 @@ TEST_F(conntrack_dump_flush, test_dump_by_zone) { int ret; - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, + variant->use_cta_filter); EXPECT_EQ(ret, 2); } TEST_F(conntrack_dump_flush, test_flush_by_zone) { + bool filter = variant->use_cta_filter; int ret; - ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 2); } TEST_F(conntrack_dump_flush, test_flush_by_zone_default) { + bool filter = variant->use_cta_filter; int ret; - ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 0); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2); + ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2, filter); EXPECT_EQ(ret, 2); - ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID); + ret = conntrack_count_zone(self->sock, NF_CT_DEFAULT_ZONE_ID, filter); EXPECT_EQ(ret, 0); } diff --git a/tools/testing/selftests/net/netfilter/conntrack_icmp_related.sh b/tools/testing/selftests/net/netfilter/conntrack_icmp_related.sh index c63d840ead61..f63b7f12b36a 100755 --- a/tools/testing/selftests/net/netfilter/conntrack_icmp_related.sh +++ b/tools/testing/selftests/net/netfilter/conntrack_icmp_related.sh @@ -171,7 +171,7 @@ table inet filter { } EOF -# make sure NAT core rewrites adress of icmp error if nat is used according to +# make sure NAT core rewrites address of icmp error if nat is used according to # conntrack nat information (icmp error will be directed at nsrouter1 address, # but it needs to be routed to nsclient1 address). ip netns exec "$nsrouter1" nft -f - <