Skip to content

Commit 93e4649

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: provide a software untagging function on RX for VLAN-aware bridges
Through code analysis, I realized that the ds->untag_bridge_pvid logic is contradictory - see the newly added FIXME above the kernel-doc for dsa_software_untag_vlan_unaware_bridge(). Moreover, for the Felix driver, I need something very similar, but which is actually _not_ contradictory: untag the bridge PVID on RX, but for VLAN-aware bridges. The existing logic does it for VLAN-unaware bridges. Since I don't want to change the functionality of drivers which were supposedly properly tested with the ds->untag_bridge_pvid flag, I have introduced a new one: ds->untag_vlan_aware_bridge_pvid, and I have refactored the DSA reception code into a common path for both flags. TODO: both flags should be unified under a single ds->software_vlan_untag, which users of both current flags should set. This is not something that can be carried out right away. It needs very careful examination of all drivers which make use of this functionality, since some of them actually get this wrong in the first place. For example, commit 9130c2d ("net: dsa: microchip: ksz8795: Use software untagging on CPU port") uses this in a driver which has ds->configure_vlan_while_not_filtering = true. The latter mechanism has been known for many years to be broken by design: https://lore.kernel.org/netdev/CABumfLzJmXDN_W-8Z=p9KyKUVi_HhS7o_poBkeKHS2BkAiyYpw@mail.gmail.com/ and we have the situation of 2 bugs canceling each other. There is no private VLAN, and the port follows the PVID of the VLAN-unaware bridge. So, it's kinda ok for that driver to use the ds->untag_bridge_pvid mechanism, in a broken way. Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c5e12ac commit 93e4649

File tree

3 files changed

+118
-38
lines changed

3 files changed

+118
-38
lines changed

include/net/dsa.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,18 @@ struct dsa_switch {
403403
*/
404404
u32 configure_vlan_while_not_filtering:1;
405405

406-
/* If the switch driver always programs the CPU port as egress tagged
407-
* despite the VLAN configuration indicating otherwise, then setting
408-
* @untag_bridge_pvid will force the DSA receive path to pop the
409-
* bridge's default_pvid VLAN tagged frames to offer a consistent
410-
* behavior between a vlan_filtering=0 and vlan_filtering=1 bridge
411-
* device.
406+
/* Pop the default_pvid of VLAN-unaware bridge ports from tagged frames.
407+
* DEPRECATED: Do NOT set this field in new drivers. Instead look at
408+
* the dsa_software_vlan_untag() comments.
412409
*/
413410
u32 untag_bridge_pvid:1;
411+
/* Pop the default_pvid of VLAN-aware bridge ports from tagged frames.
412+
* Useful if the switch cannot preserve the VLAN tag as seen on the
413+
* wire for user port ingress, and chooses to send all frames as
414+
* VLAN-tagged to the CPU, including those which were originally
415+
* untagged.
416+
*/
417+
u32 untag_vlan_aware_bridge_pvid:1;
414418

415419
/* Let DSA manage the FDB entries towards the
416420
* CPU, based on the software bridge database.

net/dsa/tag.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
105105

106106
p = netdev_priv(skb->dev);
107107

108-
if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
109-
nskb = dsa_untag_bridge_pvid(skb);
108+
if (unlikely(cpu_dp->ds->untag_bridge_pvid ||
109+
cpu_dp->ds->untag_vlan_aware_bridge_pvid)) {
110+
nskb = dsa_software_vlan_untag(skb);
110111
if (!nskb) {
111112
kfree_skb(skb);
112113
return 0;

net/dsa/tag.h

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,46 +44,81 @@ static inline struct net_device *dsa_conduit_find_user(struct net_device *dev,
4444
return NULL;
4545
}
4646

47-
/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged
48-
* frames as untagged, since the bridge will not untag them.
47+
/**
48+
* dsa_software_untag_vlan_aware_bridge: Software untagging for VLAN-aware bridge
49+
* @skb: Pointer to received socket buffer (packet)
50+
* @br: Pointer to bridge upper interface of ingress port
51+
* @vid: Parsed VID from packet
52+
*
53+
* The bridge can process tagged packets. Software like STP/PTP may not. The
54+
* bridge can also process untagged packets, to the same effect as if they were
55+
* tagged with the PVID of the ingress port. So packets tagged with the PVID of
56+
* the bridge port must be software-untagged, to support both use cases.
4957
*/
50-
static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
58+
static inline void dsa_software_untag_vlan_aware_bridge(struct sk_buff *skb,
59+
struct net_device *br,
60+
u16 vid)
5161
{
52-
struct dsa_port *dp = dsa_user_to_port(skb->dev);
53-
struct net_device *br = dsa_port_bridge_dev_get(dp);
54-
struct net_device *dev = skb->dev;
55-
struct net_device *upper_dev;
56-
u16 vid, pvid, proto;
62+
u16 pvid, proto;
5763
int err;
5864

59-
if (!br || br_vlan_enabled(br))
60-
return skb;
61-
6265
err = br_vlan_get_proto(br, &proto);
6366
if (err)
64-
return skb;
67+
return;
6568

66-
/* Move VLAN tag from data to hwaccel */
67-
if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) {
68-
skb = skb_vlan_untag(skb);
69-
if (!skb)
70-
return NULL;
71-
}
69+
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
70+
if (err)
71+
return;
7272

73-
if (!skb_vlan_tag_present(skb))
74-
return skb;
73+
if (vid == pvid && skb->vlan_proto == htons(proto))
74+
__vlan_hwaccel_clear_tag(skb);
75+
}
7576

