1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * bridged - bridging control daemon. This module handles events and general 28 * port-related operations. 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <syslog.h> 38 #include <libdlpi.h> 39 #include <libdladm.h> 40 #include <libdllink.h> 41 #include <libdlbridge.h> 42 #include <libdlvlan.h> 43 #include <libdlstat.h> 44 #include <stp_in.h> 45 #include <stp_vectors.h> 46 #include <net/if_types.h> 47 #include <net/bridge.h> 48 #include <sys/ethernet.h> 49 50 #include "global.h" 51 52 int refresh_count = 1; /* never zero */ 53 dladm_bridge_prot_t protect = DLADM_BRIDGE_PROT_STP; 54 55 /* 56 * The 'allports' array is an array of pointers to the struct portdata 57 * structures. We reallocate 'allports' as needed, but the portdata must 58 * remain where it's initially allocated, because libdlpi's notification 59 * mechanism has a copy of a pointer to this structure. 60 */ 61 uint_t nextport; 62 struct portdata **allports; 63 64 /* Port allocation increment (arbitrary) */ 65 #define ALLOCINCR 10 66 static uint_t numports; 67 68 static datalink_id_t main_linkid; 69 70 int control_fd; 71 72 static void 73 linkdown(void) 74 { 75 (void) dladm_destroy_datalink_id(dlhandle, main_linkid, 76 DLADM_OPT_ACTIVE); 77 } 78 79 void 80 open_bridge_control(void) 81 { 82 bridge_newbridge_t bnb; 83 dladm_status_t status; 84 char buf[DLADM_STRSIZE]; 85 86 if ((control_fd = open(BRIDGE_CTLPATH, O_RDWR | O_NONBLOCK)) == -1) { 87 perror(BRIDGE_CTLPATH); 88 exit(EXIT_FAILURE); 89 } 90 (void) snprintf(bnb.bnb_name, sizeof (bnb.bnb_name), "%s0", 91 instance_name); 92 status = dladm_name2info(dlhandle, bnb.bnb_name, &bnb.bnb_linkid, NULL, 93 NULL, NULL); 94 if (status != DLADM_STATUS_OK) { 95 (void) fprintf(stderr, "bridged: %s: %s\n", bnb.bnb_name, 96 dladm_status2str(status, buf)); 97 exit(EXIT_FAILURE); 98 } 99 if (strioctl(control_fd, BRIOC_NEWBRIDGE, &bnb, sizeof (bnb)) == -1) { 100 perror("NEWBRIDGE"); 101 exit(EXIT_FAILURE); 102 } 103 main_linkid = bnb.bnb_linkid; 104 if (strioctl(control_fd, BRIOC_TABLEMAX, &tablemax, 105 sizeof (tablemax)) == -1) { 106 syslog(LOG_ERR, "cannot set table max %lu on bridge %s: %m", 107 tablemax, instance_name); 108 exit(EXIT_FAILURE); 109 } 110 /* 111 * This covers for any previous incarnation where we might have crashed 112 * or been SIGKILL'd and failed to take down the datalink. 113 */ 114 linkdown(); 115 (void) atexit(linkdown); 116 status = dladm_up_datalink_id(dlhandle, bnb.bnb_linkid); 117 if (status != DLADM_STATUS_OK) { 118 (void) fprintf(stderr, "bridged: %s link up: %s\n", 119 bnb.bnb_name, dladm_status2str(status, buf)); 120 exit(EXIT_FAILURE); 121 } 122 } 123 124 struct portdata * 125 find_by_linkid(datalink_id_t linkid) 126 { 127 int i; 128 struct portdata *port; 129 130 for (i = 0; i < nextport; i++) { 131 port = allports[i]; 132 if (port->linkid == linkid) 133 return (port); 134 } 135 return (NULL); 136 } 137 138 /*ARGSUSED2*/ 139 static int 140 set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg) 141 { 142 struct portdata *port; 143 dladm_status_t status; 144 dladm_vlan_attr_t vinfo; 145 char pointless[DLADM_STRSIZE]; 146 bridge_vlanenab_t bve; 147 148 status = dladm_vlan_info(handle, linkid, &vinfo, DLADM_OPT_ACTIVE); 149 if (status != DLADM_STATUS_OK) { 150 syslog(LOG_DEBUG, "can't get VLAN info on link ID %u: %s", 151 linkid, dladm_status2str(status, pointless)); 152 return (DLADM_WALK_CONTINUE); 153 } 154 155 port = find_by_linkid(vinfo.dv_linkid); 156 if (port == NULL || !port->kern_added) 157 return (DLADM_WALK_CONTINUE); 158 159 bve.bve_linkid = port->linkid; 160 bve.bve_vlan = vinfo.dv_vid; 161 bve.bve_onoff = B_TRUE; 162 if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) { 163 syslog(LOG_ERR, "unable to enable VLAN %d on linkid %u: %m", 164 vinfo.dv_vid, port->linkid); 165 return (DLADM_WALK_TERMINATE); 166 } else { 167 return (DLADM_WALK_CONTINUE); 168 } 169 } 170 171 /* 172 * If the named port already exists, then update its configuration. If it 173 * doesn't, then create and enable it. 174 */ 175 static void 176 update_port(int vlan_id, const char *portname, datalink_id_t linkid, 177 datalink_class_t class) 178 { 179 int posn; 180 struct portdata *port; 181 struct pollfd *fds; 182 int port_index; 183 struct { 184 datalink_id_t linkid; 185 char linkname[MAXLINKNAMELEN]; 186 } adddata; 187 bridge_setpvid_t bsv; 188 uint_t propval, valcnt; 189 dladm_status_t status; 190 191 for (posn = 0; posn < nextport; posn++) { 192 if (allports[posn]->linkid == linkid) 193 break; 194 } 195 196 /* If we need to allocate more array space, then do so in chunks. */ 197 if (posn >= numports) { 198 struct portdata **newarr; 199 200 newarr = realloc(allports, 201 sizeof (*newarr) * (nextport + ALLOCINCR)); 202 if (newarr != NULL) 203 allports = newarr; 204 fds = realloc(fdarray, 205 sizeof (*fds) * (nextport + ALLOCINCR + FDOFFSET)); 206 if (fds != NULL) 207 fdarray = fds; 208 if (newarr == NULL || fds == NULL) { 209 syslog(LOG_ERR, "unable to add %s; no memory", 210 portname); 211 return; 212 } 213 numports = nextport + ALLOCINCR; 214 } 215 216 port_index = posn + 1; 217 fds = fdarray + posn + FDOFFSET; 218 219 /* If our linkid search ran to the end, then this is a new port. */ 220 if (posn == nextport) { 221 if ((port = calloc(1, sizeof (*port))) == NULL) { 222 syslog(LOG_ERR, "unable to add %s; no memory", 223 portname); 224 return; 225 } 226 allports[posn] = port; 227 port->vlan_id = vlan_id; 228 port->linkid = linkid; 229 port->port_index = port_index; 230 port->phys_status = B_TRUE; 231 port->admin_status = B_TRUE; 232 port->state = BLS_BLOCKLISTEN; 233 nextport++; 234 } else { 235 /* Located port by linkid; we're just updating existing data */ 236 port = allports[posn]; 237 238 /* 239 * If it changed name, then close and reopen so we log under 240 * the most current name for this port. 241 */ 242 if (port->name != NULL && strcmp(portname, port->name) != 0) { 243 if (port->dlpi != NULL) 244 dlpi_close(port->dlpi); 245 port->dlpi = NULL; 246 port->name = NULL; 247 fds->fd = -1; 248 fds->events = 0; 249 } 250 } 251 252 /* 253 * If the port is not yet attached to the bridge in the kernel, then do 254 * that now. 255 */ 256 if (!port->kern_added) { 257 adddata.linkid = linkid; 258 (void) strlcpy(adddata.linkname, portname, 259 sizeof (adddata.linkname)); 260 if (strioctl(control_fd, BRIOC_ADDLINK, &adddata, 261 sizeof (adddata.linkid) + strlen(adddata.linkname)) == -1) { 262 syslog(LOG_ERR, "cannot bridge %s: %m", portname); 263 goto failure; 264 } 265 port->kern_added = B_TRUE; 266 } 267 268 port->referenced = B_TRUE; 269 270 valcnt = 1; 271 status = dladm_get_linkprop_values(dlhandle, linkid, 272 DLADM_PROP_VAL_PERSISTENT, "forward", &propval, &valcnt); 273 if (status == DLADM_STATUS_OK) 274 port->admin_status = propval; 275 276 bsv.bsv_vlan = 1; 277 status = dladm_get_linkprop_values(dlhandle, linkid, 278 DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt); 279 if (status == DLADM_STATUS_OK) 280 bsv.bsv_vlan = propval; 281 282 bsv.bsv_linkid = linkid; 283 if (strioctl(control_fd, BRIOC_SETPVID, &bsv, sizeof (bsv)) == -1) { 284 syslog(LOG_ERR, "can't set PVID on %s: %m", portname); 285 goto failure; 286 } 287 288 if (port->dlpi == NULL) { 289 if (!port_dlpi_open(portname, port, class)) 290 goto failure; 291 fds->fd = dlpi_fd(port->dlpi); 292 fds->events = POLLIN; 293 } 294 295 if (rstp_add_port(port)) 296 return; 297 298 failure: 299 if (port->dlpi != NULL) { 300 dlpi_close(port->dlpi); 301 port->dlpi = NULL; 302 port->name = NULL; 303 fds->fd = -1; 304 fds->events = 0; 305 } 306 if (port->kern_added) { 307 if (strioctl(control_fd, BRIOC_REMLINK, &port->linkid, 308 sizeof (port->linkid)) == -1) 309 syslog(LOG_ERR, "cannot remove from bridge %s: %m", 310 portname); 311 else 312 port->kern_added = B_FALSE; 313 } 314 if (posn + 1 == nextport) { 315 free(port); 316 nextport--; 317 } 318 } 319 320 /*ARGSUSED2*/ 321 static int 322 update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg) 323 { 324 dladm_status_t status; 325 char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN]; 326 char pointless[DLADM_STRSIZE]; 327 datalink_class_t class; 328 329 status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)); 330 if (status == DLADM_STATUS_OK && strcmp(bridge, instance_name) == 0) { 331 status = dladm_datalink_id2info(handle, linkid, NULL, &class, 332 NULL, linkname, sizeof (linkname)); 333 if (status == DLADM_STATUS_OK) { 334 update_port(0, linkname, linkid, class); 335 } else { 336 syslog(LOG_ERR, "unable to get link info for ID %u: %s", 337 linkid, dladm_status2str(status, pointless)); 338 } 339 } else if (debugging) { 340 if (status != DLADM_STATUS_OK) 341 syslog(LOG_DEBUG, 342 "unable to get bridge data for ID %u: %s", 343 linkid, dladm_status2str(status, pointless)); 344 else 345 syslog(LOG_DEBUG, "link ID %u is on bridge %s, not %s", 346 linkid, bridge, instance_name); 347 } 348 return (DLADM_WALK_CONTINUE); 349 } 350 351 /* 352 * Refresh action - reread configuration properties. 353 */ 354 static void 355 handle_refresh(int sigfd) 356 { 357 int i; 358 struct portdata *pdp; 359 struct pollfd *fdp; 360 char buf[16]; 361 dladm_status_t status; 362 boolean_t new_debug; 363 uint32_t new_tablemax; 364 365 /* Drain signal events from pipe */ 366 if (sigfd != -1) 367 (void) read(sigfd, buf, sizeof (buf)); 368 369 status = dladm_bridge_get_privprop(instance_name, &new_debug, 370 &new_tablemax); 371 if (status == DLADM_STATUS_OK) { 372 if (debugging && !new_debug) 373 syslog(LOG_DEBUG, "disabling debugging"); 374 debugging = new_debug; 375 if (new_tablemax != tablemax) { 376 syslog(LOG_DEBUG, "changed tablemax from %lu to %lu", 377 tablemax, new_tablemax); 378 if (strioctl(control_fd, BRIOC_TABLEMAX, &new_tablemax, 379 sizeof (tablemax)) == -1) 380 syslog(LOG_ERR, "cannot set table max " 381 "%lu on bridge %s: %m", tablemax, 382 instance_name); 383 else 384 tablemax = new_tablemax; 385 } 386 } else { 387 syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s", 388 instance_name, dladm_status2str(status, buf)); 389 } 390 391 rstp_refresh(); 392 393 for (i = 0; i < nextport; i++) 394 allports[i]->referenced = B_FALSE; 395 396 /* 397 * libdladm doesn't guarantee anything about link ordering in a walk, 398 * so we do this walk twice: once to pick up the ports, and a second 399 * time to get the enabled VLANs on all ports. 400 */ 401 (void) dladm_walk_datalink_id(update_link, dlhandle, NULL, 402 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 403 404 (void) dladm_walk_datalink_id(set_vlan, dlhandle, NULL, 405 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 406 407 /* 408 * If any ports now show up as unreferenced, then they've been removed 409 * from the configuration. 410 */ 411 for (i = 0; i < nextport; i++) { 412 pdp = allports[i]; 413 fdp = fdarray + i + FDOFFSET; 414 if (!pdp->referenced) { 415 if (pdp->stp_added) { 416 (void) STP_IN_port_remove(pdp->vlan_id, 417 pdp->port_index); 418 pdp->stp_added = B_FALSE; 419 } 420 if (pdp->dlpi != NULL) { 421 dlpi_close(pdp->dlpi); 422 pdp->dlpi = NULL; 423 pdp->name = NULL; 424 fdp->fd = -1; 425 fdp->events = 0; 426 } 427 if (pdp->kern_added) { 428 if (strioctl(control_fd, BRIOC_REMLINK, 429 &pdp->linkid, sizeof (pdp->linkid)) == -1) 430 syslog(LOG_ERR, "cannot remove linkid " 431 "%u from bridge %s: %m", 432 pdp->linkid, instance_name); 433 pdp->kern_added = B_FALSE; 434 } 435 } 436 } 437 438 if (++refresh_count == 0) 439 refresh_count = 1; 440 } 441 442 /* 443 * Handle messages on the common control stream. This currently just deals 444 * with port SDU mismatches. 445 */ 446 static void 447 handle_control(void) 448 { 449 bridge_ctl_t bc; 450 ssize_t retv; 451 struct portdata *port; 452 int rc; 453 454 retv = read(control_fd, &bc, sizeof (bc)); 455 if (retv != sizeof (bc)) 456 return; 457 if ((port = find_by_linkid(bc.bc_linkid)) == NULL) 458 return; 459 if (port->sdu_failed == bc.bc_failed) 460 return; 461 port->sdu_failed = bc.bc_failed; 462 if (!port->phys_status || !port->admin_status || 463 protect != DLADM_BRIDGE_PROT_STP) 464 return; 465 if (port->admin_non_stp) { 466 bridge_setstate_t bss; 467 468 bss.bss_linkid = port->linkid; 469 bss.bss_state = !port->sdu_failed && !port->bpdu_protect ? 470 BLS_FORWARDING : BLS_BLOCKLISTEN; 471 if (strioctl(control_fd, BRIOC_SETSTATE, &bss, 472 sizeof (bss)) == -1) { 473 syslog(LOG_ERR, "cannot set STP state on %s: %m", 474 port->name); 475 } 476 } 477 if ((rc = STP_IN_enable_port(port->port_index, !bc.bc_failed)) != 0) 478 syslog(LOG_ERR, "STP can't %s port %s for SDU failure: %s", 479 port->name, bc.bc_failed ? "disable" : "enable", 480 STP_IN_get_error_explanation(rc)); 481 } 482 483 static void 484 receive_packet(struct portdata *port) 485 { 486 int rc; 487 size_t buflen; 488 uint16_t buffer[ETHERMAX / sizeof (uint16_t)]; 489 struct ether_header *eh; 490 char sender[ETHERADDRL * 3]; 491 492 buflen = sizeof (buffer); 493 rc = dlpi_recv(port->dlpi, NULL, NULL, buffer, &buflen, 1, NULL); 494 if (rc != DLPI_SUCCESS) { 495 if (rc != DLPI_ETIMEDOUT) 496 syslog(LOG_ERR, "receive failure on %s: %s", port->name, 497 dlpi_strerror(rc)); 498 return; 499 } 500 501 /* 502 * If we're administratively disabled, then don't deliver packets to 503 * the STP state machine. It will re-enable the port because it uses 504 * the same variable for both link status and administrative state. 505 */ 506 if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP) { 507 if (debugging) 508 syslog(LOG_DEBUG, 509 "discard BPDU on non-forwarding interface %s", 510 port->name); 511 return; 512 } 513 514 /* 515 * There's a mismatch between the librstp and libdlpi expectations on 516 * receive. librstp wants the packet to start with the 802 length 517 * field, not the destination address. 518 */ 519 eh = (struct ether_header *)buffer; 520 rc = STP_IN_check_bpdu_header((BPDU_T *)&eh->ether_type, buflen); 521 522 /* 523 * Note that we attempt to avoid calling the relatively expensive 524 * _link_ntoa function unless we're going to use the result. In normal 525 * usage, we don't need this string. 526 */ 527 if (rc == 0) { 528 if (port->admin_non_stp && !port->bpdu_protect) { 529 bridge_setstate_t bss; 530 531 (void) _link_ntoa(eh->ether_shost.ether_addr_octet, 532 sender, ETHERADDRL, IFT_OTHER); 533 syslog(LOG_WARNING, "unexpected BPDU on %s from %s; " 534 "forwarding disabled", port->name, sender); 535 port->bpdu_protect = B_TRUE; 536 bss.bss_linkid = port->linkid; 537 bss.bss_state = BLS_BLOCKLISTEN; 538 if (strioctl(control_fd, BRIOC_SETSTATE, &bss, 539 sizeof (bss)) == -1) { 540 syslog(LOG_ERR, "cannot set STP state on " 541 "%s: %m", port->name); 542 } 543 return; 544 } 545 if (debugging) { 546 (void) _link_ntoa(eh->ether_shost.ether_addr_octet, 547 sender, ETHERADDRL, IFT_OTHER); 548 syslog(LOG_DEBUG, "got BPDU from %s on %s; %d bytes", 549 sender, port->name, buflen); 550 } 551 rc = STP_IN_rx_bpdu(port->vlan_id, port->port_index, 552 (BPDU_T *)&eh->ether_type, buflen); 553 } 554 if (rc != 0) { 555 (void) _link_ntoa(eh->ether_shost.ether_addr_octet, sender, 556 ETHERADDRL, IFT_OTHER); 557 syslog(LOG_DEBUG, 558 "discarded malformed packet on %s from %s: %s", 559 port->name, sender, STP_IN_get_error_explanation(rc)); 560 } 561 } 562 563 void 564 get_dladm_speed(struct portdata *port) 565 { 566 dladm_status_t status; 567 uint64_t ifspeed; 568 569 status = dladm_get_single_mac_stat(dlhandle, port->linkid, "ifspeed", 570 KSTAT_DATA_UINT64, &ifspeed); 571 if (status == DLADM_STATUS_OK && ifspeed != 0) 572 port->speed = ifspeed / 1000000; 573 else 574 port->speed = 10UL; 575 } 576 577 void 578 enable_forwarding(struct portdata *port) 579 { 580 bridge_setstate_t bss; 581 582 bss.bss_linkid = port->linkid; 583 bss.bss_state = BLS_FORWARDING; 584 if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) 585 syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name); 586 } 587 588 void 589 event_loop(void) 590 { 591 int i; 592 hrtime_t last_time, now; 593 int tout; 594 595 if (lock_engine() != 0) { 596 syslog(LOG_ERR, "mutex lock"); 597 exit(EXIT_FAILURE); 598 } 599 600 /* Bootstrap configuration */ 601 handle_refresh(-1); 602 603 last_time = gethrtime(); 604 while (!shutting_down) { 605 now = gethrtime(); 606 if (now - last_time >= 1000000000ll) { 607 (void) STP_IN_one_second(); 608 tout = 1000; 609 last_time = now; 610 } else { 611 tout = 1000 - (now - last_time) / 1000000ll; 612 } 613 unlock_engine(); 614 (void) poll(fdarray, nextport + FDOFFSET, tout); 615 if (lock_engine() != 0) { 616 syslog(LOG_ERR, "mutex lock"); 617 exit(EXIT_FAILURE); 618 } 619 if (fdarray[0].revents & POLLIN) 620 handle_refresh(fdarray[0].fd); 621 if (fdarray[1].revents & POLLIN) 622 handle_control(); 623 for (i = 0; i < nextport; i++) { 624 if (fdarray[i + FDOFFSET].revents & POLLIN) 625 receive_packet(allports[i]); 626 } 627 } 628 unlock_engine(); 629 } 630