]>
Commit | Line | Data |
---|---|---|
1e6d2d09 VD |
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> | |
27ef19da VD |
28 | #include <unistd.h> |
29 | #include <fcntl.h> | |
c109fcb6 | 30 | #include <linux/param.h> |
1e6d2d09 VD |
31 | #include <linux/if_bridge.h> |
32 | #include <asm/byteorder.h> | |
33 | ||
34 | #include "bridge_ctl.h" | |
c6ed0c4c | 35 | #include "ctl_functions.h" |
1e6d2d09 VD |
36 | #include "netif_utils.h" |
37 | #include "packet.h" | |
38 | #include "log.h" | |
39 | #include "mstp.h" | |
fef685fc VD |
40 | #include "driver.h" |
41 | #include "libnetlink.h" | |
1e6d2d09 VD |
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 | ||
fef685fc VD |
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 | ||
27ef19da VD |
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 | ||
c109fcb6 VD |
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 | ||
1e6d2d09 VD |
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 | ||
fef685fc VD |
495 | if(ptp->state == new_state) |
496 | return; | |
497 | ptp->state = driver_set_new_state(ptp, new_state); | |
498 | ||
499 | switch(ptp->state) | |
1e6d2d09 VD |
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: | |
1e6d2d09 VD |
514 | case BR_STATE_DISABLED: |
515 | state_name = "disabled"; | |
516 | break; | |
517 | } | |
1e6d2d09 | 518 | INFO_MSTINAME(br, ifc, ptp, "entering %s state", state_name); |
fef685fc VD |
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 | } | |
1e6d2d09 VD |
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 | { | |
27ef19da VD |
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); | |
1e6d2d09 VD |
553 | } |
554 | ||
c109fcb6 VD |
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) | |
1e6d2d09 | 559 | { |
c109fcb6 VD |
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"); | |
1e6d2d09 VD |
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 | return MSTP_IN_create_msti(br, mstid) ? 0 : -1; | |
776 | } | |
777 | ||
778 | int CTL_delete_msti(int br_index, __u16 mstid) | |
779 | { | |
780 | CTL_CHECK_BRIDGE; | |
781 | return MSTP_IN_delete_msti(br, mstid) ? 0 : -1; | |
782 | } | |
783 | ||
784 | int CTL_get_mstconfid(int br_index, mst_configuration_identifier_t *cfg) | |
785 | { | |
786 | CTL_CHECK_BRIDGE; | |
787 | *cfg = br->MstConfigId; | |
788 | return 0; | |
789 | } | |
790 | ||
791 | int CTL_set_mstconfid(int br_index, __u16 revision, char *name) | |
792 | { | |
793 | CTL_CHECK_BRIDGE; | |
794 | MSTP_IN_set_mst_config_id(br, revision, name); | |
795 | return 0; | |
796 | } | |
797 | ||
798 | int CTL_get_vids2fids(int br_index, __u16 *vids2fids) | |
799 | { | |
800 | CTL_CHECK_BRIDGE; | |
801 | memcpy(vids2fids, br->vid2fid, sizeof(br->vid2fid)); | |
802 | return 0; | |
803 | } | |
804 | ||
805 | int CTL_get_fids2mstids(int br_index, __u16 *fids2mstids) | |
806 | { | |
807 | CTL_CHECK_BRIDGE; | |
808 | int i; | |
809 | for(i = 0; i < COUNT_OF(br->fid2mstid); ++i) | |
810 | fids2mstids[i] = __be16_to_cpu(br->fid2mstid[i]); | |
811 | return 0; | |
812 | } | |
813 | ||
814 | int CTL_set_vid2fid(int br_index, __u16 vid, __u16 fid) | |
815 | { | |
816 | CTL_CHECK_BRIDGE; | |
817 | return MSTP_IN_set_vid2fid(br, vid, fid) ? 0 : -1; | |
818 | } | |
819 | ||
820 | int CTL_set_fid2mstid(int br_index, __u16 fid, __u16 mstid) | |
821 | { | |
822 | CTL_CHECK_BRIDGE; | |
823 | return MSTP_IN_set_fid2mstid(br, fid, mstid) ? 0 : -1; | |
824 | } | |
825 | ||
826 | int CTL_set_vids2fids(int br_index, __u16 *vids2fids) | |
827 | { | |
828 | CTL_CHECK_BRIDGE; | |
829 | return MSTP_IN_set_all_vids2fids(br, vids2fids) ? 0 : -1; | |
830 | } | |
831 | ||
832 | int CTL_set_fids2mstids(int br_index, __u16 *fids2mstids) | |
833 | { | |
834 | CTL_CHECK_BRIDGE; | |
835 | return MSTP_IN_set_all_fids2mstids(br, fids2mstids) ? 0 : -1; | |
836 | } |