netfilter pull request 26-09-28

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmq5lC0ACgkQ1w0aZmrP
 KyH/+A//XlfAsuJFsBE0sGf4zSuPw96RvjG9hjlbCahoMHSwwON46bl3frT+W9Zr
 YNEIJMdEqqGEQ+bGQcafLlX3ayknzhN7cTe+VoBOR12/6WeewSG/1l3JusWgb91b
 snDGU/tWaXn0ZmumpziiDWlgNapRkLlxCr7ZzqBNJowNZU/RNd/dtqiMdBz8osX7
 eOtL947O2EIjNZ0+Y5AI6uHwQimEIdzPYIi50WpyxcLYLC/AEEJFhDTafwq5NTP/
 tCzYODr2ybWXeC90yxFsLNQai+1Tyo7tq0BP1dcMg80i/kgOnVtAQqRt0E6BYaEP
 Rwbfhh+onCbdRV0DpsuyL6uy3pnJ21uj48hWuquHgGnuP6yrc2EMTreLmt0szorH
 nngUoqBNKheTqC8L+VSfjV6/+tDhwLTaH4knlasbZizWC//GjlBfK7c7499ApHrS
 Icm9pVwKYtFFRARe8wT2H3AKO3u3DaVNXBDypviCnEuvAXRuIi4nwu/aMVVeD9cs
 04uTCFqMvUv+rX3oJN13hhC88fyH4R25sMt/XIZQxel/ZwjZrcZYz8FoFi5ePdfF
 X/tBDloygOt2tB3YRnHdPxm/SYOZTQoUlD9SmtwvSYnGD0V3TFmCFILAeNPc4SrZ
 rR/H5yDV47JxXGst9xd5AJlMNhr6ZrbCqbqC6GhWPnXFXC01lvg=
 =RZ9z
 -----END PGP SIGNATURE-----

Merge tag 'nf-next-26-09-28' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next. The
fixes included in this batch are deemed to handle correctness issues
present in the Netfilter tree:

1) TCP sequence tracking is not reset inconditionally by synproxy when
   recycling an entry, sashiko reports the zero offset case skips it.
   Add a new function to inconditionally reset TCP sequence tracking.
   From Fernando F. Mancera.

2) Update documentation to reflect that the default maximum number of
   expectations (nf_conntrack_expect_max) is nf_conntrack_buckets / 64.
   From Shaojie Sun.

3) Remove useless break; after return in nft_osf, from Linkui Xiao.

4) Fix typos in comments in the netfilter tree, from Hemanth Selam.

5) Remove a few conntrack error stats duplicated updates,
   from Phil Sutter.

6) Do not bump invalid and drop conntrack error stats when packet is
   dropped, this is another duplicate. also From Phil.

7) Set on netns pointer before registering the flowtable, this is
   a requirement by the next patch, not fixing an existing issue.
   From Qingfang Deng.

8) Remove unnecessary workqueue work flush for all of the existing
   netns when device is gone. Also from Qingfang Deng.

9) Rework-fix nfnetlink_hook to correctly deal with large netlink
   dumps. Use sequence numbers to detect interference with hook
   updates while netlink dump is ongoing. From Phil Sutter.

10) Fix ctnetlink dump filtering by the IPv6 address, this has
    only work correctly for IPv4 this far, from Piotr Kubik.

11) ctnetlink filtering by zone is supported, but the ctnetlink
    dump filtering infrastructure was never updated to include a
    flag from userspace, update it to fill this gap.
    From Ilya Maximets.

* tag 'nf-next-26-09-28' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  netfilter: conntrack: make filtering by zone discoverable
  netfilter: ctnetlink: fix inverted IPv6 address match in dump filter
  netfilter: nfnetlink: Fix for interrupted hook dumps
  netfilter: flowtable: check namespace before iterating flows
  net/sched: act_ct: set net pointer before publishing flowtable
  netfilter: conntrack: Untangle drop and invalid counters
  netfilter: conntrack: Untangle insert_failed counter from others
  netfilter: fix several typos in comments
  netfilter: osf: remove unreachable break in nf_osf_ttl()
  netfilter: conntrack: fix nf_conntrack_expect_max default value in documentation
  netfilter: synproxy: fix reset of ct seqadj when reopening a connection
====================

Link: https://patch.msgid.link/20260927223436.269024-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-28 19:14:55 -07:00
commit 0a1a3898ab
20 changed files with 229 additions and 104 deletions

View File

@ -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:

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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 &&

View File

@ -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)
{

View File

@ -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;

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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);
}

View File

@ -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 - <<EOF