da77b9c2f1
Deleted (reverse-appliable): generic/pending-5.10/110-perf-jevents-fix-getting-maximum-number-of-fds.patch Manually refreshed: ramips/patches-5.10/835-asoc-add-mt7620-support.patch Signed-off-by: Rui Salvaterra <rsalvaterra@gmail.com>
375 lines
11 KiB
Diff
375 lines
11 KiB
Diff
From: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
Date: Wed, 24 Mar 2021 02:30:40 +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 <pablo@netfilter.org>
|
|
---
|
|
|
|
--- 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 {
|
|
@@ -167,6 +174,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
|
|
@@ -207,6 +207,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)
|
|
@@ -222,6 +240,7 @@ nf_flow_offload_ip_hook(void *priv, stru
|
|
struct iphdr *iph;
|
|
__be32 nexthop;
|
|
u32 hdrsize;
|
|
+ int ret;
|
|
|
|
if (skb->protocol != htons(ETH_P_IP))
|
|
return NF_ACCEPT;
|
|
@@ -244,9 +263,13 @@ nf_flow_offload_ip_hook(void *priv, stru
|
|
if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
|
|
return NF_ACCEPT;
|
|
|
|
- if (!dst_check(&rt->dst, 0)) {
|
|
- flow_offload_teardown(flow);
|
|
- 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))
|
|
@@ -263,8 +286,6 @@ 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)) {
|
|
memset(skb->cb, 0, sizeof(struct inet_skb_parm));
|
|
IPCB(skb)->iif = skb->dev->ifindex;
|
|
@@ -272,13 +293,23 @@ nf_flow_offload_ip_hook(void *priv, stru
|
|
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:
|
|
+ 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);
|
|
|
|
@@ -444,6 +475,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
|
|
struct ipv6hdr *ip6h;
|
|
struct rt6_info *rt;
|
|
u32 hdrsize;
|
|
+ int ret;
|
|
|
|
if (skb->protocol != htons(ETH_P_IPV6))
|
|
return NF_ACCEPT;
|
|
@@ -465,9 +497,13 @@ nf_flow_offload_ipv6_hook(void *priv, st
|
|
sizeof(*ip6h)))
|
|
return NF_ACCEPT;
|
|
|
|
- if (!dst_check(&rt->dst, 0)) {
|
|
- flow_offload_teardown(flow);
|
|
- 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, sizeof(*ip6h) + hdrsize))
|
|
@@ -484,8 +520,6 @@ 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)) {
|
|
memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
|
|
IP6CB(skb)->iif = skb->dev->ifindex;
|
|
@@ -493,12 +527,22 @@ nf_flow_offload_ipv6_hook(void *priv, st
|
|
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:
|
|
+ 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,27 +65,43 @@ static int nft_dev_fill_forward_path(con
|
|
|
|
struct nft_forward_info {
|
|
const struct net_device *indev;
|
|
+ const struct net_device *outdev;
|
|
+ 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->indev = 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->indev = NULL;
|
|
break;
|
|
}
|
|
}
|
|
+ if (!info->outdev)
|
|
+ info->outdev = info->indev;
|
|
}
|
|
|
|
static bool nft_flowtable_find_dev(const struct net_device *dev,
|
|
@@ -114,14 +129,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.indev || !nft_flowtable_find_dev(info.indev, ft))
|
|
return;
|
|
|
|
route->tuple[!dir].in.ifindex = info.indev->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.outdev->ifindex;
|
|
+ route->tuple[dir].xmit_type = info.xmit_type;
|
|
+ }
|
|
}
|
|
|
|
static int nft_flow_route(const struct nft_pktinfo *pkt,
|