Blame view

net/bridge/br_stp_bpdu.c 5.81 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *	Spanning tree protocol; BPDU handling
   *	Linux ethernet bridge
   *
   *	Authors:
   *	Lennert Buytenhek		<buytenh@gnu.org>
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   */
  
  #include <linux/kernel.h>
  #include <linux/netfilter_bridge.h>
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
16
17
  #include <linux/etherdevice.h>
  #include <linux/llc.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
547b4e718   Stephen Hemminger   bridge: set prior...
19
  #include <linux/pkt_sched.h>
e730c1551   Eric W. Biederman   [NET]: Make packe...
20
  #include <net/net_namespace.h>
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
21
  #include <net/llc.h>
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
22
  #include <net/llc_pdu.h>
7c85fbf06   Patrick McHardy   bridge: Use STP d...
23
  #include <net/stp.h>
4dc6d9cc3   Peter Chubb   [BRIDGE]: Unalign...
24
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
  
  #include "br_private.h"
  #include "br_private_stp.h"
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
28
  #define STP_HZ		256
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
30
  #define LLC_RESERVE sizeof(struct llc_pdu_un)
0c4b51f00   Eric W. Biederman   netfilter: Pass n...
31
32
  static int br_send_bpdu_finish(struct net *net, struct sock *sk,
  			       struct sk_buff *skb)
1f19c578d   Eric W. Biederman   bridge: Introduce...
33
34
35
  {
  	return dev_queue_xmit(skb);
  }
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
36
  static void br_send_bpdu(struct net_bridge_port *p,
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
37
  			 const unsigned char *data, int length)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
41
42
  	skb = dev_alloc_skb(length+LLC_RESERVE);
  	if (!skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
45
  	skb->dev = p->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
  	skb->protocol = htons(ETH_P_802_2);
547b4e718   Stephen Hemminger   bridge: set prior...
47
  	skb->priority = TC_PRIO_CONTROL;
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
48
49
  
  	skb_reserve(skb, LLC_RESERVE);
de77b966c   yuan linyu   net: introduce __...
50
  	__skb_put_data(skb, data, length);
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
51
52
53
54
55
56
  
  	llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
  			    LLC_SAP_BSPAN, LLC_PDU_CMD);
  	llc_pdu_init_as_ui_cmd(skb);
  
  	llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57

e6f26129e   Florian Westphal   bridge: stp: ensu...
58
  	skb_reset_mac_header(skb);
29a26a568   Eric W. Biederman   netfilter: Pass s...
59
60
  	NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
  		dev_net(p->dev), NULL, skb, NULL, skb->dev,
