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

br_stp_bpdu.c (5755B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	Spanning tree protocol; BPDU handling
      4 *	Linux ethernet bridge
      5 *
      6 *	Authors:
      7 *	Lennert Buytenhek		<buytenh@gnu.org>
      8 */
      9
     10#include <linux/kernel.h>
     11#include <linux/netfilter_bridge.h>
     12#include <linux/etherdevice.h>
     13#include <linux/llc.h>
     14#include <linux/slab.h>
     15#include <linux/pkt_sched.h>
     16#include <net/net_namespace.h>
     17#include <net/llc.h>
     18#include <net/llc_pdu.h>
     19#include <net/stp.h>
     20#include <asm/unaligned.h>
     21
     22#include "br_private.h"
     23#include "br_private_stp.h"
     24
     25#define STP_HZ		256
     26
     27#define LLC_RESERVE sizeof(struct llc_pdu_un)
     28
     29static int br_send_bpdu_finish(struct net *net, struct sock *sk,
     30			       struct sk_buff *skb)
     31{
     32	return dev_queue_xmit(skb);
     33}
     34
     35static void br_send_bpdu(struct net_bridge_port *p,
     36			 const unsigned char *data, int length)
     37{
     38	struct sk_buff *skb;
     39
     40	skb = dev_alloc_skb(length+LLC_RESERVE);
     41	if (!skb)
     42		return;
     43
     44	skb->dev = p->dev;
     45	skb->protocol = htons(ETH_P_802_2);
     46	skb->priority = TC_PRIO_CONTROL;
     47
     48	skb_reserve(skb, LLC_RESERVE);
     49	__skb_put_data(skb, data, length);
     50
     51	llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
     52			    LLC_SAP_BSPAN, LLC_PDU_CMD);
     53	llc_pdu_init_as_ui_cmd(skb);
     54
     55	llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
     56
     57	skb_reset_mac_header(skb);
     58
     59	NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
     60		dev_net(p->dev), NULL, skb, NULL, skb->dev,
     61		br_send_bpdu_finish);
     62}
     63
     64static inline void br_set_ticks(unsigned char *dest, int j)
     65{
     66	unsigned long ticks = (STP_HZ * j)/ HZ;
     67
     68	put_unaligned_be16(ticks, dest);
     69}
     70
     71static inline int br_get_ticks(const unsigned char *src)
     72{
     73	unsigned long ticks = get_unaligned_be16(src);
     74
     75	return DIV_ROUND_UP(ticks * HZ, STP_HZ);
     76}
     77
     78/* called under bridge lock */
     79void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
     80{
     81	unsigned char buf[35];
     82
     83	if (p->br->stp_enabled != BR_KERNEL_STP)
     84		return;
     85
     86	buf[0] = 0;
     87	buf[1] = 0;
     88	buf[2] = 0;
     89	buf[3] = BPDU_TYPE_CONFIG;
     90	buf[4] = (bpdu->topology_change ? 0x01 : 0) |
     91		(bpdu->topology_change_ack ? 0x80 : 0);
     92	buf[5] = bpdu->root.prio[0];
     93	buf[6] = bpdu->root.prio[1];
     94	buf[7] = bpdu->root.addr[0];
     95	buf[8] = bpdu->root.addr[1];
     96	buf[9] = bpdu->root.addr[2];
     97	buf[10] = bpdu->root.addr[3];
     98	buf[11] = bpdu->root.addr[4];
     99	buf[12] = bpdu->root.addr[5];
    100	buf[13] = (bpdu->root_path_cost >> 24) & 0xFF;
    101	buf[14] = (bpdu->root_path_cost >> 16) & 0xFF;
    102	buf[15] = (bpdu->root_path_cost >> 8) & 0xFF;
    103	buf[16] = bpdu->root_path_cost & 0xFF;
    104	buf[17] = bpdu->bridge_id.prio[0];
    105	buf[18] = bpdu->bridge_id.prio[1];
    106	buf[19] = bpdu->bridge_id.addr[0];
    107	buf[20] = bpdu->bridge_id.addr[1];
    108	buf[21] = bpdu->bridge_id.addr[2];
    109	buf[22] = bpdu->bridge_id.addr[3];
    110	buf[23] = bpdu->bridge_id.addr[4];
    111	buf[24] = bpdu->bridge_id.addr[5];
    112	buf[25] = (bpdu->port_id >> 8) & 0xFF;
    113	buf[26] = bpdu->port_id & 0xFF;
    114
    115	br_set_ticks(buf+27, bpdu->message_age);
    116	br_set_ticks(buf+29, bpdu->max_age);
    117	br_set_ticks(buf+31, bpdu->hello_time);
    118	br_set_ticks(buf+33, bpdu->forward_delay);
    119
    120	br_send_bpdu(p, buf, 35);
    121
    122	p->stp_xstats.tx_bpdu++;
    123}
    124
    125/* called under bridge lock */
    126void br_send_tcn_bpdu(struct net_bridge_port *p)
    127{
    128	unsigned char buf[4];
    129
    130	if (p->br->stp_enabled != BR_KERNEL_STP)
    131		return;
    132
    133	buf[0] = 0;
    134	buf[1] = 0;
    135	buf[2] = 0;
    136	buf[3] = BPDU_TYPE_TCN;
    137	br_send_bpdu(p, buf, 4);
    138
    139	p->stp_xstats.tx_tcn++;
    140}
    141
    142/*
    143 * Called from llc.
    144 *
    145 * NO locks, but rcu_read_lock
    146 */
    147void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
    148		struct net_device *dev)
    149{
    150	struct net_bridge_port *p;
    151	struct net_bridge *br;
    152	const unsigned char *buf;
    153
    154	if (!pskb_may_pull(skb, 4))
    155		goto err;
    156
    157	/* compare of protocol id and version */
    158	buf = skb->data;
    159	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
    160		goto err;
    161
    162	p = br_port_get_check_rcu(dev);
    163	if (!p)
    164		goto err;
    165
    166	br = p->br;
    167	spin_lock(&br->lock);
    168
    169	if (br->stp_enabled != BR_KERNEL_STP)
    170		goto out;
    171
    172	if (!(br->dev->flags & IFF_UP))
    173		goto out;
    174
    175	if (p->state == BR_STATE_DISABLED)
    176		goto out;
    177
    178	if (!ether_addr_equal(eth_hdr(skb)->h_dest, br->group_addr))
    179		goto out;
    180
    181	if (p->flags & BR_BPDU_GUARD) {
    182		br_notice(br, "BPDU received on blocked port %u(%s)\n",
    183			  (unsigned int) p->port_no, p->dev->name);
    184		br_stp_disable_port(p);
    185		goto out;
    186	}
    187
    188	buf = skb_pull(skb, 3);
    189
    190	if (buf[0] == BPDU_TYPE_CONFIG) {
    191		struct br_config_bpdu bpdu;
    192
    193		if (!pskb_may_pull(skb, 32))
    194			goto out;
    195
    196		buf = skb->data;
    197		bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
    198		bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;
    199
    200		bpdu.root.prio[0] = buf[2];
    201		bpdu.root.prio[1] = buf[3];
    202		bpdu.root.addr[0] = buf[4];
    203		bpdu.root.addr[1] = buf[5];
    204		bpdu.root.addr[2] = buf[6];
    205		bpdu.root.addr[3] = buf[7];
    206		bpdu.root.addr[4] = buf[8];
    207		bpdu.root.addr[5] = buf[9];
    208		bpdu.root_path_cost =
    209			(buf[10] << 24) |
    210			(buf[11] << 16) |
    211			(buf[12] << 8) |
    212			buf[13];
    213		bpdu.bridge_id.prio[0] = buf[14];
    214		bpdu.bridge_id.prio[1] = buf[15];
    215		bpdu.bridge_id.addr[0] = buf[16];
    216		bpdu.bridge_id.addr[1] = buf[17];
    217		bpdu.bridge_id.addr[2] = buf[18];
    218		bpdu.bridge_id.addr[3] = buf[19];
    219		bpdu.bridge_id.addr[4] = buf[20];
    220		bpdu.bridge_id.addr[5] = buf[21];
    221		bpdu.port_id = (buf[22] << 8) | buf[23];
    222
    223		bpdu.message_age = br_get_ticks(buf+24);
    224		bpdu.max_age = br_get_ticks(buf+26);
    225		bpdu.hello_time = br_get_ticks(buf+28);
    226		bpdu.forward_delay = br_get_ticks(buf+30);
    227
    228		if (bpdu.message_age > bpdu.max_age) {
    229			if (net_ratelimit())
    230				br_notice(p->br,
    231					  "port %u config from %pM"
    232					  " (message_age %ul > max_age %ul)\n",
    233					  p->port_no,
    234					  eth_hdr(skb)->h_source,
    235					  bpdu.message_age, bpdu.max_age);
    236			goto out;
    237		}
    238
    239		br_received_config_bpdu(p, &bpdu);
    240	} else if (buf[0] == BPDU_TYPE_TCN) {
    241		br_received_tcn_bpdu(p);
    242	}
    243 out:
    244	spin_unlock(&br->lock);
    245 err:
    246	kfree_skb(skb);
    247}