// SPDX-License-Identifier: GPL-2.0 /* * Handler for Realtek 4 byte DSA switch tags * Currently only supports protocol "A" found in RTL8366RB * Copyright (c) 2020 Linus Walleij * * This "proprietary tag" header looks like so: * * ------------------------------------------------- * | MAC DA | MAC SA | 0x8899 | 2 bytes tag | Type | * ------------------------------------------------- * * The 2 bytes tag form a 16 bit big endian word. The exact * meaning has been guessed from packet dumps from ingress * frames, as no working egress traffic has been available * we do not know the format of the egress tags or if they * are even supported. */ #include #include #include "dsa_priv.h" #define RTL4_A_HDR_LEN 4 #define RTL4_A_ETHERTYPE 0x8899 #define RTL4_A_PROTOCOL_SHIFT 12 /* * 0x1 = Realtek Remote Control protocol (RRCP) * 0x2/0x3 seems to be used for loopback testing * 0x9 = RTL8306 DSA protocol * 0xa = RTL8366RB DSA protocol */ #define RTL4_A_PROTOCOL_RTL8366RB 0xa static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb, struct net_device *dev) { /* * Just let it pass thru, we don't know if it is possible * to tag a frame with the 0x8899 ethertype and direct it * to a specific port, all attempts at reverse-engineering have * ended up with the frames getting dropped. * * The VLAN set-up needs to restrict the frames to the right port. * * If you have documentation on the tagging format for RTL8366RB * (tag type A) then please contribute. */ return skb; } static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) { u16 protport; __be16 *p; u16 etype; u8 *tag; u8 prot; u8 port; if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN))) return NULL; /* The RTL4 header has its own custom Ethertype 0x8899 and that * starts right at the beginning of the packet, after the src * ethernet addr. Apparantly skb->data always points 2 bytes in, * behind the Ethertype. */ tag = skb->data - 2; p = (__be16 *)tag; etype = ntohs(*p); if (etype != RTL4_A_ETHERTYPE) { /* Not custom, just pass through */ netdev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype); return skb; } p = (__be16 *)(tag + 2); protport = ntohs(*p); /* The 4 upper bits are the protocol */ prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f; if (prot != RTL4_A_PROTOCOL_RTL8366RB) { netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot); return NULL; } port = protport & 0xff; skb->dev = dsa_master_find_slave(dev, 0, port); if (!skb->dev) { netdev_dbg(dev, "could not find slave for port %d\n", port); return NULL; } /* Remove RTL4 tag and recalculate checksum */ skb_pull_rcsum(skb, RTL4_A_HDR_LEN); /* Move ethernet DA and SA in front of the data */ memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - RTL4_A_HDR_LEN, 2 * ETH_ALEN); skb->offload_fwd_mark = 1; return skb; } static int rtl4a_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto, int *offset) { *offset = RTL4_A_HDR_LEN; /* Skip past the tag and fetch the encapsulated Ethertype */ *proto = ((__be16 *)skb->data)[1]; return 0; } static const struct dsa_device_ops rtl4a_netdev_ops = { .name = "rtl4a", .proto = DSA_TAG_PROTO_RTL4_A, .xmit = rtl4a_tag_xmit, .rcv = rtl4a_tag_rcv, .flow_dissect = rtl4a_tag_flow_dissect, .overhead = RTL4_A_HDR_LEN, }; module_dsa_tag_driver(rtl4a_netdev_ops); MODULE_LICENSE("GPL"); MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A);