]>
Commit | Line | Data |
---|---|---|
ad02a0eb SH |
1 | /***************************************************************************** |
2 | Copyright (c) 2006 EMC Corporation. | |
3 | ||
4 | This program is free software; you can redistribute it and/or modify it | |
5 | under the terms of the GNU General Public License as published by the Free | |
6 | Software Foundation; either version 2 of the License, or (at your option) | |
7 | any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License along with | |
15 | this program; if not, write to the Free Software Foundation, Inc., 59 | |
16 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 | ||
18 | The full GNU General Public License is included in this distribution in the | |
19 | file called LICENSE. | |
20 | ||
21 | Authors: Srinivas Aji <Aji_Srinivas@emc.com> | |
22 | ||
23 | ******************************************************************************/ | |
24 | ||
25 | #include "bridge_ctl.h" | |
26 | #include "netif_utils.h" | |
f2592588 | 27 | #include "packet.h" |
ad02a0eb SH |
28 | |
29 | #include <unistd.h> | |
30 | #include <net/if.h> | |
f2592588 | 31 | #include <stdlib.h> |
ad02a0eb | 32 | #include <linux/if_bridge.h> |
fcc6b809 | 33 | #include <linux/if_ether.h> |
ad02a0eb SH |
34 | #include <arpa/inet.h> |
35 | #include <sys/types.h> | |
36 | ||
ad02a0eb SH |
37 | #include <stdio.h> |
38 | #include <string.h> | |
39 | ||
40 | #include "log.h" | |
41 | ||
b600a2c3 | 42 | #include "rstp.h" |
ad02a0eb SH |
43 | /*------------------------------------------------------------*/ |
44 | ||
11904a35 SH |
45 | struct ifdata { |
46 | int if_index; | |
47 | struct ifdata *next; | |
48 | int up; | |
49 | char name[IFNAMSIZ]; | |
3cf368c1 | 50 | unsigned char macaddr[ETH_ALEN]; |
11904a35 SH |
51 | |
52 | int is_bridge; | |
53 | /* If bridge */ | |
54 | struct ifdata *bridge_next; | |
55 | struct ifdata *port_list; | |
56 | int do_stp; | |
57 | int stp_up; | |
b600a2c3 SA |
58 | STP_Bridge *stp_bridge; |
59 | ||
11904a35 SH |
60 | |
61 | /* If port */ | |
62 | int speed; | |
63 | int duplex; | |
64 | struct ifdata *master; | |
65 | struct ifdata *port_next; | |
66 | /* STP port index */ | |
67 | int port_index; | |
b600a2c3 | 68 | STP_Port *stp_port; |
f2592588 SH |
69 | |
70 | struct epoll_event_handler event; | |
ad02a0eb SH |
71 | }; |
72 | ||
73 | /* Instances */ | |
74 | struct ifdata *current_br = NULL; | |
75 | ||
ad02a0eb SH |
76 | struct ifdata *find_port(int port_index) |
77 | { | |
11904a35 SH |
78 | struct ifdata *ifc = current_br->port_list; |
79 | while (ifc && ifc->port_index != port_index) | |
80 | ifc = ifc->port_next; | |
81 | return ifc; | |
ad02a0eb SH |
82 | } |
83 | ||
ad02a0eb | 84 | |
ad02a0eb SH |
85 | struct ifdata *if_head = NULL; |
86 | struct ifdata *br_head = NULL; | |
87 | ||
88 | struct ifdata *find_if(int if_index) | |
89 | { | |
11904a35 SH |
90 | struct ifdata *p = if_head; |
91 | while (p && p->if_index != if_index) | |
92 | p = p->next; | |
93 | return p; | |
ad02a0eb SH |
94 | } |
95 | ||
96 | #define ADD_TO_LIST(_list, _next, _ifc) \ | |
97 | do { \ | |
98 | (_ifc)->_next = (_list); \ | |
99 | (_list) = (_ifc); \ | |
100 | } while (0) | |
101 | ||
102 | #define REMOVE_FROM_LIST(_list, _next, _ifc, _error_fmt, _args...) \ | |
103 | do { \ | |
104 | struct ifdata **_prev = &(_list); \ | |
105 | while (*_prev && *_prev != (_ifc)) \ | |
106 | _prev = &(*_prev)->_next; \ | |
107 | if (*_prev != (_ifc)) \ | |
108 | ERROR(_error_fmt, ##_args); \ | |
109 | else \ | |
110 | *_prev = (_ifc)->_next; \ | |
111 | } while (0) | |
112 | ||
113 | /* Caller ensures that there isn't any ifdata with this index */ | |
114 | /* If br is NULL, new interface is a bridge, else it is a port of br */ | |
115 | struct ifdata *create_if(int if_index, struct ifdata *br) | |
116 | { | |
11904a35 SH |
117 | struct ifdata *p; |
118 | TST((p = malloc(sizeof(*p))) != NULL, NULL); | |
119 | ||
f2592588 SH |
120 | memset(p, 0, sizeof(*p)); |
121 | ||
11904a35 SH |
122 | /* Init fields */ |
123 | p->if_index = if_index; | |
124 | p->is_bridge = (br == NULL); | |
f2592588 SH |
125 | |
126 | /* TODO: purge use of name, due to issue with renameing */ | |
11904a35 | 127 | if_indextoname(if_index, p->name); |
017b1185 | 128 | get_hwaddr(p->name, p->macaddr); |
f2592588 | 129 | |
11904a35 SH |
130 | if (p->is_bridge) { |
131 | INFO("Add bridge %s", p->name); | |
b600a2c3 SA |
132 | p->stp_bridge = STP_IN_bridge_create(p); |
133 | if (!p->stp_bridge) { | |
134 | ERROR("Couldn't create STP Bridge"); | |
135 | free(p); | |
136 | return NULL; | |
137 | } | |
138 | STP_IN_set_bridge_address(p->stp_bridge, | |
139 | (STP_MacAddress *)p->macaddr); | |
140 | INFO("Set bridge address %s to %02x:%02x:%02x:%02x:%02x:%02x", | |
141 | p->name, | |
142 | p->macaddr[0], p->macaddr[1], p->macaddr[2], | |
143 | p->macaddr[1], p->macaddr[4], p->macaddr[5] | |
144 | ); | |
11904a35 SH |
145 | /* Init slave list */ |
146 | p->port_list = NULL; | |
147 | ||
148 | p->do_stp = 0; | |
149 | p->up = 0; | |
150 | p->stp_up = 0; | |
b600a2c3 | 151 | ADD_TO_LIST(br_head, bridge_next, p); /* Add to bridge list */ |
11904a35 SH |
152 | } else { |
153 | INFO("Add iface %s to bridge %s", p->name, br->name); | |
154 | p->up = 0; | |
155 | p->speed = 0; | |
156 | p->duplex = 0; | |
157 | p->master = br; | |
f2592588 | 158 | |
b600a2c3 SA |
159 | p->port_index = get_bridge_portno(p->name); |
160 | if (p->port_index < 0) { | |
161 | ERROR("Couldn't get port number for %s", p->name); | |
162 | free(p); | |
163 | return NULL; | |
164 | } | |
165 | p->stp_port = STP_IN_port_create(p->master->stp_bridge, | |
166 | p->port_index, p); | |
167 | if (!p->stp_port) { | |
168 | ERROR("Couldn't create STP Port"); | |
169 | free(p); | |
170 | return NULL; | |
171 | } | |
172 | ||
11904a35 | 173 | ADD_TO_LIST(br->port_list, port_next, p); /* Add to bridge port list */ |
f2592588 | 174 | |
11904a35 | 175 | } |
f2592588 | 176 | |
11904a35 SH |
177 | /* Add to interface list */ |
178 | ADD_TO_LIST(if_head, next, p); | |
179 | ||
180 | return p; | |
ad02a0eb SH |
181 | } |
182 | ||
183 | void delete_if(struct ifdata *ifc) | |
184 | { | |
11904a35 SH |
185 | INFO("Delete iface %s", ifc->name); |
186 | if (ifc->is_bridge) { /* Bridge: */ | |
b600a2c3 | 187 | STP_IN_set_bridge_enable(ifc->stp_bridge, 0); |
11904a35 SH |
188 | /* Delete ports */ |
189 | while (ifc->port_list) | |
190 | delete_if(ifc->port_list); | |
191 | /* Remove from bridge list */ | |
192 | REMOVE_FROM_LIST(br_head, bridge_next, ifc, | |
193 | "Can't find interface ifindex %d bridge list", | |
194 | ifc->if_index); | |
b600a2c3 | 195 | STP_IN_bridge_delete(ifc->stp_bridge); |
11904a35 | 196 | } else { /* Port */ |
11904a35 SH |
197 | /* Remove from bridge port list */ |
198 | REMOVE_FROM_LIST(ifc->master->port_list, port_next, ifc, | |
199 | "Can't find interface ifindex %d on br %d's port list", | |
200 | ifc->if_index, ifc->master->if_index); | |
b600a2c3 | 201 | STP_IN_port_delete(ifc->stp_port); |
11904a35 | 202 | } |
f2592588 | 203 | |
11904a35 SH |
204 | /* Remove from bridge interface list */ |
205 | REMOVE_FROM_LIST(if_head, next, ifc, | |
206 | "Can't find interface ifindex %d on iflist", | |
207 | ifc->if_index); | |
f2592588 | 208 | free(ifc); |
ad02a0eb SH |
209 | } |
210 | ||
017b1185 SA |
211 | /* New MAC address is stored in addr, which also holds the old value on entry. |
212 | Return nonzero if the address changed */ | |
213 | static int check_mac_address(char *name, unsigned char *addr) | |
214 | { | |
215 | unsigned char temp_addr[6]; | |
216 | if (get_hwaddr(name, temp_addr)) { | |
b600a2c3 | 217 | LOG("Error getting hw address: %s", name); |
017b1185 SA |
218 | /* Error. Ignore the new value */ |
219 | return 0; | |
220 | } | |
221 | if (memcmp(addr, temp_addr, sizeof(temp_addr)) == 0) | |
222 | return 0; | |
223 | else { | |
224 | memcpy(addr, temp_addr, sizeof(temp_addr)); | |
225 | return 1; | |
226 | } | |
227 | } | |
228 | ||
229 | ||
358260ff SH |
230 | static int stp_enabled(struct ifdata *br) |
231 | { | |
232 | char path[40 + IFNAMSIZ]; | |
c768cf44 | 233 | int ret; |
358260ff SH |
234 | sprintf(path, "/sys/class/net/%s/bridge/stp_state", br->name); |
235 | FILE *f = fopen(path, "r"); | |
236 | if (!f) { | |
237 | LOG("Open %s failed", path); | |
238 | return 0; | |
239 | } | |
240 | int enabled = 0; | |
c768cf44 DF |
241 | ret = fscanf(f, "%d", &enabled); |
242 | if (!ret) { | |
243 | LOG("%s, stp_state parsing error", path); | |
244 | return 0; | |
245 | } | |
358260ff SH |
246 | fclose(f); |
247 | INFO("STP on %s state %d", br->name, enabled); | |
248 | ||
249 | return enabled == 2; /* ie user mode STP */ | |
250 | } | |
251 | ||
d19d0e35 | 252 | static void set_br_up(struct ifdata *br, int up) |
ad02a0eb | 253 | { |
358260ff | 254 | int stp_up = stp_enabled(br); |
d19d0e35 SH |
255 | INFO("%s was %s stp was %s", br->name, |
256 | up ? "up" : "down", | |
257 | br->stp_up ? "up" : "down"); | |
358260ff | 258 | INFO("Set bridge %s %s stp %s" , br->name, |
d19d0e35 SH |
259 | up ? "up" : "down", |
260 | stp_up ? "up" : "down"); | |
358260ff | 261 | |
b600a2c3 SA |
262 | int changed = 0; |
263 | ||
264 | if (up != br->up) { | |
11904a35 | 265 | br->up = up; |
b600a2c3 SA |
266 | changed = 1; |
267 | } | |
358260ff | 268 | |
b600a2c3 SA |
269 | if (br->stp_up != stp_up) { |
270 | br->stp_up = stp_up; | |
271 | changed = 1; | |
272 | } | |
273 | ||
017b1185 SA |
274 | if (check_mac_address(br->name, br->macaddr)) { |
275 | /* MAC address changed */ | |
b600a2c3 SA |
276 | /* Notify bridge address change */ |
277 | STP_IN_set_bridge_address( | |
278 | br->stp_bridge, (STP_MacAddress *)br->macaddr); | |
017b1185 SA |
279 | } |
280 | ||
b600a2c3 SA |
281 | if (changed) |
282 | STP_IN_set_bridge_enable(br->stp_bridge, | |
283 | (br->up && br->stp_up)?1:0); | |
ad02a0eb SH |
284 | } |
285 | ||
d19d0e35 | 286 | static void set_if_up(struct ifdata *ifc, int up) |
ad02a0eb | 287 | { |
11904a35 SH |
288 | INFO("Port %s : %s", ifc->name, (up ? "up" : "down")); |
289 | int speed = -1; | |
290 | int duplex = -1; | |
b600a2c3 | 291 | int changed = 0; |
017b1185 SA |
292 | |
293 | if (check_mac_address(ifc->name, ifc->macaddr)) { | |
294 | /* MAC address changed */ | |
295 | if (check_mac_address(ifc->master->name, ifc->master->macaddr) | |
b600a2c3 | 296 | ) { |
017b1185 | 297 | /* Notify bridge address change */ |
b600a2c3 SA |
298 | STP_IN_set_bridge_address( |
299 | ifc->master->stp_bridge, | |
300 | (STP_MacAddress *)ifc->master->macaddr); | |
017b1185 SA |
301 | } |
302 | } | |
303 | ||
11904a35 SH |
304 | if (!up) { /* Down */ |
305 | if (ifc->up) { | |
306 | ifc->up = up; | |
b600a2c3 | 307 | changed = 1; |
11904a35 SH |
308 | } |
309 | } else { /* Up */ | |
310 | int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex); | |
311 | if (r < 0) { /* Didn't succeed */ | |
312 | } | |
313 | if (speed < 0) | |
314 | speed = 10; | |
315 | if (duplex < 0) | |
316 | duplex = 0; /* Assume half duplex */ | |
317 | ||
318 | if (speed != ifc->speed) { | |
319 | ifc->speed = speed; | |
b600a2c3 | 320 | changed = 1; |
11904a35 SH |
321 | } |
322 | if (duplex != ifc->duplex) { | |
323 | ifc->duplex = duplex; | |
b600a2c3 | 324 | changed = 1; |
11904a35 SH |
325 | } |
326 | if (!ifc->up) { | |
327 | ifc->up = 1; | |
b600a2c3 | 328 | changed = 1; |
11904a35 SH |
329 | } |
330 | } | |
b600a2c3 SA |
331 | if (changed) |
332 | STP_IN_set_port_enable(ifc->stp_port, | |
333 | ifc->up, ifc->speed, ifc->duplex); | |
ad02a0eb SH |
334 | } |
335 | ||
336 | /*------------------------------------------------------------*/ | |
337 | ||
968d3d40 SH |
338 | int bridge_notify(int br_index, int if_index, int newlink, |
339 | unsigned flags) | |
ad02a0eb | 340 | { |
11904a35 | 341 | struct ifdata *br = NULL; |
968d3d40 SH |
342 | |
343 | LOG("br_index %d, if_index %d, up %d running %d", | |
344 | br_index, if_index, (flags & IFF_UP), flags & IFF_RUNNING); | |
345 | ||
11904a35 SH |
346 | if (br_index >= 0) { |
347 | br = find_if(br_index); | |
348 | if (br && !br->is_bridge) { | |
349 | ERROR | |
350 | ("Notification shows non bridge interface %d as bridge.", | |
351 | br_index); | |
352 | return -1; | |
353 | } | |
354 | if (!br) | |
355 | br = create_if(br_index, NULL); | |
356 | if (!br) { | |
357 | ERROR("Couldn't create data for bridge interface %d", | |
358 | br_index); | |
359 | return -1; | |
360 | } | |
361 | /* Bridge must be up if we get such notifications */ | |
b600a2c3 | 362 | // Not true anymore - set_br_up(br, 1); |
11904a35 SH |
363 | } |
364 | ||
365 | struct ifdata *ifc = find_if(if_index); | |
366 | ||
367 | if (br) { | |
368 | if (ifc) { | |
369 | if (ifc->is_bridge) { | |
370 | ERROR | |
371 | ("Notification shows bridge interface %d as slave of %d", | |
372 | if_index, br_index); | |
373 | return -1; | |
374 | } | |
375 | if (ifc->master != br) { | |
376 | INFO("Device %d has come to bridge %d. " | |
377 | "Missed notify for deletion from bridge %d", | |
378 | if_index, br_index, ifc->master->if_index); | |
379 | delete_if(ifc); | |
380 | ifc = NULL; | |
381 | } | |
382 | } | |
09dffbd4 SA |
383 | if (!ifc) { |
384 | if (!newlink) { | |
385 | INFO("Got DELLINK for unknown port %d on " | |
386 | "bridge %d", if_index, br_index); | |
387 | return -1; | |
388 | } | |
11904a35 | 389 | ifc = create_if(if_index, br); |
09dffbd4 | 390 | } |
11904a35 SH |
391 | if (!ifc) { |
392 | ERROR | |
393 | ("Couldn't create data for interface %d (master %d)", | |
394 | if_index, br_index); | |
395 | return -1; | |
396 | } | |
09dffbd4 | 397 | if (!newlink) { |
11904a35 SH |
398 | delete_if(ifc); |
399 | return 0; | |
400 | } | |
968d3d40 SH |
401 | int up = (flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING); |
402 | ||
11904a35 SH |
403 | if (ifc->up != up) |
404 | set_if_up(ifc, up); /* And speed and duplex */ | |
405 | } else { /* No br_index */ | |
406 | if (!newlink) { | |
407 | /* DELLINK not from bridge means interface unregistered. */ | |
408 | /* Cleanup removed bridge or removed bridge slave */ | |
409 | if (ifc) | |
410 | delete_if(ifc); | |
411 | return 0; | |
412 | } else { /* This may be a new link */ | |
413 | if (!ifc) { | |
414 | char ifname[IFNAMSIZ]; | |
415 | if (if_indextoname(if_index, ifname) | |
416 | && is_bridge(ifname)) { | |
417 | ifc = create_if(if_index, NULL); | |
418 | if (!ifc) { | |
419 | ERROR | |
420 | ("Couldn't create data for bridge interface %d", | |
421 | if_index); | |
422 | return -1; | |
423 | } | |
424 | } | |
425 | } | |
426 | if (ifc && !ifc->is_bridge && | |
427 | !is_bridge_slave(ifc->master->name, ifc->name)) { | |
428 | /* Interface might have left bridge and we might have missed deletion */ | |
429 | delete_if(ifc); | |
430 | return 0; | |
431 | } | |
968d3d40 | 432 | |
017b1185 | 433 | if (ifc) { |
968d3d40 SH |
434 | if (ifc->is_bridge) { |
435 | int up = (flags & IFF_UP) != 0; | |
436 | if (ifc->up != up) | |
437 | set_br_up(ifc, up); | |
438 | } else { | |
439 | int up = (flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING); | |
440 | ||
441 | if (ifc->up != up) | |
442 | set_if_up(ifc, up); | |
443 | } | |
11904a35 SH |
444 | } |
445 | } | |
446 | } | |
447 | return 0; | |
ad02a0eb SH |
448 | } |
449 | ||
fcc6b809 SA |
450 | struct llc_header |
451 | { | |
452 | uint8_t dest_addr[ETH_ALEN]; | |
453 | uint8_t src_addr[ETH_ALEN]; | |
454 | uint16_t len8023; | |
455 | uint8_t d_sap; /* 0x42 */ | |
456 | uint8_t s_sap; /* 0x42 */ | |
457 | uint8_t llc_ui; /* 0x03 */ | |
458 | } __attribute__((packed)); | |
459 | ||
460 | const unsigned char bridge_group_address[ETH_ALEN] = { | |
461 | 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 | |
462 | }; | |
463 | ||
464 | const unsigned char STP_SAP = 0x42; | |
465 | ||
96e20123 | 466 | void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) |
ad02a0eb | 467 | { |
96e20123 | 468 | struct ifdata *ifc = find_if(if_index); |
f2592588 | 469 | |
96e20123 | 470 | LOG("ifindex %d, len %d", if_index, len); |
b79b9a98 | 471 | if (!ifc || !ifc->master) |
96e20123 SH |
472 | return; |
473 | ||
358260ff | 474 | TST(ifc->up,); |
b600a2c3 SA |
475 | if (!ifc->master->stp_up) |
476 | return; | |
f2592588 | 477 | |
fcc6b809 SA |
478 | /* Validate Ethernet and LLC header */ |
479 | { | |
480 | struct llc_header *h; | |
481 | unsigned int l; | |
482 | TST(len > sizeof(struct llc_header),); | |
483 | h = (struct llc_header *)data; | |
484 | TST(memcmp(h->dest_addr, bridge_group_address, ETH_ALEN) == 0, | |
b600a2c3 SA |
485 | INFO("ifindex %d, len %d, %02x:%02x:%02x:%02x:%02x:%02x", |
486 | if_index, len, | |
487 | h->dest_addr[0],h->dest_addr[1],h->dest_addr[2], | |
488 | h->dest_addr[3],h->dest_addr[4],h->dest_addr[5])); | |
fcc6b809 SA |
489 | l = ntohs(h->len8023); |
490 | TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= 3,); | |
491 | TST(h->d_sap == STP_SAP && h->s_sap == STP_SAP | |
492 | && (h->llc_ui & 0x3) == 0x3 /* LLC UI */,); | |
fcc6b809 | 493 | |
b600a2c3 SA |
494 | STP_IN_rx_bpdu(ifc->stp_port, |
495 | /* Don't include LLC header */ | |
496 | data + sizeof(*h), l - 3); | |
11904a35 | 497 | } |
ad02a0eb SH |
498 | } |
499 | ||
500 | void bridge_one_second(void) | |
501 | { | |
11904a35 SH |
502 | // LOG(""); |
503 | struct ifdata *br; | |
504 | for (br = br_head; br; br = br->bridge_next) { | |
b600a2c3 | 505 | STP_IN_one_second(br->stp_bridge); |
11904a35 | 506 | } |
ad02a0eb SH |
507 | } |
508 | ||
509 | /* Implementing STP_OUT functions */ | |
510 | ||
511 | int flush_port(char *sys_name) | |
512 | { | |
11904a35 SH |
513 | FILE *f = fopen(sys_name, "w"); |
514 | TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name); | |
515 | int r = fwrite("1", 1, 1, f); | |
516 | fclose(f); | |
517 | TST(r == 1, -1); | |
518 | return 0; | |
ad02a0eb SH |
519 | } |
520 | ||
b600a2c3 | 521 | void STP_OUT_port_fdb_flush(void *user_ref) |
11904a35 | 522 | { |
b600a2c3 | 523 | struct ifdata *port = user_ref; |
11904a35 | 524 | char fname[128]; |
b600a2c3 SA |
525 | snprintf(fname, sizeof(fname), |
526 | "/sys/class/net/%s/brport/flush", port->name); | |
527 | fname[sizeof(fname) - 1] = 0; | |
528 | TST(flush_port(fname) == 0,); | |
ad02a0eb SH |
529 | } |
530 | ||
b600a2c3 | 531 | void STP_OUT_port_set_state(void *user_ref, unsigned int flags) |
ad02a0eb | 532 | { |
b600a2c3 | 533 | struct ifdata *port = user_ref; |
11904a35 | 534 | int br_state; |
b600a2c3 SA |
535 | |
536 | LOG("port index %d, flags %d", port->if_index, flags); | |
537 | ||
538 | if (flags & STP_PORT_STATE_FLAG_FORWARDING) | |
11904a35 | 539 | br_state = BR_STATE_FORWARDING; |
b600a2c3 SA |
540 | else if (flags & STP_PORT_STATE_FLAG_LEARNING) |
541 | br_state = BR_STATE_LEARNING; | |
542 | else | |
543 | br_state = BR_STATE_BLOCKING; | |
544 | ||
11904a35 SH |
545 | if (port->up) |
546 | bridge_set_state(port->if_index, br_state); | |
11904a35 SH |
547 | } |
548 | ||
ad02a0eb | 549 | |
b600a2c3 | 550 | void STP_OUT_tx_bpdu(void *port_user_ref, void *base, unsigned int len) |
ad02a0eb | 551 | { |
b600a2c3 SA |
552 | struct ifdata *port = port_user_ref; |
553 | ||
554 | LOG("port index %d, len %d", port->if_index, len); | |
358260ff | 555 | |
3cf368c1 SA |
556 | struct llc_header h; |
557 | memcpy(h.dest_addr, bridge_group_address, ETH_ALEN); | |
558 | memcpy(h.src_addr, port->macaddr, ETH_ALEN); | |
559 | /* bpdu_len excludes MAC and LLC headers */ | |
b600a2c3 | 560 | h.len8023 = htons(len + 3); |
3cf368c1 | 561 | h.d_sap = h.s_sap = STP_SAP; |
b600a2c3 | 562 | h.llc_ui = 0x03; /* LLC UI packet */ |
3cf368c1 SA |
563 | |
564 | struct iovec iov[2] = { | |
565 | { .iov_base = &h, .iov_len = sizeof(h) }, | |
b600a2c3 | 566 | { .iov_base = base, .iov_len = len } |
3cf368c1 SA |
567 | }; |
568 | ||
b600a2c3 | 569 | packet_send(port->if_index, iov, 2, sizeof(h) + len); |
11904a35 | 570 | } |
ad02a0eb | 571 | |
ad02a0eb | 572 | |
b600a2c3 SA |
573 | void STP_OUT_logmsg(void *br_user_ref, void *port_user_ref, |
574 | int level, char *fmt, ...) | |
11904a35 | 575 | { |
b600a2c3 SA |
576 | struct ifdata *bridge = br_user_ref; |
577 | struct ifdata *port = port_user_ref; | |
578 | char buf[256]; | |
579 | int r; | |
580 | int ll = (level < STP_LOG_LEVEL_DEBUG) ? | |
581 | LOG_LEVEL_INFO : LOG_LEVEL_DEBUG; | |
582 | struct timeval tv; | |
583 | gettimeofday(&tv, NULL); | |
584 | r = snprintf(buf, sizeof(buf), "LOG Level %d:%s:%s: %02d.%03d: ", | |
585 | level, | |
586 | (bridge?bridge->name:""), (port?port->name:""), | |
587 | (int)tv.tv_sec % 60, (int)tv.tv_usec / 1000); | |
588 | if (r >= sizeof(buf)) { | |
589 | buf[sizeof(buf) - 1] = 0; | |
590 | Dprintf(ll, "%s", buf); | |
591 | r = 0; | |
592 | } | |
11904a35 | 593 | |
b600a2c3 SA |
594 | va_list ap; |
595 | va_start(ap, fmt); | |
596 | vsnprintf(buf + r, sizeof(buf) - r, fmt, ap); | |
597 | buf[sizeof(buf) - 1] = 0; | |
598 | Dprintf(ll, "%s", buf); | |
599 | va_end(ap); | |
600 | if (level == STP_LOG_LEVEL_ERROR) | |
601 | ctl_err_log("%s", buf + r); | |
ad02a0eb SH |
602 | } |
603 | ||
b600a2c3 | 604 | void *STP_OUT_mem_zalloc(unsigned int size) |
ad02a0eb | 605 | { |
b600a2c3 | 606 | return calloc(1, size); |
ad02a0eb SH |
607 | } |
608 | ||
b600a2c3 SA |
609 | |
610 | void STP_OUT_mem_free(void *p) | |
ad02a0eb | 611 | { |
b600a2c3 | 612 | free(p); |
ad02a0eb SH |
613 | } |
614 | ||
b600a2c3 | 615 | |
ad02a0eb SH |
616 | /* Commands and status */ |
617 | #include "ctl_functions.h" | |
618 | ||
619 | #define CTL_CHECK_BRIDGE \ | |
b600a2c3 SA |
620 | struct ifdata *br = find_if(br_index); \ |
621 | if (br == NULL || !br->is_bridge) { \ | |
622 | ERROR("Couldn't find bridge with index %d", br_index); \ | |
623 | return -1; \ | |
624 | } \ | |
625 | do { } while (0) | |
ad02a0eb SH |
626 | |
627 | #define CTL_CHECK_BRIDGE_PORT \ | |
b600a2c3 SA |
628 | CTL_CHECK_BRIDGE; \ |
629 | struct ifdata *port = find_if(port_index); \ | |
630 | if (port == NULL || port->is_bridge || port->master != br) { \ | |
631 | ERROR("Interface with index %d not a port of bridge " \ | |
632 | "with index %d", port_index, br_index); \ | |
633 | return -1; \ | |
634 | } \ | |
635 | do { } while (0) | |
ad02a0eb SH |
636 | |
637 | int CTL_enable_bridge_rstp(int br_index, int enable) | |
638 | { | |
11904a35 SH |
639 | INFO("bridge %d, enable %d", br_index, enable); |
640 | int r = 0; | |
641 | if (enable) | |
642 | enable = 1; | |
643 | struct ifdata *br = find_if(br_index); | |
644 | if (br == NULL) { | |
645 | char ifname[IFNAMSIZ]; | |
646 | if (if_indextoname(br_index, ifname) && is_bridge(ifname)) | |
647 | br = create_if(br_index, NULL); | |
648 | } | |
b600a2c3 SA |
649 | if (br == NULL || !br->is_bridge) { |
650 | ERROR("Couldn't find bridge with index %d", br_index); | |
651 | return -1; | |
11904a35 | 652 | } |
b600a2c3 SA |
653 | if (br->up) |
654 | set_br_up(br, 1); | |
11904a35 | 655 | return r; |
ad02a0eb SH |
656 | } |
657 | ||
b600a2c3 | 658 | int CTL_get_bridge_status(int br_index, STP_BridgeStatus *status) |
11904a35 SH |
659 | { |
660 | LOG("bridge %d", br_index); | |
661 | CTL_CHECK_BRIDGE; | |
b600a2c3 SA |
662 | |
663 | STP_IN_get_bridge_status(br->stp_bridge, status); | |
11904a35 SH |
664 | return 0; |
665 | } | |
666 | ||
b600a2c3 | 667 | int CTL_set_bridge_config(int br_index, STP_BridgeConfig *cfg) |
11904a35 | 668 | { |
b600a2c3 | 669 | INFO("bridge %d", br_index); |
11904a35 | 670 | CTL_CHECK_BRIDGE; |
b600a2c3 SA |
671 | |
672 | if (cfg->set_bridge_address) { | |
673 | ERROR("Setting bridge address not permitted: %s", br->name); | |
674 | return -1; | |
675 | } | |
676 | ||
677 | int r = STP_IN_set_bridge_config(br->stp_bridge, cfg); | |
678 | ||
11904a35 | 679 | if (r) { |
b600a2c3 | 680 | ERROR("Error setting bridge config for %s", br->name); |
11904a35 SH |
681 | return r; |
682 | } | |
11904a35 | 683 | return 0; |
ad02a0eb SH |
684 | } |
685 | ||
b600a2c3 | 686 | int CTL_get_port_status(int br_index, int port_index, STP_PortStatus *status) |
11904a35 SH |
687 | { |
688 | LOG("bridge %d port %d", br_index, port_index); | |
689 | CTL_CHECK_BRIDGE_PORT; | |
b600a2c3 SA |
690 | |
691 | STP_IN_get_port_status(port->stp_port, status); | |
692 | return 0; | |
693 | } | |
694 | ||
695 | int CTL_set_port_config(int br_index, int port_index, STP_PortConfig *cfg) | |
696 | { | |
697 | INFO("bridge %d, port %d", br_index, port_index); | |
698 | CTL_CHECK_BRIDGE_PORT; | |
699 | ||
700 | int r = STP_IN_set_port_config(port->stp_port, cfg); | |
11904a35 | 701 | if (r) { |
b600a2c3 | 702 | ERROR("Error setting port config for %s", port->name); |
11904a35 SH |
703 | return r; |
704 | } | |
b600a2c3 | 705 | |
11904a35 | 706 | return 0; |
11904a35 SH |
707 | } |
708 | ||
b600a2c3 | 709 | int CTL_port_mcheck(int br_index, int port_index) |
11904a35 | 710 | { |
b600a2c3 | 711 | INFO("bridge %d, port %d", br_index, port_index); |
11904a35 | 712 | CTL_CHECK_BRIDGE_PORT; |
b600a2c3 SA |
713 | |
714 | int r = STP_IN_port_mcheck(port->stp_port); | |
11904a35 | 715 | if (r) { |
b600a2c3 | 716 | ERROR("Error doing port mcheck for %s", port->name); |
11904a35 SH |
717 | return r; |
718 | } | |
b600a2c3 | 719 | |
11904a35 | 720 | return 0; |
ad02a0eb SH |
721 | } |
722 | ||
723 | int CTL_set_debug_level(int level) | |
724 | { | |
11904a35 SH |
725 | INFO("level %d", level); |
726 | log_level = level; | |
727 | return 0; | |
ad02a0eb SH |
728 | } |
729 | ||
ad02a0eb SH |
730 | #undef CTL_CHECK_BRIDGE_PORT |
731 | #undef CTL_CHECK_BRIDGE |