From b10d6044599d8c1fa7fbb2374bcbf30118d39db1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 24 Oct 2020 21:14:16 +0200 Subject: kernel: add linux 5.10 support Signed-off-by: Felix Fietkau --- ...lowtable-use-dev_fill_forward_path-to-obt.patch | 336 +++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch (limited to 'target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch') diff --git a/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch b/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch new file mode 100644 index 0000000000..d4bf68a690 --- /dev/null +++ b/target/linux/generic/pending-5.10/640-06-netfilter-flowtable-use-dev_fill_forward_path-to-obt.patch @@ -0,0 +1,336 @@ +From: Pablo Neira Ayuso +Date: Fri, 20 Nov 2020 13:49:19 +0100 +Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to + obtain egress device + +The egress device in the tuple is obtained from route. Use +dev_fill_forward_path() instead to provide the real egress device for +this flow whenever this is available. + +The new FLOW_OFFLOAD_XMIT_DIRECT type uses dev_queue_xmit() to transmit +ethernet frames. Cache the source and destination hardware address to +use dev_queue_xmit() to transfer packets. + +The FLOW_OFFLOAD_XMIT_DIRECT replaces FLOW_OFFLOAD_XMIT_NEIGH if +dev_fill_forward_path() finds a direct transmit path. + +In case of topology updates, if peer is moved to different bridge port, +the connection will time out, reconnect will result in a new entry with +the correct path. Snooping fdb updates would allow for cleaning up stale +flowtable entries. + +Signed-off-by: Pablo Neira Ayuso +--- + +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -92,6 +92,7 @@ enum flow_offload_tuple_dir { + enum flow_offload_xmit_type { + FLOW_OFFLOAD_XMIT_NEIGH = 0, + FLOW_OFFLOAD_XMIT_XFRM, ++ FLOW_OFFLOAD_XMIT_DIRECT, + }; + + struct flow_offload_tuple { +@@ -120,8 +121,14 @@ struct flow_offload_tuple { + xmit_type:2; + + u16 mtu; +- +- struct dst_entry *dst_cache; ++ union { ++ struct dst_entry *dst_cache; ++ struct { ++ u32 ifidx; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ } out; ++ }; + }; + + struct flow_offload_tuple_rhash { +@@ -168,6 +175,11 @@ struct nf_flow_route { + struct { + u32 ifindex; + } in; ++ struct { ++ u32 ifindex; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ } out; + enum flow_offload_xmit_type xmit_type; + } tuple[FLOW_OFFLOAD_DIR_MAX]; + }; +--- a/net/netfilter/nf_flow_table_core.c ++++ b/net/netfilter/nf_flow_table_core.c +@@ -81,9 +81,6 @@ static int flow_offload_fill_route(struc + struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; + struct dst_entry *dst = route->tuple[dir].dst; + +- if (!dst_hold_safe(route->tuple[dir].dst)) +- return -1; +- + switch (flow_tuple->l3proto) { + case NFPROTO_IPV4: + flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true); +@@ -94,12 +91,36 @@ static int flow_offload_fill_route(struc + } + + flow_tuple->iifidx = route->tuple[dir].in.ifindex; ++ ++ switch (route->tuple[dir].xmit_type) { ++ case FLOW_OFFLOAD_XMIT_DIRECT: ++ memcpy(flow_tuple->out.h_dest, route->tuple[dir].out.h_dest, ++ ETH_ALEN); ++ memcpy(flow_tuple->out.h_source, route->tuple[dir].out.h_source, ++ ETH_ALEN); ++ flow_tuple->out.ifidx = route->tuple[dir].out.ifindex; ++ break; ++ case FLOW_OFFLOAD_XMIT_XFRM: ++ case FLOW_OFFLOAD_XMIT_NEIGH: ++ if (!dst_hold_safe(route->tuple[dir].dst)) ++ return -1; ++ ++ flow_tuple->dst_cache = dst; ++ break; ++ } + flow_tuple->xmit_type = route->tuple[dir].xmit_type; +- flow_tuple->dst_cache = dst; + + return 0; + } + ++static void nft_flow_dst_release(struct flow_offload *flow, ++ enum flow_offload_tuple_dir dir) ++{ ++ if (flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH || ++ flow->tuplehash[dir].tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) ++ dst_release(flow->tuplehash[dir].tuple.dst_cache); ++} ++ + int flow_offload_route_init(struct flow_offload *flow, + const struct nf_flow_route *route) + { +@@ -118,7 +139,7 @@ int flow_offload_route_init(struct flow_ + return 0; + + err_route_reply: +- dst_release(route->tuple[FLOW_OFFLOAD_DIR_ORIGINAL].dst); ++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL); + + return err; + } +@@ -169,8 +190,8 @@ static void flow_offload_fixup_ct(struct + + static void flow_offload_route_release(struct flow_offload *flow) + { +- dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.dst_cache); +- dst_release(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_cache); ++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_ORIGINAL); ++ nft_flow_dst_release(flow, FLOW_OFFLOAD_DIR_REPLY); + } + + void flow_offload_free(struct flow_offload *flow) +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -248,6 +248,24 @@ static unsigned int nf_flow_xmit_xfrm(st + return NF_STOLEN; + } + ++static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb, ++ const struct flow_offload_tuple_rhash *tuplehash, ++ unsigned short type) ++{ ++ struct net_device *outdev; ++ ++ outdev = dev_get_by_index_rcu(net, tuplehash->tuple.out.ifidx); ++ if (!outdev) ++ return NF_DROP; ++ ++ skb->dev = outdev; ++ dev_hard_header(skb, skb->dev, type, tuplehash->tuple.out.h_dest, ++ tuplehash->tuple.out.h_source, skb->len); ++ dev_queue_xmit(skb); ++ ++ return NF_STOLEN; ++} ++ + unsigned int + nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +@@ -262,6 +280,7 @@ nf_flow_offload_ip_hook(void *priv, stru + unsigned int thoff; + struct iphdr *iph; + __be32 nexthop; ++ int ret; + + if (skb->protocol != htons(ETH_P_IP)) + return NF_ACCEPT; +@@ -303,22 +322,32 @@ nf_flow_offload_ip_hook(void *priv, stru + if (flow_table->flags & NF_FLOWTABLE_COUNTER) + nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + +- rt = (struct rtable *)tuplehash->tuple.dst_cache; +- + 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; + return nf_flow_xmit_xfrm(skb, state, &rt->dst); + } + +- outdev = rt->dst.dev; +- skb->dev = outdev; +- nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr); +- skb_dst_set_noref(skb, &rt->dst); +- neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb); ++ 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); ++ skb_dst_set_noref(skb, &rt->dst); ++ neigh_xmit(NEIGH_ARP_TABLE, outdev, &nexthop, skb); ++ ret = NF_STOLEN; ++ break; ++ case FLOW_OFFLOAD_XMIT_DIRECT: ++ ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IP); ++ if (ret == NF_DROP) ++ flow_offload_teardown(flow); ++ break; ++ } + +- return NF_STOLEN; ++ return ret; + } + EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook); + +@@ -504,6 +533,7 @@ nf_flow_offload_ipv6_hook(void *priv, st + struct net_device *outdev; + struct ipv6hdr *ip6h; + struct rt6_info *rt; ++ int ret; + + if (skb->protocol != htons(ETH_P_IPV6)) + return NF_ACCEPT; +@@ -545,21 +575,31 @@ nf_flow_offload_ipv6_hook(void *priv, st + if (flow_table->flags & NF_FLOWTABLE_COUNTER) + nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); + +- rt = (struct rt6_info *)tuplehash->tuple.dst_cache; +- + 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; + return nf_flow_xmit_xfrm(skb, state, &rt->dst); + } + +- outdev = rt->dst.dev; +- skb->dev = outdev; +- nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); +- skb_dst_set_noref(skb, &rt->dst); +- neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb); ++ 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); ++ skb_dst_set_noref(skb, &rt->dst); ++ neigh_xmit(NEIGH_ND_TABLE, outdev, nexthop, skb); ++ ret = NF_STOLEN; ++ break; ++ case FLOW_OFFLOAD_XMIT_DIRECT: ++ ret = nf_flow_queue_xmit(state->net, skb, tuplehash, ETH_P_IPV6); ++ if (ret == NF_DROP) ++ flow_offload_teardown(flow); ++ break; ++ } + +- return NF_STOLEN; ++ return ret; + } + EXPORT_SYMBOL_GPL(nf_flow_offload_ipv6_hook); +--- a/net/netfilter/nft_flow_offload.c ++++ b/net/netfilter/nft_flow_offload.c +@@ -39,12 +39,11 @@ static void nft_default_forward_path(str + static int nft_dev_fill_forward_path(const struct nf_flow_route *route, + const struct dst_entry *dst_cache, + const struct nf_conn *ct, +- enum ip_conntrack_dir dir, ++ enum ip_conntrack_dir dir, u8 *ha, + struct net_device_path_stack *stack) + { + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; + struct net_device *dev = dst_cache->dev; +- unsigned char ha[ETH_ALEN]; + struct neighbour *n; + u8 nud_state; + +@@ -66,22 +65,35 @@ static int nft_dev_fill_forward_path(con + + struct nft_forward_info { + const struct net_device *dev; ++ u8 h_source[ETH_ALEN]; ++ u8 h_dest[ETH_ALEN]; ++ enum flow_offload_xmit_type xmit_type; + }; + + static void nft_dev_path_info(const struct net_device_path_stack *stack, +- struct nft_forward_info *info) ++ struct nft_forward_info *info, ++ unsigned char *ha) + { + const struct net_device_path *path; + int i; + ++ memcpy(info->h_dest, ha, ETH_ALEN); ++ + for (i = 0; i < stack->num_paths; i++) { + path = &stack->path[i]; + switch (path->type) { + case DEV_PATH_ETHERNET: + info->dev = path->dev; ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); + break; +- case DEV_PATH_VLAN: + case DEV_PATH_BRIDGE: ++ if (is_zero_ether_addr(info->h_source)) ++ memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN); ++ ++ info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; ++ break; ++ case DEV_PATH_VLAN: + default: + info->dev = NULL; + break; +@@ -114,14 +126,22 @@ static void nft_dev_forward_path(struct + const struct dst_entry *dst = route->tuple[dir].dst; + struct net_device_path_stack stack; + struct nft_forward_info info = {}; ++ unsigned char ha[ETH_ALEN]; + +- if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0) +- nft_dev_path_info(&stack, &info); ++ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) ++ nft_dev_path_info(&stack, &info, ha); + + if (!info.dev || !nft_flowtable_find_dev(info.dev, ft)) + return; + + route->tuple[!dir].in.ifindex = info.dev->ifindex; ++ ++ if (info.xmit_type == FLOW_OFFLOAD_XMIT_DIRECT) { ++ memcpy(route->tuple[dir].out.h_source, info.h_source, ETH_ALEN); ++ memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); ++ route->tuple[dir].out.ifindex = info.dev->ifindex; ++ route->tuple[dir].xmit_type = info.xmit_type; ++ } + } + + static int nft_flow_route(const struct nft_pktinfo *pkt, -- cgit v1.2.3