1f19c578d   Eric W. Biederman   bridge: Introduce...
61
  		br_send_bpdu_finish);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  }
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
63
  static inline void br_set_ticks(unsigned char *dest, int j)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  {
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
65
  	unsigned long ticks = (STP_HZ * j)/ HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

d3e2ce3bc   Harvey Harrison   net: use get/put_...
67
  	put_unaligned_be16(ticks, dest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  }
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
69
  static inline int br_get_ticks(const unsigned char *src)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
d3e2ce3bc   Harvey Harrison   net: use get/put_...
71
  	unsigned long ticks = get_unaligned_be16(src);
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
72

172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
73
  	return DIV_ROUND_UP(ticks * HZ, STP_HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
  }
  
  /* called under bridge lock */
  void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
  {
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
79
  	unsigned char buf[35];
9cde07087   Stephen Hemminger   bridge: add suppo...
80
81
  	if (p->br->stp_enabled != BR_KERNEL_STP)
  		return;
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
82
83
84
85
86
  	buf[0] = 0;
  	buf[1] = 0;
  	buf[2] = 0;
  	buf[3] = BPDU_TYPE_CONFIG;
  	buf[4] = (bpdu->topology_change ? 0x01 : 0) |
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  		(bpdu->topology_change_ack ? 0x80 : 0);
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	buf[5] = bpdu->root.prio[0];
  	buf[6] = bpdu->root.prio[1];
  	buf[7] = bpdu->root.addr[0];
  	buf[8] = bpdu->root.addr[1];
  	buf[9] = bpdu->root.addr[2];
  	buf[10] = bpdu->root.addr[3];
  	buf[11] = bpdu->root.addr[4];
  	buf[12] = bpdu->root.addr[5];
  	buf[13] = (bpdu->root_path_cost >> 24) & 0xFF;
  	buf[14] = (bpdu->root_path_cost >> 16) & 0xFF;
  	buf[15] = (bpdu->root_path_cost >> 8) & 0xFF;
  	buf[16] = bpdu->root_path_cost & 0xFF;
  	buf[17] = bpdu->bridge_id.prio[0];
  	buf[18] = bpdu->bridge_id.prio[1];
  	buf[19] = bpdu->bridge_id.addr[0];
  	buf[20] = bpdu->bridge_id.addr[1];
  	buf[21] = bpdu->bridge_id.addr[2];
  	buf[22] = bpdu->bridge_id.addr[3];
  	buf[23] = bpdu->bridge_id.addr[4];
  	buf[24] = bpdu->bridge_id.addr[5];
  	buf[25] = (bpdu->port_id >> 8) & 0xFF;
  	buf[26] = bpdu->port_id & 0xFF;
  
  	br_set_ticks(buf+27, bpdu->message_age);
  	br_set_ticks(buf+29, bpdu->max_age);
  	br_set_ticks(buf+31, bpdu->hello_time);
  	br_set_ticks(buf+33, bpdu->forward_delay);
  
  	br_send_bpdu(p, buf, 35);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
  }
  
  /* called under bridge lock */
  void br_send_tcn_bpdu(struct net_bridge_port *p)
  {
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
122
  	unsigned char buf[4];
9cde07087   Stephen Hemminger   bridge: add suppo...
123
124
  	if (p->br->stp_enabled != BR_KERNEL_STP)
  		return;
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
125
126
127
128
  	buf[0] = 0;
  	buf[1] = 0;
  	buf[2] = 0;
  	buf[3] = BPDU_TYPE_TCN;
485c2967d   Stephen Hemminger   [BRIDGE]: random ...
129
  	br_send_bpdu(p, buf, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
  }
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
131
132
133
  /*
   * Called from llc.
   *
eeaf61d88   stephen hemminger   bridge: add rcu_r...
134
   * NO locks, but rcu_read_lock
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
135
   */
7c85fbf06   Patrick McHardy   bridge: Use STP d...
136
137
  void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
  		struct net_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
139
  	const unsigned char *dest = eth_hdr(skb)->h_dest;
f350a0a87   Jiri Pirko   bridge: use rx_ha...
140
  	struct net_bridge_port *p;
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
141
  	struct net_bridge *br;
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
142
  	const unsigned char *buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143

cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
144
145
146
147
148
149
150
  	if (!pskb_may_pull(skb, 4))
  		goto err;
  
  	/* compare of protocol id and version */
  	buf = skb->data;
  	if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
  		goto err;
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
151

859828c0e   Jiri Pirko   br: fix use of ->...
152
  	p = br_port_get_check_rcu(dev);
b5ed54e94   stephen hemminger   bridge: fix RCU r...
153
154
  	if (!p)
  		goto err;
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
155
156
  	br = p->br;
  	spin_lock(&br->lock);
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
157

9cde07087   Stephen Hemminger   bridge: add suppo...
158
159
160
161
162
163
164
  	if (br->stp_enabled != BR_KERNEL_STP)
  		goto out;
  
  	if (!(br->dev->flags & IFF_UP))
  		goto out;
  
  	if (p->state == BR_STATE_DISABLED)
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
165
  		goto out;
85967bb46   Stephen Hemminger   [BRIDGE]: prevent...
166

9a7b6ef9b   Joe Perches   bridge: Convert c...
167
  	if (!ether_addr_equal(dest, br->group_addr))
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
168
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169

a2e01a65c   stephen hemminger   bridge: implement...
170
171
172
173
174
175
176
  	if (p->flags & BR_BPDU_GUARD) {
  		br_notice(br, "BPDU received on blocked port %u(%s)
  ",
  			  (unsigned int) p->port_no, p->dev->name);
  		br_stp_disable_port(p);
  		goto out;
  	}
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
177
  	buf = skb_pull(skb, 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
182
  	if (buf[0] == BPDU_TYPE_CONFIG) {
  		struct br_config_bpdu bpdu;
  
  		if (!pskb_may_pull(skb, 32))
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
183
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  
  		buf = skb->data;
  		bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
  		bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;
  
  		bpdu.root.prio[0] = buf[2];
  		bpdu.root.prio[1] = buf[3];
  		bpdu.root.addr[0] = buf[4];
  		bpdu.root.addr[1] = buf[5];
  		bpdu.root.addr[2] = buf[6];
  		bpdu.root.addr[3] = buf[7];
  		bpdu.root.addr[4] = buf[8];
  		bpdu.root.addr[5] = buf[9];
  		bpdu.root_path_cost =
  			(buf[10] << 24) |
  			(buf[11] << 16) |
  			(buf[12] << 8) |
  			buf[13];
  		bpdu.bridge_id.prio[0] = buf[14];
  		bpdu.bridge_id.prio[1] = buf[15];
  		bpdu.bridge_id.addr[0] = buf[16];
  		bpdu.bridge_id.addr[1] = buf[17];
  		bpdu.bridge_id.addr[2] = buf[18];
  		bpdu.bridge_id.addr[3] = buf[19];
  		bpdu.bridge_id.addr[4] = buf[20];
  		bpdu.bridge_id.addr[5] = buf[21];
  		bpdu.port_id = (buf[22] << 8) | buf[23];
  
  		bpdu.message_age = br_get_ticks(buf+24);
  		bpdu.max_age = br_get_ticks(buf+26);
  		bpdu.hello_time = br_get_ticks(buf+28);
  		bpdu.forward_delay = br_get_ticks(buf+30);
0652cac22   stephen hemminger   bridge: ignore bo...
216
217
218
219
220
221
222
223
224
225
226
  		if (bpdu.message_age > bpdu.max_age) {
  			if (net_ratelimit())
  				br_notice(p->br,
  					  "port %u config from %pM"
  					  " (message_age %ul > max_age %ul)
  ",
  					  p->port_no,
  					  eth_hdr(skb)->h_source,
  					  bpdu.message_age, bpdu.max_age);
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  		br_received_config_bpdu(p, &bpdu);
160d73b84   stephen hemminger   bridge: minor cle...
228
  	} else if (buf[0] == BPDU_TYPE_TCN) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
  		br_received_tcn_bpdu(p);
  	}
   out:
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
232
  	spin_unlock(&br->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
   err:
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
  }