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 } 734
This page was automatically generated by LXR 0.3.1. • OpenWrt