tag_rtl8_4.c (8065B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Handler for Realtek 8 byte switch tags 4 * 5 * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> 6 * 7 * NOTE: Currently only supports protocol "4" found in the RTL8365MB, hence 8 * named tag_rtl8_4. 9 * 10 * This tag has the following format: 11 * 12 * 0 7|8 15 13 * |-----------------------------------+-----------------------------------|--- 14 * | (16-bit) | ^ 15 * | Realtek EtherType [0x8899] | | 16 * |-----------------------------------+-----------------------------------| 8 17 * | (8-bit) | (8-bit) | 18 * | Protocol [0x04] | REASON | b 19 * |-----------------------------------+-----------------------------------| y 20 * | (1) | (1) | (2) | (1) | (3) | (1) | (1) | (1) | (5) | t 21 * | FID_EN | X | FID | PRI_EN | PRI | KEEP | X | LEARN_DIS | X | e 22 * |-----------------------------------+-----------------------------------| s 23 * | (1) | (15-bit) | | 24 * | ALLOW | TX/RX | v 25 * |-----------------------------------+-----------------------------------|--- 26 * 27 * With the following field descriptions: 28 * 29 * field | description 30 * ------------+------------- 31 * Realtek | 0x8899: indicates that this is a proprietary Realtek tag; 32 * EtherType | note that Realtek uses the same EtherType for 33 * | other incompatible tag formats (e.g. tag_rtl4_a.c) 34 * Protocol | 0x04: indicates that this tag conforms to this format 35 * X | reserved 36 * ------------+------------- 37 * REASON | reason for forwarding packet to CPU 38 * | 0: packet was forwarded or flooded to CPU 39 * | 80: packet was trapped to CPU 40 * FID_EN | 1: packet has an FID 41 * | 0: no FID 42 * FID | FID of packet (if FID_EN=1) 43 * PRI_EN | 1: force priority of packet 44 * | 0: don't force priority 45 * PRI | priority of packet (if PRI_EN=1) 46 * KEEP | preserve packet VLAN tag format 47 * LEARN_DIS | don't learn the source MAC address of the packet 48 * ALLOW | 1: treat TX/RX field as an allowance port mask, meaning the 49 * | packet may only be forwarded to ports specified in the 50 * | mask 51 * | 0: no allowance port mask, TX/RX field is the forwarding 52 * | port mask 53 * TX/RX | TX (switch->CPU): port number the packet was received on 54 * | RX (CPU->switch): forwarding port mask (if ALLOW=0) 55 * | allowance port mask (if ALLOW=1) 56 * 57 * The tag can be positioned before Ethertype, using tag "rtl8_4": 58 * 59 * +--------+--------+------------+------+----- 60 * | MAC DA | MAC SA | 8 byte tag | Type | ... 61 * +--------+--------+------------+------+----- 62 * 63 * The tag can also appear between the end of the payload and before the CRC, 64 * using tag "rtl8_4t": 65 * 66 * +--------+--------+------+-----+---------+------------+-----+ 67 * | MAC DA | MAC SA | TYPE | ... | payload | 8-byte tag | CRC | 68 * +--------+--------+------+-----+---------+------------+-----+ 69 * 70 * The added bytes after the payload will break most checksums, either in 71 * software or hardware. To avoid this issue, if the checksum is still pending, 72 * this tagger checksums the packet in software before adding the tag. 73 * 74 */ 75 76#include <linux/bitfield.h> 77#include <linux/bits.h> 78#include <linux/etherdevice.h> 79 80#include "dsa_priv.h" 81 82/* Protocols supported: 83 * 84 * 0x04 = RTL8365MB DSA protocol 85 */ 86 87#define RTL8_4_TAG_LEN 8 88 89#define RTL8_4_PROTOCOL GENMASK(15, 8) 90#define RTL8_4_PROTOCOL_RTL8365MB 0x04 91#define RTL8_4_REASON GENMASK(7, 0) 92#define RTL8_4_REASON_FORWARD 0 93#define RTL8_4_REASON_TRAP 80 94 95#define RTL8_4_LEARN_DIS BIT(5) 96 97#define RTL8_4_TX GENMASK(3, 0) 98#define RTL8_4_RX GENMASK(10, 0) 99 100static void rtl8_4_write_tag(struct sk_buff *skb, struct net_device *dev, 101 void *tag) 102{ 103 struct dsa_port *dp = dsa_slave_to_port(dev); 104 __be16 tag16[RTL8_4_TAG_LEN / 2]; 105 106 /* Set Realtek EtherType */ 107 tag16[0] = htons(ETH_P_REALTEK); 108 109 /* Set Protocol; zero REASON */ 110 tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)); 111 112 /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ 113 tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); 114 115 /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ 116 tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index))); 117 118 memcpy(tag, tag16, RTL8_4_TAG_LEN); 119} 120 121static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb, 122 struct net_device *dev) 123{ 124 skb_push(skb, RTL8_4_TAG_LEN); 125 126 dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN); 127 128 rtl8_4_write_tag(skb, dev, dsa_etype_header_pos_tx(skb)); 129 130 return skb; 131} 132 133static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb, 134 struct net_device *dev) 135{ 136 /* Calculate the checksum here if not done yet as trailing tags will 137 * break either software or hardware based checksum 138 */ 139 if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) 140 return NULL; 141 142 rtl8_4_write_tag(skb, dev, skb_put(skb, RTL8_4_TAG_LEN)); 143 144 return skb; 145} 146 147static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, 148 void *tag) 149{ 150 __be16 tag16[RTL8_4_TAG_LEN / 2]; 151 u16 etype; 152 u8 reason; 153 u8 proto; 154 u8 port; 155 156 memcpy(tag16, tag, RTL8_4_TAG_LEN); 157 158 /* Parse Realtek EtherType */ 159 etype = ntohs(tag16[0]); 160 if (unlikely(etype != ETH_P_REALTEK)) { 161 dev_warn_ratelimited(&dev->dev, 162 "non-realtek ethertype 0x%04x\n", etype); 163 return -EPROTO; 164 } 165 166 /* Parse Protocol */ 167 proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1])); 168 if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) { 169 dev_warn_ratelimited(&dev->dev, 170 "unknown realtek protocol 0x%02x\n", 171 proto); 172 return -EPROTO; 173 } 174 175 /* Parse REASON */ 176 reason = FIELD_GET(RTL8_4_REASON, ntohs(tag16[1])); 177 178 /* Parse TX (switch->CPU) */ 179 port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3])); 180 skb->dev = dsa_master_find_slave(dev, 0, port); 181 if (!skb->dev) { 182 dev_warn_ratelimited(&dev->dev, 183 "could not find slave for port %d\n", 184 port); 185 return -ENOENT; 186 } 187 188 if (reason != RTL8_4_REASON_TRAP) 189 dsa_default_offload_fwd_mark(skb); 190 191 return 0; 192} 193 194static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, 195 struct net_device *dev) 196{ 197 if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) 198 return NULL; 199 200 if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) 201 return NULL; 202 203 /* Remove tag and recalculate checksum */ 204 skb_pull_rcsum(skb, RTL8_4_TAG_LEN); 205 206 dsa_strip_etype_header(skb, RTL8_4_TAG_LEN); 207 208 return skb; 209} 210 211static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb, 212 struct net_device *dev) 213{ 214 if (skb_linearize(skb)) 215 return NULL; 216 217 if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) 218 return NULL; 219 220 if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) 221 return NULL; 222 223 return skb; 224} 225 226/* Ethertype version */ 227static const struct dsa_device_ops rtl8_4_netdev_ops = { 228 .name = "rtl8_4", 229 .proto = DSA_TAG_PROTO_RTL8_4, 230 .xmit = rtl8_4_tag_xmit, 231 .rcv = rtl8_4_tag_rcv, 232 .needed_headroom = RTL8_4_TAG_LEN, 233}; 234 235DSA_TAG_DRIVER(rtl8_4_netdev_ops); 236 237MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4); 238 239/* Tail version */ 240static const struct dsa_device_ops rtl8_4t_netdev_ops = { 241 .name = "rtl8_4t", 242 .proto = DSA_TAG_PROTO_RTL8_4T, 243 .xmit = rtl8_4t_tag_xmit, 244 .rcv = rtl8_4t_tag_rcv, 245 .needed_tailroom = RTL8_4_TAG_LEN, 246}; 247 248DSA_TAG_DRIVER(rtl8_4t_netdev_ops); 249 250MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T); 251 252static struct dsa_tag_driver *dsa_tag_drivers[] = { 253 &DSA_TAG_DRIVER_NAME(rtl8_4_netdev_ops), 254 &DSA_TAG_DRIVER_NAME(rtl8_4t_netdev_ops), 255}; 256module_dsa_tag_drivers(dsa_tag_drivers); 257 258MODULE_LICENSE("GPL");