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

Pablo Neira Ayuso says:

====================
Netfilter updates for net

This includes an enhancement to detect ct memleaks easier via
DEBUG_NET and flowtable preparation patches for IPv4 over IPV6
and vice-versa. This also includes a fix for the nft_ct custom
expectation support.

1) Add DEBUG_NET_WARN_ON_ONCE to nf_ct_set() to spot ct memleaks.

2) Pass struct net_device_path_ctx to dev_fill_forward_path() to
   make it easier to pass more parameters to this function.
   From Lorenzo Bianconi.

3) Add ether_type field to net_device_path context structucture.

4) Rename tun.l3_proto field to tun.inner_proto.

5) Rename ctx.tun.proto to ctx.tun.inner_proto.

6) Store ether_type in flowtable context.

7) Move IPv4 and IPv6 xmit path to a helper function.

8) Move encapsulation header parser out of the flowtable lookup
   function.

9) Rework nft_ct custom expectation support to address a possible
   reallocation of ct extension area while expectation list also
   contains expectations. Move datapath to a ct helper to fix it.

10) Ensure timeout is always lowered for the non-closing RST case
    in the TCP connection tracking.

11) Bail out when inserting already dead expectation, this should
    not ever happen, hence report it via DEBUG_NET.

12) Comestic updates for improving the conntrack selftest dump and
    flush userspace program, from Qingshuang Fu.

* tag 'nf-next-26-08-10' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  selftests: netfilter: conntrack_dump_flush: remove unused variables and fix typo
  netfilter: nf_conntrack_expect: bail out on insert dead expectations
  netfilter: conntrack: always lower timeout for non-closing RST packets
  netfilter: nft_ct: move custom expectation support to helper
  netfilter: flowtable: detach layer 2 encapsulation parser from lookup
  netfilter: flowtable: move ipv4 and ipv6 xmit path to function
  netfilter: flowtable: store ethertype in flowtable context
  netfilter: flowtable: rename ctx.tun.proto to ctx.tun.inner_proto
  netfilter: flowtable: rename tun.l3_proto to tun.inner_proto
  net: netfilter: add ether_type to net_device_path_ctx and use it
  net: pass net_device_path_ctx to dev_fill_forward_path()
  netfilter: add DEBUG_NET_WARN_ON_ONCE to skb_set_nfct()
====================

