aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/pending-5.10/640-17-netfilter-flowtable-rework-ingress-vlan-matching.patch
blob: 67ec263f5171bfc872158817cb475a6cd0d85b43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 10 Feb 2021 19:39:23 +0100
Subject: [PATCH] netfilter: flowtable: rework ingress vlan matching

When dealing with bridges with VLAN filtering and DSA/switchdev offload,
the hardware could offload adding a VLAN tag configured in the bridge.
Since there doesn't seem to be an easy way to detect that, this patch
reworks the code to optionally match the last VLAN tag that would otherwise
be inserted by the bridge.
This matters when bypassing the bridge and attaching an ingress hook on
a DSA port below it.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---

--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -115,14 +115,15 @@ struct flow_offload_tuple {
 
 	u8				l3proto;
 	u8				l4proto;
-	struct {
-		u16			id;
-		__be16			proto;
-	} in_vlan[NF_FLOW_TABLE_VLAN_MAX];
 
 	/* All members above are keys for lookups, see flow_offload_hash(). */
 	struct { }			__hash;
 
+	struct {
+		u16			id;
+		__be16			proto;
+	} in_vlan[NF_FLOW_TABLE_VLAN_MAX], in_pvid;
+
 	u8				dir:4,
 					xmit_type:2,
 					in_vlan_num:2;
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -281,12 +281,13 @@ static bool nf_flow_skb_vlan_protocol(co
 }
 
 static void nf_flow_vlan_pop(struct sk_buff *skb,
-			     struct flow_offload_tuple_rhash *tuplehash)
+			     struct flow_offload_tuple_rhash *tuplehash,
+			     bool strip_pvid)
 {
 	struct vlan_hdr *vlan_hdr;
 	int i;
 
-	for (i = 0; i < tuplehash->tuple.in_vlan_num; i++) {
+	for (i = 0; i < tuplehash->tuple.in_vlan_num + strip_pvid; i++) {
 		if (skb_vlan_tag_present(skb)) {
 			__vlan_hwaccel_clear_tag(skb);
 			continue;
@@ -316,6 +317,31 @@ static unsigned int nf_flow_queue_xmit(s
 	return NF_STOLEN;
 }
 
+static bool
+nf_flow_offload_check_vlan(struct flow_offload_tuple *tuple,
+			   struct flow_offload_tuple *flow_tuple,
+			   bool *strip_pvid)
+{
+	int i, cur = 0;
+
+	if (flow_tuple->in_pvid.proto &&
+	    !memcmp(&tuple->in_vlan[0], &flow_tuple->in_pvid,
+		    sizeof(tuple->in_vlan[0])))
+		cur++;
+
+	*strip_pvid = cur;
+
+	for (i = 0; i < flow_tuple->in_vlan_num; i++, cur++) {
+		if (!memcmp(&tuple->in_vlan[cur], &flow_tuple->in_vlan[i],
+			    sizeof(tuple->in_vlan[0])))
+			continue;
+
+		return false;
+	}
+
+	return true;
+}
+
 unsigned int
 nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
 			const struct nf_hook_state *state)
@@ -329,6 +355,7 @@ nf_flow_offload_ip_hook(void *priv, stru
 	struct rtable *rt;
 	unsigned int thoff;
 	struct iphdr *iph;
+	bool strip_pvid;
 	__be32 nexthop;
 	u32 offset = 0;
 	int ret;
@@ -344,6 +371,10 @@ nf_flow_offload_ip_hook(void *priv, stru
 	if (tuplehash == NULL)
 		return NF_ACCEPT;
 
+	if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
+					&strip_pvid))
+		return NF_ACCEPT;
+
 	dir = tuplehash->tuple.dir;
 	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
 
@@ -368,7 +399,7 @@ nf_flow_offload_ip_hook(void *priv, stru
 		return NF_ACCEPT;
 	}
 
-	nf_flow_vlan_pop(skb, tuplehash);
+	nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
 	thoff -= offset;
 
 	if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
@@ -596,6 +627,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
 	struct net_device *outdev;
 	struct ipv6hdr *ip6h;
 	struct rt6_info *rt;
+	bool strip_pvid;
 	u32 offset = 0;
 	int ret;
 
@@ -610,6 +642,10 @@ nf_flow_offload_ipv6_hook(void *priv, st
 	if (tuplehash == NULL)
 		return NF_ACCEPT;
 
+	if (!nf_flow_offload_check_vlan(&tuple, &tuplehash->tuple,
+					&strip_pvid))
+		return NF_ACCEPT;
+
 	dir = tuplehash->tuple.dir;
 	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
 
@@ -630,7 +666,7 @@ nf_flow_offload_ipv6_hook(void *priv, st
 		return NF_ACCEPT;
 	}
 
-	nf_flow_vlan_pop(skb, tuplehash);
+	nf_flow_vlan_pop(skb, tuplehash, strip_pvid);
 
 	if (skb_try_make_writable(skb, sizeof(*ip6h)))
 		return NF_DROP;