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); 129cf85d08fSLennert Buytenhek u8 *dsa_header; 130cf85d08fSLennert Buytenhek 131cf85d08fSLennert Buytenhek if (skb->protocol == htons(ETH_P_8021Q)) { 132469ee5feSTobias Waldekranz if (extra) { 133469ee5feSTobias Waldekranz skb_push(skb, extra); 134469ee5feSTobias Waldekranz memmove(skb->data, skb->data + extra, 2 * ETH_ALEN); 135469ee5feSTobias Waldekranz } 136469ee5feSTobias Waldekranz 13713f49b6fSTobias Waldekranz /* Construct tagged FROM_CPU DSA tag from 802.1Q tag. */ 138469ee5feSTobias Waldekranz dsa_header = skb->data + 2 * ETH_ALEN + extra; 139469ee5feSTobias Waldekranz dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | 0x20 | dp->ds->index; 140d945097bSVivien Didelot dsa_header[1] = dp->index << 3; 141cf85d08fSLennert Buytenhek 14213f49b6fSTobias Waldekranz /* Move CFI field from byte 2 to byte 1. */ 143cf85d08fSLennert Buytenhek if (dsa_header[2] & 0x10) { 144cf85d08fSLennert Buytenhek dsa_header[1] |= 0x01; 145cf85d08fSLennert Buytenhek dsa_header[2] &= ~0x10; 146cf85d08fSLennert Buytenhek } 147cf85d08fSLennert Buytenhek } else { 148469ee5feSTobias Waldekranz skb_push(skb, DSA_HLEN + extra); 149469ee5feSTobias Waldekranz memmove(skb->data, skb->data + DSA_HLEN + extra, 2 * ETH_ALEN); 150cf85d08fSLennert Buytenhek 15113f49b6fSTobias Waldekranz /* Construct untagged FROM_CPU DSA tag. */ 152469ee5feSTobias Waldekranz dsa_header = skb->data + 2 * ETH_ALEN + extra; 153469ee5feSTobias Waldekranz dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | dp->ds->index; 154d945097bSVivien Didelot dsa_header[1] = dp->index << 3; 155cf85d08fSLennert Buytenhek dsa_header[2] = 0x00; 156cf85d08fSLennert Buytenhek dsa_header[3] = 0x00; 157cf85d08fSLennert Buytenhek } 158cf85d08fSLennert Buytenhek 1594ed70ce9SFlorian Fainelli return skb; 160cf85d08fSLennert Buytenhek } 161cf85d08fSLennert Buytenhek 162469ee5feSTobias Waldekranz static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, 163469ee5feSTobias Waldekranz u8 extra) 164cf85d08fSLennert Buytenhek { 165469ee5feSTobias Waldekranz int source_device, source_port; 1665b60dadbSTobias Waldekranz bool trunk = false; 167469ee5feSTobias Waldekranz enum dsa_code code; 168469ee5feSTobias Waldekranz enum dsa_cmd cmd; 169cf85d08fSLennert Buytenhek u8 *dsa_header; 170cf85d08fSLennert Buytenhek 17113f49b6fSTobias Waldekranz /* The ethertype field is part of the DSA header. */ 172cf85d08fSLennert Buytenhek dsa_header = skb->data - 2; 173cf85d08fSLennert Buytenhek 174469ee5feSTobias Waldekranz cmd = dsa_header[0] >> 6; 175469ee5feSTobias Waldekranz switch (cmd) { 176469ee5feSTobias Waldekranz case DSA_CMD_FORWARD: 177e468d141STobias Waldekranz skb->offload_fwd_mark = 1; 1785b60dadbSTobias Waldekranz 1795b60dadbSTobias Waldekranz trunk = !!(dsa_header[1] & 7); 180e468d141STobias Waldekranz break; 181e468d141STobias Waldekranz 182469ee5feSTobias Waldekranz case DSA_CMD_TO_CPU: 183469ee5feSTobias Waldekranz code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1); 184469ee5feSTobias Waldekranz 185469ee5feSTobias Waldekranz switch (code) { 186469ee5feSTobias Waldekranz case DSA_CODE_FRAME2REG: 187469ee5feSTobias Waldekranz /* Remote management is not implemented yet, 188469ee5feSTobias Waldekranz * drop. 189469ee5feSTobias Waldekranz */ 190469ee5feSTobias Waldekranz return NULL; 191469ee5feSTobias Waldekranz case DSA_CODE_ARP_MIRROR: 192469ee5feSTobias Waldekranz case DSA_CODE_POLICY_MIRROR: 193469ee5feSTobias Waldekranz /* Mark mirrored packets to notify any upper 194469ee5feSTobias Waldekranz * device (like a bridge) that forwarding has 195469ee5feSTobias Waldekranz * already been done by hardware. 196469ee5feSTobias Waldekranz */ 197e468d141STobias Waldekranz skb->offload_fwd_mark = 1; 198e468d141STobias Waldekranz break; 199469ee5feSTobias Waldekranz case DSA_CODE_MGMT_TRAP: 200469ee5feSTobias Waldekranz case DSA_CODE_IGMP_MLD_TRAP: 201469ee5feSTobias Waldekranz case DSA_CODE_POLICY_TRAP: 202469ee5feSTobias Waldekranz /* Traps have, by definition, not been 203469ee5feSTobias Waldekranz * forwarded by hardware, so don't mark them. 204469ee5feSTobias Waldekranz */ 205469ee5feSTobias Waldekranz break; 206469ee5feSTobias Waldekranz default: 207469ee5feSTobias Waldekranz /* Reserved code, this could be anything. Drop 208469ee5feSTobias Waldekranz * seems like the safest option. 209469ee5feSTobias Waldekranz */ 210469ee5feSTobias Waldekranz return NULL; 211469ee5feSTobias Waldekranz } 212469ee5feSTobias Waldekranz 213469ee5feSTobias Waldekranz break; 214e468d141STobias Waldekranz 215e468d141STobias Waldekranz default: 21654709795SVivien Didelot return NULL; 217e468d141STobias Waldekranz } 218cf85d08fSLennert Buytenhek 219e84665c9SLennert Buytenhek source_device = dsa_header[0] & 0x1f; 220cf85d08fSLennert Buytenhek source_port = (dsa_header[1] >> 3) & 0x1f; 221e84665c9SLennert Buytenhek 2225b60dadbSTobias Waldekranz if (trunk) { 2235b60dadbSTobias Waldekranz struct dsa_port *cpu_dp = dev->dsa_ptr; 2245b60dadbSTobias Waldekranz 2255b60dadbSTobias Waldekranz /* The exact source port is not available in the tag, 2265b60dadbSTobias Waldekranz * so we inject the frame directly on the upper 2275b60dadbSTobias Waldekranz * team/bond. 2285b60dadbSTobias Waldekranz */ 2295b60dadbSTobias Waldekranz skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); 2305b60dadbSTobias Waldekranz } else { 2315b60dadbSTobias Waldekranz skb->dev = dsa_master_find_slave(dev, source_device, 2325b60dadbSTobias Waldekranz source_port); 2335b60dadbSTobias Waldekranz } 2345b60dadbSTobias Waldekranz 2353775b1b7SVivien Didelot if (!skb->dev) 23654709795SVivien Didelot return NULL; 237cf85d08fSLennert Buytenhek 238469ee5feSTobias Waldekranz /* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q 239469ee5feSTobias Waldekranz * tag, and delete the ethertype (extra) if applicable. If the 240469ee5feSTobias Waldekranz * 'tagged' bit is cleared; delete the DSA tag, and ethertype 241469ee5feSTobias Waldekranz * if applicable. 242cf85d08fSLennert Buytenhek */ 243cf85d08fSLennert Buytenhek if (dsa_header[0] & 0x20) { 244cf85d08fSLennert Buytenhek u8 new_header[4]; 245cf85d08fSLennert Buytenhek 24613f49b6fSTobias Waldekranz /* Insert 802.1Q ethertype and copy the VLAN-related 247cf85d08fSLennert Buytenhek * fields, but clear the bit that will hold CFI (since 248cf85d08fSLennert Buytenhek * DSA uses that bit location for another purpose). 249cf85d08fSLennert Buytenhek */ 250cf85d08fSLennert Buytenhek new_header[0] = (ETH_P_8021Q >> 8) & 0xff; 251cf85d08fSLennert Buytenhek new_header[1] = ETH_P_8021Q & 0xff; 252cf85d08fSLennert Buytenhek new_header[2] = dsa_header[2] & ~0x10; 253cf85d08fSLennert Buytenhek new_header[3] = dsa_header[3]; 254cf85d08fSLennert Buytenhek 25513f49b6fSTobias Waldekranz /* Move CFI bit from its place in the DSA header to 25613f49b6fSTobias Waldekranz * its 802.1Q-designated place. 257cf85d08fSLennert Buytenhek */ 258cf85d08fSLennert Buytenhek if (dsa_header[1] & 0x01) 259cf85d08fSLennert Buytenhek new_header[2] |= 0x10; 260cf85d08fSLennert Buytenhek 26113f49b6fSTobias Waldekranz /* Update packet checksum if skb is CHECKSUM_COMPLETE. */ 262cf85d08fSLennert Buytenhek if (skb->ip_summed == CHECKSUM_COMPLETE) { 263cf85d08fSLennert Buytenhek __wsum c = skb->csum; 264cf85d08fSLennert Buytenhek c = csum_add(c, csum_partial(new_header + 2, 2, 0)); 265cf85d08fSLennert Buytenhek c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0)); 266cf85d08fSLennert Buytenhek skb->csum = c; 267cf85d08fSLennert Buytenhek } 268cf85d08fSLennert Buytenhek 269cf85d08fSLennert Buytenhek memcpy(dsa_header, new_header, DSA_HLEN); 270469ee5feSTobias Waldekranz 271469ee5feSTobias Waldekranz if (extra) 272469ee5feSTobias Waldekranz memmove(skb->data - ETH_HLEN, 273469ee5feSTobias Waldekranz skb->data - ETH_HLEN - extra, 274469ee5feSTobias Waldekranz 2 * ETH_ALEN); 275cf85d08fSLennert Buytenhek } else { 276cf85d08fSLennert Buytenhek skb_pull_rcsum(skb, DSA_HLEN); 277cf85d08fSLennert Buytenhek memmove(skb->data - ETH_HLEN, 278469ee5feSTobias Waldekranz skb->data - ETH_HLEN - DSA_HLEN - extra, 279cf85d08fSLennert Buytenhek 2 * ETH_ALEN); 280cf85d08fSLennert Buytenhek } 281cf85d08fSLennert Buytenhek 282a86d8becSFlorian Fainelli return skb; 283cf85d08fSLennert Buytenhek } 284cf85d08fSLennert Buytenhek 285469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) 286469ee5feSTobias Waldekranz 287469ee5feSTobias Waldekranz static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) 288469ee5feSTobias Waldekranz { 289469ee5feSTobias Waldekranz return dsa_xmit_ll(skb, dev, 0); 290469ee5feSTobias Waldekranz } 291469ee5feSTobias Waldekranz 292469ee5feSTobias Waldekranz static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, 293469ee5feSTobias Waldekranz struct packet_type *pt) 294469ee5feSTobias Waldekranz { 295469ee5feSTobias Waldekranz if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) 296469ee5feSTobias Waldekranz return NULL; 297469ee5feSTobias Waldekranz 298469ee5feSTobias Waldekranz return dsa_rcv_ll(skb, dev, 0); 299469ee5feSTobias Waldekranz } 300469ee5feSTobias Waldekranz 301f81a43e8SAndrew Lunn static const struct dsa_device_ops dsa_netdev_ops = { 302875138f8SAndrew Lunn .name = "dsa", 303056eed2fSAndrew Lunn .proto = DSA_TAG_PROTO_DSA, 3043e8a72d1SFlorian Fainelli .xmit = dsa_xmit, 3053e8a72d1SFlorian Fainelli .rcv = dsa_rcv, 306*4e500251SVladimir Oltean .needed_headroom = DSA_HLEN, 307cf85d08fSLennert Buytenhek }; 3080b42f033SAndrew Lunn 309469ee5feSTobias Waldekranz DSA_TAG_DRIVER(dsa_netdev_ops); 3100b42f033SAndrew Lunn MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA); 311469ee5feSTobias Waldekranz #endif /* CONFIG_NET_DSA_TAG_DSA */ 312d3b8c049SAndrew Lunn 313469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) 314469ee5feSTobias Waldekranz 315469ee5feSTobias Waldekranz #define EDSA_HLEN 8 316469ee5feSTobias Waldekranz 317469ee5feSTobias Waldekranz static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) 318469ee5feSTobias Waldekranz { 319469ee5feSTobias Waldekranz u8 *edsa_header; 320469ee5feSTobias Waldekranz 321469ee5feSTobias Waldekranz skb = dsa_xmit_ll(skb, dev, EDSA_HLEN - DSA_HLEN); 322469ee5feSTobias Waldekranz if (!skb) 323469ee5feSTobias Waldekranz return NULL; 324469ee5feSTobias Waldekranz 325469ee5feSTobias Waldekranz edsa_header = skb->data + 2 * ETH_ALEN; 326469ee5feSTobias Waldekranz edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff; 327469ee5feSTobias Waldekranz edsa_header[1] = ETH_P_EDSA & 0xff; 328469ee5feSTobias Waldekranz edsa_header[2] = 0x00; 329469ee5feSTobias Waldekranz edsa_header[3] = 0x00; 330469ee5feSTobias Waldekranz return skb; 331469ee5feSTobias Waldekranz } 332469ee5feSTobias Waldekranz 333469ee5feSTobias Waldekranz static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, 334469ee5feSTobias Waldekranz struct packet_type *pt) 335469ee5feSTobias Waldekranz { 336469ee5feSTobias Waldekranz if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) 337469ee5feSTobias Waldekranz return NULL; 338469ee5feSTobias Waldekranz 339469ee5feSTobias Waldekranz skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); 340469ee5feSTobias Waldekranz 341469ee5feSTobias Waldekranz return dsa_rcv_ll(skb, dev, EDSA_HLEN - DSA_HLEN); 342469ee5feSTobias Waldekranz } 343469ee5feSTobias Waldekranz 344469ee5feSTobias Waldekranz static const struct dsa_device_ops edsa_netdev_ops = { 345469ee5feSTobias Waldekranz .name = "edsa", 346469ee5feSTobias Waldekranz .proto = DSA_TAG_PROTO_EDSA, 347469ee5feSTobias Waldekranz .xmit = edsa_xmit, 348469ee5feSTobias Waldekranz .rcv = edsa_rcv, 349*4e500251SVladimir Oltean .needed_headroom = EDSA_HLEN, 350469ee5feSTobias Waldekranz }; 351469ee5feSTobias Waldekranz 352469ee5feSTobias Waldekranz DSA_TAG_DRIVER(edsa_netdev_ops); 353469ee5feSTobias Waldekranz MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA); 354469ee5feSTobias Waldekranz #endif /* CONFIG_NET_DSA_TAG_EDSA */ 355469ee5feSTobias Waldekranz 356469ee5feSTobias Waldekranz static struct dsa_tag_driver *dsa_tag_drivers[] = { 357469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA) 358469ee5feSTobias Waldekranz &DSA_TAG_DRIVER_NAME(dsa_netdev_ops), 359469ee5feSTobias Waldekranz #endif 360469ee5feSTobias Waldekranz #if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA) 361469ee5feSTobias Waldekranz &DSA_TAG_DRIVER_NAME(edsa_netdev_ops), 362469ee5feSTobias Waldekranz #endif 363469ee5feSTobias Waldekranz }; 364469ee5feSTobias Waldekranz 365469ee5feSTobias Waldekranz module_dsa_tag_drivers(dsa_tag_drivers); 366469ee5feSTobias Waldekranz 367469ee5feSTobias Waldekranz MODULE_LICENSE("GPL"); 368