kernel: add linux 5.10 support
[openwrt/staging/mkresin.git] / target / linux / generic / pending-5.10 / 640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Wed, 10 Feb 2021 19:39:23 +0100
3 Subject: [PATCH] netfilter: flowtable: rework ingress vlan matching
4
5 When dealing with bridges with VLAN filtering and DSA/switchdev offload,
6 the hardware could offload adding a VLAN tag configured in the bridge.
7 Since there doesn't seem to be an easy way to detect that, this patch
8 reworks the code to optionally match the last VLAN tag that would otherwise
9 be inserted by the bridge.
10 This matters when bypassing the bridge and attaching an ingress hook on
11 a DSA port below it.
12
13 Signed-off-by: Felix Fietkau <nbd@nbd.name>
14 ---
15
16 --- a/include/net/netfilter/nf_flow_table.h
17 +++ b/include/net/netfilter/nf_flow_table.h
18 @@ -115,14 +115,15 @@ struct flow_offload_tuple {
19
20 u8 l3proto;
21 u8 l4proto;
22 - struct {
23 - u16 id;
24 - __be16 proto;
25 - } in_vlan[NF_FLOW_TABLE_VLAN_MAX];
26
27 /* All members above are keys for lookups, see flow_offload_hash(). */
28 struct { } __hash;
29
30 + struct {
31 + u16 id;
32 + __be16 proto;
33 + } in_vlan[NF_FLOW_TABLE_VLAN_MAX], in_pvid;
34 +
35 u8 dir:4,
36 xmit_type:2,
37 in_vlan_num:2;
38 --- a/net/netfilter/nf_flow_table_ip.c
39 +++ b/net/netfilter/nf_flow_table_ip.c
40 @@ -281,12 +281,13 @@ static bool nf_flow_skb_vlan_protocol(co
41 }
42
43 static void nf_flow_vlan_pop(struct sk_buff *skb,
44 - struct flow_offload_tuple_rhash *tuplehash)
45 + struct flow_offload_tuple_rhash *tuplehash,
46 + bool strip_pvid)
47 {
48 struct vlan_hdr *vlan_hdr;
49 int i;
50
51 - for (i = 0; i < tuplehash->tuple.in_vlan_num; i++) {
52 + for (i = 0; i < tuplehash->tuple.in_vlan_num + strip_pvid; i++) {
53 if (skb_vlan_tag_present(skb)) {
54 __vlan_hwaccel_clear_tag(skb);
55 continue;
56 @@ -316,6 +317,31 @@ static unsigned int nf_flow_queue_xmit(s
57 return NF_STOLEN;
58 }
59
60 +static bool
61 +nf_flow_offload_check_vlan(struct flow_offload_tuple *tuple,
62 + struct flow_offload_tuple *flow_tuple,
63 + bool *strip_pvid)
64 +{
65 + int i, cur = 0;
66 +
67 + if (flow_tuple->in_pvid.proto &&
68 + !memcmp(&tuple->in_vlan[0], &flow_tuple->in_pvid,
69 + sizeof(tuple->in_vlan[0])))
70 + cur++;
71 +
72 + *strip_pvid = cur;
73 +
74 + for (i = 0; i < flow_tuple->in_vlan_num; i++, cur++) {
75 + if (!memcmp(&tuple->in_vlan[cur], &flow_tuple->in_vlan[i],
76 + sizeof(tuple->in_vlan[0])))
77 + continue;
78 +
79 + return false;
80 + }
81 +
82 + return true;
83 +}
84 +
85 unsigned int
86 nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
87 const struct nf_hook_state *state)
88 @@ -329,6 +355,7 @@ nf_flow_offload_ip_hook(void *priv, stru
89 struct rtable *rt;
90 unsigned int thoff;
91 struct iphdr *iph;
92 + bool strip_pvid;
93 __be32 nexthop;
94 u32 offset = 0;
95 int ret;
96 @@ -344,6 +371,10 @@ nf_flow_offload_ip_hook(void *priv, stru
97 if (tuplehash == NULL)
98 return NF_ACCEPT;
99
100 + if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
101 + &strip_pvid))
102 + return NF_ACCEPT;
103 +
104 dir = tuplehash->tuple.dir;
105 flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
106
107 @@ -368,7 +399,7 @@ nf_flow_offload_ip_hook(void *priv, stru
108 return NF_ACCEPT;
109 }
110
111 - nf_flow_vlan_pop(skb, tuplehash);
112 + nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
113 thoff -= offset;
114
115 if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
116 @@ -596,6 +627,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
117 struct net_device *outdev;
118 struct ipv6hdr *ip6h;
119 struct rt6_info *rt;
120 + bool strip_pvid;
121 u32 offset = 0;
122 int ret;
123
124 @@ -610,6 +642,10 @@ nf_flow_offload_ipv6_hook(void *priv, st
125 if (tuplehash == NULL)
126 return NF_ACCEPT;
127
128 + if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
129 + &strip_pvid))
130 + return NF_ACCEPT;
131 +
132 dir = tuplehash->tuple.dir;
133 flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
134
135 @@ -630,7 +666,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
136 return NF_ACCEPT;
137 }
138
139 - nf_flow_vlan_pop(skb, tuplehash);
140 + nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
141
142 if (skb_try_make_writable(skb, sizeof(*ip6h)))
143 return NF_DROP;