1 /*****************************************************************************
2 Copyright (c) 2006 EMC Corporation.
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)
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
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.
18 The full GNU General Public License is included in this distribution in the
21 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
23 ******************************************************************************/
25 #include "bridge_ctl.h"
26 #include "netif_utils.h"
32 #include <linux/if_bridge.h>
33 #include <linux/if_ether.h>
34 #include <arpa/inet.h>
35 #include <sys/types.h>
43 /*------------------------------------------------------------*/
50 unsigned char macaddr
[ETH_ALEN
];
54 struct ifdata
*bridge_next
;
55 struct ifdata
*port_list
;
58 STP_Bridge
*stp_bridge
;
64 struct ifdata
*master
;
65 struct ifdata
*port_next
;
70 struct epoll_event_handler event
;
74 struct ifdata
*current_br
= NULL
;
76 struct ifdata
*find_port(int port_index
)
78 struct ifdata
*ifc
= current_br
->port_list
;
79 while (ifc
&& ifc
->port_index
!= port_index
)
85 struct ifdata
*if_head
= NULL
;
86 struct ifdata
*br_head
= NULL
;
88 struct ifdata
*find_if(int if_index
)
90 struct ifdata
*p
= if_head
;
91 while (p
&& p
->if_index
!= if_index
)
96 #define ADD_TO_LIST(_list, _next, _ifc) \
98 (_ifc)->_next = (_list); \
102 #define REMOVE_FROM_LIST(_list, _next, _ifc, _error_fmt, _args...) \
104 struct ifdata **_prev = &(_list); \
105 while (*_prev && *_prev != (_ifc)) \
106 _prev = &(*_prev)->_next; \
107 if (*_prev != (_ifc)) \
108 ERROR(_error_fmt, ##_args); \
110 *_prev = (_ifc)->_next; \
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
)
118 TST((p
= malloc(sizeof(*p
))) != NULL
, NULL
);
120 memset(p
, 0, sizeof(*p
));
123 p
->if_index
= if_index
;
124 p
->is_bridge
= (br
== NULL
);
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
);
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");
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",
142 p
->macaddr
[0], p
->macaddr
[1], p
->macaddr
[2],
143 p
->macaddr
[1], p
->macaddr
[4], p
->macaddr
[5]
145 /* Init slave list */
151 ADD_TO_LIST(br_head
, bridge_next
, p
); /* Add to bridge list */
153 INFO("Add iface %s to bridge %s", p
->name
, br
->name
);
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
);
165 p
->stp_port
= STP_IN_port_create(p
->master
->stp_bridge
,
168 ERROR("Couldn't create STP Port");
173 ADD_TO_LIST(br
->port_list
, port_next
, p
); /* Add to bridge port list */
177 /* Add to interface list */
178 ADD_TO_LIST(if_head
, next
, p
);
183 void delete_if(struct ifdata
*ifc
)
185 INFO("Delete iface %s", ifc
->name
);
186 if (ifc
->is_bridge
) { /* Bridge: */
187 STP_IN_set_bridge_enable(ifc
->stp_bridge
, 0);
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",
195 STP_IN_bridge_delete(ifc
->stp_bridge
);
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
);
204 /* Remove from bridge interface list */
205 REMOVE_FROM_LIST(if_head
, next
, ifc
,
206 "Can't find interface ifindex %d on iflist",
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
)
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 */
221 if (memcmp(addr
, temp_addr
, sizeof(temp_addr
)) == 0)
224 memcpy(addr
, temp_addr
, sizeof(temp_addr
));
230 static int stp_enabled(struct ifdata
*br
)
232 char path
[40 + IFNAMSIZ
];
234 sprintf(path
, "/sys/class/net/%s/bridge/stp_state", br
->name
);
235 FILE *f
= fopen(path
, "r");
237 LOG("Open %s failed", path
);
241 ret
= fscanf(f
, "%d", &enabled
);
243 LOG("%s, stp_state parsing error", path
);
247 INFO("STP on %s state %d", br
->name
, enabled
);
249 return enabled
== 2; /* ie user mode STP */
252 static void set_br_up(struct ifdata
*br
, int up
)
254 int stp_up
= stp_enabled(br
);
255 INFO("%s was %s stp was %s", br
->name
,
257 br
->stp_up
? "up" : "down");
258 INFO("Set bridge %s %s stp %s" , br
->name
,
260 stp_up
? "up" : "down");
269 if (br
->stp_up
!= stp_up
) {
274 if (check_mac_address(br
->name
, br
->macaddr
)) {
275 /* MAC address changed */
276 /* Notify bridge address change */
277 STP_IN_set_bridge_address(
278 br
->stp_bridge
, (STP_MacAddress
*)br
->macaddr
);
282 STP_IN_set_bridge_enable(br
->stp_bridge
,
283 (br
->up
&& br
->stp_up
)?1:0);
286 static void set_if_up(struct ifdata
*ifc
, int up
)
288 INFO("Port %s : %s", ifc
->name
, (up
? "up" : "down"));
293 if (check_mac_address(ifc
->name
, ifc
->macaddr
)) {
294 /* MAC address changed */
295 if (check_mac_address(ifc
->master
->name
, ifc
->master
->macaddr
)
297 /* Notify bridge address change */
298 STP_IN_set_bridge_address(
299 ifc
->master
->stp_bridge
,
300 (STP_MacAddress
*)ifc
->master
->macaddr
);
304 if (!up
) { /* Down */
310 int r
= ethtool_get_speed_duplex(ifc
->name
, &speed
, &duplex
);
311 if (r
< 0) { /* Didn't succeed */
316 duplex
= 0; /* Assume half duplex */
318 if (speed
!= ifc
->speed
) {
322 if (duplex
!= ifc
->duplex
) {
323 ifc
->duplex
= duplex
;
332 STP_IN_set_port_enable(ifc
->stp_port
,
333 ifc
->up
, ifc
->speed
, ifc
->duplex
);
336 /*------------------------------------------------------------*/
338 int bridge_notify(int br_index
, int if_index
, int newlink
,
341 struct ifdata
*br
= NULL
;
343 LOG("br_index %d, if_index %d, up %d running %d",
344 br_index
, if_index
, (flags
& IFF_UP
), flags
& IFF_RUNNING
);
347 br
= find_if(br_index
);
348 if (br
&& !br
->is_bridge
) {
350 ("Notification shows non bridge interface %d as bridge.",
355 br
= create_if(br_index
, NULL
);
357 ERROR("Couldn't create data for bridge interface %d",
361 /* Bridge must be up if we get such notifications */
362 // Not true anymore - set_br_up(br, 1);
365 struct ifdata
*ifc
= find_if(if_index
);
369 if (ifc
->is_bridge
) {
371 ("Notification shows bridge interface %d as slave of %d",
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
);
385 INFO("Got DELLINK for unknown port %d on "
386 "bridge %d", if_index
, br_index
);
389 ifc
= create_if(if_index
, br
);
393 ("Couldn't create data for interface %d (master %d)",
401 int up
= (flags
& (IFF_UP
|IFF_RUNNING
)) == (IFF_UP
|IFF_RUNNING
);
404 set_if_up(ifc
, up
); /* And speed and duplex */
405 } else { /* No br_index */
407 /* DELLINK not from bridge means interface unregistered. */
408 /* Cleanup removed bridge or removed bridge slave */
412 } else { /* This may be a new link */
414 char ifname
[IFNAMSIZ
];
415 if (if_indextoname(if_index
, ifname
)
416 && is_bridge(ifname
)) {
417 ifc
= create_if(if_index
, NULL
);
420 ("Couldn't create data for bridge interface %d",
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 */
434 if (ifc
->is_bridge
) {
435 int up
= (flags
& IFF_UP
) != 0;
439 int up
= (flags
& (IFF_UP
|IFF_RUNNING
)) == (IFF_UP
|IFF_RUNNING
);
452 uint8_t dest_addr
[ETH_ALEN
];
453 uint8_t src_addr
[ETH_ALEN
];
455 uint8_t d_sap
; /* 0x42 */
456 uint8_t s_sap
; /* 0x42 */
457 uint8_t llc_ui
; /* 0x03 */
458 } __attribute__((packed
));
460 const unsigned char bridge_group_address
[ETH_ALEN
] = {
461 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
464 const unsigned char STP_SAP
= 0x42;
466 void bridge_bpdu_rcv(int if_index
, const unsigned char *data
, int len
)
468 struct ifdata
*ifc
= find_if(if_index
);
470 LOG("ifindex %d, len %d", if_index
, len
);
471 if (!ifc
|| !ifc
->master
)
475 if (!ifc
->master
->stp_up
)
478 /* Validate Ethernet and LLC header */
480 struct llc_header
*h
;
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,
485 INFO("ifindex %d, len %d, %02x:%02x:%02x:%02x:%02x:%02x",
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]));
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 */,);
494 STP_IN_rx_bpdu(ifc
->stp_port
,
495 /* Don't include LLC header */
496 data
+ sizeof(*h
), l
- 3);
500 void bridge_one_second(void)
504 for (br
= br_head
; br
; br
= br
->bridge_next
) {
505 STP_IN_one_second(br
->stp_bridge
);
509 /* Implementing STP_OUT functions */
511 int flush_port(char *sys_name
)
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
);
521 void STP_OUT_port_fdb_flush(void *user_ref
)
523 struct ifdata
*port
= user_ref
;
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,);
531 void STP_OUT_port_set_state(void *user_ref
, unsigned int flags
)
533 struct ifdata
*port
= user_ref
;
536 LOG("port index %d, flags %d", port
->if_index
, flags
);
538 if (flags
& STP_PORT_STATE_FLAG_FORWARDING
)
539 br_state
= BR_STATE_FORWARDING
;
540 else if (flags
& STP_PORT_STATE_FLAG_LEARNING
)
541 br_state
= BR_STATE_LEARNING
;
543 br_state
= BR_STATE_BLOCKING
;
546 bridge_set_state(port
->if_index
, br_state
);
550 void STP_OUT_tx_bpdu(void *port_user_ref
, void *base
, unsigned int len
)
552 struct ifdata
*port
= port_user_ref
;
554 LOG("port index %d, len %d", port
->if_index
, len
);
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 */
560 h
.len8023
= htons(len
+ 3);
561 h
.d_sap
= h
.s_sap
= STP_SAP
;
562 h
.llc_ui
= 0x03; /* LLC UI packet */
564 struct iovec iov
[2] = {
565 { .iov_base
= &h
, .iov_len
= sizeof(h
) },
566 { .iov_base
= base
, .iov_len
= len
}
569 packet_send(port
->if_index
, iov
, 2, sizeof(h
) + len
);
573 void STP_OUT_logmsg(void *br_user_ref
, void *port_user_ref
,
574 int level
, char *fmt
, ...)
576 struct ifdata
*bridge
= br_user_ref
;
577 struct ifdata
*port
= port_user_ref
;
580 int ll
= (level
< STP_LOG_LEVEL_DEBUG
) ?
581 LOG_LEVEL_INFO
: LOG_LEVEL_DEBUG
;
583 gettimeofday(&tv
, NULL
);
584 r
= snprintf(buf
, sizeof(buf
), "LOG Level %d:%s:%s: %02d.%03d: ",
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
);
596 vsnprintf(buf
+ r
, sizeof(buf
) - r
, fmt
, ap
);
597 buf
[sizeof(buf
) - 1] = 0;
598 Dprintf(ll
, "%s", buf
);
600 if (level
== STP_LOG_LEVEL_ERROR
)
601 ctl_err_log("%s", buf
+ r
);
604 void *STP_OUT_mem_zalloc(unsigned int size
)
606 return calloc(1, size
);
610 void STP_OUT_mem_free(void *p
)
616 /* Commands and status */
617 #include "ctl_functions.h"
619 #define CTL_CHECK_BRIDGE \
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); \
627 #define CTL_CHECK_BRIDGE_PORT \
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); \
637 int CTL_enable_bridge_rstp(int br_index
, int enable
)
639 INFO("bridge %d, enable %d", br_index
, enable
);
643 struct ifdata
*br
= find_if(br_index
);
645 char ifname
[IFNAMSIZ
];
646 if (if_indextoname(br_index
, ifname
) && is_bridge(ifname
))
647 br
= create_if(br_index
, NULL
);
649 if (br
== NULL
|| !br
->is_bridge
) {
650 ERROR("Couldn't find bridge with index %d", br_index
);
658 int CTL_get_bridge_status(int br_index
, STP_BridgeStatus
*status
)
660 LOG("bridge %d", br_index
);
663 STP_IN_get_bridge_status(br
->stp_bridge
, status
);
667 int CTL_set_bridge_config(int br_index
, STP_BridgeConfig
*cfg
)
669 INFO("bridge %d", br_index
);
672 if (cfg
->set_bridge_address
) {
673 ERROR("Setting bridge address not permitted: %s", br
->name
);
677 int r
= STP_IN_set_bridge_config(br
->stp_bridge
, cfg
);
680 ERROR("Error setting bridge config for %s", br
->name
);
686 int CTL_get_port_status(int br_index
, int port_index
, STP_PortStatus
*status
)
688 LOG("bridge %d port %d", br_index
, port_index
);
689 CTL_CHECK_BRIDGE_PORT
;
691 STP_IN_get_port_status(port
->stp_port
, status
);
695 int CTL_set_port_config(int br_index
, int port_index
, STP_PortConfig
*cfg
)
697 INFO("bridge %d, port %d", br_index
, port_index
);
698 CTL_CHECK_BRIDGE_PORT
;
700 int r
= STP_IN_set_port_config(port
->stp_port
, cfg
);
702 ERROR("Error setting port config for %s", port
->name
);
709 int CTL_port_mcheck(int br_index
, int port_index
)
711 INFO("bridge %d, port %d", br_index
, port_index
);
712 CTL_CHECK_BRIDGE_PORT
;
714 int r
= STP_IN_port_mcheck(port
->stp_port
);
716 ERROR("Error doing port mcheck for %s", port
->name
);
723 int CTL_set_debug_level(int level
)
725 INFO("level %d", level
);
730 #undef CTL_CHECK_BRIDGE_PORT
731 #undef CTL_CHECK_BRIDGE