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 "tag.h" |
81 | |
82 | /* Protocols supported: |
83 | * |
84 | * 0x04 = RTL8365MB DSA protocol |
85 | */ |
86 | |
87 | #define RTL8_4_NAME "rtl8_4" |
88 | #define RTL8_4T_NAME "rtl8_4t" |
89 | |
90 | #define RTL8_4_TAG_LEN 8 |
91 | |
92 | #define RTL8_4_PROTOCOL GENMASK(15, 8) |
93 | #define RTL8_4_PROTOCOL_RTL8365MB 0x04 |
94 | #define RTL8_4_REASON GENMASK(7, 0) |
95 | #define RTL8_4_REASON_FORWARD 0 |
96 | #define RTL8_4_REASON_TRAP 80 |
97 | |
98 | #define RTL8_4_LEARN_DIS BIT(5) |
99 | |
100 | #define RTL8_4_TX GENMASK(3, 0) |
101 | #define RTL8_4_RX GENMASK(10, 0) |
102 | |
103 | static void rtl8_4_write_tag(struct sk_buff *skb, struct net_device *dev, |
104 | void *tag) |
105 | { |
106 | struct dsa_port *dp = dsa_user_to_port(dev); |
107 | __be16 tag16[RTL8_4_TAG_LEN / 2]; |
108 | |
109 | /* Set Realtek EtherType */ |
110 | tag16[0] = htons(ETH_P_REALTEK); |
111 | |
112 | /* Set Protocol; zero REASON */ |
113 | tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)); |
114 | |
115 | /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ |
116 | tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); |
117 | |
118 | /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ |
119 | tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index))); |
120 | |
121 | memcpy(tag, tag16, RTL8_4_TAG_LEN); |
122 | } |
123 | |
124 | static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb, |
125 | struct net_device *dev) |
126 | { |
127 | skb_push(skb, RTL8_4_TAG_LEN); |
128 | |
129 | dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN); |
130 | |
131 | rtl8_4_write_tag(skb, dev, tag: dsa_etype_header_pos_tx(skb)); |
132 | |
133 | return skb; |
134 | } |
135 | |
136 | static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb, |
137 | struct net_device *dev) |
138 | { |
139 | /* Calculate the checksum here if not done yet as trailing tags will |
140 | * break either software or hardware based checksum |
141 | */ |
142 | if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) |
143 | return NULL; |
144 | |
145 | rtl8_4_write_tag(skb, dev, tag: skb_put(skb, RTL8_4_TAG_LEN)); |
146 | |
147 | return skb; |
148 | } |
149 | |
150 | static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, |
151 | void *tag) |
152 | { |
153 | __be16 tag16[RTL8_4_TAG_LEN / 2]; |
154 | u16 etype; |
155 | u8 reason; |
156 | u8 proto; |
157 | u8 port; |
158 | |
159 | memcpy(tag16, tag, RTL8_4_TAG_LEN); |
160 | |
161 | /* Parse Realtek EtherType */ |
162 | etype = ntohs(tag16[0]); |
163 | if (unlikely(etype != ETH_P_REALTEK)) { |
164 | dev_warn_ratelimited(&dev->dev, |
165 | "non-realtek ethertype 0x%04x\n" , etype); |
166 | return -EPROTO; |
167 | } |
168 | |
169 | /* Parse Protocol */ |
170 | proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1])); |
171 | if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) { |
172 | dev_warn_ratelimited(&dev->dev, |
173 | "unknown realtek protocol 0x%02x\n" , |
174 | proto); |
175 | return -EPROTO; |
176 | } |
177 | |
178 | /* Parse REASON */ |
179 | reason = FIELD_GET(RTL8_4_REASON, ntohs(tag16[1])); |
180 | |
181 | /* Parse TX (switch->CPU) */ |
182 | port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3])); |
183 | skb->dev = dsa_conduit_find_user(dev, device: 0, port); |
184 | if (!skb->dev) { |
185 | dev_warn_ratelimited(&dev->dev, |
186 | "could not find user for port %d\n" , |
187 | port); |
188 | return -ENOENT; |
189 | } |
190 | |
191 | if (reason != RTL8_4_REASON_TRAP) |
192 | dsa_default_offload_fwd_mark(skb); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, |
198 | struct net_device *dev) |
199 | { |
200 | if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) |
201 | return NULL; |
202 | |
203 | if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) |
204 | return NULL; |
205 | |
206 | /* Remove tag and recalculate checksum */ |
207 | skb_pull_rcsum(skb, RTL8_4_TAG_LEN); |
208 | |
209 | dsa_strip_etype_header(skb, RTL8_4_TAG_LEN); |
210 | |
211 | return skb; |
212 | } |
213 | |
214 | static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb, |
215 | struct net_device *dev) |
216 | { |
217 | if (skb_linearize(skb)) |
218 | return NULL; |
219 | |
220 | if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) |
221 | return NULL; |
222 | |
223 | if (pskb_trim_rcsum(skb, len: skb->len - RTL8_4_TAG_LEN)) |
224 | return NULL; |
225 | |
226 | return skb; |
227 | } |
228 | |
229 | /* Ethertype version */ |
230 | static const struct dsa_device_ops rtl8_4_netdev_ops = { |
231 | .name = "rtl8_4" , |
232 | .proto = DSA_TAG_PROTO_RTL8_4, |
233 | .xmit = rtl8_4_tag_xmit, |
234 | .rcv = rtl8_4_tag_rcv, |
235 | .needed_headroom = RTL8_4_TAG_LEN, |
236 | }; |
237 | |
238 | DSA_TAG_DRIVER(rtl8_4_netdev_ops); |
239 | |
240 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4, RTL8_4_NAME); |
241 | |
242 | /* Tail version */ |
243 | static const struct dsa_device_ops rtl8_4t_netdev_ops = { |
244 | .name = "rtl8_4t" , |
245 | .proto = DSA_TAG_PROTO_RTL8_4T, |
246 | .xmit = rtl8_4t_tag_xmit, |
247 | .rcv = rtl8_4t_tag_rcv, |
248 | .needed_tailroom = RTL8_4_TAG_LEN, |
249 | }; |
250 | |
251 | DSA_TAG_DRIVER(rtl8_4t_netdev_ops); |
252 | |
253 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T, RTL8_4T_NAME); |
254 | |
255 | static struct dsa_tag_driver *dsa_tag_drivers[] = { |
256 | &DSA_TAG_DRIVER_NAME(rtl8_4_netdev_ops), |
257 | &DSA_TAG_DRIVER_NAME(rtl8_4t_netdev_ops), |
258 | }; |
259 | module_dsa_tag_drivers(dsa_tag_drivers); |
260 | |
261 | MODULE_DESCRIPTION("DSA tag driver for Realtek 8 byte protocol 4 tags" ); |
262 | MODULE_LICENSE("GPL" ); |
263 | |