From 64ed3d80567280e5cccb4c4642464223862dabc6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 7 Jul 2021 17:36:21 +0200 Subject: kernel: backport a few upstream flow offloading fixes Signed-off-by: Felix Fietkau --- ...lowtable-dst_check-from-garbage-collector.patch | 159 +++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 target/linux/generic/backport-5.10/610-v5.13-54-netfilter-flowtable-dst_check-from-garbage-collector.patch (limited to 'target/linux/generic/backport-5.10/610-v5.13-54-netfilter-flowtable-dst_check-from-garbage-collector.patch') diff --git a/target/linux/generic/backport-5.10/610-v5.13-54-netfilter-flowtable-dst_check-from-garbage-collector.patch b/target/linux/generic/backport-5.10/610-v5.13-54-netfilter-flowtable-dst_check-from-garbage-collector.patch new file mode 100644 index 0000000000..42b55f021a --- /dev/null +++ b/target/linux/generic/backport-5.10/610-v5.13-54-netfilter-flowtable-dst_check-from-garbage-collector.patch @@ -0,0 +1,159 @@ +From: Pablo Neira Ayuso +Date: Sun, 28 Mar 2021 23:08:55 +0200 +Subject: [PATCH] netfilter: flowtable: dst_check() from garbage collector path + +Move dst_check() to the garbage collector path. Stale routes trigger the +flow entry teardown state which makes affected flows go back to the +classic forwarding path to re-evaluate flow offloading. + +IPv6 requires the dst cookie to work, store it in the flow_tuple, +otherwise dst_check() always fails. + +Fixes: e5075c0badaa ("netfilter: flowtable: call dst_check() to fall back to classic forwarding") +Signed-off-by: Pablo Neira Ayuso +--- + +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -129,7 +129,10 @@ struct flow_offload_tuple { + in_vlan_ingress:2; + u16 mtu; + union { +- struct dst_entry *dst_cache; ++ struct { ++ struct dst_entry *dst_cache; ++ u32 dst_cookie; ++ }; + struct { + u32 ifidx; + u32 hw_ifidx; +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -74,6 +74,18 @@ err_ct_refcnt: + } + EXPORT_SYMBOL_GPL(flow_offload_alloc); + ++static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple) ++{ ++ const struct rt6_info *rt; ++ ++ if (flow_tuple->l3proto == NFPROTO_IPV6) { ++ rt = (const struct rt6_info *)flow_tuple->dst_cache; ++ return rt6_get_cookie(rt); ++ } ++ ++ return 0; ++} ++ + static int flow_offload_fill_route(struct flow_offload *flow, + const struct nf_flow_route *route, + enum flow_offload_tuple_dir dir) +@@ -116,6 +128,7 @@ static int flow_offload_fill_route(struc + return -1; + + flow_tuple->dst_cache = dst; ++ flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple); + break; + } + flow_tuple->xmit_type = route->tuple[dir].xmit_type; +@@ -389,11 +402,33 @@ nf_flow_table_iterate(struct nf_flowtabl + return err; + } + ++static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple) ++{ ++ struct dst_entry *dst; ++ ++ if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || ++ tuple->xmit_type == FLOW_OFFLOAD_XMIT_XFRM) { ++ dst = tuple->dst_cache; ++ if (!dst_check(dst, tuple->dst_cookie)) ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool nf_flow_has_stale_dst(struct flow_offload *flow) ++{ ++ return flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) || ++ flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple); ++} ++ + static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) + { + struct nf_flowtable *flow_table = data; + +- if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct)) ++ if (nf_flow_has_expired(flow) || ++ nf_ct_is_dying(flow->ct) || ++ nf_flow_has_stale_dst(flow)) + set_bit(NF_FLOW_TEARDOWN, &flow->flags); + + if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) { +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -364,15 +364,6 @@ nf_flow_offload_ip_hook(void *priv, stru + if (nf_flow_state_check(flow, iph->protocol, skb, thoff)) + return NF_ACCEPT; + +- if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || +- tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) { +- rt = (struct rtable *)tuplehash->tuple.dst_cache; +- if (!dst_check(&rt->dst, 0)) { +- flow_offload_teardown(flow); +- return NF_ACCEPT; +- } +- } +- + if (skb_try_make_writable(skb, thoff + hdrsize)) + return NF_DROP; + +@@ -391,6 +382,7 @@ nf_flow_offload_ip_hook(void *priv, stru + nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + + if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { ++ rt = (struct rtable *)tuplehash->tuple.dst_cache; + memset(skb->cb, 0, sizeof(struct inet_skb_parm)); + IPCB(skb)->iif = skb->dev->ifindex; + IPCB(skb)->flags = IPSKB_FORWARDED; +@@ -399,6 +391,7 @@ nf_flow_offload_ip_hook(void *priv, stru + + switch (tuplehash->tuple.xmit_type) { + case FLOW_OFFLOAD_XMIT_NEIGH: ++ rt = (struct rtable *)tuplehash->tuple.dst_cache; + outdev = rt->dst.dev; + skb->dev = outdev; + nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr); +@@ -607,15 +600,6 @@ nf_flow_offload_ipv6_hook(void *priv, st + if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff)) + return NF_ACCEPT; + +- if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || +- tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) { +- rt = (struct rt6_info *)tuplehash->tuple.dst_cache; +- if (!dst_check(&rt->dst, 0)) { +- flow_offload_teardown(flow); +- return NF_ACCEPT; +- } +- } +- + if (skb_try_make_writable(skb, thoff + hdrsize)) + return NF_DROP; + +@@ -633,6 +617,7 @@ nf_flow_offload_ipv6_hook(void *priv, st + nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + + if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { ++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache; + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + IP6CB(skb)->iif = skb->dev->ifindex; + IP6CB(skb)->flags = IP6SKB_FORWARDED; +@@ -641,6 +626,7 @@ nf_flow_offload_ipv6_hook(void *priv, st + + switch (tuplehash->tuple.xmit_type) { + case FLOW_OFFLOAD_XMIT_NEIGH: ++ rt = (struct rt6_info *)tuplehash->tuple.dst_cache; + outdev = rt->dst.dev; + skb->dev = outdev; + nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); -- cgit v1.2.3