1dfedd3b6SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+ 2cf85d08fSLennert Buytenhek /* 3469ee5feSTobias Waldekranz * Regular and Ethertype DSA tagging 4e84665c9SLennert Buytenhek * Copyright (c) 2008-2009 Marvell Semiconductor 5469ee5feSTobias Waldekranz * 6469ee5feSTobias Waldekranz * Regular DSA 7469ee5feSTobias Waldekranz * ----------- 8469ee5feSTobias Waldekranz 9469ee5feSTobias Waldekranz * For untagged (in 802.1Q terms) packets, the switch will splice in 10469ee5feSTobias Waldekranz * the tag between the SA and the ethertype of the original 11469ee5feSTobias Waldekranz * packet. Tagged frames will instead have their outermost .1Q tag 12469ee5feSTobias Waldekranz * converted to a DSA tag. It expects the same layout when receiving 13469ee5feSTobias Waldekranz * packets from the CPU. 14469ee5feSTobias Waldekranz * 15469ee5feSTobias Waldekranz * Example: 16469ee5feSTobias Waldekranz * 17469ee5feSTobias Waldekranz * .----.----.----.--------- 18469ee5feSTobias Waldekranz * Pu: | DA | SA | ET | Payload ... 19469ee5feSTobias Waldekranz * '----'----'----'--------- 20469ee5feSTobias Waldekranz * 6 6 2 N 21469ee5feSTobias Waldekranz * .----.----.--------.-----.----.--------- 22469ee5feSTobias Waldekranz * Pt: | DA | SA | 0x8100 | TCI | ET | Payload ... 23469ee5feSTobias Waldekranz * '----'----'--------'-----'----'--------- 24469ee5feSTobias Waldekranz * 6 6 2 2 2 N 25469ee5feSTobias Waldekranz * .----.----.-----.----.--------- 26469ee5feSTobias Waldekranz * Pd: | DA | SA | DSA | ET | Payload ... 27469ee5feSTobias Waldekranz * '----'----'-----'----'--------- 28469ee5feSTobias Waldekranz * 6 6 4 2 N 29469ee5feSTobias Waldekranz * 30469ee5feSTobias Waldekranz * No matter if a packet is received untagged (Pu) or tagged (Pt), 31469ee5feSTobias Waldekranz * they will both have the same layout (Pd) when they are sent to the 32469ee5feSTobias Waldekranz * CPU. This is done by ignoring 802.3, replacing the ethertype field 33469ee5feSTobias Waldekranz * with more metadata, among which is a bit to signal if the original 34469ee5feSTobias Waldekranz * packet was tagged or not. 35469ee5feSTobias Waldekranz * 36469ee5feSTobias Waldekranz * Ethertype DSA 37469ee5feSTobias Waldekranz * ------------- 38469ee5feSTobias Waldekranz * Uses the exact same tag format as regular DSA, but also includes a 39469ee5feSTobias Waldekranz * proper ethertype field (which the mv88e6xxx driver sets to 40469ee5feSTobias Waldekranz * ETH_P_EDSA/0xdada) followed by two zero bytes: 41469ee5feSTobias Waldekranz * 42469ee5feSTobias Waldekranz * .----.----.--------.--------.-----.----.--------- 43469ee5feSTobias Waldekranz * | DA | SA | 0xdada | 0x0000 | DSA | ET | Payload ... 44469ee5feSTobias Waldekranz * '----'----'--------'--------'-----'----'--------- 45469ee5feSTobias Waldekranz * 6 6 2 2 4 2 N 46cf85d08fSLennert Buytenhek */ 47cf85d08fSLennert Buytenhek 48cf85d08fSLennert Buytenhek #include <linux/etherdevice.h> 49cf85d08fSLennert Buytenhek #include <linux/list.h> 505a0e3ad6STejun Heo #include <linux/slab.h> 51ea5dd34bSVivien Didelot 52cf85d08fSLennert Buytenhek #include "dsa_priv.h" 53cf85d08fSLennert Buytenhek 54cf85d08fSLennert Buytenhek #define DSA_HLEN 4 55cf85d08fSLennert Buytenhek 56469ee5feSTobias Waldekranz /** 57469ee5feSTobias Waldekranz * enum dsa_cmd - DSA Command 58469ee5feSTobias Waldekranz * @DSA_CMD_TO_CPU: Set on packets that were trapped or mirrored to 59469ee5feSTobias Waldekranz * the CPU port. This is needed to implement control protocols, 60469ee5feSTobias Waldekranz * e.g. STP and LLDP, that must not allow those control packets to 61469ee5feSTobias Waldekranz * be switched according to the normal rules. 62469ee5feSTobias Waldekranz * @DSA_CMD_FROM_CPU: Used by the CPU to send a packet to a specific 63469ee5feSTobias Waldekranz * port, ignoring all the barriers that the switch normally 64469ee5feSTobias Waldekranz * enforces (VLANs, STP port states etc.). No source address 65469ee5feSTobias Waldekranz * learning takes place. "sudo send packet" 66469ee5feSTobias Waldekranz * @DSA_CMD_TO_SNIFFER: Set on the copies of packets that matched some 67469ee5feSTobias Waldekranz * user configured ingress or egress monitor criteria. These are 68469ee5feSTobias Waldekranz * forwarded by the switch tree to the user configured ingress or 69469ee5feSTobias Waldekranz * egress monitor port, which can be set to the CPU port or a 70469ee5feSTobias Waldekranz * regular port. If the destination is a regular port, the tag 71469ee5feSTobias Waldekranz * will be removed before egressing the port. If the destination 72469ee5feSTobias Waldekranz * is the CPU port, the tag will not be removed. 73469ee5feSTobias Waldekranz * @DSA_CMD_FORWARD: This tag is used on all bulk traffic passing 74469ee5feSTobias Waldekranz * through the switch tree, including the flows that are directed 75469ee5feSTobias Waldekranz * towards the CPU. Its device/port tuple encodes the original 76469ee5feSTobias Waldekranz * source port on which the packet ingressed. It can also be used 77469ee5feSTobias Waldekranz * on transmit by the CPU to defer the forwarding decision to the 78469ee5feSTobias Waldekranz * hardware, based on the current config of PVT/VTU/ATU 79469ee5feSTobias Waldekranz * etc. Source address learning takes places if enabled on the 80469ee5feSTobias Waldekranz * receiving DSA/CPU port. 81469ee5feSTobias Waldekranz */ 82469ee5feSTobias Waldekranz enum dsa_cmd { 83469ee5feSTobias Waldekranz DSA_CMD_TO_CPU = 0, 84469ee5feSTobias Waldekranz DSA_CMD_FROM_CPU = 1, 85469ee5feSTobias Waldekranz DSA_CMD_TO_SNIFFER = 2, 86469ee5feSTobias Waldekranz DSA_CMD_FORWARD = 3 87469ee5feSTobias Waldekranz }; 88e468d141STobias Waldekranz 89469ee5feSTobias Waldekranz /** 90469ee5feSTobias Waldekranz * enum dsa_code - TO_CPU Code 91469ee5feSTobias Waldekranz * 92469ee5feSTobias Waldekranz * @DSA_CODE_MGMT_TRAP: DA was classified as a management 93469ee5feSTobias Waldekranz * address. Typical examples include STP BPDUs and LLDP. 94469ee5feSTobias Waldekranz * @DSA_CODE_FRAME2REG: Response to a "remote management" request. 95469ee5feSTobias Waldekranz * @DSA_CODE_IGMP_MLD_TRAP: IGMP/MLD signaling. 96469ee5feSTobias Waldekranz * @DSA_CODE_POLICY_TRAP: Frame matched some policy configuration on 97469ee5feSTobias Waldekranz * the device. Typical examples are matching on DA/SA/VID and DHCP 98469ee5feSTobias Waldekranz * snooping. 99469ee5feSTobias Waldekranz * @DSA_CODE_ARP_MIRROR: The name says it all really. 100469ee5feSTobias Waldekranz * @DSA_CODE_POLICY_MIRROR: Same as @DSA_CODE_POLICY_TRAP, but the 101469ee5feSTobias Waldekranz * particular policy was set to trigger a mirror instead of a 102469ee5feSTobias Waldekranz * trap. 103469ee5feSTobias Waldekranz * @DSA_CODE_RESERVED_6: Unused on all devices up to at least 6393X. 104469ee5feSTobias Waldekranz * @DSA_CODE_RESERVED_7: Unused on all devices up to at least 6393X. 105469ee5feSTobias Waldekranz * 106469ee5feSTobias Waldekranz * A 3-bit code is used to relay why a particular frame was sent to 107469ee5feSTobias Waldekranz * the CPU. We only use this to determine if the packet was mirrored 108469ee5feSTobias Waldekranz * or trapped, i.e. whether the packet has been forwarded by hardware 109469ee5feSTobias Waldekranz * or not. 110469ee5feSTobias Waldekranz * 111469ee5feSTobias Waldekranz * This is the superset of all possible codes. Any particular device 112469ee5feSTobias Waldekranz * may only implement a subset. 113469ee5feSTobias Waldekranz */ 114469ee5feSTobias Waldekranz enum dsa_code { 115469ee5feSTobias Waldekranz DSA_CODE_MGMT_TRAP = 0, 116469ee5feSTobias Waldekranz DSA_CODE_FRAME2REG = 1, 117469ee5feSTobias Waldekranz DSA_CODE_IGMP_MLD_TRAP = 2, 118469ee5feSTobias Waldekranz DSA_CODE_POLICY_TRAP = 3, 119469ee5feSTobias Waldekranz DSA_CODE_ARP_MIRROR = 4, 120469ee5feSTobias Waldekranz DSA_CODE_POLICY_MIRROR = 5, 121469ee5feSTobias Waldekranz DSA_CODE_RESERVED_6 = 6, 122469ee5feSTobias Waldekranz DSA_CODE_RESERVED_7 = 7 123469ee5feSTobias Waldekranz }; 124e468d141STobias Waldekranz 125469ee5feSTobias Waldekranz static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev, 126469ee5feSTobias Waldekranz u8 extra) 127cf85d08fSLennert Buytenhek { 128d945097bSVivien Didelot struct dsa_port *dp = dsa_slave_to_port(dev); 129d82f8ab0STobias Waldekranz u8 tag_dev, tag_port; 130d82f8ab0STobias Waldekranz enum dsa_cmd cmd; 131cf85d08fSLennert Buytenhek u8 *dsa_header; 132d82f8ab0STobias Waldekranz u16 pvid = 0; 133d82f8ab0STobias Waldekranz int err; 134d82f8ab0STobias Waldekranz 135d82f8ab0STobias Waldekranz if (skb->offload_fwd_mark) { 136d82f8ab0STobias Waldekranz struct dsa_switch_tree *dst = dp->ds->dst; 137d82f8ab0STobias Waldekranz struct net_device *br = dp->bridge_dev; 138d82f8ab0STobias Waldekranz 139d82f8ab0STobias Waldekranz cmd = DSA_CMD_FORWARD; 140d82f8ab0STobias Waldekranz 141d82f8ab0STobias Waldekranz /* When offloading forwarding for a bridge, inject FORWARD 142d82f8ab0STobias Waldekranz * packets on behalf of a virtual switch device with an index 143d82f8ab0STobias Waldekranz * past the physical switches. 144d82f8ab0STobias Waldekranz */ 145d82f8ab0STobias Waldekranz tag_dev = dst->last_switch + 1 + dp->bridge_num; 146d82f8ab0STobias Waldekranz tag_port = 0; 147d82f8ab0STobias Waldekranz 148d82f8ab0STobias Waldekranz /* If we are offloading forwarding for a VLAN-unaware bridge, 149d82f8ab0STobias Waldekranz * inject packets to hardware using the bridge's pvid, since 150d82f8ab0STobias Waldekranz * that's where the packets ingressed from. 151d82f8ab0STobias Waldekranz */ 152d82f8ab0STobias Waldekranz if (!br_vlan_enabled(br)) { 153d82f8ab0STobias Waldekranz /* Safe because __dev_queue_xmit() runs under 154d82f8ab0STobias Waldekranz * rcu_read_lock_bh() 155d82f8ab0STobias Waldekranz */ 156d82f8ab0STobias Waldekranz err = br_vlan_get_pvid_rcu(br, &pvid); 157d82f8ab0STobias Waldekranz if (err) 158d82f8ab0STobias Waldekranz return NULL; 159d82f8ab0STobias Waldekranz } 160d82f8ab0STobias Waldekranz } else { 161d82f8ab0STobias Waldekranz cmd = DSA_CMD_FROM_CPU; 162d82f8ab0STobias Waldekranz tag_dev = dp->ds->index; 163d82f8ab0STobias Waldekranz tag_port = dp->index; 164d82f8ab0STobias Waldekranz } 165cf85d08fSLennert Buytenhek 166cf85d08fSLennert Buytenhek if (skb->protocol == htons(ETH_P_8021Q)) { 167469ee5feSTobias Waldekranz if (extra) { 168469ee5feSTobias Waldekranz skb_push(skb, extra); 169*6bef794dSVladimir Oltean dsa_alloc_etype_header(skb, extra); 170469ee5feSTobias Waldekranz } 171469ee5feSTobias Waldekranz 172d82f8ab0STobias Waldekranz /* Construct tagged DSA tag from 802.1Q tag. */ 173469ee5feSTobias Waldekranz dsa_header = skb->data + 2 * ETH_ALEN + extra; 174d82f8ab0STobias Waldekranz dsa_header[0] = (cmd << 6) | 0x20 | tag_dev; 175d82f8ab0STobias Waldekranz dsa_header[1] = tag_port << 3; 176cf85d08fSLennert Buytenhek 17713f49b6fSTobias Waldekranz /* Move CFI field from byte 2 to byte 1. */ 178cf85d08fSLennert Buytenhek if (dsa_header[2] & 0x10) { 179cf85d08fSLennert Buytenhek dsa_header[1] |= 0x01; 180cf85d08fSLennert Buytenhek dsa_header[2] &= ~0x10; 181cf85d08fSLennert Buytenhek } 182cf85d08fSLennert Buytenhek } else { 183469ee5feSTobias Waldekranz skb_push(skb, DSA_HLEN + extra); 184*6bef794dSVladimir Oltean dsa_alloc_etype_header(skb, DSA_HLEN + extra); 185cf85d08fSLennert Buytenhek 186d82f8ab0STobias Waldekranz /* Construct untagged DSA tag. */ 187469ee5feSTobias Waldekranz dsa_header = skb->data + 2 * ETH_ALEN + extra; 188d82f8ab0STobias Waldekranz 189d82f8ab0STobias Waldekranz dsa_header[0] = (cmd << 6) | tag_dev; 190d82f8ab0STobias Waldekranz dsa_header[1] = tag_port << 3; 191d82f8ab0STobias Waldekranz dsa_header[2] = pvid >> 8; 192d82f8ab0STobias Waldekranz dsa_header[3] = pvid & 0xff; 193cf85d08fSLennert Buytenhek } 194cf85d08fSLennert Buytenhek 1954ed70ce9SFlorian Fainelli return skb; 196cf85d08fSLennert Buytenhek } 197cf85d08fSLennert Buytenhek 198469ee5feSTobias Waldekranz static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, 199469ee5feSTobias Waldekranz u8 extra) 200cf85d08fSLennert Buytenhek { 201bea79078SVladimir Oltean bool trap = false, trunk = false; 202469ee5feSTobias Waldekranz int source_device, source_port; 203469ee5feSTobias Waldekranz enum dsa_code code; 204469ee5feSTobias Waldekranz enum dsa_cmd cmd; 205cf85d08fSLennert Buytenhek u8 *dsa_header; 206cf85d08fSLennert Buytenhek 20713f49b6fSTobias Waldekranz /* The ethertype field is part of the DSA header. */ 208cf85d08fSLennert Buytenhek dsa_header = skb->data - 2; 209cf85d08fSLennert Buytenhek 210469ee5feSTobias Waldekranz cmd = dsa_header[0] >> 6; 211469ee5feSTobias Waldekranz switch (cmd) { 212469ee5feSTobias Waldekranz case DSA_CMD_FORWARD: 2135b60dadbSTobias Waldekranz trunk = !!(dsa_header[1] & 7); 214e468d141STobias Waldekranz break; 215e468d141STobias Waldekranz 216469ee5feSTobias Waldekranz case DSA_CMD_TO_CPU: 217469ee5feSTobias Waldekranz code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1); 218469ee5feSTobias Waldekranz 219469ee5feSTobias Waldekranz switch (code) { 220469ee5feSTobias Waldekranz case DSA_CODE_FRAME2REG: 221469ee5feSTobias Waldekranz /* Remote management is not implemented yet, 222469ee5feSTobias Waldekranz * drop. 223469ee5feSTobias Waldekranz */ 224469ee5feSTobias Waldekranz return NULL; 225469ee5feSTobias Waldekranz case DSA_CODE_ARP_MIRROR: 226469ee5feSTobias Waldekranz case DSA_CODE_POLICY_MIRROR: 227469ee5feSTobias Waldekranz /* Mark mirrored packets to notify any upper 228469ee5feSTobias Waldekranz * device (like a bridge) that forwarding has 229469ee5feSTobias Waldekranz * already been done by hardware. 230469ee5feSTobias Waldekranz */ 231e468d141STobias Waldekranz break; 232469ee5feSTobias Waldekranz case DSA_CODE_MGMT_TRAP: 233469ee5feSTobias Waldekranz case DSA_CODE_IGMP_MLD_TRAP: 234469ee5feSTobias Waldekranz case DSA_CODE_POLICY_TRAP: 235469ee5feSTobias Waldekranz /* Traps have, by definition, not been 236469ee5feSTobias Waldekranz * forwarded by hardware, so don't mark them. 237469ee5feSTobias Waldekranz */ 238bea79078SVladimir Oltean trap = true; 239469ee5feSTobias Waldekranz break; 240469ee5feSTobias Waldekranz default: 241469ee5feSTobias Waldekranz /* Reserved code, this could be anything. Drop 242469ee5feSTobias Waldekranz * seems like the safest option. 243469ee5feSTobias Waldekranz */ 244469ee5feSTobias Waldekranz return NULL; 245469ee5feSTobias Waldekranz } 246469ee5feSTobias Waldekranz 247469ee5feSTobias Waldekranz break; 248e468d141STobias Waldekranz 249e468d141STobias Waldekranz default: 25054709795SVivien Didelot return NULL; 251e468d141STobias Waldekranz } 252cf85d08fSLennert Buytenhek 253e84665c9SLennert Buytenhek source_device = dsa_header[0] & 0x1f; 254cf85d08fSLennert Buytenhek source_port = (dsa_header[1] >> 3) & 0x1f; 255e84665c9SLennert Buytenhek 2565b60dadbSTobias Waldekranz if (trunk) { 2575b60dadbSTobias Waldekranz struct dsa_port *cpu_dp = dev->dsa_ptr; 2585b60dadbSTobias Waldekranz 2595b60dadbSTobias Waldekranz /* The exact source port is not available in the tag, 2605b60dadbSTobias Waldekranz * so we inject the frame directly on the upper 2615b60dadbSTobias Waldekranz * team/bond. 2625b60dadbSTobias Waldekranz */ 2635b60dadbSTobias Waldekranz skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); 2645b60dadbSTobias Waldekranz } else { 2655b60dadbSTobias Waldekranz skb->dev = dsa_master_find_slave(dev, source_device, 2665b60dadbSTobias Waldekranz source_port); 2675b60dadbSTobias Waldekranz } 2685b60dadbSTobias Waldekranz 2693775b1b7SVivien Didelot if (!skb->dev) 27054709795SVivien Didelot return NULL; 271cf85d08fSLennert Buytenhek 272bea79078SVladimir Oltean /* When using LAG offload, skb->dev is not a DSA slave interface, 273bea79078SVladimir Oltean * so we cannot call dsa_default_offload_fwd_mark and we need to 274bea79078SVladimir Oltean * special-case it. 275bea79078SVladimir Oltean */ 276bea79078SVladimir Oltean if (trunk) 277bea79078SVladimir Oltean skb->offload_fwd_mark = true; 278bea79078SVladimir Oltean else if (!trap) 279bea79078SVladimir Oltean dsa_default_offload_fwd_mark(skb); 280bea79078SVladimir Oltean 281469ee5feSTobias Waldekranz /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q 282469ee5feSTobias Waldekranz * tag, and delete the ethertype (extra) if applicable. If the 283469ee5feSTobias Waldekranz * 'tagged' bit is cleared; delete the DSA tag, and ethertype 284469ee5feSTobias Waldekranz * if applicable. 285cf85d08fSLennert Buytenhek */ 286cf85d08fSLennert Buytenhek if (dsa_header[0] & 0x20) { 287cf85d08fSLennert Buytenhek u8 new_header[4]; 288cf85d08fSLennert Buytenhek 28913f49b6fSTobias Waldekranz /* Insert 802.1Q ethertype and copy the VLAN-related 290cf85d08fSLennert Buytenhek * fields, but clear the bit that will hold CFI (since 291cf85d08fSLennert Buytenhek * DSA uses that bit location for another purpose). 292cf85d08fSLennert Buytenhek */ 293cf85d08fSLennert Buytenhek new_header[0] = (ETH_P_8021Q >> 8) & 0xff; 294cf85d08fSLennert Buytenhek new_header[1] = ETH_P_8021Q & 0xff; 295cf85d08fSLennert Buytenhek new_header[2] = dsa_header[2] & ~0x10; 296cf85d08fSLennert Buytenhek new_header[3] = dsa_header[3]; 297cf85d08fSLennert Buytenhek 29813f49b6fSTobias Waldekranz /* Move CFI bit from its place in the DSA header to 29913f49b6fSTobias Waldekranz * its 802.1Q-designated place. 300cf85d08fSLennert Buytenhek */ 301cf85d08fSLennert Buytenhek if (dsa_header[1] & 0x01) 302cf85d08fSLennert Buytenhek new_header[2] |= 0x10; 303cf85d08fSLennert Buytenhek 30413f49b6fSTobias Waldekranz /* Update packet checksum if skb is CHECKSUM_COMPLETE. */ 305cf85d08fSLennert Buytenhek if (skb->ip_summed == CHECKSUM_COMPLETE) { 306cf85d08fSLennert Buytenhek __wsum c = skb->csum; 307cf85d08fSLennert Buytenhek c = csum_add(c, csum_partial(new_header + 2, 2, 0)); 308cf85d08fSLennert Buytenhek c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0)); 309cf85d08fSLennert Buytenhek skb->csum = c; 310cf85d08fSLennert Buytenhek } 311cf85d08fSLennert Buytenhek 312cf85d08fSLennert Buytenhek memcpy(dsa_header, new_header, DSA_HLEN); 313469ee5feSTobias Waldekranz 314469ee5feSTobias Waldekranz if (extra) 315f1dacd7aSVladimir Oltean dsa_strip_etype_header(skb, extra); 316cf85d08fSLennert Buytenhek } else { 317cf85d08fSLennert Buytenhek skb_pull_rcsum(skb, DSA_HLEN); 318f1dacd7aSVladimir Oltean dsa_strip_etype_header(skb, DSA_HLEN + extra); 319cf85d08fSLennert Buytenhek } 320cf85d08fSLennert Buytenhek 321a86d8becSFlorian Fainelli return skb; 322cf85d08fSLennert Buytenhek } 323cf85d08fSLennert Buytenhek 324469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) 325469ee5feSTobias Waldekranz 326469ee5feSTobias Waldekranz static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) 327469ee5feSTobias Waldekranz { 328469ee5feSTobias Waldekranz return dsa_xmit_ll(skb, dev, 0); 329469ee5feSTobias Waldekranz } 330469ee5feSTobias Waldekranz 33129a097b7SVladimir Oltean static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev) 332469ee5feSTobias Waldekranz { 333469ee5feSTobias Waldekranz if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) 334469ee5feSTobias Waldekranz return NULL; 335469ee5feSTobias Waldekranz 336469ee5feSTobias Waldekranz return dsa_rcv_ll(skb, dev, 0); 337469ee5feSTobias Waldekranz } 338469ee5feSTobias Waldekranz 339f81a43e8SAndrew Lunn static const struct dsa_device_ops dsa_netdev_ops = { 340875138f8SAndrew Lunn .name = "dsa", 341056eed2fSAndrew Lunn .proto = DSA_TAG_PROTO_DSA, 3423e8a72d1SFlorian Fainelli .xmit = dsa_xmit, 3433e8a72d1SFlorian Fainelli .rcv = dsa_rcv, 3444e500251SVladimir Oltean .needed_headroom = DSA_HLEN, 345cf85d08fSLennert Buytenhek }; 3460b42f033SAndrew Lunn 347469ee5feSTobias Waldekranz DSA_TAG_DRIVER(dsa_netdev_ops); 3480b42f033SAndrew Lunn MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA); 349469ee5feSTobias Waldekranz #endif /* CONFIG_NET_DSA_TAG_DSA */ 350d3b8c049SAndrew Lunn 351469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) 352469ee5feSTobias Waldekranz 353469ee5feSTobias Waldekranz #define EDSA_HLEN 8 354469ee5feSTobias Waldekranz 355469ee5feSTobias Waldekranz static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) 356469ee5feSTobias Waldekranz { 357469ee5feSTobias Waldekranz u8 *edsa_header; 358469ee5feSTobias Waldekranz 359469ee5feSTobias Waldekranz skb = dsa_xmit_ll(skb, dev, EDSA_HLEN - DSA_HLEN); 360469ee5feSTobias Waldekranz if (!skb) 361469ee5feSTobias Waldekranz return NULL; 362469ee5feSTobias Waldekranz 363469ee5feSTobias Waldekranz edsa_header = skb->data + 2 * ETH_ALEN; 364469ee5feSTobias Waldekranz edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; 365469ee5feSTobias Waldekranz edsa_header[1] = ETH_P_EDSA & 0xff; 366469ee5feSTobias Waldekranz edsa_header[2] = 0x00; 367469ee5feSTobias Waldekranz edsa_header[3] = 0x00; 368469ee5feSTobias Waldekranz return skb; 369469ee5feSTobias Waldekranz } 370469ee5feSTobias Waldekranz 37129a097b7SVladimir Oltean static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev) 372469ee5feSTobias Waldekranz { 373469ee5feSTobias Waldekranz if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) 374469ee5feSTobias Waldekranz return NULL; 375469ee5feSTobias Waldekranz 376469ee5feSTobias Waldekranz skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); 377469ee5feSTobias Waldekranz 378469ee5feSTobias Waldekranz return dsa_rcv_ll(skb, dev, EDSA_HLEN - DSA_HLEN); 379469ee5feSTobias Waldekranz } 380469ee5feSTobias Waldekranz 381469ee5feSTobias Waldekranz static const struct dsa_device_ops edsa_netdev_ops = { 382469ee5feSTobias Waldekranz .name = "edsa", 383469ee5feSTobias Waldekranz .proto = DSA_TAG_PROTO_EDSA, 384469ee5feSTobias Waldekranz .xmit = edsa_xmit, 385469ee5feSTobias Waldekranz .rcv = edsa_rcv, 3864e500251SVladimir Oltean .needed_headroom = EDSA_HLEN, 387469ee5feSTobias Waldekranz }; 388469ee5feSTobias Waldekranz 389469ee5feSTobias Waldekranz DSA_TAG_DRIVER(edsa_netdev_ops); 390469ee5feSTobias Waldekranz MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); 391469ee5feSTobias Waldekranz #endif /* CONFIG_NET_DSA_TAG_EDSA */ 392469ee5feSTobias Waldekranz 393469ee5feSTobias Waldekranz static struct dsa_tag_driver *dsa_tag_drivers[] = { 394469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) 395469ee5feSTobias Waldekranz &DSA_TAG_DRIVER_NAME(dsa_netdev_ops), 396469ee5feSTobias Waldekranz #endif 397469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) 398469ee5feSTobias Waldekranz &DSA_TAG_DRIVER_NAME(edsa_netdev_ops), 399469ee5feSTobias Waldekranz #endif 400469ee5feSTobias Waldekranz }; 401469ee5feSTobias Waldekranz 402469ee5feSTobias Waldekranz module_dsa_tag_drivers(dsa_tag_drivers); 403469ee5feSTobias Waldekranz 404469ee5feSTobias Waldekranz MODULE_LICENSE("GPL"); 405