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