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;
221 if(check_mac_address(prt
->sysdeps
.name
, prt
->sysdeps
.macaddr
))
223 /* MAC address changed */
224 if(check_mac_address(prt
->bridge
->sysdeps
.name
,
225 prt
->bridge
->sysdeps
.macaddr
))
227 /* Notify bridge address change */
228 MSTP_IN_set_bridge_address(prt
->bridge
,
229 prt
->bridge
->sysdeps
.macaddr
);
237 prt
->sysdeps
.up
= false;
243 int r
= ethtool_get_speed_duplex(prt
->sysdeps
.name
, &speed
, &duplex
);
244 if((r
< 0) || (speed
< 0))
246 if((r
< 0) || (duplex
< 0))
247 duplex
= 1; /* Assume full duplex */
249 if(speed
!= prt
->sysdeps
.speed
)
251 prt
->sysdeps
.speed
= speed
;
254 if(duplex
!= prt
->sysdeps
.duplex
)
256 prt
->sysdeps
.duplex
= duplex
;
261 prt
->sysdeps
.up
= true;
266 MSTP_IN_set_port_enable(prt
, prt
->sysdeps
.up
, prt
->sysdeps
.speed
,
267 prt
->sysdeps
.duplex
);
270 /* br_index == if_index means: interface is bridge master */
271 int bridge_notify(int br_index
, int if_index
, bool newlink
, unsigned flags
)
274 bridge_t
*br
= NULL
, *other_br
;
275 bool up
= !!(flags
& IFF_UP
);
276 bool running
= up
&& (flags
& IFF_RUNNING
);
278 LOG("br_index %d, if_index %d, newlink %d, up %d, running %d",
279 br_index
, if_index
, newlink
, up
, running
);
281 if((br_index
>= 0) && (br_index
!= if_index
))
283 if(!(br
= find_br(br_index
)))
284 return -2; /* bridge not in list */
285 int br_flags
= get_flags(br
->sysdeps
.name
);
287 set_br_up(br
, !!(br_flags
& IFF_UP
));
292 if(!(prt
= find_if(br
, if_index
)))
296 INFO("Got DELLINK for unknown port %d on "
297 "bridge %d", if_index
, br_index
);
300 /* Check if this interface is slave of another bridge */
301 list_for_each_entry(other_br
, &bridges
, list
)
304 if(delete_if_byindex(other_br
, if_index
))
306 INFO("Device %d has come to bridge %d. "
307 "Missed notify for deletion from bridge %d",
308 if_index
, br_index
, other_br
->sysdeps
.if_index
);
312 prt
= create_if(br
, if_index
);
316 ERROR("Couldn't create data for interface %d (master %d)",
325 set_if_up(prt
, running
); /* And speed and duplex */
328 { /* Interface is not a bridge slave */
331 /* DELLINK not from bridge means interface unregistered. */
332 /* Cleanup removed bridge or removed bridge slave */
333 if(!delete_br_byindex(if_index
))
334 list_for_each_entry(br
, &bridges
, list
)
336 if(delete_if_byindex(br
, if_index
))
342 { /* This may be a new link */
343 if(br_index
== if_index
)
345 if(!(br
= find_br(br_index
)))
346 return -2; /* bridge not in list */
356 __u8 dest_addr
[ETH_ALEN
];
357 __u8 src_addr
[ETH_ALEN
];
362 } __attribute__((packed
));
364 /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
365 #define LLC_PDU_LEN_U 3 /* header and 1 control byte */
366 #define LLC_PDU_TYPE_U 3 /* first two bits */
368 /* 7.12.3 of 802.1D */
369 #define LLC_SAP_BSPAN 0x42
370 static const __u8 bridge_group_address
[ETH_ALEN
] =
372 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
375 void bridge_bpdu_rcv(int if_index
, const unsigned char *data
, int len
)
380 LOG("ifindex %d, len %d", if_index
, len
);
382 list_for_each_entry(br
, &bridges
, list
)
384 if((prt
= find_if(br
, if_index
)))
391 TSTM(br
== prt
->bridge
,, "Bridge mismatch. This bridge is '%s' but port "
392 "'%s' belongs to bridge '%s'", br
->sysdeps
.name
, prt
->bridge
->sysdeps
.name
);
393 TSTM(prt
->sysdeps
.up
,, "Port '%s' should be up", prt
->sysdeps
.name
);
395 /* Validate Ethernet and LLC header,
396 * maybe we can skip this check thanks to Berkeley filter in packet socket?
398 struct llc_header
*h
;
400 TST(len
> sizeof(struct llc_header
),);
401 h
= (struct llc_header
*)data
;
402 TST(0 == memcmp(h
->dest_addr
, bridge_group_address
, ETH_ALEN
),
403 INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
405 h
->dest_addr
[0], h
->dest_addr
[1], h
->dest_addr
[2],
406 h
->dest_addr
[3], h
->dest_addr
[4], h
->dest_addr
[5])
408 l
= __be16_to_cpu(h
->len8023
);
409 TST(l
<= ETH_DATA_LEN
&& l
<= len
- ETH_HLEN
&& l
>= LLC_PDU_LEN_U
, );
410 TST(h
->d_sap
== LLC_SAP_BSPAN
&& h
->s_sap
== LLC_SAP_BSPAN
&& (h
->llc_ctrl
& 0x3) == LLC_PDU_TYPE_U
,);
413 /* Don't include LLC header */
414 (bpdu_t
*)(data
+ sizeof(*h
)), l
- LLC_PDU_LEN_U
);
417 static int br_set_state(struct rtnl_handle
*rth
, unsigned ifindex
, __u8 state
)
422 struct ifinfomsg ifi
;
426 memset(&req
, 0, sizeof(req
));
428 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
429 req
.n
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_REPLACE
;
430 req
.n
.nlmsg_type
= RTM_SETLINK
;
431 req
.ifi
.ifi_family
= AF_BRIDGE
;
432 req
.ifi
.ifi_index
= ifindex
;
434 addattr8(&req
.n
, sizeof(req
.buf
), IFLA_PROTINFO
, state
);
436 return rtnl_talk(rth
, &req
.n
, 0, 0, NULL
, NULL
, NULL
);
439 static int br_flush_port(char *ifname
)
442 snprintf(fname
, sizeof(fname
), SYSFS_CLASS_NET
"/%s/brport/flush", ifname
);
443 int fd
= open(fname
, O_WRONLY
);
444 TSTM(0 <= fd
, -1, "Couldn't open flush file %s for write: %m", fname
);
445 int write_result
= write(fd
, "1", 1);
447 TST(1 == write_result
, -1);
451 static int br_set_ageing_time(char *brname
, unsigned int ageing_time
)
453 char fname
[128], str_time
[32];
454 snprintf(fname
, sizeof(fname
), SYSFS_CLASS_NET
"/%s/bridge/ageing_time",
456 int fd
= open(fname
, O_WRONLY
);
457 TSTM(0 <= fd
, -1, "Couldn't open file %s for write: %m", fname
);
458 int len
= sprintf(str_time
, "%u", ageing_time
* HZ
);
459 int write_result
= write(fd
, str_time
, len
);
461 TST(len
== write_result
, -1);
465 /* External actions for MSTP protocol */
467 void MSTP_OUT_set_state(per_tree_port_t
*ptp
, int new_state
)
470 port_t
*prt
= ptp
->port
;
471 bridge_t
*br
= prt
->bridge
;
473 if(ptp
->state
== new_state
)
475 ptp
->state
= driver_set_new_state(ptp
, new_state
);
479 case BR_STATE_LISTENING
:
480 state_name
= "listening";
482 case BR_STATE_LEARNING
:
483 state_name
= "learning";
485 case BR_STATE_FORWARDING
:
486 state_name
= "forwarding";
487 ++(prt
->num_trans_fwd
);
489 case BR_STATE_BLOCKING
:
490 state_name
= "blocking";
491 ++(prt
->num_trans_blk
);
494 case BR_STATE_DISABLED
:
495 state_name
= "disabled";
498 INFO_MSTINAME(br
, prt
, ptp
, "entering %s state", state_name
);
500 /* Translate new CIST state to the kernel bridge code */
503 if(0 > br_set_state(&rth_state
, prt
->sysdeps
.if_index
, ptp
->state
))
504 INFO_PRTNAME(br
, prt
, "Couldn't set kernel bridge state %s",
509 /* This function initiates process of flushing
510 * all entries for the given port in all FIDs for the
512 * When this process finishes, implementation should signal
513 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
515 void MSTP_OUT_flush_all_fids(per_tree_port_t
* ptp
)
517 port_t
*prt
= ptp
->port
;
518 bridge_t
*br
= prt
->bridge
;
520 /* Translate CIST flushing to the kernel bridge code */
523 if(0 > br_flush_port(prt
->sysdeps
.name
))
524 ERROR_PRTNAME(br
, prt
,
525 "Couldn't flush kernel bridge forwarding database");
527 /* Completion signal MSTP_IN_all_fids_flushed will be called by driver */
528 INFO_MSTINAME(br
, prt
, ptp
, "Flushing forwarding database");
529 driver_flush_all_fids(ptp
);
532 void MSTP_OUT_set_ageing_time(port_t
*prt
, unsigned int ageingTime
)
534 unsigned int actual_ageing_time
;
535 bridge_t
*br
= prt
->bridge
;
537 actual_ageing_time
= driver_set_ageing_time(prt
, ageingTime
);
538 INFO_PRTNAME(br
, prt
, "Setting new ageing time to %u", actual_ageing_time
);
541 * Translate new ageing time to the kernel bridge code.
542 * Kernel bridging code does not support per-port ageing time,
543 * so set ageing time for the whole bridge.
545 if(0 > br_set_ageing_time(br
->sysdeps
.name
, actual_ageing_time
))
546 ERROR_BRNAME(br
, "Couldn't set new ageing time in kernel bridge");
549 void MSTP_OUT_tx_bpdu(port_t
*prt
, bpdu_t
* bpdu
, int size
)
551 char *bpdu_type
, *tcflag
;
552 bridge_t
*br
= prt
->bridge
;
554 switch(bpdu
->protocolVersion
)
557 switch(bpdu
->bpduType
)
560 bpdu_type
= "STP-Config";
563 bpdu_type
= "STP-TCN";
566 bpdu_type
= "STP-UnknownType";
576 bpdu_type
= "UnknownProto";
579 ++(prt
->num_tx_bpdu
);
580 if((protoSTP
== bpdu
->protocolVersion
) && (bpduTypeTCN
== bpdu
->bpduType
))
583 LOG_PRTNAME(br
, prt
, "sending %s BPDU", bpdu_type
);
588 if(bpdu
->flags
& (1 << offsetTc
))
593 LOG_PRTNAME(br
, prt
, "sending %s BPDU%s", bpdu_type
, tcflag
);
597 memcpy(h
.dest_addr
, bridge_group_address
, ETH_ALEN
);
598 memcpy(h
.src_addr
, prt
->sysdeps
.macaddr
, ETH_ALEN
);
599 h
.len8023
= __cpu_to_be16(size
+ LLC_PDU_LEN_U
);
600 h
.d_sap
= h
.s_sap
= LLC_SAP_BSPAN
;
601 h
.llc_ctrl
= LLC_PDU_TYPE_U
;
603 struct iovec iov
[2] =
605 { .iov_base
= &h
, .iov_len
= sizeof(h
) },
606 { .iov_base
= bpdu
, .iov_len
= size
}
609 packet_send(prt
->sysdeps
.if_index
, iov
, 2, sizeof(h
) + size
);
612 void MSTP_OUT_shutdown_port(port_t
*prt
)
614 if(0 > if_shutdown(prt
->sysdeps
.name
))
615 ERROR_PRTNAME(prt
->bridge
, prt
, "Couldn't shutdown port");
618 static int not_dot_dotdot(const struct dirent
*entry
)
620 const char *n
= entry
->d_name
;
622 return strcmp(n
, ".") || strcmp(n
, "..");
625 static int get_port_list(const char *br_ifname
, struct dirent
***namelist
)
629 snprintf(buf
, sizeof(buf
), SYSFS_CLASS_NET
"/%.230s/brif", br_ifname
);
631 return scandir(buf
, namelist
, not_dot_dotdot
, versionsort
);
634 int bridge_create(int bridge_idx
, CIST_BridgeConfig
*cfg
)
636 struct dirent
**namelist
;
637 int *port_list
, n_ports
;
638 bridge_t
*br
, *other_br
;
644 br
= find_br(bridge_idx
);
646 br
= create_br(bridge_idx
);
650 MSTP_IN_set_cist_bridge_config(br
, cfg
);
652 flags
= get_flags(br
->sysdeps
.name
);
654 set_br_up(br
, !!(flags
& IFF_UP
));
656 n_ports
= get_port_list(br
->sysdeps
.name
, &namelist
);
657 port_list
= alloca(n_ports
* sizeof(*port_list
));
659 for (i
= 0; i
< n_ports
; i
++) {
660 port_list
[i
] = if_nametoindex(namelist
[i
]->d_name
);
665 list_for_each_entry_safe(port
, tmp
, &br
->ports
, br_list
) {
667 for (i
= 0; i
< n_ports
; i
++) {
668 if (port
->sysdeps
.if_index
!= port_list
[i
])
680 for (i
= 0; i
< n_ports
; i
++) {
681 port
= find_if(br
, port_list
[i
]);
685 list_for_each_entry(other_br
, &bridges
, list
) {
689 delete_if_byindex(other_br
, port_list
[i
]);
692 port
= find_if(br
, port_list
[i
]);
694 port
= create_if(br
, port_list
[i
]);
698 flags
= get_flags(port
->sysdeps
.name
);
702 set_if_up(port
, !(~flags
& (IFF_UP
| IFF_RUNNING
)));
708 void bridge_delete(int index
)
710 delete_br_byindex(index
);
713 int bridge_track_fini(void)
715 INFO("Stopping all bridges");
717 list_for_each_entry(br
, &bridges
, list
)
719 set_br_up(br
, false);