Link: https://patch.msgid.link/20260810194015.932627-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-08-14 12:23:12 -07:00
20 changed files with 306 additions and 174 deletions
+6 -1
View File
@@ -283,14 +283,19 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
struct airoha_wdma_info *info)
{
struct net_device_path_stack stack;
struct net_device_path_ctx ctx = {
.dev = dev,
};
struct net_device_path *path;
int err;
if (!dev)
return -ENODEV;
ether_addr_copy(ctx.daddr, addr);
rcu_read_lock();
err = dev_fill_forward_path(dev, addr, &stack);
err = dev_fill_forward_path(&ctx, &stack);
rcu_read_unlock();
if (err)
return err;
@@ -92,6 +92,9 @@ static int
mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
{
struct net_device_path_stack stack;
struct net_device_path_ctx ctx = {
.dev = dev,
};
struct net_device_path *path;
int err;
@@ -101,8 +104,10 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
if (!IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED))
return -1;
ether_addr_copy(ctx.daddr, addr);
rcu_read_lock();
err = dev_fill_forward_path(dev, addr, &stack);
err = dev_fill_forward_path(&ctx, &stack);
rcu_read_unlock();
if (err)
return err;
+3 -2
View File
@@ -904,7 +904,7 @@ struct net_device_path {
struct in6_addr dst_v6;
};
u8 l3_proto;
u8 inner_proto;
} tun;
struct {
enum {
@@ -941,6 +941,7 @@ struct net_device_path_stack {
struct net_device_path_ctx {
const struct net_device *dev;
u8 daddr[ETH_ALEN];
__be16 ether_type;
int num_vlans;
struct {
@@ -3428,7 +3429,7 @@ void dev_remove_offload(struct packet_offload *po);
int dev_get_iflink(const struct net_device *dev);
int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
int dev_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path_stack *stack);
void dev_fill_forward_path_release(struct net_device_path_stack *stack);
struct net_device *dev_get_by_name(struct net *net, const char *name);
+1
View File
@@ -5004,6 +5004,7 @@ static inline unsigned long skb_get_nfct(const struct sk_buff *skb)
static inline void skb_set_nfct(struct sk_buff *skb, unsigned long nfct)
{
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
DEBUG_NET_WARN_ON_ONCE(skb->_nfct & NFCT_PTRMASK);
skb->slow_gro |= !!nfct;
skb->_nfct = nfct;
#endif
+1 -1
View File
@@ -2131,7 +2131,7 @@ static inline void ip_vs_notrack(struct sk_buff *skb)
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct) {
nf_conntrack_put(&ct->ct_general);
nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
}
#endif
@@ -106,6 +106,7 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper,
int nf_conntrack_helper_register(struct nf_conntrack_helper *, struct nf_conntrack_helper **);
int __nf_conntrack_helper_register(struct nf_conntrack_helper *);
void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
void nf_conntrack_helper_release(struct nf_conntrack_helper *);
int nf_conntrack_helpers_register(struct nf_conntrack_helper *, unsigned int,
struct nf_conntrack_helper **);
+1 -1
View File
@@ -117,7 +117,7 @@ struct flow_offload_tunnel {
struct in6_addr dst_v6;
};
u8 l3_proto;
u8 inner_proto;
};
struct flow_offload_tuple {
+7 -11
View File
@@ -769,35 +769,31 @@ void dev_fill_forward_path_release(struct net_device_path_stack *stack)
}
EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);
int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
int dev_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path_stack *stack)
{
const struct net_device *last_dev;
struct net_device_path_ctx ctx = {
.dev = dev,
};
struct net_device_path *path;
int ret = 0;
memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
stack->num_paths = 0;
while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
last_dev = ctx.dev;
while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) {
last_dev = ctx->dev;
path = dev_fwd_path(stack);
if (!path)
goto err_out;
memset(path, 0, sizeof(struct net_device_path));
ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path);
if (ret < 0)
goto err_out;
stack->num_paths++;
if (WARN_ON_ONCE(last_dev == ctx.dev))
if (WARN_ON_ONCE(last_dev == ctx->dev))
goto err_out;
}
if (!ctx.dev)
if (!ctx->dev)
return ret;
path = dev_fwd_path(stack);
@@ -805,7 +801,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
goto err_out;
path->type = DEV_PATH_ETHERNET;
path->dev = ctx.dev;
path->dev = ctx->dev;
stack->num_paths++;
return 0;
+4 -1
View File
@@ -360,6 +360,9 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
const struct iphdr *tiph = &tunnel->parms.iph;
struct rtable *rt;
if (ctx->ether_type != cpu_to_be16(ETH_P_IP))
return -EOPNOTSUPP;
if (tunnel->collect_md)
return -EOPNOTSUPP;
@@ -375,7 +378,7 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
path->type = DEV_PATH_TUN;
path->tun.src_v4.s_addr = tiph->saddr;
path->tun.dst_v4.s_addr = tiph->daddr;
path->tun.l3_proto = IPPROTO_IPIP;
path->tun.inner_proto = IPPROTO_IPIP;
path->tun.dst = &rt->dst;
path->dev = ctx->dev;
+4 -1
View File
@@ -1852,6 +1852,9 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
struct flowi6 fl6;
int err;
if (ctx->ether_type != cpu_to_be16(ETH_P_IPV6))
return -EOPNOTSUPP;
if (t->parms.flags & (IP6_TNL_F_USE_ORIG_TCLASS |
IP6_TNL_F_USE_ORIG_FLOWLABEL |
IP6_TNL_F_USE_ORIG_FWMARK))
@@ -1872,7 +1875,7 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->type = DEV_PATH_TUN;
path->tun.src_v6 = fl6.saddr;
path->tun.dst_v6 = fl6.daddr;
path->tun.l3_proto = IPPROTO_IPV6;
path->tun.inner_proto = IPPROTO_IPV6;
path->tun.dst = dst;
path->dev = ctx->dev;
ctx->dev = dst->dev;
+1 -1
View File
@@ -1031,7 +1031,7 @@ static int __nf_ct_resolve_clash(struct sk_buff *skb,
nf_conntrack_get(&ct->ct_general);
nf_ct_acct_merge(ct, ctinfo, loser_ct);
nf_ct_put(loser_ct);
nf_reset_ct(skb);
nf_ct_set(skb, ct, ctinfo);
NF_CT_STAT_INC(net, clash_resolve);
+6
View File
@@ -528,6 +528,12 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
int ret;
spin_lock_bh(&nf_conntrack_expect_lock);
if (expect->flags & NF_CT_EXPECT_DEAD) {
DEBUG_NET_WARN_ON_ONCE(1);
ret = -EINVAL;
goto out;
}
master_help = nfct_help(expect->master);
if (!master_help) {
ret = -ESHUTDOWN;
+10 -4
View File
@@ -448,6 +448,15 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
return this == me;
}
void nf_conntrack_helper_release(struct nf_conntrack_helper *me)
{
nf_ct_expect_iterate_destroy(expect_iter_me, me);
if (refcount_dec_and_test(&me->ct_refcnt))
kfree_rcu(me, rcu);
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_release);
void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
{
mutex_lock(&nf_ct_helper_mutex);
@@ -463,10 +472,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
*/
synchronize_rcu();
nf_ct_expect_iterate_destroy(expect_iter_me, me);
if (refcount_dec_and_test(&me->ct_refcnt))
kfree_rcu(me, rcu);
nf_conntrack_helper_release(me);
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
+2 -1
View File
@@ -1326,7 +1326,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
timeout = timeouts[TCP_CONNTRACK_RETRANS];
else if (unlikely(index == TCP_RST_SET &&
new_state == TCP_CONNTRACK_ESTABLISHED))
new_state == TCP_CONNTRACK_ESTABLISHED) &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])
timeout = timeouts[TCP_CONNTRACK_UNACK];
else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) &
IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
+96 -71
View File
@@ -147,13 +147,14 @@ static bool ip_has_options(unsigned int thoff)
struct nf_flowtable_ctx {
const struct net_device *in;
__be16 ether_type;
u32 offset;
u32 hdrsize;
struct {
/* Tunnel IP header size */
u32 hdr_size;
/* IP tunnel protocol */
u8 proto;
u8 inner_proto;
} tun;
};
@@ -161,7 +162,6 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
struct sk_buff *skb,
struct flow_offload_tuple *tuple)
{
__be16 inner_proto = skb->protocol;
struct vlan_ethhdr *veth;
struct pppoe_hdr *phdr;
struct ipv6hdr *ip6h;
@@ -179,33 +179,31 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
veth = (struct vlan_ethhdr *)skb_mac_header(skb);
tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
tuple->encap[i].proto = skb->protocol;
inner_proto = veth->h_vlan_encapsulated_proto;
offset += VLAN_HLEN;
break;
case htons(ETH_P_PPP_SES):
phdr = (struct pppoe_hdr *)skb_network_header(skb);
tuple->encap[i].id = ntohs(phdr->sid);
tuple->encap[i].proto = skb->protocol;
inner_proto = *((__be16 *)(phdr + 1));
offset += PPPOE_SES_HLEN;
break;
}
switch (inner_proto) {
switch (ctx->ether_type) {
case htons(ETH_P_IP):
iph = (struct iphdr *)(skb_network_header(skb) + offset);
if (ctx->tun.proto == IPPROTO_IPIP) {
if (ctx->tun.inner_proto == IPPROTO_IPIP) {
tuple->tun.dst_v4.s_addr = iph->daddr;
tuple->tun.src_v4.s_addr = iph->saddr;
tuple->tun.l3_proto = IPPROTO_IPIP;
tuple->tun.inner_proto = IPPROTO_IPIP;
}
break;
case htons(ETH_P_IPV6):
ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
if (ctx->tun.proto == IPPROTO_IPV6) {
if (ctx->tun.inner_proto == IPPROTO_IPV6) {
tuple->tun.dst_v6 = ip6h->daddr;
tuple->tun.src_v6 = ip6h->saddr;
tuple->tun.l3_proto = IPPROTO_IPV6;
tuple->tun.inner_proto = IPPROTO_IPV6;
}
break;
default:
@@ -329,7 +327,7 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx,
return false;
if (iph->protocol == IPPROTO_IPIP) {
ctx->tun.proto = iph->protocol;
ctx->tun.inner_proto = iph->protocol;
ctx->tun.hdr_size = size;
ctx->offset += ctx->tun.hdr_size;
}
@@ -354,7 +352,7 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx,
return false;
if (ip6h->nexthdr == IPPROTO_IPV6) {
ctx->tun.proto = ip6h->nexthdr;
ctx->tun.inner_proto = ip6h->nexthdr;
ctx->tun.hdr_size = sizeof(*ip6h);
ctx->offset += ctx->tun.hdr_size;
}
@@ -368,8 +366,8 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx,
static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
struct sk_buff *skb)
{
if (ctx->tun.proto != IPPROTO_IPIP &&
ctx->tun.proto != IPPROTO_IPV6)
if (ctx->tun.inner_proto != IPPROTO_IPIP &&
ctx->tun.inner_proto != IPPROTO_IPV6)
return;
skb_pull(skb, ctx->tun.hdr_size);
@@ -377,10 +375,10 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
}
static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
struct sk_buff *skb, __be16 proto)
struct sk_buff *skb)
{
__be16 inner_proto = skb->protocol;
struct vlan_ethhdr *veth;
__be16 ether_type;
bool ret = false;
switch (skb->protocol) {
@@ -389,22 +387,27 @@ static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
return false;
veth = (struct vlan_ethhdr *)skb_mac_header(skb);
if (veth->h_vlan_encapsulated_proto == proto) {
ctx->offset += VLAN_HLEN;
inner_proto = proto;
ret = true;
}
ctx->ether_type = veth->h_vlan_encapsulated_proto;
ctx->offset += VLAN_HLEN;
ret = true;
break;
case htons(ETH_P_PPP_SES):
if (nf_flow_pppoe_proto(skb, &inner_proto) &&
inner_proto == proto) {
ctx->offset += PPPOE_SES_HLEN;
ret = true;
}
if (!nf_flow_pppoe_proto(skb, &ether_type))
return false;
ctx->ether_type = ether_type;
ctx->offset += PPPOE_SES_HLEN;
ret = true;
break;
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
ctx->ether_type = skb->protocol;
break;
default:
return false;
}
switch (inner_proto) {
switch (ctx->ether_type) {
case htons(ETH_P_IP):
ret = nf_flow_ip4_tunnel_proto(ctx, skb);
break;
@@ -456,9 +459,6 @@ nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
{
struct flow_offload_tuple tuple = {};
if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IP)))
return NULL;
if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
return NULL;
@@ -612,7 +612,7 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
iph->version = 4;
iph->ihl = sizeof(*iph) >> 2;
iph->frag_off = ip_mtu_locked(&rt->dst) ? 0 : frag_off;
iph->protocol = tuple->tun.l3_proto;
iph->protocol = tuple->tun.inner_proto;
iph->tos = tos;
iph->daddr = tuple->tun.src_v4.s_addr;
iph->saddr = tuple->tun.dst_v4.s_addr;
@@ -795,33 +795,17 @@ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
return NF_STOLEN;
}
unsigned int
nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
static int nf_flow_queue_xmit4(struct sk_buff *skb,
struct flow_offload_tuple_rhash *tuplehash,
const struct nf_hook_state *state)
{
struct flow_offload_tuple_rhash *tuplehash;
struct nf_flowtable *flow_table = priv;
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flowtable_ctx ctx = {
.in = state->in,
};
struct nf_flow_xmit xmit = {};
struct flow_offload *flow;
struct neighbour *neigh;
struct rtable *rt;
__be32 ip_daddr;
int ret;
tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
if (!tuplehash)
return NF_ACCEPT;
ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
if (ret < 0)
return NF_DROP;
else if (ret == 0)
return NF_ACCEPT;
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = dst_rtable(tuplehash->tuple.dst_cache);
@@ -875,6 +859,36 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
return nf_flow_queue_xmit(state->net, skb, &xmit);
}
unsigned int
nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct flow_offload_tuple_rhash *tuplehash;
struct nf_flowtable *flow_table = priv;
struct nf_flowtable_ctx ctx = {
.in = state->in,
};
int ret;
if (!nf_flow_skb_encap_protocol(&ctx, skb))
return NF_ACCEPT;
if (unlikely(ctx.ether_type != htons(ETH_P_IP)))
return NF_ACCEPT;
tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
if (!tuplehash)
return NF_ACCEPT;
ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
if (ret < 0)
return NF_DROP;
else if (ret == 0)
return NF_ACCEPT;
return nf_flow_queue_xmit4(skb, tuplehash, state);
}
EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
@@ -1103,42 +1117,23 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
{
struct flow_offload_tuple tuple = {};
if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IPV6)))
return NULL;
if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
return NULL;
return flow_offload_lookup(flow_table, &tuple);
}
unsigned int
nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
static int nf_flow_queue_xmit6(struct sk_buff *skb,
struct flow_offload_tuple_rhash *tuplehash,
const struct nf_hook_state *state)
{
struct flow_offload_tuple_rhash *tuplehash;
struct nf_flowtable *flow_table = priv;
struct flow_offload_tuple *other_tuple;
enum flow_offload_tuple_dir dir;
struct nf_flowtable_ctx ctx = {
.in = state->in,
};
struct nf_flow_xmit xmit = {};
struct in6_addr *ip6_daddr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rt6_info *rt;
int ret;
tuplehash = nf_flow_offload_ipv6_lookup(&ctx, flow_table, skb);
if (tuplehash == NULL)
return NF_ACCEPT;
ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb);
if (ret < 0)
return NF_DROP;
else if (ret == 0)
return NF_ACCEPT;
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = dst_rt6_info(tuplehash->tuple.dst_cache);
@@ -1193,4 +1188,34 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
return nf_flow_queue_xmit(state->net, skb, &xmit);
}
unsigned int
nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct flow_offload_tuple_rhash *tuplehash;
struct nf_flowtable *flow_table = priv;
struct nf_flowtable_ctx ctx = {
.in = state->in,
};
int ret;
if (!nf_flow_skb_encap_protocol(&ctx, skb))
return NF_ACCEPT;
if (unlikely(ctx.ether_type != htons(ETH_P_IPV6)))
return NF_ACCEPT;
tuplehash = nf_flow_offload_ipv6_lookup(&ctx, flow_table, skb);
if (!tuplehash)
return NF_ACCEPT;
ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb);
if (ret < 0)
return NF_DROP;
else if (ret == 0)
return NF_ACCEPT;
return nf_flow_queue_xmit6(skb, tuplehash, state);
}
EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook);
+12 -5
View File
@@ -44,11 +44,16 @@ static bool nft_is_valid_ether_device(const struct net_device *dev)
static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache,
const struct nf_conn *ct,
enum ip_conntrack_dir dir, u8 *ha,
enum ip_conntrack_dir dir,
u8 *ha, __be16 ether_type,
struct net_device_path_stack *stack)
{
const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
struct net_device *dev = dst_cache->dev;
struct net_device_path_ctx ctx = {
.dev = dev,
.ether_type = ether_type,
};
struct neighbour *n;
u8 nud_state;
@@ -71,7 +76,9 @@ static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache,
return -1;
out:
return dev_fill_forward_path(dev, ha, stack);
ether_addr_copy(ctx.daddr, ha);
return dev_fill_forward_path(&ctx, stack);
}
struct nft_forward_info {
@@ -126,7 +133,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
info->tun.src_v6 = path->tun.src_v6;
info->tun.dst_v6 = path->tun.dst_v6;
info->tun.l3_proto = path->tun.l3_proto;
info->tun.inner_proto = path->tun.inner_proto;
info->tun_dst = path->tun.dst;
info->num_tuns++;
} else {
@@ -223,7 +230,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
unsigned char ha[ETH_ALEN];
int i;
if (nft_dev_fill_forward_path(dst, ct, dir, ha, &stack) < 0 ||
if (nft_dev_fill_forward_path(dst, ct, dir, ha, pkt->ethertype, &stack) < 0 ||
nft_dev_path_info(&stack, &info, ha, ft) < 0)
return -ENOENT;
@@ -238,7 +245,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
if (info.num_tuns) {
route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6;
route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6;
route->tuple[!dir].in.tun.l3_proto = info.tun.l3_proto;
route->tuple[!dir].in.tun.inner_proto = info.tun.inner_proto;
route->tuple[!dir].in.num_tuns = info.num_tuns;
dst_release(route->tuple[dir].dst);
route->tuple[dir].dst = info.tun_dst;
+125 -44
View File
@@ -1213,6 +1213,8 @@ struct nft_ct_expect_obj {
u8 l4proto;
u8 size;
u32 timeout;
struct nf_conntrack_helper *helper;
};
static int nft_ct_expect_timeout_get(const struct nlattr *attr, u32 *val)
@@ -1226,6 +1228,93 @@ static int nft_ct_expect_timeout_get(const struct nlattr *attr, u32 *val)
return 0;
}
#if IS_ENABLED(CONFIG_NF_NAT)
static void nft_ct_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this)
{
const struct nf_ct_helper_expectfn *expfn;
expfn = nf_ct_helper_expectfn_find_by_name("nat-follow-master");
if (expfn)
expfn->expectfn(ct, this);
}
#endif
struct nft_ct_expect_data {
struct nft_ct_expect_obj obj;
enum ip_conntrack_dir dir;
};
static int ct_expect_help(struct sk_buff *skb, unsigned int protoff,
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nft_ct_expect_data *expect_data;
struct nf_conntrack_expect *exp;
int ret = NF_ACCEPT;
u16 l3num;
if (nf_ct_is_confirmed(ct))
return NF_ACCEPT;
expect_data = nfct_help_data(ct);
if (!expect_data)
return NF_ACCEPT;
if (expect_data->dir != dir)
return NF_ACCEPT;
exp = nf_ct_expect_alloc(ct);
if (!exp)
return NF_DROP;
if (expect_data->obj.l3num == NFPROTO_INET)
l3num = nf_ct_l3num(ct);
else
l3num = expect_data->obj.l3num;
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, l3num,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
expect_data->obj.l4proto, NULL, &expect_data->obj.dport);
exp->timeout += expect_data->obj.timeout;
#if IS_ENABLED(CONFIG_NF_NAT)
if (ct->status & IPS_NAT_MASK) {
exp->saved_proto.tcp.port = expect_data->obj.dport;
exp->dir = !dir;
exp->expectfn = nft_ct_nat_follow_master;
}
#endif
if (nf_ct_expect_related(exp, 0) != 0)
ret = NF_ACCEPT;
nf_ct_expect_put(exp);
return ret;
}
static int nft_ct_expect_helper_alloc(struct nft_ct_expect_obj *priv)
{
struct nf_conntrack_helper *ct_expect_helper;
ct_expect_helper = kzalloc_obj(struct nf_conntrack_helper,
GFP_KERNEL_ACCOUNT);
if (!ct_expect_helper)
return -ENOMEM;
snprintf(ct_expect_helper->name, sizeof(ct_expect_helper->name), "%s",
"nft_ct_expect");
ct_expect_helper->me = THIS_MODULE;
ct_expect_helper->expect_policy[NF_CT_EXPECT_CLASS_DEFAULT].max_expected = priv->size;
rcu_assign_pointer(ct_expect_helper->help, ct_expect_help);
refcount_set(&ct_expect_helper->ct_refcnt, 1);
/* No need to register this helper, this is internal. */
priv->helper = ct_expect_helper;
return 0;
}
static int nft_ct_expect_obj_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[],
struct nft_object *obj)
@@ -1233,6 +1322,8 @@ static int nft_ct_expect_obj_init(const struct nft_ctx *ctx,
struct nft_ct_expect_obj *priv = nft_obj_data(obj);
int err;
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nft_ct_expect_data));
if (!tb[NFTA_CT_EXPECT_L4PROTO] ||
!tb[NFTA_CT_EXPECT_DPORT] ||
!tb[NFTA_CT_EXPECT_TIMEOUT] ||
@@ -1272,13 +1363,31 @@ static int nft_ct_expect_obj_init(const struct nft_ctx *ctx,
priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]);
priv->size = nla_get_u8(tb[NFTA_CT_EXPECT_SIZE]);
if (!priv->size)
priv->size = NF_CT_EXPECT_MAX_CNT;
return nf_ct_netns_get(ctx->net, ctx->family);
err = nf_ct_netns_get(ctx->net, ctx->family);
if (err < 0)
return err;
err = nft_ct_expect_helper_alloc(priv);
if (err < 0) {
nf_ct_netns_put(ctx->net, ctx->family);
return err;
}
return err;
}
static void nft_ct_expect_obj_destroy(const struct nft_ctx *ctx,
struct nft_object *obj)
struct nft_object *obj)
{
const struct nft_ct_expect_obj *priv = nft_obj_data(obj);
struct nf_conntrack_helper *me = priv->helper;
/* This helper is going away, disable it. */
rcu_assign_pointer(me->help, NULL);
nf_conntrack_helper_release(me);
nf_ct_netns_put(ctx->net, ctx->family);
}
@@ -1297,27 +1406,14 @@ static int nft_ct_expect_obj_dump(struct sk_buff *skb,
return 0;
}
#if IS_ENABLED(CONFIG_NF_NAT)
static void nft_ct_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this)
{
const struct nf_ct_helper_expectfn *expfn;
expfn = nf_ct_helper_expectfn_find_by_name("nat-follow-master");
if (expfn)
expfn->expectfn(ct, this);
}
#endif
static void nft_ct_expect_obj_eval(struct nft_object *obj,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_ct_expect_obj *priv = nft_obj_data(obj);
struct nf_conntrack_expect *exp;
struct nft_ct_expect_data *expect_data;
enum ip_conntrack_info ctinfo;
struct nf_conn_help *help;
enum ip_conntrack_dir dir;
u16 l3num = priv->l3num;
struct nf_conn *ct;
ct = nf_ct_get(pkt->skb, &ctinfo);
@@ -1325,45 +1421,30 @@ static void nft_ct_expect_obj_eval(struct nft_object *obj,
regs->verdict.code = NFT_BREAK;
return;
}
dir = CTINFO2DIR(ctinfo);
help = nfct_help(ct);
if (!help)
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help) {
regs->verdict.code = NFT_BREAK;
return;
}
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (!help) {
regs->verdict.code = NF_DROP;
return;
}
if (help->expecting[NF_CT_EXPECT_CLASS_DEFAULT] >= priv->size) {
expect_data = nfct_help_data(ct);
if (!expect_data) {
regs->verdict.code = NFT_BREAK;
return;
}
if (l3num == NFPROTO_INET)
l3num = nf_ct_l3num(ct);
expect_data->obj = *priv;
expect_data->obj.helper = NULL;
expect_data->dir = CTINFO2DIR(ctinfo);
exp = nf_ct_expect_alloc(ct);
if (exp == NULL) {
regs->verdict.code = NF_DROP;
return;
}
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, l3num,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
priv->l4proto, NULL, &priv->dport);
exp->timeout += priv->timeout;
#if IS_ENABLED(CONFIG_NF_NAT)
if (ct->status & IPS_NAT_MASK) {
exp->saved_proto.tcp.port = priv->dport;
exp->dir = !dir;
exp->expectfn = nft_ct_nat_follow_master;
}
#endif
if (nf_ct_expect_related(exp, 0) != 0)
regs->verdict.code = NF_DROP;
nf_ct_expect_put(exp);
if (help && refcount_inc_not_zero(&priv->helper->ct_refcnt))
rcu_assign_pointer(help->helper, priv->helper);
}
static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = {
+3 -9
View File
@@ -603,7 +603,7 @@ static bool skb_nfct_cached(struct net *net,
if (nf_ct_is_confirmed(ct))
nf_ct_delete(ct, 0, 0);
nf_ct_put(ct);
nf_reset_ct(skb);
nf_ct_set(skb, NULL, 0);
return false;
}
@@ -745,8 +745,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
/* Associate skb with specified zone. */
if (tmpl) {
ct = nf_ct_get(skb, &ctinfo);
nf_ct_put(ct);
nf_reset_ct(skb);
nf_conntrack_get(&tmpl->ct_general);
nf_ct_set(skb, tmpl, IP_CT_NEW);
}
@@ -1075,12 +1074,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
nf_ct_put(ct);
nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
if (key)
+3 -3
View File
@@ -782,7 +782,7 @@ static bool tcf_ct_skb_nfct_cached(struct net *net, struct sk_buff *skb,
return true;
drop_ct:
nf_ct_put(ct);
nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
return false;
@@ -1008,7 +1008,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
qdisc_skb_cb(skb)->post_ct = false;
ct = nf_ct_get(skb, &ctinfo);
if (ct) {
nf_ct_put(ct);
nf_reset_ct(skb);
nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
}
@@ -1055,7 +1055,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a,
/* Associate skb with specified zone. */
if (tmpl) {
nf_conntrack_put(skb_nfct(skb));
nf_reset_ct(skb);
nf_conntrack_get(&tmpl->ct_general);
nf_ct_set(skb, tmpl, IP_CT_NEW);
}
@@ -102,7 +102,6 @@ static int conntrack_data_insert(struct mnl_socket *sock, struct nlmsghdr *nlh,
uint16_t zone)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *rplnlh;
unsigned int portid;
int ret;
@@ -216,12 +215,11 @@ 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 int conntrack_count_zone(struct mnl_socket *sock, uint16_t zone)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh, *rplnlh;
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
struct nlattr *nest;
unsigned int portid;
int ret;
@@ -266,9 +264,8 @@ static int conntracK_count_zone(struct mnl_socket *sock, uint16_t zone)
static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh, *rplnlh;
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
struct nlattr *nest;
unsigned int portid;
int ret;
@@ -326,7 +323,7 @@ FIXTURE_SETUP(conntrack_dump_flush)
ret = mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID);
EXPECT_EQ(ret, 0);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID);
if (ret < 0 && errno == EPERM)
SKIP(return, "Needs to be run as root");
else if (ret < 0 && errno == EOPNOTSUPP)
@@ -423,7 +420,7 @@ FIXTURE_SETUP(conntrack_dump_flush)
NF_CT_DEFAULT_ZONE_ID);
EXPECT_EQ(ret, 0);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID);
EXPECT_GE(ret, 2);
if (ret > 2)
SKIP(return, "kernel does not support filtering by zone");
@@ -437,7 +434,7 @@ 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);
EXPECT_EQ(ret, 2);
}
@@ -447,13 +444,13 @@ TEST_F(conntrack_dump_flush, test_flush_by_zone)
ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID);
EXPECT_EQ(ret, 0);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID);
EXPECT_EQ(ret, 0);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1);
EXPECT_EQ(ret, 2);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2);
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);
EXPECT_EQ(ret, 2);
}
@@ -463,13 +460,13 @@ TEST_F(conntrack_dump_flush, test_flush_by_zone_default)
ret = conntrack_flush_zone(self->sock, NF_CT_DEFAULT_ZONE_ID);
EXPECT_EQ(ret, 0);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID);
EXPECT_EQ(ret, 2);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 1);
EXPECT_EQ(ret, 2);
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2);
ret = conntrack_count_zone(self->sock, TEST_ZONE_ID + 2);
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);
EXPECT_EQ(ret, 0);
}