1 /*****************************************************************************
2 Copyright (c) 2006 EMC Corporation.
3 Copyright (c) 2011 Factor-SPE
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the Free
7 Software Foundation; either version 2 of the License, or (at your option)
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 You should have received a copy of the GNU General Public License along with
16 this program; if not, write to the Free Software Foundation, Inc., 59
17 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 The full GNU General Public License is included in this distribution in the
22 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
23 Authors: Vitalii Demianets <dvitasgs@gmail.com>
25 ******************************************************************************/
30 #include <linux/param.h>
31 #include <netinet/in.h>
32 #include <linux/if_bridge.h>
33 #include <asm/byteorder.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
39 #include "bridge_ctl.h"
40 #include "bridge_track.h"
41 #include "netif_utils.h"
46 #include "libnetlink.h"
48 #ifndef SYSFS_CLASS_NET
49 #define SYSFS_CLASS_NET "/sys/class/net"
52 static LIST_HEAD(bridges
);
54 static bridge_t
* create_br(int if_index
)
57 TST((br
= calloc(1, sizeof(*br
))) != NULL
, NULL
);
59 /* Init system dependent info */
60 br
->sysdeps
.if_index
= if_index
;
61 if (!if_indextoname(if_index
, br
->sysdeps
.name
))
63 if (get_hwaddr(br
->sysdeps
.name
, br
->sysdeps
.macaddr
))
66 INFO("Add bridge %s", br
->sysdeps
.name
);
67 if(!MSTP_IN_bridge_create(br
, br
->sysdeps
.macaddr
))
70 list_add_tail(&br
->list
, &bridges
);
77 static bridge_t
* find_br(int if_index
)
80 list_for_each_entry(br
, &bridges
, list
)
82 if(br
->sysdeps
.if_index
== if_index
)
88 static port_t
* create_if(bridge_t
* br
, int if_index
)
91 TST((prt
= calloc(1, sizeof(*prt
))) != NULL
, NULL
);
93 /* Init system dependent info */
94 prt
->sysdeps
.if_index
= if_index
;
95 if (!if_indextoname(if_index
, prt
->sysdeps
.name
))
97 if (get_hwaddr(prt
->sysdeps
.name
, prt
->sysdeps
.macaddr
))
101 if(0 > (portno
= get_bridge_portno(prt
->sysdeps
.name
)))
103 ERROR("Couldn't get port number for %s", prt
->sysdeps
.name
);
106 if((0 == portno
) || (portno
> MAX_PORT_NUMBER
))
108 ERROR("Port number for %s is invalid (%d)", prt
->sysdeps
.name
, portno
);
112 INFO("Add iface %s as port#%d to bridge %s", prt
->sysdeps
.name
,
113 portno
, br
->sysdeps
.name
);
115 if(!MSTP_IN_port_create_and_add_tail(prt
, portno
))
124 static port_t
* find_if(bridge_t
* br
, int if_index
)
127 list_for_each_entry(prt
, &br
->ports
, br_list
)
129 if(prt
->sysdeps
.if_index
== if_index
)
135 static inline void delete_if(port_t
*prt
)
137 MSTP_IN_delete_port(prt
);
141 static inline bool delete_if_byindex(bridge_t
* br
, int if_index
)
144 if(!(prt
= find_if(br
, if_index
)))
150 static bool delete_br_byindex(int if_index
)
153 if(!(br
= find_br(if_index
)))
156 INFO("Delete bridge %s (%d)", br
->sysdeps
.name
, if_index
);
159 MSTP_IN_delete_bridge(br
);
164 void bridge_one_second(void)
167 list_for_each_entry(br
, &bridges
, list
)
168 MSTP_IN_one_second(br
);
171 /* New MAC address is stored in addr, which also holds the old value on entry.
172 Return true if the address changed */
173 static bool check_mac_address(char *name
, __u8
*addr
)
175 __u8 temp_addr
[ETH_ALEN
];
176 if(get_hwaddr(name
, temp_addr
))
178 LOG("Error getting hw address: %s", name
);
179 /* Error. Ignore the new value */
182 if(memcmp(addr
, temp_addr
, sizeof(temp_addr
)) == 0)
186 memcpy(addr
, temp_addr
, sizeof(temp_addr
));
191 static void set_br_up(bridge_t
* br
, bool up
)
193 bool changed
= false;
195 if(up
!= br
->sysdeps
.up
)
197 INFO("%s was %s. Set %s", br
->sysdeps
.name
,
198 br
->sysdeps
.up
? "up" : "down", up
? "up" : "down");
203 if(check_mac_address(br
->sysdeps
.name
, br
->sysdeps
.macaddr
))
205 /* MAC address changed */
206 /* Notify bridge address change */
207 MSTP_IN_set_bridge_address(br
, br
->sysdeps
.macaddr
);
211 MSTP_IN_set_bridge_enable(br
, br
->sysdeps
.up
);
214 static void set_if_up(port_t
*prt
, bool up
)
216 INFO("Port %s : %s", prt
->sysdeps
.name
, (up
? "up" : "down"));
219 bool changed
= false;
222 if(check_mac_address(prt
->sysdeps
.name
, prt
->sysdeps
.macaddr
))
224 /* MAC address changed */
225 if(check_mac_address(prt
->bridge
->sysdeps
.name
,
226 prt
->bridge
->sysdeps
.macaddr
))
228 /* Notify bridge address change */
229 MSTP_IN_set_bridge_address(prt
->bridge
,
230 prt
->bridge
->sysdeps
.macaddr
);
238 prt
->sysdeps
.up
= false;
244 int r
= ethtool_get_speed_duplex(prt
->sysdeps
.name
, &speed
, &duplex
);
245 if((r
< 0) || (speed
< 0))
247 if((r
< 0) || (duplex
< 0))
248 duplex
= 1; /* Assume full duplex */
250 if(speed
!= prt
->sysdeps
.speed
)
252 prt
->sysdeps
.speed
= speed
;
255 if(duplex
!= prt
->sysdeps
.duplex
)
257 prt
->sysdeps
.duplex
= duplex
;
262 prt
->sysdeps
.up
= true;
266 bpdu_filter
= get_bpdu_filter(prt
->sysdeps
.name
);
267 if (bpdu_filter
!= prt
->bpduFilterPort
) {
268 CIST_PortConfig cfg
= {
269 .bpdu_filter_port
= bpdu_filter
,
270 .set_bpdu_filter_port
= true
273 MSTP_IN_set_cist_port_config(prt
, &cfg
);
277 MSTP_IN_set_port_enable(prt
, prt
->sysdeps
.up
, prt
->sysdeps
.speed
,
278 prt
->sysdeps
.duplex
);
281 /* br_index == if_index means: interface is bridge master */
282 int bridge_notify(int br_index
, int if_index
, bool newlink
, unsigned flags
)
285 bridge_t
*br
= NULL
, *other_br
;
286 bool up
= !!(flags
& IFF_UP
);
287 bool running
= up
&& (flags
& IFF_RUNNING
);
289 LOG("br_index %d, if_index %d, newlink %d, up %d, running %d",
290 br_index
, if_index
, newlink
, up
, running
);
292 if((br_index
>= 0) && (br_index
!= if_index
))
294 if(!(br
= find_br(br_index
)))
295 return -2; /* bridge not in list */
296 int br_flags
= get_flags(br
->sysdeps
.name
);
298 set_br_up(br
, !!(br_flags
& IFF_UP
));
303 if(!(prt
= find_if(br
, if_index
)))
307 INFO("Got DELLINK for unknown port %d on "
308 "bridge %d", if_index
, br_index
);
311 /* Check if this interface is slave of another bridge */
312 list_for_each_entry(other_br
, &bridges
, list
)
315 if(delete_if_byindex(other_br
, if_index
))
317 INFO("Device %d has come to bridge %d. "
318 "Missed notify for deletion from bridge %d",
319 if_index
, br_index
, other_br
->sysdeps
.if_index
);
323 prt
= create_if(br
, if_index
);
327 ERROR("Couldn't create data for interface %d (master %d)",
336 set_if_up(prt
, running
); /* And speed and duplex */
339 { /* Interface is not a bridge slave */
342 /* DELLINK not from bridge means interface unregistered. */
343 /* Cleanup removed bridge or removed bridge slave */
344 if(!delete_br_byindex(if_index
))
345 list_for_each_entry(br
, &bridges
, list
)
347 if(delete_if_byindex(br
, if_index
))
353 { /* This may be a new link */
354 if(br_index
== if_index
)
356 if(!(br
= find_br(br_index
)))
357 return -2; /* bridge not in list */
367 __u8 dest_addr
[ETH_ALEN
];
368 __u8 src_addr
[ETH_ALEN
];
373 } __attribute__((packed
));
375 /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
376 #define LLC_PDU_LEN_U 3 /* header and 1 control byte */
377 #define LLC_PDU_TYPE_U 3 /* first two bits */
379 /* 7.12.3 of 802.1D */
380 #define LLC_SAP_BSPAN 0x42
381 static const __u8 bridge_group_address
[ETH_ALEN
] =
383 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
386 void bridge_bpdu_rcv(int if_index
, const unsigned char *data
, int len
)
391 LOG("ifindex %d, len %d", if_index
, len
);
393 list_for_each_entry(br
, &bridges
, list
)
395 if((prt
= find_if(br
, if_index
)))
402 TSTM(br
== prt
->bridge
,, "Bridge mismatch. This bridge is '%s' but port "
403 "'%s' belongs to bridge '%s'", br
->sysdeps
.name
, prt
->bridge
->sysdeps
.name
);
404 TSTM(prt
->sysdeps
.up
,, "Port '%s' should be up", prt
->sysdeps
.name
);
406 /* Validate Ethernet and LLC header,
407 * maybe we can skip this check thanks to Berkeley filter in packet socket?
409 struct llc_header
*h
;
411 TST(len
> sizeof(struct llc_header
),);
412 h
= (struct llc_header
*)data
;
413 TST(0 == memcmp(h
->dest_addr
, bridge_group_address
, ETH_ALEN
),
414 INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
416 h
->dest_addr
[0], h
->dest_addr
[1], h
->dest_addr
[2],
417 h
->dest_addr
[3], h
->dest_addr
[4], h
->dest_addr
[5])
419 l
= __be16_to_cpu(h
->len8023
);
420 TST(l
<= ETH_DATA_LEN
&& l
<= len
- ETH_HLEN
&& l
>= LLC_PDU_LEN_U
, );
421 TST(h
->d_sap
== LLC_SAP_BSPAN
&& h
->s_sap
== LLC_SAP_BSPAN
&& (h
->llc_ctrl
& 0x3) == LLC_PDU_TYPE_U
,);
424 /* Don't include LLC header */
425 (bpdu_t
*)(data
+ sizeof(*h
)), l
- LLC_PDU_LEN_U
);
428 static int br_set_state(struct rtnl_handle
*rth
, unsigned ifindex
, __u8 state
)
433 struct ifinfomsg ifi
;
437 memset(&req
, 0, sizeof(req
));
439 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
440 req
.n
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_REPLACE
;
441 req
.n
.nlmsg_type
= RTM_SETLINK
;
442 req
.ifi
.ifi_family
= AF_BRIDGE
;
443 req
.ifi
.ifi_index
= ifindex
;
445 addattr8(&req
.n
, sizeof(req
.buf
), IFLA_PROTINFO
, state
);
447 return rtnl_talk(rth
, &req
.n
, 0, 0, NULL
, NULL
, NULL
);
450 static int br_flush_port(char *ifname
)
453 snprintf(fname
, sizeof(fname
), SYSFS_CLASS_NET
"/%s/brport/flush", ifname
);
454 int fd
= open(fname
, O_WRONLY
);
455 TSTM(0 <= fd
, -1, "Couldn't open flush file %s for write: %m", fname
);
456 int write_result
= write(fd
, "1", 1);
458 TST(1 == write_result
, -1);
462 static int br_set_ageing_time(char *brname
, unsigned int ageing_time
)
464 char fname
[128], str_time
[32];
465 snprintf(fname
, sizeof(fname
), SYSFS_CLASS_NET
"/%s/bridge/ageing_time",
467 int fd
= open(fname
, O_WRONLY
);
468 TSTM(0 <= fd
, -1, "Couldn't open file %s for write: %m", fname
);
469 int len
= sprintf(str_time
, "%u", ageing_time
* HZ
);
470 int write_result
= write(fd
, str_time
, len
);
472 TST(len
== write_result
, -1);
476 /* External actions for MSTP protocol */
478 void MSTP_OUT_set_state(per_tree_port_t
*ptp
, int new_state
)
481 port_t
*prt
= ptp
->port
;
482 bridge_t
*br
= prt
->bridge
;
484 if(ptp
->state
== new_state
)
486 ptp
->state
= driver_set_new_state(ptp
, new_state
);
490 case BR_STATE_LISTENING
:
491 state_name
= "listening";
493 case BR_STATE_LEARNING
:
494 state_name
= "learning";
496 case BR_STATE_FORWARDING
:
497 state_name
= "forwarding";
498 ++(prt
->num_trans_fwd
);
500 case BR_STATE_BLOCKING
:
501 state_name
= "blocking";
502 ++(prt
->num_trans_blk
);
505 case BR_STATE_DISABLED
:
506 state_name
= "disabled";
509 INFO_MSTINAME(br
, prt
, ptp
, "entering %s state", state_name
);
511 /* Translate new CIST state to the kernel bridge code */
514 if(0 > br_set_state(&rth_state
, prt
->sysdeps
.if_index
, ptp
->state
))
515 INFO_PRTNAME(br
, prt
, "Couldn't set kernel bridge state %s",
520 /* This function initiates process of flushing
521 * all entries for the given port in all FIDs for the
523 * When this process finishes, implementation should signal
524 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
526 void MSTP_OUT_flush_all_fids(per_tree_port_t
* ptp
)
528 port_t
*prt
= ptp
->port
;
529 bridge_t
*br
= prt
->bridge
;
531 /* Translate CIST flushing to the kernel bridge code */
534 if(0 > br_flush_port(prt
->sysdeps
.name
))
535 ERROR_PRTNAME(br
, prt
,
536 "Couldn't flush kernel bridge forwarding database");
538 /* Completion signal MSTP_IN_all_fids_flushed will be called by driver */
539 INFO_MSTINAME(br
, prt
, ptp
, "Flushing forwarding database");
540 driver_flush_all_fids(ptp
);
543 void MSTP_OUT_set_ageing_time(port_t
*prt
, unsigned int ageingTime
)
545 unsigned int actual_ageing_time
;
546 bridge_t
*br
= prt
->bridge
;
548 actual_ageing_time
= driver_set_ageing_time(prt
, ageingTime
);
549 INFO_PRTNAME(br
, prt
, "Setting new ageing time to %u", actual_ageing_time
);
552 * Translate new ageing time to the kernel bridge code.
553 * Kernel bridging code does not support per-port ageing time,
554 * so set ageing time for the whole bridge.
556 if(0 > br_set_ageing_time(br
->sysdeps
.name
, actual_ageing_time
))
557 ERROR_BRNAME(br
, "Couldn't set new ageing time in kernel bridge");
560 void MSTP_OUT_tx_bpdu(port_t
*prt
, bpdu_t
* bpdu
, int size
)
562 char *bpdu_type
, *tcflag
;
563 bridge_t
*br
= prt
->bridge
;
565 switch(bpdu
->protocolVersion
)
568 switch(bpdu
->bpduType
)
571 bpdu_type
= "STP-Config";
574 bpdu_type
= "STP-TCN";
577 bpdu_type
= "STP-UnknownType";
587 bpdu_type
= "UnknownProto";
590 ++(prt
->num_tx_bpdu
);
591 if((protoSTP
== bpdu
->protocolVersion
) && (bpduTypeTCN
== bpdu
->bpduType
))
594 LOG_PRTNAME(br
, prt
, "sending %s BPDU", bpdu_type
);
599 if(bpdu
->flags
& (1 << offsetTc
))
604 LOG_PRTNAME(br
, prt
, "sending %s BPDU%s", bpdu_type
, tcflag
);
608 memcpy(h
.dest_addr
, bridge_group_address
, ETH_ALEN
);
609 memcpy(h
.src_addr
, prt
->sysdeps
.macaddr
, ETH_ALEN
);
610 h
.len8023
= __cpu_to_be16(size
+ LLC_PDU_LEN_U
);
611 h
.d_sap
= h
.s_sap
= LLC_SAP_BSPAN
;
612 h
.llc_ctrl
= LLC_PDU_TYPE_U
;
614 struct iovec iov
[2] =
616 { .iov_base
= &h
, .iov_len
= sizeof(h
) },
617 { .iov_base
= bpdu
, .iov_len
= size
}
620 packet_send(prt
->sysdeps
.if_index
, iov
, 2, sizeof(h
) + size
);
623 void MSTP_OUT_shutdown_port(port_t
*prt
)
625 if(0 > if_shutdown(prt
->sysdeps
.name
))
626 ERROR_PRTNAME(prt
->bridge
, prt
, "Couldn't shutdown port");
629 static int not_dot_dotdot(const struct dirent
*entry
)
631 const char *n
= entry
->d_name
;
633 return strcmp(n
, ".") || strcmp(n
, "..");
636 static int get_port_list(const char *br_ifname
, struct dirent
***namelist
)
640 snprintf(buf
, sizeof(buf
), SYSFS_CLASS_NET
"/%.230s/brif", br_ifname
);
642 return scandir(buf
, namelist
, not_dot_dotdot
, versionsort
);
645 int bridge_create(int bridge_idx
, CIST_BridgeConfig
*cfg
)
647 struct dirent
**namelist
;
648 int *port_list
, n_ports
;
649 bridge_t
*br
, *other_br
;
655 br
= find_br(bridge_idx
);
657 br
= create_br(bridge_idx
);
661 MSTP_IN_set_cist_bridge_config(br
, cfg
);
663 flags
= get_flags(br
->sysdeps
.name
);
665 set_br_up(br
, !!(flags
& IFF_UP
));
667 n_ports
= get_port_list(br
->sysdeps
.name
, &namelist
);
668 port_list
= alloca(n_ports
* sizeof(*port_list
));
670 for (i
= 0; i
< n_ports
; i
++) {
671 port_list
[i
] = if_nametoindex(namelist
[i
]->d_name
);
676 list_for_each_entry_safe(port
, tmp
, &br
->ports
, br_list
) {
678 for (i
= 0; i
< n_ports
; i
++) {
679 if (port
->sysdeps
.if_index
!= port_list
[i
])
691 for (i
= 0; i
< n_ports
; i
++) {
692 port
= find_if(br
, port_list
[i
]);
696 list_for_each_entry(other_br
, &bridges
, list
) {
700 delete_if_byindex(other_br
, port_list
[i
]);
703 port
= find_if(br
, port_list
[i
]);
705 port
= create_if(br
, port_list
[i
]);
709 flags
= get_flags(port
->sysdeps
.name
);
713 set_if_up(port
, !(~flags
& (IFF_UP
| IFF_RUNNING
)));
719 void bridge_delete(int index
)
721 delete_br_byindex(index
);
724 int bridge_track_fini(void)
726 INFO("Stopping all bridges");
728 list_for_each_entry(br
, &bridges
, list
)
730 set_br_up(br
, false);