cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");