Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 9130c2d30c17 ("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 <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
93e4649e c5e12ac3

+121 -41
+10 -6
include/net/dsa.h
··· 403 403 */ 404 404 u32 configure_vlan_while_not_filtering:1; 405 405 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. 412 409 */ 413 410 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; 414 418 415 419 /* Let DSA manage the FDB entries towards the 416 420 * CPU, based on the software bridge database.
+3 -2
net/dsa/tag.c
··· 105 105 106 106 p = netdev_priv(skb->dev); 107 107 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); 110 111 if (!nskb) { 111 112 kfree_skb(skb); 112 113 return 0;
+108 -33
net/dsa/tag.h
··· 44 44 return NULL; 45 45 } 46 46 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. 49 57 */ 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) 51 61 { 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; 57 63 int err; 58 - 59 - if (!br || br_vlan_enabled(br)) 60 - return skb; 61 64 62 65 err = br_vlan_get_proto(br, &proto); 63 66 if (err) 64 - return skb; 67 + return; 65 68 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 - } 72 - 73 - if (!skb_vlan_tag_present(skb)) 74 - return skb; 75 - 76 - vid = skb_vlan_tag_get_id(skb); 77 - 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); 69 + err = br_vlan_get_pvid_rcu(skb->dev, &pvid); 82 70 if (err) 83 - return skb; 71 + return; 84 72 85 - if (vid != pvid) 86 - return skb; 73 + if (vid == pvid && skb->vlan_proto == htons(proto)) 74 + __vlan_hwaccel_clear_tag(skb); 75 + } 76 + 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; 111 + 112 + err = br_vlan_get_proto(br, &proto); 113 + if (err) 114 + return; 115 + 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; 87 122 88 123 /* The sad part about attempting to untag from DSA is that we 89 124 * don't know, unless we check, if the skb will end up in ··· 130 95 * definitely keep the tag, to make sure it keeps working. 131 96 */ 132 97 upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid); 133 - if (upper_dev) 98 + if (!upper_dev) 99 + __vlan_hwaccel_clear_tag(skb); 100 + } 101 + 102 + /** 103 + * dsa_software_vlan_untag: Software VLAN untagging in DSA receive path 104 + * @skb: Pointer to socket buffer (packet) 105 + * 106 + * Receive path method for switches which cannot avoid tagging all packets 107 + * towards the CPU port. Called when ds->untag_bridge_pvid (legacy) or 108 + * ds->untag_vlan_aware_bridge_pvid is set to true. 109 + * 110 + * As a side effect of this method, any VLAN tag from the skb head is moved 111 + * to hwaccel. 112 + */ 113 + static inline struct sk_buff *dsa_software_vlan_untag(struct sk_buff *skb) 114 + { 115 + struct dsa_port *dp = dsa_user_to_port(skb->dev); 116 + struct net_device *br = dsa_port_bridge_dev_get(dp); 117 + u16 vid; 118 + 119 + /* software untagging for standalone ports not yet necessary */ 120 + if (!br) 134 121 return skb; 135 122 136 - __vlan_hwaccel_clear_tag(skb); 123 + /* Move VLAN tag from data to hwaccel */ 124 + if (!skb_vlan_tag_present(skb)) { 125 + skb = skb_vlan_untag(skb); 126 + if (!skb) 127 + return NULL; 128 + } 129 + 130 + if (!skb_vlan_tag_present(skb)) 131 + return skb; 132 + 133 + vid = skb_vlan_tag_get_id(skb); 134 + 135 + if (br_vlan_enabled(br)) { 136 + if (dp->ds->untag_vlan_aware_bridge_pvid) 137 + dsa_software_untag_vlan_aware_bridge(skb, br, vid); 138 + } else { 139 + if (dp->ds->untag_bridge_pvid) 140 + dsa_software_untag_vlan_unaware_bridge(skb, br, vid); 141 + } 137 142 138 143 return skb; 139 144 }