]> git.ipfire.org Git - people/ms/mstpd.git/blob - bridge_track.c
driver hooks for creating/deleting new MSTI
[people/ms/mstpd.git] / bridge_track.c
1 /*****************************************************************************
2 Copyright (c) 2006 EMC Corporation.
3 Copyright (c) 2011 Factor-SPE
4
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)
8 any later version.
9
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
13 more details.
14
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.
18
19 The full GNU General Public License is included in this distribution in the
20 file called LICENSE.
21
22 Authors: Srinivas Aji <Aji_Srinivas@emc.com>
23 Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
24
25 ******************************************************************************/
26
27 #include <string.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <linux/param.h>
31 #include <linux/if_bridge.h>
32 #include <asm/byteorder.h>
33
34 #include "bridge_ctl.h"
35 #include "ctl_functions.h"
36 #include "netif_utils.h"
37 #include "packet.h"
38 #include "log.h"
39 #include "mstp.h"
40 #include "driver.h"
41 #include "libnetlink.h"
42
43 static LIST_HEAD(bridges);
44
45 static bridge_t * create_br(int if_index)
46 {
47 bridge_t *br;
48 TST((br = calloc(1, sizeof(*br))) != NULL, NULL);
49
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);
54
55 INFO("Add bridge %s", br->sysdeps.name);
56 if(!MSTP_IN_bridge_create(br, br->sysdeps.macaddr))
57 {
58 free(br);
59 return NULL;
60 }
61
62 list_add_tail(&br->list, &bridges);
63 return br;
64 }
65
66 static bridge_t * find_br(int if_index)
67 {
68 bridge_t *br;
69 list_for_each_entry(br, &bridges, list)
70 {
71 if(br->sysdeps.if_index == if_index)
72 return br;
73 }
74 return NULL;
75 }
76
77 static port_t * create_if(bridge_t * br, int if_index)
78 {
79 port_t *ifc;
80 TST((ifc = calloc(1, sizeof(*ifc))) != NULL, NULL);
81
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);
86
87 int portno;
88 if(0 > (portno = get_bridge_portno(ifc->sysdeps.name)))
89 {
90 ERROR("Couldn't get port number for %s", ifc->sysdeps.name);
91 free(ifc);
92 return NULL;
93 }
94 if((0 == portno) || (portno > MAX_PORT_NUMBER))
95 {
96 ERROR("Port number for %s is invalid (%d)", ifc->sysdeps.name, portno);
97 free(ifc);
98 return NULL;
99 }
100
101 INFO("Add iface %s as port#%d to bridge %s", ifc->sysdeps.name,
102 portno, br->sysdeps.name);
103 ifc->bridge = br;
104 if(!MSTP_IN_port_create_and_add_tail(ifc, portno))
105 {
106 free(ifc);
107 return NULL;
108 }
109
110 return ifc;
111 }
112
113 static port_t * find_if(bridge_t * br, int if_index)
114 {
115 port_t *ifc;
116 list_for_each_entry(ifc, &br->ports, br_list)
117 {
118 if(ifc->sysdeps.if_index == if_index)
119 return ifc;
120 }
121 return NULL;
122 }
123
124 static inline void delete_if(port_t * ifc)
125 {
126 list_del(&ifc->br_list);
127 MSTP_IN_delete_port(ifc);
128 free(ifc);
129 }
130
131 static inline bool delete_if_byindex(bridge_t * br, int if_index)
132 {
133 port_t *ifc;
134 if(!(ifc = find_if(br, if_index)))
135 return false;
136 delete_if(ifc);
137 return true;
138 }
139
140 static bool delete_br_byindex(int if_index)
141 {
142 bridge_t *br;
143 if(!(br = find_br(if_index)))
144 return false;
145 list_del(&br->list);
146 MSTP_IN_delete_bridge(br);
147 free(br);
148 return true;
149 }
150
151 void bridge_one_second(void)
152 {
153 bridge_t *br;
154 list_for_each_entry(br, &bridges, list)
155 MSTP_IN_one_second(br);
156 }
157
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)
161 {
162 __u8 temp_addr[ETH_ALEN];
163 if(get_hwaddr(name, temp_addr))
164 {
165 LOG("Error getting hw address: %s", name);
166 /* Error. Ignore the new value */
167 return false;
168 }
169 if(memcmp(addr, temp_addr, sizeof(temp_addr)) == 0)
170 return false;
171 else
172 {
173 memcpy(addr, temp_addr, sizeof(temp_addr));
174 return true;
175 }
176 }
177
178 static int stp_enabled(bridge_t * br)
179 {
180 char path[40 + IFNAMSIZ];
181 sprintf(path, "/sys/class/net/%s/bridge/stp_state", br->sysdeps.name);
182 FILE *f = fopen(path, "r");
183 int enabled = 0;
184 if(!f || (1 != fscanf(f, "%d", &enabled)))
185 ERROR("Can't read from %s", path);
186 fclose(f);
187 INFO("STP on %s state %d", br->sysdeps.name, enabled);
188
189 return enabled == 2; /* ie user mode STP */
190 }
191
192 static void set_br_up(bridge_t * br, bool up)
193 {
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");
199
200 bool changed = false;
201
202 if(up != br->sysdeps.up)
203 {
204 br->sysdeps.up = up;
205 changed = true;
206 }
207
208 if(br->sysdeps.stp_up != stp_up)
209 {
210 br->sysdeps.stp_up = stp_up;
211 changed = true;
212 }
213
214 if(check_mac_address(br->sysdeps.name, br->sysdeps.macaddr))
215 {
216 /* MAC address changed */
217 /* Notify bridge address change */
218 MSTP_IN_set_bridge_address(br, br->sysdeps.macaddr);
219 }
220
221 if(changed)
222 MSTP_IN_set_bridge_enable(br, br->sysdeps.up && br->sysdeps.stp_up);
223 }
224
225 static void set_if_up(port_t * ifc, bool up)
226 {
227 INFO("Port %s : %s", ifc->sysdeps.name, (up ? "up" : "down"));
228 int speed = -1;
229 int duplex = -1;
230 bool changed = false;
231
232 if(check_mac_address(ifc->sysdeps.name, ifc->sysdeps.macaddr))
233 {
234 /* MAC address changed */
235 if(check_mac_address(ifc->bridge->sysdeps.name,
236 ifc->bridge->sysdeps.macaddr))
237 {
238 /* Notify bridge address change */
239 MSTP_IN_set_bridge_address(ifc->bridge,
240 ifc->bridge->sysdeps.macaddr);
241 }
242 }
243
244 if(!up)
245 { /* Down */
246 if(ifc->sysdeps.up)
247 {
248 ifc->sysdeps.up = false;
249 changed = true;
250 }
251 }
252 else
253 { /* Up */
254 int r = ethtool_get_speed_duplex(ifc->sysdeps.name, &speed, &duplex);
255 if((r < 0) || (speed < 0))
256 speed = 10;
257 if((r < 0) || (duplex < 0))
258 duplex = 0; /* Assume half duplex */
259
260 if(speed != ifc->sysdeps.speed)
261 {
262 ifc->sysdeps.speed = speed;
263 changed = true;
264 }
265 if(duplex != ifc->sysdeps.duplex)
266 {
267 ifc->sysdeps.duplex = duplex;
268 changed = true;
269 }
270 if(!ifc->sysdeps.up)
271 {
272 ifc->sysdeps.up = true;
273 changed = true;
274 }
275 }
276 if(changed)
277 MSTP_IN_set_port_enable(ifc, ifc->sysdeps.up, ifc->sysdeps.speed,
278 ifc->sysdeps.duplex);
279 }
280
281 /* br_index == if_index means: interface is bridge master */
282 int bridge_notify(int br_index, int if_index, bool newlink, bool up)
283 {
284 port_t *ifc;
285 bridge_t *br = NULL, *other_br;
286
287 LOG("br_index %d, if_index %d, newlink %d, up %d",
288 br_index, if_index, newlink, up);
289
290 if((br_index >= 0) && (br_index != if_index))
291 {
292 if(!(br = find_br(br_index)))
293 br = create_br(br_index);
294 if(!br)
295 {
296 ERROR("Couldn't create data for bridge interface %d", br_index);
297 return -1;
298 }
299 int br_up = ethtool_get_link(br->sysdeps.name);
300 if(br_up >= 0)
301 set_br_up(br, !!br_up);
302 }
303
304 if(br)
305 {
306 if(!(ifc = find_if(br, if_index)))
307 {
308 if(!newlink)
309 {
310 INFO("Got DELLINK for unknown port %d on "
311 "bridge %d", if_index, br_index);
312 return -1;
313 }
314 /* Check if this interface is slave of another bridge */
315 list_for_each_entry(other_br, &bridges, list)
316 {
317 if(other_br != br)
318 if(delete_if_byindex(other_br, if_index))
319 {
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);
323 break;
324 }
325 }
326 ifc = create_if(br, if_index);
327 }
328 if(!ifc)
329 {
330 ERROR("Couldn't create data for interface %d (master %d)",
331 if_index, br_index);
332 return -1;
333 }
334 if(!newlink)
335 {
336 delete_if(ifc);
337 return 0;
338 }
339 set_if_up(ifc, up); /* And speed and duplex */
340 }
341 else
342 { /* Interface is not a bridge slave */
343 if(!newlink)
344 {
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)
349 {
350 if(delete_if_byindex(br, if_index))
351 break;
352 }
353 return 0;
354 }
355 else
356 { /* This may be a new link */
357 if(br_index == if_index)
358 {
359 if(!(br = find_br(br_index)))
360 {
361 if(!(br = create_br(br_index)))
362 {
363 ERROR("Couldn't create data for bridge interface %d",
364 br_index);
365 return -1;
366 }
367 }
368 set_br_up(br, up);
369 }
370 }
371 }
372 return 0;
373 }
374
375 struct llc_header
376 {
377 __u8 dest_addr[ETH_ALEN];
378 __u8 src_addr[ETH_ALEN];
379 __be16 len8023;
380 __u8 d_sap;
381 __u8 s_sap;
382 __u8 llc_ctrl;
383 } __attribute__((packed));
384
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 */
388
389 /* 7.12.3 of 802.1D */
390 #define LLC_SAP_BSPAN 0x42
391 static const __u8 bridge_group_address[ETH_ALEN] =
392 {
393 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
394 };
395
396 void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
397 {
398 port_t *ifc = NULL;
399 bridge_t *br;
400
401 LOG("ifindex %d, len %d", if_index, len);
402
403 list_for_each_entry(br, &bridges, list)
404 {
405 if((ifc = find_if(br, if_index)))
406 break;
407 }
408 if(!ifc)
409 return;
410
411 /* sanity checks */
412 TST(br == ifc->bridge,);
413 TST(ifc->sysdeps.up,);
414 if(!br->sysdeps.stp_up)
415 return;
416
417 /* Validate Ethernet and LLC header,
418 * maybe we can skip this check thanks to Berkeley filter in packet socket?
419 */
420 struct llc_header *h;
421 unsigned int l;
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",
426 if_index, len,
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])
429 );
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,);
433
434 MSTP_IN_rx_bpdu(ifc,
435 /* Don't include LLC header */
436 (bpdu_t *)(data + sizeof(*h)), l - LLC_PDU_LEN_U);
437 }
438
439 static int br_set_state(struct rtnl_handle *rth, unsigned ifindex, __u8 state)
440 {
441 struct
442 {
443 struct nlmsghdr n;
444 struct ifinfomsg ifi;
445 char buf[256];
446 } req;
447
448 memset(&req, 0, sizeof(req));
449
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;
455
456 addattr8(&req.n, sizeof(req.buf), IFLA_PROTINFO, state);
457
458 return rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL);
459 }
460
461 static int br_flush_port(char *ifname)
462 {
463 char fname[128];
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);
468 close(fd);
469 TST(1 == write_result, -1);
470 return 0;
471 }
472
473 static int br_set_ageing_time(char *brname, unsigned int ageing_time)
474 {
475 char fname[128], str_time[32];
476 snprintf(fname, sizeof(fname), "/sys/class/net/%s/bridge/ageing_time",
477 brname);
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);
482 close(fd);
483 TST(len == write_result, -1);
484 return 0;
485 }
486
487 /* External actions for MSTP protocol */
488
489 void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state)
490 {
491 char * state_name;
492 port_t *ifc = ptp->port;
493 bridge_t *br = ifc->bridge;
494
495 if(ptp->state == new_state)
496 return;
497 ptp->state = driver_set_new_state(ptp, new_state);
498
499 switch(ptp->state)
500 {
501 case BR_STATE_LISTENING:
502 state_name = "listening";
503 break;
504 case BR_STATE_LEARNING:
505 state_name = "learning";
506 break;
507 case BR_STATE_FORWARDING:
508 state_name = "forwarding";
509 break;
510 case BR_STATE_BLOCKING:
511 state_name = "blocking";
512 break;
513 default:
514 case BR_STATE_DISABLED:
515 state_name = "disabled";
516 break;
517 }
518 INFO_MSTINAME(br, ifc, ptp, "entering %s state", state_name);
519
520 /* Translate new CIST state to the kernel bridge code */
521 if(0 == ptp->MSTID)
522 { /* CIST */
523 if(ifc->sysdeps.up)
524 {
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",
527 state_name);
528 }
529 }
530 }
531
532 /* This function initiates process of flushing
533 * all entries for the given port in all FIDs for the
534 * given tree.
535 * When this process finishes, implementation should signal
536 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
537 */
538 void MSTP_OUT_flush_all_fids(per_tree_port_t * ptp)
539 {
540 port_t *ifc = ptp->port;
541 bridge_t *br = ifc->bridge;
542
543 /* Translate CIST flushing to the kernel bridge code */
544 if(0 == ptp->MSTID)
545 { /* CIST */
546 if(0 > br_flush_port(ifc->sysdeps.name))
547 ERROR_PRTNAME(br, ifc,
548 "Couldn't flush kernel bridge forwarding database");
549 }
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);
553 }
554
555 /* 802.1Q-2005 wants per-port ageing time.
556 * We do not support it, so set ageing time for the whole bridge.
557 */
558 void MSTP_OUT_set_ageing_time(bridge_t * br, unsigned int ageingTime)
559 {
560 unsigned int actual_ageing_time;
561
562 actual_ageing_time = driver_set_ageing_time(br, ageingTime);
563 INFO_BRNAME(br, "Setting new ageing time to %u", actual_ageing_time);
564
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");
568 }
569
570 void MSTP_OUT_tx_bpdu(port_t * ifc, bpdu_t * bpdu, int size)
571 {
572 char *bpdu_type;
573 bridge_t *br = ifc->bridge;
574
575 switch(bpdu->protocolVersion)
576 {
577 case protoSTP:
578 switch(bpdu->bpduType)
579 {
580 case bpduTypeConfig:
581 bpdu_type = "STP-Config";
582 break;
583 case bpduTypeTCN:
584 bpdu_type = "STP-TCN";
585 break;
586 default:
587 bpdu_type = "STP-UnknownType";
588 }
589 break;
590 case protoRSTP:
591 bpdu_type = "RST";
592 break;
593 case protoMSTP:
594 bpdu_type = "MST";
595 break;
596 default:
597 bpdu_type = "UnknownProto";
598 }
599
600 LOG_PRTNAME(br, ifc, "sending %s BPDU", bpdu_type);
601
602 struct llc_header h;
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;
608
609 struct iovec iov[2] =
610 {
611 { .iov_base = &h, .iov_len = sizeof(h) },
612 { .iov_base = bpdu, .iov_len = size }
613 };
614
615 packet_send(ifc->sysdeps.if_index, iov, 2, sizeof(h) + size);
616 }
617
618 /* User interface commands */
619
620 #define CTL_CHECK_BRIDGE \
621 bridge_t *br = find_br(br_index); \
622 if(NULL == br) \
623 { \
624 ERROR("Couldn't find bridge with index %d", br_index); \
625 return -1; \
626 }
627
628 #define CTL_CHECK_BRIDGE_PORT \
629 CTL_CHECK_BRIDGE; \
630 port_t *prt = find_if(br, port_index); \
631 if(NULL == prt) \
632 { \
633 ERROR_BRNAME(br, "Couldn't find port with index %d", port_index); \
634 return -1; \
635 }
636
637 #define CTL_CHECK_BRIDGE_TREE \
638 CTL_CHECK_BRIDGE; \
639 tree_t *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) \
644 { \
645 found = true; \
646 break; \
647 } \
648 if(!found) \
649 { \
650 ERROR_BRNAME(br, "Couldn't find MSTI with ID %hu", mstid); \
651 return -1; \
652 }
653
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) \
661 { \
662 found = true; \
663 break; \
664 } \
665 if(!found) \
666 { \
667 ERROR_PRTNAME(br, prt, "Couldn't find MSTI with ID %hu", mstid); \
668 return -1; \
669 }
670
671 int CTL_get_cist_bridge_status(int br_index, CIST_BridgeStatus *status,
672 char *root_port_name)
673 {
674 tree_t *cist;
675 per_tree_port_t *ptp;
676
677 CTL_CHECK_BRIDGE;
678 MSTP_IN_get_cist_bridge_status(br, status);
679
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)
685 {
686 strncpy(root_port_name, ptp->port->sysdeps.name, IFNAMSIZ);
687 break;
688 }
689 return 0;
690 }
691
692 int CTL_get_msti_bridge_status(int br_index, __u16 mstid,
693 MSTI_BridgeStatus *status, char *root_port_name)
694 {
695 per_tree_port_t *ptp;
696
697 CTL_CHECK_BRIDGE_TREE;
698 MSTP_IN_get_msti_bridge_status(tree, status);
699
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)
704 {
705 strncpy(root_port_name, ptp->port->sysdeps.name, IFNAMSIZ);
706 break;
707 }
708 return 0;
709 }
710
711 int CTL_set_cist_bridge_config(int br_index, CIST_BridgeConfig *cfg)
712 {
713 CTL_CHECK_BRIDGE;
714 return MSTP_IN_set_cist_bridge_config(br, cfg);
715 }
716
717 int CTL_set_msti_bridge_config(int br_index, __u16 mstid, __u8 bridge_priority)
718 {
719 CTL_CHECK_BRIDGE_TREE;
720 return MSTP_IN_set_msti_bridge_config(tree, bridge_priority);
721 }
722
723 int CTL_get_cist_port_status(int br_index, int port_index,
724 CIST_PortStatus *status)
725 {
726 CTL_CHECK_BRIDGE_PORT;
727 MSTP_IN_get_cist_port_status(prt, status);
728 return 0;
729 }
730
731 int CTL_get_msti_port_status(int br_index, int port_index, __u16 mstid,
732 MSTI_PortStatus *status)
733 {
734 CTL_CHECK_BRIDGE_PERTREEPORT;
735 MSTP_IN_get_msti_port_status(ptp, status);
736 return 0;
737 }
738
739 int CTL_set_cist_port_config(int br_index, int port_index,
740 CIST_PortConfig *cfg)
741 {
742 CTL_CHECK_BRIDGE_PORT;
743 return MSTP_IN_set_cist_port_config(prt, cfg);
744 }
745
746 int CTL_set_msti_port_config(int br_index, int port_index, __u16 mstid,
747 MSTI_PortConfig *cfg)
748 {
749 CTL_CHECK_BRIDGE_PERTREEPORT;
750 return MSTP_IN_set_msti_port_config(ptp, cfg);
751 }
752
753 int CTL_port_mcheck(int br_index, int port_index)
754 {
755 CTL_CHECK_BRIDGE_PORT;
756 return MSTP_IN_port_mcheck(prt);
757 }
758
759 int CTL_set_debug_level(int level)
760 {
761 INFO("level %d", level);
762 log_level = level;
763 return 0;
764 }
765
766 int CTL_get_mstilist(int br_index, int *num_mstis, __u16 *mstids)
767 {
768 CTL_CHECK_BRIDGE;
769 return MSTP_IN_get_mstilist(br, num_mstis, mstids) ? 0 : -1;
770 }
771
772 int CTL_create_msti(int br_index, __u16 mstid)
773 {
774 CTL_CHECK_BRIDGE;
775 if((!driver_create_msti(br, mstid)) || (!MSTP_IN_create_msti(br, mstid)))
776 return -1;
777 return 0;
778 }
779
780 int CTL_delete_msti(int br_index, __u16 mstid)
781 {
782 CTL_CHECK_BRIDGE;
783 if((!driver_delete_msti(br, mstid)) || (!MSTP_IN_delete_msti(br, mstid)))
784 return -1;
785 return 0;
786 }
787
788 int CTL_get_mstconfid(int br_index, mst_configuration_identifier_t *cfg)
789 {
790 CTL_CHECK_BRIDGE;
791 *cfg = br->MstConfigId;
792 return 0;
793 }
794
795 int CTL_set_mstconfid(int br_index, __u16 revision, char *name)
796 {
797 CTL_CHECK_BRIDGE;
798 MSTP_IN_set_mst_config_id(br, revision, name);
799 return 0;
800 }
801
802 int CTL_get_vids2fids(int br_index, __u16 *vids2fids)
803 {
804 CTL_CHECK_BRIDGE;
805 memcpy(vids2fids, br->vid2fid, sizeof(br->vid2fid));
806 return 0;
807 }
808
809 int CTL_get_fids2mstids(int br_index, __u16 *fids2mstids)
810 {
811 CTL_CHECK_BRIDGE;
812 int i;
813 for(i = 0; i < COUNT_OF(br->fid2mstid); ++i)
814 fids2mstids[i] = __be16_to_cpu(br->fid2mstid[i]);
815 return 0;
816 }
817
818 int CTL_set_vid2fid(int br_index, __u16 vid, __u16 fid)
819 {
820 CTL_CHECK_BRIDGE;
821 return MSTP_IN_set_vid2fid(br, vid, fid) ? 0 : -1;
822 }
823
824 int CTL_set_fid2mstid(int br_index, __u16 fid, __u16 mstid)
825 {
826 CTL_CHECK_BRIDGE;
827 return MSTP_IN_set_fid2mstid(br, fid, mstid) ? 0 : -1;
828 }
829
830 int CTL_set_vids2fids(int br_index, __u16 *vids2fids)
831 {
832 CTL_CHECK_BRIDGE;
833 return MSTP_IN_set_all_vids2fids(br, vids2fids) ? 0 : -1;
834 }
835
836 int CTL_set_fids2mstids(int br_index, __u16 *fids2mstids)
837 {
838 CTL_CHECK_BRIDGE;
839 return MSTP_IN_set_all_fids2mstids(br, fids2mstids) ? 0 : -1;
840 }