File Index
Files and directories
Sign in
FILE

tag_rtl8_4.c

File preview with syntax highlighting.

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