netif_utils: correctly close fd on read error
[project/ustp.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 <dvitasgs@gmail.com>
24
25 ******************************************************************************/
26 #define _GNU_SOURCE
27 #include <string.h>
28 #include <unistd.h>
29 #include <fcntl.h>
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>
36 #include <net/if.h>
37 #include <dirent.h>
38
39 #include "bridge_ctl.h"
40 #include "bridge_track.h"
41 #include "netif_utils.h"
42 #include "packet.h"
43 #include "log.h"
44 #include "mstp.h"
45 #include "driver.h"
46 #include "libnetlink.h"
47
48 #ifndef SYSFS_CLASS_NET
49 #define SYSFS_CLASS_NET "/sys/class/net"
50 #endif
51
52 static LIST_HEAD(bridges);
53
54 static bridge_t * create_br(int if_index)
55 {
56 bridge_t *br;
57 TST((br = calloc(1, sizeof(*br))) != NULL, NULL);
58
59 /* Init system dependent info */
60 br->sysdeps.if_index = if_index;
61 if (!if_indextoname(if_index, br->sysdeps.name))
62 goto err;
63 if (get_hwaddr(br->sysdeps.name, br->sysdeps.macaddr))
64 goto err;
65
66 INFO("Add bridge %s", br->sysdeps.name);
67 if(!MSTP_IN_bridge_create(br, br->sysdeps.macaddr))
68 goto err;
69
70 list_add_tail(&br->list, &bridges);
71 return br;
72 err:
73 free(br);
74 return NULL;
75 }
76
77 static bridge_t * find_br(int if_index)
78 {
79 bridge_t *br;
80 list_for_each_entry(br, &bridges, list)
81 {
82 if(br->sysdeps.if_index == if_index)
83 return br;
84 }
85 return NULL;
86 }
87
88 static port_t * create_if(bridge_t * br, int if_index)
89 {
90 port_t *prt;
91 TST((prt = calloc(1, sizeof(*prt))) != NULL, NULL);
92
93 /* Init system dependent info */
94 prt->sysdeps.if_index = if_index;
95 if (!if_indextoname(if_index, prt->sysdeps.name))
96 goto err;
97 if (get_hwaddr(prt->sysdeps.name, prt->sysdeps.macaddr))
98 goto err;
99
100 int portno;
101 if(0 > (portno = get_bridge_portno(prt->sysdeps.name)))
102 {
103 ERROR("Couldn't get port number for %s", prt->sysdeps.name);
104 goto err;
105 }
106 if((0 == portno) || (portno > MAX_PORT_NUMBER))
107 {
108 ERROR("Port number for %s is invalid (%d)", prt->sysdeps.name, portno);
109 goto err;
110 }
111
112 INFO("Add iface %s as port#%d to bridge %s", prt->sysdeps.name,
113 portno, br->sysdeps.name);
114 prt->bridge = br;
115 if(!MSTP_IN_port_create_and_add_tail(prt, portno))
116 goto err;
117
118 return prt;
119 err:
120 free(prt);
121 return NULL;
122 }
123
124 static port_t * find_if(bridge_t * br, int if_index)
125 {
126 port_t *prt;
127 list_for_each_entry(prt, &br->ports, br_list)
128 {
129 if(prt->sysdeps.if_index == if_index)
130 return prt;
131 }
132 return NULL;
133 }
134
135 static inline void delete_if(port_t *prt)
136 {
137 MSTP_IN_delete_port(prt);
138 free(prt);
139 }
140
141 static inline bool delete_if_byindex(bridge_t * br, int if_index)
142 {
143 port_t *prt;
144 if(!(prt = find_if(br, if_index)))
145 return false;
146 delete_if(prt);
147 return true;
148 }
149
150 static bool delete_br_byindex(int if_index)
151 {
152 bridge_t *br;
153 if(!(br = find_br(if_index)))
154 return false;
155
156 INFO("Delete bridge %s (%d)", br->sysdeps.name, if_index);
157
158 list_del(&br->list);
159 MSTP_IN_delete_bridge(br);
160 free(br);
161 return true;
162 }
163
164 void bridge_one_second(void)
165 {
166 bridge_t *br;
167 list_for_each_entry(br, &bridges, list)
168 MSTP_IN_one_second(br);
169 }
170
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)
174 {
175 __u8 temp_addr[ETH_ALEN];
176 if(get_hwaddr(name, temp_addr))
177 {
178 LOG("Error getting hw address: %s", name);
179 /* Error. Ignore the new value */
180 return false;
181 }
182 if(memcmp(addr, temp_addr, sizeof(temp_addr)) == 0)
183 return false;
184 else
185 {
186 memcpy(addr, temp_addr, sizeof(temp_addr));
187 return true;
188 }
189 }
190
191 static void set_br_up(bridge_t * br, bool up)
192 {
193 bool changed = false;
194
195 if(up != br->sysdeps.up)
196 {
197 INFO("%s was %s. Set %s", br->sysdeps.name,
198 br->sysdeps.up ? "up" : "down", up ? "up" : "down");
199 br->sysdeps.up = up;
200 changed = true;
201 }
202
203 if(check_mac_address(br->sysdeps.name, br->sysdeps.macaddr))
204 {
205 /* MAC address changed */
206 /* Notify bridge address change */
207 MSTP_IN_set_bridge_address(br, br->sysdeps.macaddr);
208 }
209
210 if(changed)
211 MSTP_IN_set_bridge_enable(br, br->sysdeps.up);
212 }
213
214 static void set_if_up(port_t *prt, bool up)
215 {
216 INFO("Port %s : %s", prt->sysdeps.name, (up ? "up" : "down"));
217 int speed = -1;
218 int duplex = -1;
219 bool changed = false;
220 bool bpdu_filter;
221
222 if(check_mac_address(prt->sysdeps.name, prt->sysdeps.macaddr))
223 {
224 /* MAC address changed */
225 if(check_mac_address(prt->bridge->sysdeps.name,
226 prt->bridge->sysdeps.macaddr))
227 {
228 /* Notify bridge address change */
229 MSTP_IN_set_bridge_address(prt->bridge,
230 prt->bridge->sysdeps.macaddr);
231 }
232 }
233
234 if(!up)
235 { /* Down */
236 if(prt->sysdeps.up)
237 {
238 prt->sysdeps.up = false;
239 changed = true;
240 }
241 }
242 else
243 { /* Up */
244 int r = ethtool_get_speed_duplex(prt->sysdeps.name, &speed, &duplex);
245 if((r < 0) || (speed < 0))
246 speed = 10;
247 if((r < 0) || (duplex < 0))
248 duplex = 1; /* Assume full duplex */
249
250 if(speed != prt->sysdeps.speed)
251 {
252 prt->sysdeps.speed = speed;
253 changed = true;
254 }
255 if(duplex != prt->sysdeps.duplex)
256 {
257 prt->sysdeps.duplex = duplex;
258 changed = true;
259 }
260 if(!prt->sysdeps.up)
261 {
262 prt->sysdeps.up = true;
263 changed = true;
264 }
265
266 bpdu_filter = get_bpdu_filter(prt->sysdeps.name);
267 if (bpdu_filter != prt->bpduFilterPort) {
268 CIST_PortConfig cfg = {
269 .bpdu_filter_port = bpdu_filter,
270 .set_bpdu_filter_port = true
271 };
272
273 MSTP_IN_set_cist_port_config(prt, &cfg);
274 }
275 }
276 if(changed)
277 MSTP_IN_set_port_enable(prt, prt->sysdeps.up, prt->sysdeps.speed,
278 prt->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, unsigned flags)
283 {
284 port_t *prt;
285 bridge_t *br = NULL, *other_br;
286 bool up = !!(flags & IFF_UP);
287 bool running = up && (flags & IFF_RUNNING);
288
289 LOG("br_index %d, if_index %d, newlink %d, up %d, running %d",
290 br_index, if_index, newlink, up, running);
291
292 if((br_index >= 0) && (br_index != if_index))
293 {
294 if(!(br = find_br(br_index)))
295 return -2; /* bridge not in list */
296 int br_flags = get_flags(br->sysdeps.name);
297 if(br_flags >= 0)
298 set_br_up(br, !!(br_flags & IFF_UP));
299 }
300
301 if(br)
302 {
303 if(!(prt = find_if(br, if_index)))
304 {
305 if(!newlink)
306 {
307 INFO("Got DELLINK for unknown port %d on "
308 "bridge %d", if_index, br_index);
309 return -1;
310 }
311 /* Check if this interface is slave of another bridge */
312 list_for_each_entry(other_br, &bridges, list)
313 {
314 if(other_br != br)
315 if(delete_if_byindex(other_br, if_index))
316 {
317 INFO("Device %d has come to bridge %d. "
318 "Missed notify for deletion from bridge %d",
319 if_index, br_index, other_br->sysdeps.if_index);
320 break;
321 }
322 }
323 prt = create_if(br, if_index);
324 }
325 if(!prt)
326 {
327 ERROR("Couldn't create data for interface %d (master %d)",
328 if_index, br_index);
329 return -1;
330 }
331 if(!newlink)
332 {
333 delete_if(prt);
334 return 0;
335 }
336 set_if_up(prt, running); /* And speed and duplex */
337 }
338 else
339 { /* Interface is not a bridge slave */
340 if(!newlink)
341 {
342 /* DELLINK not from bridge means interface unregistered. */
343 /* Cleanup removed bridge or removed bridge slave */
344 if(!delete_br_byindex(if_index))
345 list_for_each_entry(br, &bridges, list)
346 {
347 if(delete_if_byindex(br, if_index))
348 break;
349 }
350 return 0;
351 }
352 else
353 { /* This may be a new link */
354 if(br_index == if_index)
355 {
356 if(!(br = find_br(br_index)))
357 return -2; /* bridge not in list */
358 set_br_up(br, up);
359 }
360 }
361 }
362 return 0;
363 }
364
365 struct llc_header
366 {
367 __u8 dest_addr[ETH_ALEN];
368 __u8 src_addr[ETH_ALEN];
369 __be16 len8023;
370 __u8 d_sap;
371 __u8 s_sap;
372 __u8 llc_ctrl;
373 } __attribute__((packed));
374
375 /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
376 #define LLC_PDU_LEN_U 3 /* header and 1 control byte */
377 #define LLC_PDU_TYPE_U 3 /* first two bits */
378
379 /* 7.12.3 of 802.1D */
380 #define LLC_SAP_BSPAN 0x42
381 static const __u8 bridge_group_address[ETH_ALEN] =
382 {
383 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
384 };
385
386 void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
387 {
388 port_t *prt = NULL;
389 bridge_t *br;
390
391 LOG("ifindex %d, len %d", if_index, len);
392
393 list_for_each_entry(br, &bridges, list)
394 {
395 if((prt = find_if(br, if_index)))
396 break;
397 }
398 if(!prt)
399 return;
400
401 /* sanity checks */
402 TSTM(br == prt->bridge,, "Bridge mismatch. This bridge is '%s' but port "
403 "'%s' belongs to bridge '%s'", br->sysdeps.name, prt->bridge->sysdeps.name);
404 TSTM(prt->sysdeps.up,, "Port '%s' should be up", prt->sysdeps.name);
405
406 /* Validate Ethernet and LLC header,
407 * maybe we can skip this check thanks to Berkeley filter in packet socket?
408 */
409 struct llc_header *h;
410 unsigned int l;
411 TST(len > sizeof(struct llc_header),);
412 h = (struct llc_header *)data;
413 TST(0 == memcmp(h->dest_addr, bridge_group_address, ETH_ALEN),
414 INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
415 if_index, len,
416 h->dest_addr[0], h->dest_addr[1], h->dest_addr[2],
417 h->dest_addr[3], h->dest_addr[4], h->dest_addr[5])
418 );
419 l = __be16_to_cpu(h->len8023);
420 TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= LLC_PDU_LEN_U, );
421 TST(h->d_sap == LLC_SAP_BSPAN && h->s_sap == LLC_SAP_BSPAN && (h->llc_ctrl & 0x3) == LLC_PDU_TYPE_U,);
422
423 MSTP_IN_rx_bpdu(prt,
424 /* Don't include LLC header */
425 (bpdu_t *)(data + sizeof(*h)), l - LLC_PDU_LEN_U);
426 }
427
428 static int br_set_state(struct rtnl_handle *rth, unsigned ifindex, __u8 state)
429 {
430 struct
431 {
432 struct nlmsghdr n;
433 struct ifinfomsg ifi;
434 char buf[256];
435 } req;
436
437 memset(&req, 0, sizeof(req));
438
439 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
440 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE;
441 req.n.nlmsg_type = RTM_SETLINK;
442 req.ifi.ifi_family = AF_BRIDGE;
443 req.ifi.ifi_index = ifindex;
444
445 addattr8(&req.n, sizeof(req.buf), IFLA_PROTINFO, state);
446
447 return rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL);
448 }
449
450 static int br_flush_port(char *ifname)
451 {
452 char fname[128];
453 snprintf(fname, sizeof(fname), SYSFS_CLASS_NET "/%s/brport/flush", ifname);
454 int fd = open(fname, O_WRONLY);
455 TSTM(0 <= fd, -1, "Couldn't open flush file %s for write: %m", fname);
456 int write_result = write(fd, "1", 1);
457 close(fd);
458 TST(1 == write_result, -1);
459 return 0;
460 }
461
462 static int br_set_ageing_time(char *brname, unsigned int ageing_time)
463 {
464 char fname[128], str_time[32];
465 snprintf(fname, sizeof(fname), SYSFS_CLASS_NET "/%s/bridge/ageing_time",
466 brname);
467 int fd = open(fname, O_WRONLY);
468 TSTM(0 <= fd, -1, "Couldn't open file %s for write: %m", fname);
469 int len = sprintf(str_time, "%u", ageing_time * HZ);
470 int write_result = write(fd, str_time, len);
471 close(fd);
472 TST(len == write_result, -1);
473 return 0;
474 }
475
476 /* External actions for MSTP protocol */
477
478 void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state)
479 {
480 char * state_name;
481 port_t *prt = ptp->port;
482 bridge_t *br = prt->bridge;
483
484 if(ptp->state == new_state)
485 return;
486 ptp->state = driver_set_new_state(ptp, new_state);
487
488 switch(ptp->state)
489 {
490 case BR_STATE_LISTENING:
491 state_name = "listening";
492 break;
493 case BR_STATE_LEARNING:
494 state_name = "learning";
495 break;
496 case BR_STATE_FORWARDING:
497 state_name = "forwarding";
498 ++(prt->num_trans_fwd);
499 break;
500 case BR_STATE_BLOCKING:
501 state_name = "blocking";
502 ++(prt->num_trans_blk);
503 break;
504 default:
505 case BR_STATE_DISABLED:
506 state_name = "disabled";
507 break;
508 }
509 INFO_MSTINAME(br, prt, ptp, "entering %s state", state_name);
510
511 /* Translate new CIST state to the kernel bridge code */
512 if(0 == ptp->MSTID)
513 { /* CIST */
514 if(0 > br_set_state(&rth_state, prt->sysdeps.if_index, ptp->state))
515 INFO_PRTNAME(br, prt, "Couldn't set kernel bridge state %s",
516 state_name);
517 }
518 }
519
520 /* This function initiates process of flushing
521 * all entries for the given port in all FIDs for the
522 * given tree.
523 * When this process finishes, implementation should signal
524 * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
525 */
526 void MSTP_OUT_flush_all_fids(per_tree_port_t * ptp)
527 {
528 port_t *prt = ptp->port;
529 bridge_t *br = prt->bridge;
530
531 /* Translate CIST flushing to the kernel bridge code */
532 if(0 == ptp->MSTID)
533 { /* CIST */
534 if(0 > br_flush_port(prt->sysdeps.name))
535 ERROR_PRTNAME(br, prt,
536 "Couldn't flush kernel bridge forwarding database");
537 }
538 /* Completion signal MSTP_IN_all_fids_flushed will be called by driver */
539 INFO_MSTINAME(br, prt, ptp, "Flushing forwarding database");
540 driver_flush_all_fids(ptp);
541 }
542
543 void MSTP_OUT_set_ageing_time(port_t *prt, unsigned int ageingTime)
544 {
545 unsigned int actual_ageing_time;
546 bridge_t *br = prt->bridge;
547
548 actual_ageing_time = driver_set_ageing_time(prt, ageingTime);
549 INFO_PRTNAME(br, prt, "Setting new ageing time to %u", actual_ageing_time);
550
551 /*
552 * Translate new ageing time to the kernel bridge code.
553 * Kernel bridging code does not support per-port ageing time,
554 * so set ageing time for the whole bridge.
555 */
556 if(0 > br_set_ageing_time(br->sysdeps.name, actual_ageing_time))
557 ERROR_BRNAME(br, "Couldn't set new ageing time in kernel bridge");
558 }
559
560 void MSTP_OUT_tx_bpdu(port_t *prt, bpdu_t * bpdu, int size)
561 {
562 char *bpdu_type, *tcflag;
563 bridge_t *br = prt->bridge;
564
565 switch(bpdu->protocolVersion)
566 {
567 case protoSTP:
568 switch(bpdu->bpduType)
569 {
570 case bpduTypeConfig:
571 bpdu_type = "STP-Config";
572 break;
573 case bpduTypeTCN:
574 bpdu_type = "STP-TCN";
575 break;
576 default:
577 bpdu_type = "STP-UnknownType";
578 }
579 break;
580 case protoRSTP:
581 bpdu_type = "RST";
582 break;
583 case protoMSTP:
584 bpdu_type = "MST";
585 break;
586 default:
587 bpdu_type = "UnknownProto";
588 }
589
590 ++(prt->num_tx_bpdu);
591 if((protoSTP == bpdu->protocolVersion) && (bpduTypeTCN == bpdu->bpduType))
592 {
593 ++(prt->num_tx_tcn);
594 LOG_PRTNAME(br, prt, "sending %s BPDU", bpdu_type);
595 }
596 else
597 {
598 tcflag = "";
599 if(bpdu->flags & (1 << offsetTc))
600 {
601 ++(prt->num_tx_tcn);
602 tcflag = ", tcFlag";
603 }
604 LOG_PRTNAME(br, prt, "sending %s BPDU%s", bpdu_type, tcflag);
605 }
606
607 struct llc_header h;
608 memcpy(h.dest_addr, bridge_group_address, ETH_ALEN);
609 memcpy(h.src_addr, prt->sysdeps.macaddr, ETH_ALEN);
610 h.len8023 = __cpu_to_be16(size + LLC_PDU_LEN_U);
611 h.d_sap = h.s_sap = LLC_SAP_BSPAN;
612 h.llc_ctrl = LLC_PDU_TYPE_U;
613
614 struct iovec iov[2] =
615 {
616 { .iov_base = &h, .iov_len = sizeof(h) },
617 { .iov_base = bpdu, .iov_len = size }
618 };
619
620 packet_send(prt->sysdeps.if_index, iov, 2, sizeof(h) + size);
621 }
622
623 void MSTP_OUT_shutdown_port(port_t *prt)
624 {
625 if(0 > if_shutdown(prt->sysdeps.name))
626 ERROR_PRTNAME(prt->bridge, prt, "Couldn't shutdown port");
627 }
628
629 static int not_dot_dotdot(const struct dirent *entry)
630 {
631 const char *n = entry->d_name;
632
633 return strcmp(n, ".") || strcmp(n, "..");
634 }
635
636 static int get_port_list(const char *br_ifname, struct dirent ***namelist)
637 {
638 char buf[256];
639
640 snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%.230s/brif", br_ifname);
641
642 return scandir(buf, namelist, not_dot_dotdot, versionsort);
643 }
644
645 int bridge_create(int bridge_idx, CIST_BridgeConfig *cfg)
646 {
647 struct dirent **namelist;
648 int *port_list, n_ports;
649 bridge_t *br, *other_br;
650 port_t *port, *tmp;
651 int flags;
652 bool found;
653 int i;
654
655 br = find_br(bridge_idx);
656 if (!br)
657 br = create_br(bridge_idx);
658 if (!br)
659 return -1;
660
661 MSTP_IN_set_cist_bridge_config(br, cfg);
662
663 flags = get_flags(br->sysdeps.name);
664 if (flags >= 0)
665 set_br_up(br, !!(flags & IFF_UP));
666
667 n_ports = get_port_list(br->sysdeps.name, &namelist);
668 port_list = alloca(n_ports * sizeof(*port_list));
669
670 for (i = 0; i < n_ports; i++) {
671 port_list[i] = if_nametoindex(namelist[i]->d_name);
672 free(namelist[i]);
673 }
674 free(namelist);
675
676 list_for_each_entry_safe(port, tmp, &br->ports, br_list) {
677 found = false;
678 for (i = 0; i < n_ports; i++) {
679 if (port->sysdeps.if_index != port_list[i])
680 continue;
681 found = true;
682 break;
683 }
684
685 if (found)
686 continue;
687
688 delete_if(port);
689 }
690
691 for (i = 0; i < n_ports; i++) {
692 port = find_if(br, port_list[i]);
693 if (port)
694 continue;
695
696 list_for_each_entry(other_br, &bridges, list) {
697 if (br == other_br)
698 continue;
699
700 delete_if_byindex(other_br, port_list[i]);
701 }
702
703 port = find_if(br, port_list[i]);
704 if (!port)
705 port = create_if(br, port_list[i]);
706 if (!port)
707 continue;
708
709 flags = get_flags(port->sysdeps.name);
710 if (flags < 0)
711 continue;
712
713 set_if_up(port, !(~flags & (IFF_UP | IFF_RUNNING)));
714 }
715
716 return 0;
717 }
718
719 void bridge_delete(int index)
720 {
721 delete_br_byindex(index);
722 }
723
724 int bridge_track_fini(void)
725 {
726 INFO("Stopping all bridges");
727 bridge_t *br;
728 list_for_each_entry(br, &bridges, list)
729 {
730 set_br_up(br, false);
731 }
732 return 0;
733 }