76-
vid = skb_vlan_tag_get_id(skb);
77+
/**
78+
* dsa_software_untag_vlan_unaware_bridge: Software untagging for VLAN-unaware bridge
79+
* @skb: Pointer to received socket buffer (packet)
80+
* @br: Pointer to bridge upper interface of ingress port
81+
* @vid: Parsed VID from packet
82+
*
83+
* The bridge ignores all VLAN tags. Software like STP/PTP may not (it may run
84+
* on the plain port, or on a VLAN upper interface). Maybe packets are coming
85+
* to software as tagged with a driver-defined VID which is NOT equal to the
86+
* PVID of the bridge port (since the bridge is VLAN-unaware, its configuration
87+
* should NOT be committed to hardware). DSA needs a method for this private
88+
* VID to be communicated by software to it, and if packets are tagged with it,
89+
* software-untag them. Note: the private VID may be different per bridge, to
90+
* support the FDB isolation use case.
91+
*
92+
* FIXME: this is currently implemented based on the broken assumption that
93+
* the "private VID" used by the driver in VLAN-unaware mode is equal to the
94+
* bridge PVID. It should not be, except for a coincidence; the bridge PVID is
95+
* irrelevant to the data path in the VLAN-unaware mode. Thus, the VID that
96+
* this function removes is wrong.
97+
*
98+
* All users of ds->untag_bridge_pvid should fix their drivers, if necessary,
99+
* to make the two independent. Only then, if there still remains a need to
100+
* strip the private VID from packets, then a new ds->ops->get_private_vid()
101+
* API shall be introduced to communicate to DSA what this VID is, which needs
102+
* to be stripped here.
103+
*/
104+
static inline void dsa_software_untag_vlan_unaware_bridge(struct sk_buff *skb,
105+
struct net_device *br,
106+
u16 vid)
107+
{
108+
struct net_device *upper_dev;
109+
u16 pvid, proto;
110+
int err;
77111

78-
/* We already run under an RCU read-side critical section since
79-
* we are called from netif_receive_skb_list_internal().
80-
*/
81-
err = br_vlan_get_pvid_rcu(dev, &pvid);
112+
err = br_vlan_get_proto(br, &proto);
82113
if (err)
83-
return skb;
114+
return;
84115

85-
if (vid != pvid)
86-
return skb;
116+
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
117+
if (err)
118+
return;
119+
120+
if (vid != pvid || skb->vlan_proto != htons(proto))
121+
return;
87122

88123
/* The sad part about attempting to untag from DSA is that we
89124
* don't know, unless we check, if the skb will end up in
@@ -95,10 +130,50 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
95130
* definitely keep the tag, to make sure it keeps working.
96131
*/
97132
upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid);
98-
if (upper_dev)
133+
if (!upper_dev)
134+
__vlan_hwaccel_clear_tag(skb);
135+
}
136+
137+
/**
138+
* dsa_software_vlan_untag: Software VLAN untagging in DSA receive path
139+
* @skb: Pointer to socket buffer (packet)
140+
*
141+
* Receive path method for switches which cannot avoid tagging all packets
142+
* towards the CPU port. Called when ds->untag_bridge_pvid (legacy) or
143+
* ds->untag_vlan_aware_bridge_pvid is set to true.
144+
*
145+
* As a side effect of this method, any VLAN tag from the skb head is moved
146+
* to hwaccel.
147+
*/
148+
static inline struct sk_buff *dsa_software_vlan_untag(struct sk_buff *skb)
149+
{
150+
struct dsa_port *dp = dsa_user_to_port(skb->dev);
151+
struct net_device *br = dsa_port_bridge_dev_get(dp);
152+
u16 vid;
153+
154+
/* software untagging for standalone ports not yet necessary */
155+
if (!br)
99156
return skb;
100157

101-
__vlan_hwaccel_clear_tag(skb);
158+
/* Move VLAN tag from data to hwaccel */
159+
if (!skb_vlan_tag_present(skb)) {
160+
skb = skb_vlan_untag(skb);
161+
if (!skb)
162+
return NULL;
163+
}
164+
165+
if (!skb_vlan_tag_present(skb))
166+
return skb;
167+
168+
vid = skb_vlan_tag_get_id(skb);
169+
170+
if (br_vlan_enabled(br)) {
171+
if (dp->ds->untag_vlan_aware_bridge_pvid)
172+
dsa_software_untag_vlan_aware_bridge(skb, br, vid);
173+
} else {
174+
if (dp->ds->untag_bridge_pvid)
175+
dsa_software_untag_vlan_unaware_bridge(skb, br, vid);
176+
}
102177

103178
return skb;
104179
}

0 commit comments

Comments
 (0)