]> git.ipfire.org Git - people/ms/rstp.git/blob - bridge_track.c
f5efa3ff69c81b12c3b2f9edbfc5bdb14fe91648
[people/ms/rstp.git] / bridge_track.c
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"
27 #include "packet.h"
28
29 #include <unistd.h>
30 #include <net/if.h>
31 #include <stdlib.h>
32 #include <linux/if_bridge.h>
33 #include <linux/if_ether.h>
34 #include <arpa/inet.h>
35 #include <sys/types.h>
36
37 #include <stdio.h>
38 #include <string.h>
39
40 #include "log.h"
41
42 #include "rstp.h"
43 /*------------------------------------------------------------*/
44
45 struct ifdata {
46 int if_index;
47 struct ifdata *next;
48 int up;
49 char name[IFNAMSIZ];
50 unsigned char macaddr[ETH_ALEN];
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;
58 STP_Bridge *stp_bridge;
59
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;
68 STP_Port *stp_port;
69
70 struct epoll_event_handler event;
71 };
72
73 /* Instances */
74 struct ifdata *current_br = NULL;
75
76 struct ifdata *find_port(int port_index)
77 {
78 struct ifdata *ifc = current_br->port_list;
79 while (ifc && ifc->port_index != port_index)
80 ifc = ifc->port_next;
81 return ifc;
82 }
83
84
85 struct ifdata *if_head = NULL;
86 struct ifdata *br_head = NULL;
87
88 struct ifdata *find_if(int if_index)
89 {
90 struct ifdata *p = if_head;
91 while (p && p->if_index != if_index)
92 p = p->next;
93 return p;
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 {
117 struct ifdata *p;
118 TST((p = malloc(sizeof(*p))) != NULL, NULL);
119
120 memset(p, 0, sizeof(*p));
121
122 /* Init fields */
123 p->if_index = if_index;
124 p->is_bridge = (br == NULL);
125
126 /* TODO: purge use of name, due to issue with renameing */
127 if_indextoname(if_index, p->name);
128 get_hwaddr(p->name, p->macaddr);
129
130 if (p->is_bridge) {
131 INFO("Add bridge %s", p->name);
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 );
145 /* Init slave list */
146 p->port_list = NULL;
147
148 p->do_stp = 0;
149 p->up = 0;
150 p->stp_up = 0;
151 ADD_TO_LIST(br_head, bridge_next, p); /* Add to bridge list */
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;
158
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
173 ADD_TO_LIST(br->port_list, port_next, p); /* Add to bridge port list */
174
175 }
176
177 /* Add to interface list */
178 ADD_TO_LIST(if_head, next, p);
179
180 return p;
181 }
182
183 void delete_if(struct ifdata *ifc)
184 {
185 INFO("Delete iface %s", ifc->name);
186 if (ifc->is_bridge) { /* Bridge: */
187 STP_IN_set_bridge_enable(ifc->stp_bridge, 0);
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);
195 STP_IN_bridge_delete(ifc->stp_bridge);
196 } else { /* Port */
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);
201 STP_IN_port_delete(ifc->stp_port);
202 }
203
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);
208 free(ifc);
209 }
210
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)) {
217 LOG("Error getting hw address: %s", name);
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
230 static int stp_enabled(struct ifdata *br)
231 {
232 char path[40 + IFNAMSIZ];
233 int ret;
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;
241 ret = fscanf(f, "%d", &enabled);
242 if (!ret) {
243 LOG("%s, stp_state parsing error", path);
244 return 0;
245 }
246 fclose(f);
247 INFO("STP on %s state %d", br->name, enabled);
248
249 return enabled == 2; /* ie user mode STP */
250 }
251
252 void set_br_up(struct ifdata *br, int up)
253 {
254 int stp_up = stp_enabled(br);
255 INFO("%s was %s stp was %s", br->name, br->up ? "up" : "down", br->stp_up ? "up" : "down");
256 INFO("Set bridge %s %s stp %s" , br->name,
257 up ? "up" : "down", stp_up ? "up" : "down");
258
259 int changed = 0;
260
261 if (up != br->up) {
262 br->up = up;
263 changed = 1;
264 }
265
266 if (br->stp_up != stp_up) {
267 br->stp_up = stp_up;
268 changed = 1;
269 }
270
271 if (check_mac_address(br->name, br->macaddr)) {
272 /* MAC address changed */
273 /* Notify bridge address change */
274 STP_IN_set_bridge_address(
275 br->stp_bridge, (STP_MacAddress *)br->macaddr);
276 }
277
278 if (changed)
279 STP_IN_set_bridge_enable(br->stp_bridge,
280 (br->up && br->stp_up)?1:0);
281 }
282
283 void set_if_up(struct ifdata *ifc, int up)
284 {
285 INFO("Port %s : %s", ifc->name, (up ? "up" : "down"));
286 int speed = -1;
287 int duplex = -1;
288 int changed = 0;
289
290 if (check_mac_address(ifc->name, ifc->macaddr)) {
291 /* MAC address changed */
292 if (check_mac_address(ifc->master->name, ifc->master->macaddr)
293 ) {
294 /* Notify bridge address change */
295 STP_IN_set_bridge_address(
296 ifc->master->stp_bridge,
297 (STP_MacAddress *)ifc->master->macaddr);
298 }
299 }
300
301 if (!up) { /* Down */
302 if (ifc->up) {
303 ifc->up = up;
304 changed = 1;
305 }
306 } else { /* Up */
307 int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex);
308 if (r < 0) { /* Didn't succeed */
309 }
310 if (speed < 0)
311 speed = 10;
312 if (duplex < 0)
313 duplex = 0; /* Assume half duplex */
314
315 if (speed != ifc->speed) {
316 ifc->speed = speed;
317 changed = 1;
318 }
319 if (duplex != ifc->duplex) {
320 ifc->duplex = duplex;
321 changed = 1;
322 }
323 if (!ifc->up) {
324 ifc->up = 1;
325 changed = 1;
326 }
327 }
328 if (changed)
329 STP_IN_set_port_enable(ifc->stp_port,
330 ifc->up, ifc->speed, ifc->duplex);
331 }
332
333 /*------------------------------------------------------------*/
334
335 int bridge_notify(int br_index, int if_index, int newlink, int up)
336 {
337 if (up)
338 up = 1;
339 LOG("br_index %d, if_index %d, newlink %d, up %d",
340 br_index, if_index, newlink, up);
341
342 struct ifdata *br = NULL;
343 if (br_index >= 0) {
344 br = find_if(br_index);
345 if (br && !br->is_bridge) {
346 ERROR
347 ("Notification shows non bridge interface %d as bridge.",
348 br_index);
349 return -1;
350 }
351 if (!br)
352 br = create_if(br_index, NULL);
353 if (!br) {
354 ERROR("Couldn't create data for bridge interface %d",
355 br_index);
356 return -1;
357 }
358 /* Bridge must be up if we get such notifications */
359 // Not true anymore - set_br_up(br, 1);
360 }
361
362 struct ifdata *ifc = find_if(if_index);
363
364 if (br) {
365 if (ifc) {
366 if (ifc->is_bridge) {
367 ERROR
368 ("Notification shows bridge interface %d as slave of %d",
369 if_index, br_index);
370 return -1;
371 }
372 if (ifc->master != br) {
373 INFO("Device %d has come to bridge %d. "
374 "Missed notify for deletion from bridge %d",
375 if_index, br_index, ifc->master->if_index);
376 delete_if(ifc);
377 ifc = NULL;
378 }
379 }
380 if (!ifc) {
381 if (!newlink) {
382 INFO("Got DELLINK for unknown port %d on "
383 "bridge %d", if_index, br_index);
384 return -1;
385 }
386 ifc = create_if(if_index, br);
387 }
388 if (!ifc) {
389 ERROR
390 ("Couldn't create data for interface %d (master %d)",
391 if_index, br_index);
392 return -1;
393 }
394 if (!newlink) {
395 delete_if(ifc);
396 return 0;
397 }
398 set_if_up(ifc, up); /* And speed and duplex */
399 } else { /* No br_index */
400 if (!newlink) {
401 /* DELLINK not from bridge means interface unregistered. */
402 /* Cleanup removed bridge or removed bridge slave */
403 if (ifc)
404 delete_if(ifc);
405 return 0;
406 } else { /* This may be a new link */
407 if (!ifc) {
408 char ifname[IFNAMSIZ];
409 if (if_indextoname(if_index, ifname)
410 && is_bridge(ifname)) {
411 ifc = create_if(if_index, NULL);
412 if (!ifc) {
413 ERROR
414 ("Couldn't create data for bridge interface %d",
415 if_index);
416 return -1;
417 }
418 }
419 }
420 if (ifc) {
421 if (ifc->is_bridge)
422 set_br_up(ifc, up);
423 else
424 set_if_up(ifc, up);
425 }
426 }
427 }
428 return 0;
429 }
430
431 struct llc_header
432 {
433 uint8_t dest_addr[ETH_ALEN];
434 uint8_t src_addr[ETH_ALEN];
435 uint16_t len8023;
436 uint8_t d_sap; /* 0x42 */
437 uint8_t s_sap; /* 0x42 */
438 uint8_t llc_ui; /* 0x03 */
439 } __attribute__((packed));
440
441 const unsigned char bridge_group_address[ETH_ALEN] = {
442 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
443 };
444
445 const unsigned char STP_SAP = 0x42;
446
447 void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
448 {
449 struct ifdata *ifc = find_if(if_index);
450
451 LOG("ifindex %d, len %d", if_index, len);
452 if (!ifc || !ifc->master)
453 return;
454
455 TST(ifc->up,);
456 if (!ifc->master->stp_up)
457 return;
458
459 /* Validate Ethernet and LLC header */
460 {
461 struct llc_header *h;
462 unsigned int l;
463 TST(len > sizeof(struct llc_header),);
464 h = (struct llc_header *)data;
465 TST(memcmp(h->dest_addr, bridge_group_address, ETH_ALEN) == 0,
466 INFO("ifindex %d, len %d, %02x:%02x:%02x:%02x:%02x:%02x",
467 if_index, len,
468 h->dest_addr[0],h->dest_addr[1],h->dest_addr[2],
469 h->dest_addr[3],h->dest_addr[4],h->dest_addr[5]));
470 l = ntohs(h->len8023);
471 TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= 3,);
472 TST(h->d_sap == STP_SAP && h->s_sap == STP_SAP
473 && (h->llc_ui & 0x3) == 0x3 /* LLC UI */,);
474
475 STP_IN_rx_bpdu(ifc->stp_port,
476 /* Don't include LLC header */
477 data + sizeof(*h), l - 3);
478 }
479 }
480
481 void bridge_one_second(void)
482 {
483 // LOG("");
484 struct ifdata *br;
485 for (br = br_head; br; br = br->bridge_next) {
486 STP_IN_one_second(br->stp_bridge);
487 }
488 }
489
490 /* Implementing STP_OUT functions */
491
492 int flush_port(char *sys_name)
493 {
494 FILE *f = fopen(sys_name, "w");
495 TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name);
496 int r = fwrite("1", 1, 1, f);
497 fclose(f);
498 TST(r == 1, -1);
499 return 0;
500 }
501
502 void STP_OUT_port_fdb_flush(void *user_ref)
503 {
504 struct ifdata *port = user_ref;
505 char fname[128];
506 snprintf(fname, sizeof(fname),
507 "/sys/class/net/%s/brport/flush", port->name);
508 fname[sizeof(fname) - 1] = 0;
509 TST(flush_port(fname) == 0,);
510 }
511
512 void STP_OUT_port_set_state(void *user_ref, unsigned int flags)
513 {
514 struct ifdata *port = user_ref;
515 int br_state;
516
517 LOG("port index %d, flags %d", port->if_index, flags);
518
519 if (flags & STP_PORT_STATE_FLAG_FORWARDING)
520 br_state = BR_STATE_FORWARDING;
521 else if (flags & STP_PORT_STATE_FLAG_LEARNING)
522 br_state = BR_STATE_LEARNING;
523 else
524 br_state = BR_STATE_BLOCKING;
525
526 if (port->up)
527 bridge_set_state(port->if_index, br_state);
528 }
529
530
531 void STP_OUT_tx_bpdu(void *port_user_ref, void *base, unsigned int len)
532 {
533 struct ifdata *port = port_user_ref;
534
535 LOG("port index %d, len %d", port->if_index, len);
536
537 struct llc_header h;
538 memcpy(h.dest_addr, bridge_group_address, ETH_ALEN);
539 memcpy(h.src_addr, port->macaddr, ETH_ALEN);
540 /* bpdu_len excludes MAC and LLC headers */
541 h.len8023 = htons(len + 3);
542 h.d_sap = h.s_sap = STP_SAP;
543 h.llc_ui = 0x03; /* LLC UI packet */
544
545 struct iovec iov[2] = {
546 { .iov_base = &h, .iov_len = sizeof(h) },
547 { .iov_base = base, .iov_len = len }
548 };
549
550 packet_send(port->if_index, iov, 2, sizeof(h) + len);
551 }
552
553
554 void STP_OUT_logmsg(void *br_user_ref, void *port_user_ref,
555 int level, char *fmt, ...)
556 {
557 struct ifdata *bridge = br_user_ref;
558 struct ifdata *port = port_user_ref;
559 char buf[256];
560 int r;
561 int ll = (level < STP_LOG_LEVEL_DEBUG) ?
562 LOG_LEVEL_INFO : LOG_LEVEL_DEBUG;
563 struct timeval tv;
564 gettimeofday(&tv, NULL);
565 r = snprintf(buf, sizeof(buf), "LOG Level %d:%s:%s: %02d.%03d: ",
566 level,
567 (bridge?bridge->name:""), (port?port->name:""),
568 (int)tv.tv_sec % 60, (int)tv.tv_usec / 1000);
569 if (r >= sizeof(buf)) {
570 buf[sizeof(buf) - 1] = 0;
571 Dprintf(ll, "%s", buf);
572 r = 0;
573 }
574
575 va_list ap;
576 va_start(ap, fmt);
577 vsnprintf(buf + r, sizeof(buf) - r, fmt, ap);
578 buf[sizeof(buf) - 1] = 0;
579 Dprintf(ll, "%s", buf);
580 va_end(ap);
581 if (level == STP_LOG_LEVEL_ERROR)
582 ctl_err_log("%s", buf + r);
583 }
584
585 void *STP_OUT_mem_zalloc(unsigned int size)
586 {
587 return calloc(1, size);
588 }
589
590
591 void STP_OUT_mem_free(void *p)
592 {
593 free(p);
594 }
595
596
597 /* Commands and status */
598 #include "ctl_functions.h"
599
600 #define CTL_CHECK_BRIDGE \
601 struct ifdata *br = find_if(br_index); \
602 if (br == NULL || !br->is_bridge) { \
603 ERROR("Couldn't find bridge with index %d", br_index); \
604 return -1; \
605 } \
606 do { } while (0)
607
608 #define CTL_CHECK_BRIDGE_PORT \
609 CTL_CHECK_BRIDGE; \
610 struct ifdata *port = find_if(port_index); \
611 if (port == NULL || port->is_bridge || port->master != br) { \
612 ERROR("Interface with index %d not a port of bridge " \
613 "with index %d", port_index, br_index); \
614 return -1; \
615 } \
616 do { } while (0)
617
618 int CTL_enable_bridge_rstp(int br_index, int enable)
619 {
620 INFO("bridge %d, enable %d", br_index, enable);
621 int r = 0;
622 if (enable)
623 enable = 1;
624 struct ifdata *br = find_if(br_index);
625 if (br == NULL) {
626 char ifname[IFNAMSIZ];
627 if (if_indextoname(br_index, ifname) && is_bridge(ifname))
628 br = create_if(br_index, NULL);
629 }
630 if (br == NULL || !br->is_bridge) {
631 ERROR("Couldn't find bridge with index %d", br_index);
632 return -1;
633 }
634 if (br->up)
635 set_br_up(br, 1);
636 return r;
637 }
638
639 int CTL_get_bridge_status(int br_index, STP_BridgeStatus *status)
640 {
641 LOG("bridge %d", br_index);
642 CTL_CHECK_BRIDGE;
643
644 STP_IN_get_bridge_status(br->stp_bridge, status);
645 return 0;
646 }
647
648 int CTL_set_bridge_config(int br_index, STP_BridgeConfig *cfg)
649 {
650 INFO("bridge %d", br_index);
651 CTL_CHECK_BRIDGE;
652
653 if (cfg->set_bridge_address) {
654 ERROR("Setting bridge address not permitted: %s", br->name);
655 return -1;
656 }
657
658 int r = STP_IN_set_bridge_config(br->stp_bridge, cfg);
659
660 if (r) {
661 ERROR("Error setting bridge config for %s", br->name);
662 return r;
663 }
664 return 0;
665 }
666
667 int CTL_get_port_status(int br_index, int port_index, STP_PortStatus *status)
668 {
669 LOG("bridge %d port %d", br_index, port_index);
670 CTL_CHECK_BRIDGE_PORT;
671
672 STP_IN_get_port_status(port->stp_port, status);
673 return 0;
674 }
675
676 int CTL_set_port_config(int br_index, int port_index, STP_PortConfig *cfg)
677 {
678 INFO("bridge %d, port %d", br_index, port_index);
679 CTL_CHECK_BRIDGE_PORT;
680
681 int r = STP_IN_set_port_config(port->stp_port, cfg);
682 if (r) {
683 ERROR("Error setting port config for %s", port->name);
684 return r;
685 }
686
687 return 0;
688 }
689
690 int CTL_port_mcheck(int br_index, int port_index)
691 {
692 INFO("bridge %d, port %d", br_index, port_index);
693 CTL_CHECK_BRIDGE_PORT;
694
695 int r = STP_IN_port_mcheck(port->stp_port);
696 if (r) {
697 ERROR("Error doing port mcheck for %s", port->name);
698 return r;
699 }
700
701 return 0;
702 }
703
704 int CTL_set_debug_level(int level)
705 {
706 INFO("level %d", level);
707 log_level = level;
708 return 0;
709 }
710
711 #undef CTL_CHECK_BRIDGE_PORT
712 #undef CTL_CHECK_BRIDGE