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