Blame view

net/bridge/br_stp_bpdu.c 5.42 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>
e730c1551   Eric W. Biederman   [NET]: Make packe...
19
  #include <net/net_namespace.h>
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
20
  #include <net/llc.h>
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
21
  #include <net/llc_pdu.h>
7c85fbf06   Patrick McHardy   bridge: Use STP d...
22
  #include <net/stp.h>
4dc6d9cc3   Peter Chubb   [BRIDGE]: Unalign...
23
  #include <asm/unaligned.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
  
  #include "br_private.h"
  #include "br_private_stp.h"
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
27
  #define STP_HZ		256
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
29
30
31
  #define LLC_RESERVE sizeof(struct llc_pdu_un)
  
  static void br_send_bpdu(struct net_bridge_port *p,
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
32
  			 const unsigned char *data, int length)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
36
37
  	skb = dev_alloc_skb(length+LLC_RESERVE);
  	if (!skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39

12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
40
  	skb->dev = p->dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
  	skb->protocol = htons(ETH_P_802_2);
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
42
43
44
45
46
47
48
49
50
  
  	skb_reserve(skb, LLC_RESERVE);
  	memcpy(__skb_put(skb, length), data, length);
  
  	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
51

e6f26129e   Florian Westphal   bridge: stp: ensu...
52
  	skb_reset_mac_header(skb);
713aefa3f   Jan Engelhardt   netfilter: bridge...
53
  	NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
  		dev_queue_xmit);
  }
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
56
  static inline void br_set_ticks(unsigned char *dest, int j)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  {
18fdb2b25   Stephen Hemminger   [BRIDGE]: stp tim...
58
  	unsigned long ticks = (STP_HZ * j)/ HZ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59

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

172589ccd   Ilpo Järvinen   [NET]: DIV_ROUND_...
66
  	return DIV_ROUND_UP(ticks * HZ, STP_HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
  }
  
  /* 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...
72
  	unsigned char buf[35];
9cde07087   Stephen Hemminger   bridge: add suppo...
73
74
  	if (p->br->stp_enabled != BR_KERNEL_STP)
  		return;
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
75
76
77
78
79
  	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
80
  		(bpdu->topology_change_ack ? 0x80 : 0);
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  	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
110
111
112
113
114
  }
  
  /* called under bridge lock */
  void br_send_tcn_bpdu(struct net_bridge_port *p)
  {
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
115
  	unsigned char buf[4];
9cde07087   Stephen Hemminger   bridge: add suppo...
116
117
  	if (p->br->stp_enabled != BR_KERNEL_STP)
  		return;
12ac84c4a   Stephen Hemminger   [BRIDGE]: use LLC...
118
119
120
121
  	buf[0] = 0;
  	buf[1] = 0;
  	buf[2] = 0;
  	buf[3] = BPDU_TYPE_TCN;
485c2967d   Stephen Hemminger   [BRIDGE]: random ...
122
  	br_send_bpdu(p, buf, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  }
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
124
125
126
  /*
   * Called from llc.
   *
eeaf61d88   stephen hemminger   bridge: add rcu_r...
127
   * NO locks, but rcu_read_lock
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
128
   */
7c85fbf06   Patrick McHardy   bridge: Use STP d...
129
130
  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
131
  {
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
132
  	const unsigned char *dest = eth_hdr(skb)->h_dest;
f350a0a87   Jiri Pirko   bridge: use rx_ha...
133
  	struct net_bridge_port *p;
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
134
  	struct net_bridge *br;
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
135
  	const unsigned char *buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
137
138
139
140
141
142
143
  	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...
144

b5ed54e94   stephen hemminger   bridge: fix RCU r...
145
146
147
  	p = br_port_get_rcu(dev);
  	if (!p)
  		goto err;
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
148
149
  	br = p->br;
  	spin_lock(&br->lock);
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
150

9cde07087   Stephen Hemminger   bridge: add suppo...
151
152
153
154
155
156
157
  	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...
158
  		goto out;
85967bb46   Stephen Hemminger   [BRIDGE]: prevent...
159

fda93d92d   Stephen Hemminger   [BRIDGE]: allow s...
160
  	if (compare_ether_addr(dest, br->group_addr) != 0)
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
161
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162

cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
163
  	buf = skb_pull(skb, 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
  	if (buf[0] == BPDU_TYPE_CONFIG) {
  		struct br_config_bpdu bpdu;
  
  		if (!pskb_may_pull(skb, 32))
cf0f02d04   Stephen Hemminger   [BRIDGE]: use llc...
169
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  
  		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...
202
203
204
205
206
207
208
209
210
211
212
  		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
213
  		br_received_config_bpdu(p, &bpdu);
160d73b84   stephen hemminger   bridge: minor cle...
214
  	} else if (buf[0] == BPDU_TYPE_TCN) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
  		br_received_tcn_bpdu(p);
  	}
   out:
b3f1be4b5   Stephen Hemminger   [BRIDGE]: fix for...
218
  	spin_unlock(&br->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
   err:
  	kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  }