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 ******************************************************************************/
28 #include <linux/if_bridge.h>
29 #include <asm/byteorder.h>
31 #include "bridge_ctl.h"
32 #include "netif_utils.h"
37 static LIST_HEAD(bridges
);
39 static bridge_t
* create_br(int if_index
)
42 TST((br
= calloc(1, sizeof(*br
))) != NULL
, NULL
);
44 /* Init system dependent info */
45 br
->sysdeps
.if_index
= if_index
;
46 if_indextoname(if_index
, br
->sysdeps
.name
);
47 get_hwaddr(br
->sysdeps
.name
, br
->sysdeps
.macaddr
);
49 INFO("Add bridge %s", br
->sysdeps
.name
);
50 if(!MSTP_IN_bridge_create(br
, br
->sysdeps
.macaddr
))
56 list_add_tail(&br
->list
, &bridges
);
60 static bridge_t
* find_br(int if_index
)
63 list_for_each_entry(br
, &bridges
, list
)
65 if(br
->sysdeps
.if_index
== if_index
)
71 static port_t
* create_if(bridge_t
* br
, int if_index
)
74 TST((ifc
= calloc(1, sizeof(*ifc
))) != NULL
, NULL
);
76 /* Init system dependent info */
77 ifc
->sysdeps
.if_index
= if_index
;
78 if_indextoname(if_index
, ifc
->sysdeps
.name
);
79 get_hwaddr(ifc
->sysdeps
.name
, ifc
->sysdeps
.macaddr
);
82 if(0 > (portno
= get_bridge_portno(ifc
->sysdeps
.name
)))
84 ERROR("Couldn't get port number for %s", ifc
->sysdeps
.name
);
88 if((0 == portno
) || (portno
> MAX_PORT_NUMBER
))
90 ERROR("Port number for %s is invalid (%d)", ifc
->sysdeps
.name
, portno
);
95 INFO("Add iface %s as port#%d to bridge %s", ifc
->sysdeps
.name
,
96 portno
, br
->sysdeps
.name
);
98 if(!MSTP_IN_port_create_and_add_tail(ifc
, portno
))
107 static port_t
* find_if(bridge_t
* br
, int if_index
)
110 list_for_each_entry(ifc
, &br
->ports
, br_list
)
112 if(ifc
->sysdeps
.if_index
== if_index
)
118 static inline void delete_if(port_t
* ifc
)
120 list_del(&ifc
->br_list
);
121 MSTP_IN_delete_port(ifc
);
125 static inline bool delete_if_byindex(bridge_t
* br
, int if_index
)
128 if(!(ifc
= find_if(br
, if_index
)))
134 static bool delete_br_byindex(int if_index
)
137 if(!(br
= find_br(if_index
)))
140 MSTP_IN_delete_bridge(br
);
145 void bridge_one_second(void)
148 list_for_each_entry(br
, &bridges
, list
)
149 MSTP_IN_one_second(br
);
152 /* New MAC address is stored in addr, which also holds the old value on entry.
153 Return true if the address changed */
154 static bool check_mac_address(char *name
, __u8
*addr
)
156 __u8 temp_addr
[ETH_ALEN
];
157 if(get_hwaddr(name
, temp_addr
))
159 LOG("Error getting hw address: %s", name
);
160 /* Error. Ignore the new value */
163 if(memcmp(addr
, temp_addr
, sizeof(temp_addr
)) == 0)
167 memcpy(addr
, temp_addr
, sizeof(temp_addr
));
172 static int stp_enabled(bridge_t
* br
)
174 char path
[40 + IFNAMSIZ
];
175 sprintf(path
, "/sys/class/net/%s/bridge/stp_state", br
->sysdeps
.name
);
176 FILE *f
= fopen(path
, "r");
178 if(!f
|| (1 != fscanf(f
, "%d", &enabled
)))
179 ERROR("Can't read from %s", path
);
181 INFO("STP on %s state %d", br
->sysdeps
.name
, enabled
);
183 return enabled
== 2; /* ie user mode STP */
186 static void set_br_up(bridge_t
* br
, bool up
)
188 int stp_up
= stp_enabled(br
);
189 INFO("%s was %s stp was %s", br
->sysdeps
.name
,
190 br
->sysdeps
.up
? "up" : "down", br
->sysdeps
.stp_up
? "up" : "down");
191 INFO("Set bridge %s %s stp %s" , br
->sysdeps
.name
,
192 up
? "up" : "down", stp_up
? "up" : "down");
194 bool changed
= false;
196 if(up
!= br
->sysdeps
.up
)
202 if(br
->sysdeps
.stp_up
!= stp_up
)
204 br
->sysdeps
.stp_up
= stp_up
;
208 if(check_mac_address(br
->sysdeps
.name
, br
->sysdeps
.macaddr
))
210 /* MAC address changed */
211 /* Notify bridge address change */
212 MSTP_IN_set_bridge_address(br
, br
->sysdeps
.macaddr
);
216 MSTP_IN_set_bridge_enable(br
, br
->sysdeps
.up
&& br
->sysdeps
.stp_up
);
219 static void set_if_up(port_t
* ifc
, bool up
)
221 INFO("Port %s : %s", ifc
->sysdeps
.name
, (up
? "up" : "down"));
224 bool changed
= false;
226 if(check_mac_address(ifc
->sysdeps
.name
, ifc
->sysdeps
.macaddr
))
228 /* MAC address changed */
229 if(check_mac_address(ifc
->bridge
->sysdeps
.name
,
230 ifc
->bridge
->sysdeps
.macaddr
))
232 /* Notify bridge address change */
233 MSTP_IN_set_bridge_address(ifc
->bridge
,
234 ifc
->bridge
->sysdeps
.macaddr
);
242 ifc
->sysdeps
.up
= false;
248 int r
= ethtool_get_speed_duplex(ifc
->sysdeps
.name
, &speed
, &duplex
);
249 if((r
< 0) || (speed
< 0))
251 if((r
< 0) || (duplex
< 0))
252 duplex
= 0; /* Assume half duplex */
254 if(speed
!= ifc
->sysdeps
.speed
)
256 ifc
->sysdeps
.speed
= speed
;
259 if(duplex
!= ifc
->sysdeps
.duplex
)
261 ifc
->sysdeps
.duplex
= duplex
;
266 ifc
->sysdeps
.up
= true;
271 MSTP_IN_set_port_enable(ifc
, ifc
->sysdeps
.up
, ifc
->sysdeps
.speed
,
272 ifc
->sysdeps
.duplex
);
275 /* br_index == if_index means: interface is bridge master */
276 int bridge_notify(int br_index
, int if_index
, bool newlink
, bool up
)
279 bridge_t
*br
= NULL
, *other_br
;
281 LOG("br_index %d, if_index %d, newlink %d, up %d",
282 br_index
, if_index
, newlink
, up
);
284 if((br_index
>= 0) && (br_index
!= if_index
))
286 if(!(br
= find_br(br_index
)))
287 br
= create_br(br_index
);
290 ERROR("Couldn't create data for bridge interface %d", br_index
);
293 int br_up
= ethtool_get_link(br
->sysdeps
.name
);
295 set_br_up(br
, !!br_up
);
300 if(!(ifc
= find_if(br
, if_index
)))
304 INFO("Got DELLINK for unknown port %d on "
305 "bridge %d", if_index
, br_index
);
308 /* Check if this interface is slave of another bridge */
309 list_for_each_entry(other_br
, &bridges
, list
)
312 if(delete_if_byindex(other_br
, if_index
))
314 INFO("Device %d has come to bridge %d. "
315 "Missed notify for deletion from bridge %d",
316 if_index
, br_index
, other_br
->sysdeps
.if_index
);
320 ifc
= create_if(br
, if_index
);
324 ERROR("Couldn't create data for interface %d (master %d)",
333 set_if_up(ifc
, up
); /* And speed and duplex */
336 { /* Interface is not a bridge slave */
339 /* DELLINK not from bridge means interface unregistered. */
340 /* Cleanup removed bridge or removed bridge slave */
341 if(!delete_br_byindex(if_index
))
342 list_for_each_entry(br
, &bridges
, list
)
344 if(delete_if_byindex(br
, if_index
))
350 { /* This may be a new link */
351 if(br_index
== if_index
)
353 if(!(br
= find_br(br_index
)))
355 if(!(br
= create_br(br_index
)))
357 ERROR("Couldn't create data for bridge interface %d",
371 __u8 dest_addr
[ETH_ALEN
];
372 __u8 src_addr
[ETH_ALEN
];
377 } __attribute__((packed
));
379 /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
380 #define LLC_PDU_LEN_U 3 /* header and 1 control byte */
381 #define LLC_PDU_TYPE_U 3 /* first two bits */
383 /* 7.12.3 of 802.1D */
384 #define LLC_SAP_BSPAN 0x42
385 static const __u8 bridge_group_address
[ETH_ALEN
] =
387 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
390 void bridge_bpdu_rcv(int if_index
, const unsigned char *data
, int len
)
395 LOG("ifindex %d, len %d", if_index
, len
);
397 list_for_each_entry(br
, &bridges
, list
)
399 if((ifc
= find_if(br
, if_index
)))
406 TST(br
== ifc
->bridge
,);
407 TST(ifc
->sysdeps
.up
,);
408 if(!br
->sysdeps
.stp_up
)
411 /* Validate Ethernet and LLC header,
412 * maybe we can skip this check thanks to Berkeley filter in packet socket?
414 struct llc_header
*h
;
416 TST(len
> sizeof(struct llc_header
),);
417 h
= (struct llc_header
*)data
;
418 TST(0 == memcmp(h
->dest_addr
, bridge_group_address
, ETH_ALEN
),
419 INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
421 h
->dest_addr
[0], h
->dest_addr
[1], h
->dest_addr
[2],
422 h
->dest_addr
[3], h
->dest_addr
[4], h
->dest_addr
[5])
424 l
= __be16_to_cpu(h
->len8023
);
425 TST(l
<= ETH_DATA_LEN
&& l
<= len
- ETH_HLEN
&& l
>= LLC_PDU_LEN_U
, );
426 TST(h
->d_sap
== LLC_SAP_BSPAN
&& h
->s_sap
== LLC_SAP_BSPAN
&& (h
->llc_ctrl
& 0x3) == LLC_PDU_TYPE_U
,);
429 /* Don't include LLC header */
430 (bpdu_t
*)(data
+ sizeof(*h
)), l
- LLC_PDU_LEN_U
);
433 /* External actions for MSTP protocol */
435 void MSTP_OUT_set_state(per_tree_port_t
*ptp
, int new_state
)
438 port_t
*ifc
= ptp
->port
;
439 bridge_t
*br
= ifc
->bridge
;
443 case BR_STATE_LISTENING
:
444 state_name
= "listening";
446 case BR_STATE_LEARNING
:
447 state_name
= "learning";
449 case BR_STATE_FORWARDING
:
450 state_name
= "forwarding";
452 case BR_STATE_BLOCKING
:
453 state_name
= "blocking";
456 ERROR_MSTINAME(br
, ifc
, ptp
, "attempt to set invalid state %d",
458 new_state
= BR_STATE_DISABLED
;
459 case BR_STATE_DISABLED
:
460 state_name
= "disabled";
464 if(ptp
->state
== new_state
)
467 /* TODO: command driver to put the br:port:tree into new state */
469 ptp
->state
= new_state
;
470 INFO_MSTINAME(br
, ifc
, ptp
, "entering %s state", state_name
);
473 /* This function initiates process of flushing
474 * all entries for the given port in all FIDs for the
476 * When this process finishes, implementation should signal
477 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
479 void MSTP_OUT_flush_all_fids(per_tree_port_t
* ptp
)
481 /* TODO: do real flushing.
482 * Make it asynchronous, with completion function calling
483 * MSTP_IN_all_fids_flushed(ptp)
485 MSTP_IN_all_fids_flushed(ptp
);
488 /* ageingTime < 0 => command driver to use its internal setting */
489 void MSTP_OUT_set_ageing_time(bridge_t
* br
, int ageingTime
)
491 /* TODO: do set new ageing time */
494 void MSTP_OUT_tx_bpdu(port_t
* ifc
, bpdu_t
* bpdu
, int size
)
497 bridge_t
*br
= ifc
->bridge
;
499 switch(bpdu
->protocolVersion
)
502 switch(bpdu
->bpduType
)
505 bpdu_type
= "STP-Config";
508 bpdu_type
= "STP-TCN";
511 bpdu_type
= "STP-UnknownType";
521 bpdu_type
= "UnknownProto";
524 LOG_PRTNAME(br
, ifc
, "sending %s BPDU", bpdu_type
);
527 memcpy(h
.dest_addr
, bridge_group_address
, ETH_ALEN
);
528 memcpy(h
.src_addr
, ifc
->sysdeps
.macaddr
, ETH_ALEN
);
529 h
.len8023
= __cpu_to_be16(size
+ LLC_PDU_LEN_U
);
530 h
.d_sap
= h
.s_sap
= LLC_SAP_BSPAN
;
531 h
.llc_ctrl
= LLC_PDU_TYPE_U
;
533 struct iovec iov
[2] =
535 { .iov_base
= &h
, .iov_len
= sizeof(h
) },
536 { .iov_base
= bpdu
, .iov_len
= size
}
539 packet_send(ifc
->sysdeps
.if_index
, iov
, 2, sizeof(h
) + size
);
542 /* User interface commands */
544 #define CTL_CHECK_BRIDGE \
545 bridge_t *br = find_br(br_index); \
548 ERROR("Couldn't find bridge with index %d", br_index); \
552 #define CTL_CHECK_BRIDGE_PORT \
554 port_t *prt = find_if(br, port_index); \
557 ERROR_BRNAME(br, "Couldn't find port with index %d", port_index); \
561 #define CTL_CHECK_BRIDGE_TREE \
564 bool found = false; \
565 __be16 MSTID = __cpu_to_be16(mstid); \
566 list_for_each_entry(tree, &br->trees, bridge_list) \
567 if(tree->MSTID == MSTID) \
574 ERROR_BRNAME(br, "Couldn't find MSTI with ID %hu", mstid); \
578 #define CTL_CHECK_BRIDGE_PERTREEPORT \
579 CTL_CHECK_BRIDGE_PORT; \
580 per_tree_port_t *ptp; \
581 bool found = false; \
582 __be16 MSTID = __cpu_to_be16(mstid); \
583 list_for_each_entry(ptp, &prt->trees, port_list) \
584 if(ptp->MSTID == MSTID) \
591 ERROR_PRTNAME(br, prt, "Couldn't find MSTI with ID %hu", mstid); \
595 int CTL_get_cist_bridge_status(int br_index
, CIST_BridgeStatus
*status
,
596 char *root_port_name
)
599 per_tree_port_t
*ptp
;
602 MSTP_IN_get_cist_bridge_status(br
, status
);
604 /* find root port name by root_port_id */
605 cist
= GET_CIST_TREE(br
);
606 *root_port_name
= '\0';
607 list_for_each_entry(ptp
, &cist
->ports
, tree_list
)
608 if(ptp
->portId
== status
->root_port_id
)
610 strncpy(root_port_name
, ptp
->port
->sysdeps
.name
, IFNAMSIZ
);
616 int CTL_get_msti_bridge_status(int br_index
, __u16 mstid
,
617 MSTI_BridgeStatus
*status
, char *root_port_name
)
619 per_tree_port_t
*ptp
;
621 CTL_CHECK_BRIDGE_TREE
;
622 MSTP_IN_get_msti_bridge_status(tree
, status
);
624 /* find root port name by root_port_id */
625 *root_port_name
= '\0';
626 list_for_each_entry(ptp
, &tree
->ports
, tree_list
)
627 if(ptp
->portId
== status
->root_port_id
)
629 strncpy(root_port_name
, ptp
->port
->sysdeps
.name
, IFNAMSIZ
);
635 int CTL_set_cist_bridge_config(int br_index
, CIST_BridgeConfig
*cfg
)
638 return MSTP_IN_set_cist_bridge_config(br
, cfg
);
641 int CTL_set_msti_bridge_config(int br_index
, __u16 mstid
, __u8 bridge_priority
)
643 CTL_CHECK_BRIDGE_TREE
;
644 return MSTP_IN_set_msti_bridge_config(tree
, bridge_priority
);
647 int CTL_get_cist_port_status(int br_index
, int port_index
,
648 CIST_PortStatus
*status
)
650 CTL_CHECK_BRIDGE_PORT
;
651 MSTP_IN_get_cist_port_status(prt
, status
);
655 int CTL_get_msti_port_status(int br_index
, int port_index
, __u16 mstid
,
656 MSTI_PortStatus
*status
)
658 CTL_CHECK_BRIDGE_PERTREEPORT
;
659 MSTP_IN_get_msti_port_status(ptp
, status
);
663 int CTL_set_cist_port_config(int br_index
, int port_index
,
664 CIST_PortConfig
*cfg
)
666 CTL_CHECK_BRIDGE_PORT
;
667 return MSTP_IN_set_cist_port_config(prt
, cfg
);
670 int CTL_set_msti_port_config(int br_index
, int port_index
, __u16 mstid
,
671 MSTI_PortConfig
*cfg
)
673 CTL_CHECK_BRIDGE_PERTREEPORT
;
674 return MSTP_IN_set_msti_port_config(ptp
, cfg
);
677 int CTL_port_mcheck(int br_index
, int port_index
)
679 CTL_CHECK_BRIDGE_PORT
;
680 return MSTP_IN_port_mcheck(prt
);
683 int CTL_set_debug_level(int level
)
685 INFO("level %d", level
);
690 int CTL_get_mstilist(int br_index
, int *num_mstis
, __u16
*mstids
)
693 return MSTP_IN_get_mstilist(br
, num_mstis
, mstids
) ? 0 : -1;
696 int CTL_create_msti(int br_index
, __u16 mstid
)
699 return MSTP_IN_create_msti(br
, mstid
) ? 0 : -1;
702 int CTL_delete_msti(int br_index
, __u16 mstid
)
705 return MSTP_IN_delete_msti(br
, mstid
) ? 0 : -1;
708 int CTL_get_mstconfid(int br_index
, mst_configuration_identifier_t
*cfg
)
711 *cfg
= br
->MstConfigId
;
715 int CTL_set_mstconfid(int br_index
, __u16 revision
, char *name
)
718 MSTP_IN_set_mst_config_id(br
, revision
, name
);
722 int CTL_get_vids2fids(int br_index
, __u16
*vids2fids
)
725 memcpy(vids2fids
, br
->vid2fid
, sizeof(br
->vid2fid
));
729 int CTL_get_fids2mstids(int br_index
, __u16
*fids2mstids
)
733 for(i
= 0; i
< COUNT_OF(br
->fid2mstid
); ++i
)
734 fids2mstids
[i
] = __be16_to_cpu(br
->fid2mstid
[i
]);
738 int CTL_set_vid2fid(int br_index
, __u16 vid
, __u16 fid
)
741 return MSTP_IN_set_vid2fid(br
, vid
, fid
) ? 0 : -1;
744 int CTL_set_fid2mstid(int br_index
, __u16 fid
, __u16 mstid
)
747 return MSTP_IN_set_fid2mstid(br
, fid
, mstid
) ? 0 : -1;
750 int CTL_set_vids2fids(int br_index
, __u16
*vids2fids
)
753 return MSTP_IN_set_all_vids2fids(br
, vids2fids
) ? 0 : -1;
756 int CTL_set_fids2mstids(int br_index
, __u16
*fids2mstids
)
759 return MSTP_IN_set_all_fids2mstids(br
, fids2mstids
) ? 0 : -1;