1f9bbe447SVladimir Oltean // SPDX-License-Identifier: GPL-2.0 2f9bbe447SVladimir Oltean /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com> 3f9bbe447SVladimir Oltean * 4f9bbe447SVladimir Oltean * This module is not a complete tagger implementation. It only provides 5f9bbe447SVladimir Oltean * primitives for taggers that rely on 802.1Q VLAN tags to use. The 6f9bbe447SVladimir Oltean * dsa_8021q_netdev_ops is registered for API compliance and not used 7f9bbe447SVladimir Oltean * directly by callers. 8f9bbe447SVladimir Oltean */ 9f9bbe447SVladimir Oltean #include <linux/if_bridge.h> 10f9bbe447SVladimir Oltean #include <linux/if_vlan.h> 11f9bbe447SVladimir Oltean 12f9bbe447SVladimir Oltean #include "dsa_priv.h" 13f9bbe447SVladimir Oltean 140471dd42SVladimir Oltean /* Binary structure of the fake 12-bit VID field (when the TPID is 150471dd42SVladimir Oltean * ETH_P_DSA_8021Q): 160471dd42SVladimir Oltean * 170471dd42SVladimir Oltean * | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 180471dd42SVladimir Oltean * +-----------+-----+-----------------+-----------+-----------------------+ 190471dd42SVladimir Oltean * | DIR | RSV | SWITCH_ID | RSV | PORT | 200471dd42SVladimir Oltean * +-----------+-----+-----------------+-----------+-----------------------+ 210471dd42SVladimir Oltean * 220471dd42SVladimir Oltean * DIR - VID[11:10]: 230471dd42SVladimir Oltean * Direction flags. 240471dd42SVladimir Oltean * * 1 (0b01) for RX VLAN, 250471dd42SVladimir Oltean * * 2 (0b10) for TX VLAN. 260471dd42SVladimir Oltean * These values make the special VIDs of 0, 1 and 4095 to be left 270471dd42SVladimir Oltean * unused by this coding scheme. 280471dd42SVladimir Oltean * 290471dd42SVladimir Oltean * RSV - VID[9]: 300471dd42SVladimir Oltean * To be used for further expansion of SWITCH_ID or for other purposes. 31bcccb0a5SVladimir Oltean * Must be transmitted as zero and ignored on receive. 320471dd42SVladimir Oltean * 330471dd42SVladimir Oltean * SWITCH_ID - VID[8:6]: 34fcee85f1SVivien Didelot * Index of switch within DSA tree. Must be between 0 and 7. 350471dd42SVladimir Oltean * 360471dd42SVladimir Oltean * RSV - VID[5:4]: 370471dd42SVladimir Oltean * To be used for further expansion of PORT or for other purposes. 38bcccb0a5SVladimir Oltean * Must be transmitted as zero and ignored on receive. 390471dd42SVladimir Oltean * 400471dd42SVladimir Oltean * PORT - VID[3:0]: 41fcee85f1SVivien Didelot * Index of switch port. Must be between 0 and 15. 42f9bbe447SVladimir Oltean */ 430471dd42SVladimir Oltean 440471dd42SVladimir Oltean #define DSA_8021Q_DIR_SHIFT 10 450471dd42SVladimir Oltean #define DSA_8021Q_DIR_MASK GENMASK(11, 10) 460471dd42SVladimir Oltean #define DSA_8021Q_DIR(x) (((x) << DSA_8021Q_DIR_SHIFT) & \ 470471dd42SVladimir Oltean DSA_8021Q_DIR_MASK) 480471dd42SVladimir Oltean #define DSA_8021Q_DIR_RX DSA_8021Q_DIR(1) 490471dd42SVladimir Oltean #define DSA_8021Q_DIR_TX DSA_8021Q_DIR(2) 500471dd42SVladimir Oltean 510471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_SHIFT 6 520471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID_MASK GENMASK(8, 6) 530471dd42SVladimir Oltean #define DSA_8021Q_SWITCH_ID(x) (((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \ 540471dd42SVladimir Oltean DSA_8021Q_SWITCH_ID_MASK) 550471dd42SVladimir Oltean 560471dd42SVladimir Oltean #define DSA_8021Q_PORT_SHIFT 0 570471dd42SVladimir Oltean #define DSA_8021Q_PORT_MASK GENMASK(3, 0) 580471dd42SVladimir Oltean #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ 590471dd42SVladimir Oltean DSA_8021Q_PORT_MASK) 60f9bbe447SVladimir Oltean 61f9bbe447SVladimir Oltean /* Returns the VID to be inserted into the frame from xmit for switch steering 62f9bbe447SVladimir Oltean * instructions on egress. Encodes switch ID and port ID. 63f9bbe447SVladimir Oltean */ 64f9bbe447SVladimir Oltean u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) 65f9bbe447SVladimir Oltean { 660471dd42SVladimir Oltean return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(ds->index) | 670471dd42SVladimir Oltean DSA_8021Q_PORT(port); 68f9bbe447SVladimir Oltean } 69f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_tx_vid); 70f9bbe447SVladimir Oltean 71f9bbe447SVladimir Oltean /* Returns the VID that will be installed as pvid for this switch port, sent as 72f9bbe447SVladimir Oltean * tagged egress towards the CPU port and decoded by the rcv function. 73f9bbe447SVladimir Oltean */ 74f9bbe447SVladimir Oltean u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) 75f9bbe447SVladimir Oltean { 760471dd42SVladimir Oltean return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) | 770471dd42SVladimir Oltean DSA_8021Q_PORT(port); 78f9bbe447SVladimir Oltean } 79f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid); 80f9bbe447SVladimir Oltean 81f9bbe447SVladimir Oltean /* Returns the decoded switch ID from the RX VID. */ 82f9bbe447SVladimir Oltean int dsa_8021q_rx_switch_id(u16 vid) 83f9bbe447SVladimir Oltean { 840471dd42SVladimir Oltean return (vid & DSA_8021Q_SWITCH_ID_MASK) >> DSA_8021Q_SWITCH_ID_SHIFT; 85f9bbe447SVladimir Oltean } 86f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id); 87f9bbe447SVladimir Oltean 88f9bbe447SVladimir Oltean /* Returns the decoded port ID from the RX VID. */ 89f9bbe447SVladimir Oltean int dsa_8021q_rx_source_port(u16 vid) 90f9bbe447SVladimir Oltean { 910471dd42SVladimir Oltean return (vid & DSA_8021Q_PORT_MASK) >> DSA_8021Q_PORT_SHIFT; 92f9bbe447SVladimir Oltean } 93f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); 94f9bbe447SVladimir Oltean 955f33183bSVladimir Oltean static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port) 965f33183bSVladimir Oltean { 975f33183bSVladimir Oltean struct bridge_vlan_info vinfo; 985f33183bSVladimir Oltean struct net_device *slave; 995f33183bSVladimir Oltean u16 pvid; 1005f33183bSVladimir Oltean int err; 1015f33183bSVladimir Oltean 1025f33183bSVladimir Oltean if (!dsa_is_user_port(ds, port)) 1035f33183bSVladimir Oltean return 0; 1045f33183bSVladimir Oltean 10568bb8ea8SVivien Didelot slave = dsa_to_port(ds, port)->slave; 1065f33183bSVladimir Oltean 1075f33183bSVladimir Oltean err = br_vlan_get_pvid(slave, &pvid); 1085f33183bSVladimir Oltean if (err < 0) 1095f33183bSVladimir Oltean /* There is no pvid on the bridge for this port, which is 1105f33183bSVladimir Oltean * perfectly valid. Nothing to restore, bye-bye! 1115f33183bSVladimir Oltean */ 1125f33183bSVladimir Oltean return 0; 1135f33183bSVladimir Oltean 1145f33183bSVladimir Oltean err = br_vlan_get_info(slave, pvid, &vinfo); 1155f33183bSVladimir Oltean if (err < 0) { 1165f33183bSVladimir Oltean dev_err(ds->dev, "Couldn't determine PVID attributes\n"); 1175f33183bSVladimir Oltean return err; 1185f33183bSVladimir Oltean } 1195f33183bSVladimir Oltean 12068bb8ea8SVivien Didelot return dsa_port_vid_add(dsa_to_port(ds, port), pvid, vinfo.flags); 1215f33183bSVladimir Oltean } 1225f33183bSVladimir Oltean 1235f33183bSVladimir Oltean /* If @enabled is true, installs @vid with @flags into the switch port's HW 1245f33183bSVladimir Oltean * filter. 1255f33183bSVladimir Oltean * If @enabled is false, deletes @vid (ignores @flags) from the port. Had the 1265f33183bSVladimir Oltean * user explicitly configured this @vid through the bridge core, then the @vid 1275f33183bSVladimir Oltean * is installed again, but this time with the flags from the bridge layer. 1285f33183bSVladimir Oltean */ 1295f33183bSVladimir Oltean static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, 1305f33183bSVladimir Oltean u16 flags, bool enabled) 1315f33183bSVladimir Oltean { 13268bb8ea8SVivien Didelot struct dsa_port *dp = dsa_to_port(ds, port); 1335f33183bSVladimir Oltean struct bridge_vlan_info vinfo; 1345f33183bSVladimir Oltean int err; 1355f33183bSVladimir Oltean 1365f33183bSVladimir Oltean if (enabled) 1375f33183bSVladimir Oltean return dsa_port_vid_add(dp, vid, flags); 1385f33183bSVladimir Oltean 1395f33183bSVladimir Oltean err = dsa_port_vid_del(dp, vid); 1405f33183bSVladimir Oltean if (err < 0) 1415f33183bSVladimir Oltean return err; 1425f33183bSVladimir Oltean 1435f33183bSVladimir Oltean /* Nothing to restore from the bridge for a non-user port. 1445f33183bSVladimir Oltean * The CPU port VLANs are restored implicitly with the user ports, 1455f33183bSVladimir Oltean * similar to how the bridge does in dsa_slave_vlan_add and 1465f33183bSVladimir Oltean * dsa_slave_vlan_del. 1475f33183bSVladimir Oltean */ 1485f33183bSVladimir Oltean if (!dsa_is_user_port(ds, port)) 1495f33183bSVladimir Oltean return 0; 1505f33183bSVladimir Oltean 1515f33183bSVladimir Oltean err = br_vlan_get_info(dp->slave, vid, &vinfo); 1525f33183bSVladimir Oltean /* Couldn't determine bridge attributes for this vid, 1535f33183bSVladimir Oltean * it means the bridge had not configured it. 1545f33183bSVladimir Oltean */ 1555f33183bSVladimir Oltean if (err < 0) 1565f33183bSVladimir Oltean return 0; 1575f33183bSVladimir Oltean 1585f33183bSVladimir Oltean /* Restore the VID from the bridge */ 1595f33183bSVladimir Oltean err = dsa_port_vid_add(dp, vid, vinfo.flags); 1605f33183bSVladimir Oltean if (err < 0) 1615f33183bSVladimir Oltean return err; 1625f33183bSVladimir Oltean 1635f33183bSVladimir Oltean vinfo.flags &= ~BRIDGE_VLAN_INFO_PVID; 1645f33183bSVladimir Oltean 1655f33183bSVladimir Oltean return dsa_port_vid_add(dp->cpu_dp, vid, vinfo.flags); 1665f33183bSVladimir Oltean } 1675f33183bSVladimir Oltean 168f9bbe447SVladimir Oltean /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single 169f9bbe447SVladimir Oltean * front-panel switch port (here swp0). 170f9bbe447SVladimir Oltean * 171f9bbe447SVladimir Oltean * Port identification through VLAN (802.1Q) tags has different requirements 172f9bbe447SVladimir Oltean * for it to work effectively: 173f9bbe447SVladimir Oltean * - On RX (ingress from network): each front-panel port must have a pvid 174f9bbe447SVladimir Oltean * that uniquely identifies it, and the egress of this pvid must be tagged 175f9bbe447SVladimir Oltean * towards the CPU port, so that software can recover the source port based 176f9bbe447SVladimir Oltean * on the VID in the frame. But this would only work for standalone ports; 177f9bbe447SVladimir Oltean * if bridged, this VLAN setup would break autonomous forwarding and would 178f9bbe447SVladimir Oltean * force all switched traffic to pass through the CPU. So we must also make 179f9bbe447SVladimir Oltean * the other front-panel ports members of this VID we're adding, albeit 180f9bbe447SVladimir Oltean * we're not making it their PVID (they'll still have their own). 181f9bbe447SVladimir Oltean * By the way - just because we're installing the same VID in multiple 182f9bbe447SVladimir Oltean * switch ports doesn't mean that they'll start to talk to one another, even 183f9bbe447SVladimir Oltean * while not bridged: the final forwarding decision is still an AND between 184f9bbe447SVladimir Oltean * the L2 forwarding information (which is limiting forwarding in this case) 185f9bbe447SVladimir Oltean * and the VLAN-based restrictions (of which there are none in this case, 186f9bbe447SVladimir Oltean * since all ports are members). 187f9bbe447SVladimir Oltean * - On TX (ingress from CPU and towards network) we are faced with a problem. 188f9bbe447SVladimir Oltean * If we were to tag traffic (from within DSA) with the port's pvid, all 189f9bbe447SVladimir Oltean * would be well, assuming the switch ports were standalone. Frames would 190f9bbe447SVladimir Oltean * have no choice but to be directed towards the correct front-panel port. 191f9bbe447SVladimir Oltean * But because we also want the RX VLAN to not break bridging, then 192f9bbe447SVladimir Oltean * inevitably that means that we have to give them a choice (of what 193f9bbe447SVladimir Oltean * front-panel port to go out on), and therefore we cannot steer traffic 194f9bbe447SVladimir Oltean * based on the RX VID. So what we do is simply install one more VID on the 195f9bbe447SVladimir Oltean * front-panel and CPU ports, and profit off of the fact that steering will 196f9bbe447SVladimir Oltean * work just by virtue of the fact that there is only one other port that's 197f9bbe447SVladimir Oltean * a member of the VID we're tagging the traffic with - the desired one. 198f9bbe447SVladimir Oltean * 199f9bbe447SVladimir Oltean * So at the end, each front-panel port will have one RX VID (also the PVID), 200f9bbe447SVladimir Oltean * the RX VID of all other front-panel ports, and one TX VID. Whereas the CPU 201f9bbe447SVladimir Oltean * port will have the RX and TX VIDs of all front-panel ports, and on top of 202f9bbe447SVladimir Oltean * that, is also tagged-input and tagged-output (VLAN trunk). 203f9bbe447SVladimir Oltean * 204f9bbe447SVladimir Oltean * CPU port CPU port 205f9bbe447SVladimir Oltean * +-------------+-----+-------------+ +-------------+-----+-------------+ 206f9bbe447SVladimir Oltean * | RX VID | | | | TX VID | | | 207f9bbe447SVladimir Oltean * | of swp0 | | | | of swp0 | | | 208f9bbe447SVladimir Oltean * | +-----+ | | +-----+ | 209f9bbe447SVladimir Oltean * | ^ T | | | Tagged | 210f9bbe447SVladimir Oltean * | | | | | ingress | 211f9bbe447SVladimir Oltean * | +-------+---+---+-------+ | | +-----------+ | 212f9bbe447SVladimir Oltean * | | | | | | | | Untagged | 213f9bbe447SVladimir Oltean * | | U v U v U v | | v egress | 214f9bbe447SVladimir Oltean * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | 215f9bbe447SVladimir Oltean * | | | | | | | | | | | | | | | | | | | | 216f9bbe447SVladimir Oltean * | |PVID | | | | | | | | | | | | | | | | | | 217f9bbe447SVladimir Oltean * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ 218f9bbe447SVladimir Oltean * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 219f9bbe447SVladimir Oltean */ 220f9bbe447SVladimir Oltean int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled) 221f9bbe447SVladimir Oltean { 222f9bbe447SVladimir Oltean int upstream = dsa_upstream_port(ds, port); 223f9bbe447SVladimir Oltean u16 rx_vid = dsa_8021q_rx_vid(ds, port); 224f9bbe447SVladimir Oltean u16 tx_vid = dsa_8021q_tx_vid(ds, port); 225f9bbe447SVladimir Oltean int i, err; 226f9bbe447SVladimir Oltean 227f9bbe447SVladimir Oltean /* The CPU port is implicitly configured by 228f9bbe447SVladimir Oltean * configuring the front-panel ports 229f9bbe447SVladimir Oltean */ 230f9bbe447SVladimir Oltean if (!dsa_is_user_port(ds, port)) 231f9bbe447SVladimir Oltean return 0; 232f9bbe447SVladimir Oltean 233f9bbe447SVladimir Oltean /* Add this user port's RX VID to the membership list of all others 234f9bbe447SVladimir Oltean * (including itself). This is so that bridging will not be hindered. 235f9bbe447SVladimir Oltean * L2 forwarding rules still take precedence when there are no VLAN 236f9bbe447SVladimir Oltean * restrictions, so there are no concerns about leaking traffic. 237f9bbe447SVladimir Oltean */ 238f9bbe447SVladimir Oltean for (i = 0; i < ds->num_ports; i++) { 239f9bbe447SVladimir Oltean u16 flags; 240f9bbe447SVladimir Oltean 241f9bbe447SVladimir Oltean if (i == upstream) 242d34d2baaSIoana Ciornei continue; 243f9bbe447SVladimir Oltean else if (i == port) 244f9bbe447SVladimir Oltean /* The RX VID is pvid on this port */ 245f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED | 246f9bbe447SVladimir Oltean BRIDGE_VLAN_INFO_PVID; 247f9bbe447SVladimir Oltean else 248f9bbe447SVladimir Oltean /* The RX VID is a regular VLAN on all others */ 249f9bbe447SVladimir Oltean flags = BRIDGE_VLAN_INFO_UNTAGGED; 250f9bbe447SVladimir Oltean 2515f33183bSVladimir Oltean err = dsa_8021q_vid_apply(ds, i, rx_vid, flags, enabled); 252f9bbe447SVladimir Oltean if (err) { 253f9bbe447SVladimir Oltean dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n", 254f9bbe447SVladimir Oltean rx_vid, port, err); 255f9bbe447SVladimir Oltean return err; 256f9bbe447SVladimir Oltean } 257f9bbe447SVladimir Oltean } 258d34d2baaSIoana Ciornei 259d34d2baaSIoana Ciornei /* CPU port needs to see this port's RX VID 260d34d2baaSIoana Ciornei * as tagged egress. 261d34d2baaSIoana Ciornei */ 2625f33183bSVladimir Oltean err = dsa_8021q_vid_apply(ds, upstream, rx_vid, 0, enabled); 263d34d2baaSIoana Ciornei if (err) { 264d34d2baaSIoana Ciornei dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n", 265d34d2baaSIoana Ciornei rx_vid, port, err); 266d34d2baaSIoana Ciornei return err; 267d34d2baaSIoana Ciornei } 268d34d2baaSIoana Ciornei 269f9bbe447SVladimir Oltean /* Finally apply the TX VID on this port and on the CPU port */ 2705f33183bSVladimir Oltean err = dsa_8021q_vid_apply(ds, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, 2715f33183bSVladimir Oltean enabled); 272f9bbe447SVladimir Oltean if (err) { 273f9bbe447SVladimir Oltean dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", 274f9bbe447SVladimir Oltean tx_vid, port, err); 275f9bbe447SVladimir Oltean return err; 276f9bbe447SVladimir Oltean } 2775f33183bSVladimir Oltean err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled); 278f9bbe447SVladimir Oltean if (err) { 279f9bbe447SVladimir Oltean dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", 280f9bbe447SVladimir Oltean tx_vid, upstream, err); 281f9bbe447SVladimir Oltean return err; 282f9bbe447SVladimir Oltean } 283f9bbe447SVladimir Oltean 2845f33183bSVladimir Oltean if (!enabled) 2855f33183bSVladimir Oltean err = dsa_8021q_restore_pvid(ds, port); 2865f33183bSVladimir Oltean 2875f33183bSVladimir Oltean return err; 288f9bbe447SVladimir Oltean } 289f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging); 290f9bbe447SVladimir Oltean 291f9bbe447SVladimir Oltean struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, 292f9bbe447SVladimir Oltean u16 tpid, u16 tci) 293f9bbe447SVladimir Oltean { 294f9bbe447SVladimir Oltean /* skb->data points at skb_mac_header, which 295f9bbe447SVladimir Oltean * is fine for vlan_insert_tag. 296f9bbe447SVladimir Oltean */ 297f9bbe447SVladimir Oltean return vlan_insert_tag(skb, htons(tpid), tci); 298f9bbe447SVladimir Oltean } 299f9bbe447SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_xmit); 300f9bbe447SVladimir Oltean 301d4619336SVladimir Oltean /* In the DSA packet_type handler, skb->data points in the middle of the VLAN 302d4619336SVladimir Oltean * tag, after tpid and before tci. This is because so far, ETH_HLEN 303d4619336SVladimir Oltean * (DMAC, SMAC, EtherType) bytes were pulled. 304f9bbe447SVladimir Oltean * There are 2 bytes of VLAN tag left in skb->data, and upper 305f9bbe447SVladimir Oltean * layers expect the 'real' EtherType to be consumed as well. 306f9bbe447SVladimir Oltean * Coincidentally, a VLAN header is also of the same size as 307f9bbe447SVladimir Oltean * the number of bytes that need to be pulled. 308d4619336SVladimir Oltean * 309d4619336SVladimir Oltean * skb_mac_header skb->data 310d4619336SVladimir Oltean * | | 311d4619336SVladimir Oltean * v v 312d4619336SVladimir Oltean * | | | | | | | | | | | | | | | | | | | 313d4619336SVladimir Oltean * +-----------------------+-----------------------+-------+-------+-------+ 314d4619336SVladimir Oltean * | Destination MAC | Source MAC | TPID | TCI | EType | 315d4619336SVladimir Oltean * +-----------------------+-----------------------+-------+-------+-------+ 316d4619336SVladimir Oltean * ^ | | 317d4619336SVladimir Oltean * |<--VLAN_HLEN-->to <---VLAN_HLEN---> 318d4619336SVladimir Oltean * from | 319d4619336SVladimir Oltean * >>>>>>> v 320d4619336SVladimir Oltean * >>>>>>> | | | | | | | | | | | | | | | 321d4619336SVladimir Oltean * >>>>>>> +-----------------------+-----------------------+-------+ 322d4619336SVladimir Oltean * >>>>>>> | Destination MAC | Source MAC | EType | 323d4619336SVladimir Oltean * +-----------------------+-----------------------+-------+ 324d4619336SVladimir Oltean * ^ ^ 325d4619336SVladimir Oltean * (now part of | | 326d4619336SVladimir Oltean * skb->head) skb_mac_header skb->data 327f9bbe447SVladimir Oltean */ 328d4619336SVladimir Oltean struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb) 329d4619336SVladimir Oltean { 330d4619336SVladimir Oltean u8 *from = skb_mac_header(skb); 331d4619336SVladimir Oltean u8 *dest = from + VLAN_HLEN; 332d4619336SVladimir Oltean 333d4619336SVladimir Oltean memmove(dest, from, ETH_HLEN - VLAN_HLEN); 334d4619336SVladimir Oltean skb_pull(skb, VLAN_HLEN); 335d4619336SVladimir Oltean skb_push(skb, ETH_HLEN); 336d4619336SVladimir Oltean skb_reset_mac_header(skb); 337d4619336SVladimir Oltean skb_reset_mac_len(skb); 338d4619336SVladimir Oltean skb_pull_rcsum(skb, ETH_HLEN); 339f9bbe447SVladimir Oltean 340f9bbe447SVladimir Oltean return skb; 341f9bbe447SVladimir Oltean } 342d4619336SVladimir Oltean EXPORT_SYMBOL_GPL(dsa_8021q_remove_header); 343f9bbe447SVladimir Oltean 344f9bbe447SVladimir Oltean static const struct dsa_device_ops dsa_8021q_netdev_ops = { 345f9bbe447SVladimir Oltean .name = "8021q", 346f9bbe447SVladimir Oltean .proto = DSA_TAG_PROTO_8021Q, 347f9bbe447SVladimir Oltean .overhead = VLAN_HLEN, 348f9bbe447SVladimir Oltean }; 349f9bbe447SVladimir Oltean 350f9bbe447SVladimir Oltean MODULE_LICENSE("GPL v2"); 351f9bbe447SVladimir Oltean MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_8021Q); 352f9bbe447SVladimir Oltean 353f9bbe447SVladimir Oltean module_dsa_tag_driver(dsa_8021q_netdev_ops); 354