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 <vitas@nppfactor.kiev.ua>
25 ******************************************************************************/
30 #include <linux/param.h>
31 #include <linux/if_bridge.h>
32 #include <asm/byteorder.h>
34 #include "bridge_ctl.h"
35 #include "ctl_functions.h"
36 #include "netif_utils.h"
41 #include "libnetlink.h"
43 static LIST_HEAD(bridges
);
45 static bridge_t
* create_br(int if_index
)
48 TST((br
= calloc(1, sizeof(*br
))) != NULL
, NULL
);
50 /* Init system dependent info */
51 br
->sysdeps
.if_index
= if_index
;
52 if_indextoname(if_index
, br
->sysdeps
.name
);
53 get_hwaddr(br
->sysdeps
.name
, br
->sysdeps
.macaddr
);
55 INFO("Add bridge %s", br
->sysdeps
.name
);
56 if(!MSTP_IN_bridge_create(br
, br
->sysdeps
.macaddr
))
62 list_add_tail(&br
->list
, &bridges
);
66 static bridge_t
* find_br(int if_index
)
69 list_for_each_entry(br
, &bridges
, list
)
71 if(br
->sysdeps
.if_index
== if_index
)
77 static port_t
* create_if(bridge_t
* br
, int if_index
)
80 TST((ifc
= calloc(1, sizeof(*ifc
))) != NULL
, NULL
);
82 /* Init system dependent info */
83 ifc
->sysdeps
.if_index
= if_index
;
84 if_indextoname(if_index
, ifc
->sysdeps
.name
);
85 get_hwaddr(ifc
->sysdeps
.name
, ifc
->sysdeps
.macaddr
);
88 if(0 > (portno
= get_bridge_portno(ifc
->sysdeps
.name
)))
90 ERROR("Couldn't get port number for %s", ifc
->sysdeps
.name
);
94 if((0 == portno
) || (portno
> MAX_PORT_NUMBER
))
96 ERROR("Port number for %s is invalid (%d)", ifc
->sysdeps
.name
, portno
);
101 INFO("Add iface %s as port#%d to bridge %s", ifc
->sysdeps
.name
,
102 portno
, br
->sysdeps
.name
);
104 if(!MSTP_IN_port_create_and_add_tail(ifc
, portno
))
113 static port_t
* find_if(bridge_t
* br
, int if_index
)
116 list_for_each_entry(ifc
, &br
->ports
, br_list
)
118 if(ifc
->sysdeps
.if_index
== if_index
)
124 static inline void delete_if(port_t
* ifc
)
126 list_del(&ifc
->br_list
);
127 MSTP_IN_delete_port(ifc
);
131 static inline bool delete_if_byindex(bridge_t
* br
, int if_index
)
134 if(!(ifc
= find_if(br
, if_index
)))
140 static bool delete_br_byindex(int if_index
)
143 if(!(br
= find_br(if_index
)))
146 MSTP_IN_delete_bridge(br
);
151 void bridge_one_second(void)
154 list_for_each_entry(br
, &bridges
, list
)
155 MSTP_IN_one_second(br
);
158 /* New MAC address is stored in addr, which also holds the old value on entry.
159 Return true if the address changed */
160 static bool check_mac_address(char *name
, __u8
*addr
)
162 __u8 temp_addr
[ETH_ALEN
];
163 if(get_hwaddr(name
, temp_addr
))
165 LOG("Error getting hw address: %s", name
);
166 /* Error. Ignore the new value */
169 if(memcmp(addr
, temp_addr
, sizeof(temp_addr
)) == 0)
173 memcpy(addr
, temp_addr
, sizeof(temp_addr
));
178 static int stp_enabled(bridge_t
* br
)
180 char path
[40 + IFNAMSIZ
];
181 sprintf(path
, "/sys/class/net/%s/bridge/stp_state", br
->sysdeps
.name
);
182 FILE *f
= fopen(path
, "r");
184 if(!f
|| (1 != fscanf(f
, "%d", &enabled
)))
185 ERROR("Can't read from %s", path
);
187 INFO("STP on %s state %d", br
->sysdeps
.name
, enabled
);
189 return enabled
== 2; /* ie user mode STP */
192 static void set_br_up(bridge_t
* br
, bool up
)
194 int stp_up
= stp_enabled(br
);
195 INFO("%s was %s stp was %s", br
->sysdeps
.name
,
196 br
->sysdeps
.up
? "up" : "down", br
->sysdeps
.stp_up
? "up" : "down");
197 INFO("Set bridge %s %s stp %s" , br
->sysdeps
.name
,
198 up
? "up" : "down", stp_up
? "up" : "down");
200 bool changed
= false;
202 if(up
!= br
->sysdeps
.up
)
208 if(br
->sysdeps
.stp_up
!= stp_up
)
210 br
->sysdeps
.stp_up
= stp_up
;
214 if(check_mac_address(br
->sysdeps
.name
, br
->sysdeps
.macaddr
))
216 /* MAC address changed */
217 /* Notify bridge address change */
218 MSTP_IN_set_bridge_address(br
, br
->sysdeps
.macaddr
);
222 MSTP_IN_set_bridge_enable(br
, br
->sysdeps
.up
&& br
->sysdeps
.stp_up
);
225 static void set_if_up(port_t
* ifc
, bool up
)
227 INFO("Port %s : %s", ifc
->sysdeps
.name
, (up
? "up" : "down"));
230 bool changed
= false;
232 if(check_mac_address(ifc
->sysdeps
.name
, ifc
->sysdeps
.macaddr
))
234 /* MAC address changed */
235 if(check_mac_address(ifc
->bridge
->sysdeps
.name
,
236 ifc
->bridge
->sysdeps
.macaddr
))
238 /* Notify bridge address change */
239 MSTP_IN_set_bridge_address(ifc
->bridge
,
240 ifc
->bridge
->sysdeps
.macaddr
);
248 ifc
->sysdeps
.up
= false;
254 int r
= ethtool_get_speed_duplex(ifc
->sysdeps
.name
, &speed
, &duplex
);
255 if((r
< 0) || (speed
< 0))
257 if((r
< 0) || (duplex
< 0))
258 duplex
= 0; /* Assume half duplex */
260 if(speed
!= ifc
->sysdeps
.speed
)
262 ifc
->sysdeps
.speed
= speed
;
265 if(duplex
!= ifc
->sysdeps
.duplex
)
267 ifc
->sysdeps
.duplex
= duplex
;
272 ifc
->sysdeps
.up
= true;
277 MSTP_IN_set_port_enable(ifc
, ifc
->sysdeps
.up
, ifc
->sysdeps
.speed
,
278 ifc
->sysdeps
.duplex
);
281 /* br_index == if_index means: interface is bridge master */
282 int bridge_notify(int br_index
, int if_index
, bool newlink
, bool up
)
285 bridge_t
*br
= NULL
, *other_br
;
287 LOG("br_index %d, if_index %d, newlink %d, up %d",
288 br_index
, if_index
, newlink
, up
);
290 if((br_index
>= 0) && (br_index
!= if_index
))
292 if(!(br
= find_br(br_index
)))
293 br
= create_br(br_index
);
296 ERROR("Couldn't create data for bridge interface %d", br_index
);
299 int br_up
= ethtool_get_link(br
->sysdeps
.name
);
301 set_br_up(br
, !!br_up
);
306 if(!(ifc
= find_if(br
, if_index
)))
310 INFO("Got DELLINK for unknown port %d on "
311 "bridge %d", if_index
, br_index
);
314 /* Check if this interface is slave of another bridge */
315 list_for_each_entry(other_br
, &bridges
, list
)
318 if(delete_if_byindex(other_br
, if_index
))
320 INFO("Device %d has come to bridge %d. "
321 "Missed notify for deletion from bridge %d",
322 if_index
, br_index
, other_br
->sysdeps
.if_index
);
326 ifc
= create_if(br
, if_index
);
330 ERROR("Couldn't create data for interface %d (master %d)",
339 set_if_up(ifc
, up
); /* And speed and duplex */
342 { /* Interface is not a bridge slave */
345 /* DELLINK not from bridge means interface unregistered. */
346 /* Cleanup removed bridge or removed bridge slave */
347 if(!delete_br_byindex(if_index
))
348 list_for_each_entry(br
, &bridges
, list
)
350 if(delete_if_byindex(br
, if_index
))
356 { /* This may be a new link */
357 if(br_index
== if_index
)
359 if(!(br
= find_br(br_index
)))
361 if(!(br
= create_br(br_index
)))
363 ERROR("Couldn't create data for bridge interface %d",
377 __u8 dest_addr
[ETH_ALEN
];
378 __u8 src_addr
[ETH_ALEN
];
383 } __attribute__((packed
));
385 /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
386 #define LLC_PDU_LEN_U 3 /* header and 1 control byte */
387 #define LLC_PDU_TYPE_U 3 /* first two bits */
389 /* 7.12.3 of 802.1D */
390 #define LLC_SAP_BSPAN 0x42
391 static const __u8 bridge_group_address
[ETH_ALEN
] =
393 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
396 void bridge_bpdu_rcv(int if_index
, const unsigned char *data
, int len
)
401 LOG("ifindex %d, len %d", if_index
, len
);
403 list_for_each_entry(br
, &bridges
, list
)
405 if((ifc
= find_if(br
, if_index
)))
412 TST(br
== ifc
->bridge
,);
413 TST(ifc
->sysdeps
.up
,);
414 if(!br
->sysdeps
.stp_up
)
417 /* Validate Ethernet and LLC header,
418 * maybe we can skip this check thanks to Berkeley filter in packet socket?
420 struct llc_header
*h
;
422 TST(len
> sizeof(struct llc_header
),);
423 h
= (struct llc_header
*)data
;
424 TST(0 == memcmp(h
->dest_addr
, bridge_group_address
, ETH_ALEN
),
425 INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
427 h
->dest_addr
[0], h
->dest_addr
[1], h
->dest_addr
[2],
428 h
->dest_addr
[3], h
->dest_addr
[4], h
->dest_addr
[5])
430 l
= __be16_to_cpu(h
->len8023
);
431 TST(l
<= ETH_DATA_LEN
&& l
<= len
- ETH_HLEN
&& l
>= LLC_PDU_LEN_U
, );
432 TST(h
->d_sap
== LLC_SAP_BSPAN
&& h
->s_sap
== LLC_SAP_BSPAN
&& (h
->llc_ctrl
& 0x3) == LLC_PDU_TYPE_U
,);
435 /* Don't include LLC header */
436 (bpdu_t
*)(data
+ sizeof(*h
)), l
- LLC_PDU_LEN_U
);
439 static int br_set_state(struct rtnl_handle
*rth
, unsigned ifindex
, __u8 state
)
444 struct ifinfomsg ifi
;
448 memset(&req
, 0, sizeof(req
));
450 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
451 req
.n
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_REPLACE
;
452 req
.n
.nlmsg_type
= RTM_SETLINK
;
453 req
.ifi
.ifi_family
= AF_BRIDGE
;
454 req
.ifi
.ifi_index
= ifindex
;
456 addattr8(&req
.n
, sizeof(req
.buf
), IFLA_PROTINFO
, state
);
458 return rtnl_talk(rth
, &req
.n
, 0, 0, NULL
, NULL
, NULL
);
461 static int br_flush_port(char *ifname
)
464 snprintf(fname
, sizeof(fname
), "/sys/class/net/%s/brport/flush", ifname
);
465 int fd
= open(fname
, O_WRONLY
);
466 TSTM(0 <= fd
, -1, "Couldn't open flush file %s for write: %m", fname
);
467 int write_result
= write(fd
, "1", 1);
469 TST(1 == write_result
, -1);
473 static int br_set_ageing_time(char *brname
, unsigned int ageing_time
)
475 char fname
[128], str_time
[32];
476 snprintf(fname
, sizeof(fname
), "/sys/class/net/%s/bridge/ageing_time",
478 int fd
= open(fname
, O_WRONLY
);
479 TSTM(0 <= fd
, -1, "Couldn't open file %s for write: %m", fname
);
480 int len
= sprintf(str_time
, "%u", ageing_time
* HZ
);
481 int write_result
= write(fd
, str_time
, len
);
483 TST(len
== write_result
, -1);
487 /* External actions for MSTP protocol */
489 void MSTP_OUT_set_state(per_tree_port_t
*ptp
, int new_state
)
492 port_t
*ifc
= ptp
->port
;
493 bridge_t
*br
= ifc
->bridge
;
495 if(ptp
->state
== new_state
)
497 ptp
->state
= driver_set_new_state(ptp
, new_state
);
501 case BR_STATE_LISTENING
:
502 state_name
= "listening";
504 case BR_STATE_LEARNING
:
505 state_name
= "learning";
507 case BR_STATE_FORWARDING
:
508 state_name
= "forwarding";
510 case BR_STATE_BLOCKING
:
511 state_name
= "blocking";
514 case BR_STATE_DISABLED
:
515 state_name
= "disabled";
518 INFO_MSTINAME(br
, ifc
, ptp
, "entering %s state", state_name
);
520 /* Translate new CIST state to the kernel bridge code */
525 if(0 > br_set_state(&rth_state
, ifc
->sysdeps
.if_index
, ptp
->state
))
526 ERROR_PRTNAME(br
, ifc
, "Couldn't set kernel bridge state %s",
532 /* This function initiates process of flushing
533 * all entries for the given port in all FIDs for the
535 * When this process finishes, implementation should signal
536 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
538 void MSTP_OUT_flush_all_fids(per_tree_port_t
* ptp
)
540 port_t
*ifc
= ptp
->port
;
541 bridge_t
*br
= ifc
->bridge
;
543 /* Translate CIST flushing to the kernel bridge code */
546 if(0 > br_flush_port(ifc
->sysdeps
.name
))
547 ERROR_PRTNAME(br
, ifc
,
548 "Couldn't flush kernel bridge forwarding database");
550 /* Completion signal MSTP_IN_all_fids_flushed will be called by driver */
551 INFO_MSTINAME(br
, ifc
, ptp
, "Flushing forwarding database");
552 driver_flush_all_fids(ptp
);
555 /* 802.1Q-2005 wants per-port ageing time.
556 * We do not support it, so set ageing time for the whole bridge.
558 void MSTP_OUT_set_ageing_time(bridge_t
* br
, unsigned int ageingTime
)
560 unsigned int actual_ageing_time
;
562 actual_ageing_time
= driver_set_ageing_time(br
, ageingTime
);
563 INFO_BRNAME(br
, "Setting new ageing time to %u", actual_ageing_time
);
565 /* Translate new ageing time to the kernel bridge code */
566 if(0 > br_set_ageing_time(br
->sysdeps
.name
, actual_ageing_time
))
567 ERROR_BRNAME(br
, "Couldn't set new ageing time in kernel bridge");
570 void MSTP_OUT_tx_bpdu(port_t
* ifc
, bpdu_t
* bpdu
, int size
)
573 bridge_t
*br
= ifc
->bridge
;
575 switch(bpdu
->protocolVersion
)
578 switch(bpdu
->bpduType
)
581 bpdu_type
= "STP-Config";
584 bpdu_type
= "STP-TCN";
587 bpdu_type
= "STP-UnknownType";
597 bpdu_type
= "UnknownProto";
600 LOG_PRTNAME(br
, ifc
, "sending %s BPDU", bpdu_type
);
603 memcpy(h
.dest_addr
, bridge_group_address
, ETH_ALEN
);
604 memcpy(h
.src_addr
, ifc
->sysdeps
.macaddr
, ETH_ALEN
);
605 h
.len8023
= __cpu_to_be16(size
+ LLC_PDU_LEN_U
);
606 h
.d_sap
= h
.s_sap
= LLC_SAP_BSPAN
;
607 h
.llc_ctrl
= LLC_PDU_TYPE_U
;
609 struct iovec iov
[2] =
611 { .iov_base
= &h
, .iov_len
= sizeof(h
) },
612 { .iov_base
= bpdu
, .iov_len
= size
}
615 packet_send(ifc
->sysdeps
.if_index
, iov
, 2, sizeof(h
) + size
);
618 /* User interface commands */
620 #define CTL_CHECK_BRIDGE \
621 bridge_t *br = find_br(br_index); \
624 ERROR("Couldn't find bridge with index %d", br_index); \
628 #define CTL_CHECK_BRIDGE_PORT \
630 port_t *prt = find_if(br, port_index); \
633 ERROR_BRNAME(br, "Couldn't find port with index %d", port_index); \
637 #define CTL_CHECK_BRIDGE_TREE \
640 bool found = false; \
641 __be16 MSTID = __cpu_to_be16(mstid); \
642 list_for_each_entry(tree, &br->trees, bridge_list) \
643 if(tree->MSTID == MSTID) \
650 ERROR_BRNAME(br, "Couldn't find MSTI with ID %hu", mstid); \
654 #define CTL_CHECK_BRIDGE_PERTREEPORT \
655 CTL_CHECK_BRIDGE_PORT; \
656 per_tree_port_t *ptp; \
657 bool found = false; \
658 __be16 MSTID = __cpu_to_be16(mstid); \
659 list_for_each_entry(ptp, &prt->trees, port_list) \
660 if(ptp->MSTID == MSTID) \
667 ERROR_PRTNAME(br, prt, "Couldn't find MSTI with ID %hu", mstid); \
671 int CTL_get_cist_bridge_status(int br_index
, CIST_BridgeStatus
*status
,
672 char *root_port_name
)
675 per_tree_port_t
*ptp
;
678 MSTP_IN_get_cist_bridge_status(br
, status
);
680 /* find root port name by root_port_id */
681 cist
= GET_CIST_TREE(br
);
682 *root_port_name
= '\0';
683 list_for_each_entry(ptp
, &cist
->ports
, tree_list
)
684 if(ptp
->portId
== status
->root_port_id
)
686 strncpy(root_port_name
, ptp
->port
->sysdeps
.name
, IFNAMSIZ
);
692 int CTL_get_msti_bridge_status(int br_index
, __u16 mstid
,
693 MSTI_BridgeStatus
*status
, char *root_port_name
)
695 per_tree_port_t
*ptp
;
697 CTL_CHECK_BRIDGE_TREE
;
698 MSTP_IN_get_msti_bridge_status(tree
, status
);
700 /* find root port name by root_port_id */
701 *root_port_name
= '\0';
702 list_for_each_entry(ptp
, &tree
->ports
, tree_list
)
703 if(ptp
->portId
== status
->root_port_id
)
705 strncpy(root_port_name
, ptp
->port
->sysdeps
.name
, IFNAMSIZ
);
711 int CTL_set_cist_bridge_config(int br_index
, CIST_BridgeConfig
*cfg
)
714 return MSTP_IN_set_cist_bridge_config(br
, cfg
);
717 int CTL_set_msti_bridge_config(int br_index
, __u16 mstid
, __u8 bridge_priority
)
719 CTL_CHECK_BRIDGE_TREE
;
720 return MSTP_IN_set_msti_bridge_config(tree
, bridge_priority
);
723 int CTL_get_cist_port_status(int br_index
, int port_index
,
724 CIST_PortStatus
*status
)
726 CTL_CHECK_BRIDGE_PORT
;
727 MSTP_IN_get_cist_port_status(prt
, status
);
731 int CTL_get_msti_port_status(int br_index
, int port_index
, __u16 mstid
,
732 MSTI_PortStatus
*status
)
734 CTL_CHECK_BRIDGE_PERTREEPORT
;
735 MSTP_IN_get_msti_port_status(ptp
, status
);
739 int CTL_set_cist_port_config(int br_index
, int port_index
,
740 CIST_PortConfig
*cfg
)
742 CTL_CHECK_BRIDGE_PORT
;
743 return MSTP_IN_set_cist_port_config(prt
, cfg
);
746 int CTL_set_msti_port_config(int br_index
, int port_index
, __u16 mstid
,
747 MSTI_PortConfig
*cfg
)
749 CTL_CHECK_BRIDGE_PERTREEPORT
;
750 return MSTP_IN_set_msti_port_config(ptp
, cfg
);
753 int CTL_port_mcheck(int br_index
, int port_index
)
755 CTL_CHECK_BRIDGE_PORT
;
756 return MSTP_IN_port_mcheck(prt
);
759 int CTL_set_debug_level(int level
)
761 INFO("level %d", level
);
766 int CTL_get_mstilist(int br_index
, int *num_mstis
, __u16
*mstids
)
769 return MSTP_IN_get_mstilist(br
, num_mstis
, mstids
) ? 0 : -1;
772 int CTL_create_msti(int br_index
, __u16 mstid
)
775 if((!driver_create_msti(br
, mstid
)) || (!MSTP_IN_create_msti(br
, mstid
)))
780 int CTL_delete_msti(int br_index
, __u16 mstid
)
783 if((!driver_delete_msti(br
, mstid
)) || (!MSTP_IN_delete_msti(br
, mstid
)))
788 int CTL_get_mstconfid(int br_index
, mst_configuration_identifier_t
*cfg
)
791 *cfg
= br
->MstConfigId
;
795 int CTL_set_mstconfid(int br_index
, __u16 revision
, char *name
)
798 MSTP_IN_set_mst_config_id(br
, revision
, name
);
802 int CTL_get_vids2fids(int br_index
, __u16
*vids2fids
)
805 memcpy(vids2fids
, br
->vid2fid
, sizeof(br
->vid2fid
));
809 int CTL_get_fids2mstids(int br_index
, __u16
*fids2mstids
)
813 for(i
= 0; i
< COUNT_OF(br
->fid2mstid
); ++i
)
814 fids2mstids
[i
] = __be16_to_cpu(br
->fid2mstid
[i
]);
818 int CTL_set_vid2fid(int br_index
, __u16 vid
, __u16 fid
)
821 return MSTP_IN_set_vid2fid(br
, vid
, fid
) ? 0 : -1;
824 int CTL_set_fid2mstid(int br_index
, __u16 fid
, __u16 mstid
)
827 return MSTP_IN_set_fid2mstid(br
, fid
, mstid
) ? 0 : -1;
830 int CTL_set_vids2fids(int br_index
, __u16
*vids2fids
)
833 return MSTP_IN_set_all_vids2fids(br
, vids2fids
) ? 0 : -1;
836 int CTL_set_fids2mstids(int br_index
, __u16
*fids2mstids
)
839 return MSTP_IN_set_all_fids2mstids(br
, fids2mstids
) ? 0 : -1;