14eaa4710SRishi Srivatsavai /* 24eaa4710SRishi Srivatsavai * CDDL HEADER START 34eaa4710SRishi Srivatsavai * 44eaa4710SRishi Srivatsavai * The contents of this file are subject to the terms of the 54eaa4710SRishi Srivatsavai * Common Development and Distribution License (the "License"). 64eaa4710SRishi Srivatsavai * You may not use this file except in compliance with the License. 74eaa4710SRishi Srivatsavai * 84eaa4710SRishi Srivatsavai * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94eaa4710SRishi Srivatsavai * or http://www.opensolaris.org/os/licensing. 104eaa4710SRishi Srivatsavai * See the License for the specific language governing permissions 114eaa4710SRishi Srivatsavai * and limitations under the License. 124eaa4710SRishi Srivatsavai * 134eaa4710SRishi Srivatsavai * When distributing Covered Code, include this CDDL HEADER in each 144eaa4710SRishi Srivatsavai * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154eaa4710SRishi Srivatsavai * If applicable, add the following below this CDDL HEADER, with the 164eaa4710SRishi Srivatsavai * fields enclosed by brackets "[]" replaced with your own identifying 174eaa4710SRishi Srivatsavai * information: Portions Copyright [yyyy] [name of copyright owner] 184eaa4710SRishi Srivatsavai * 194eaa4710SRishi Srivatsavai * CDDL HEADER END 204eaa4710SRishi Srivatsavai */ 214eaa4710SRishi Srivatsavai 224eaa4710SRishi Srivatsavai /* 23*32715170SCathy Zhou * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 244eaa4710SRishi Srivatsavai */ 254eaa4710SRishi Srivatsavai 264eaa4710SRishi Srivatsavai /* 274eaa4710SRishi Srivatsavai * bridged - bridging control daemon. This module handles events and general 284eaa4710SRishi Srivatsavai * port-related operations. 294eaa4710SRishi Srivatsavai */ 304eaa4710SRishi Srivatsavai 314eaa4710SRishi Srivatsavai #include <stdio.h> 324eaa4710SRishi Srivatsavai #include <stdlib.h> 334eaa4710SRishi Srivatsavai #include <unistd.h> 344eaa4710SRishi Srivatsavai #include <fcntl.h> 354eaa4710SRishi Srivatsavai #include <string.h> 364eaa4710SRishi Srivatsavai #include <sys/types.h> 374eaa4710SRishi Srivatsavai #include <syslog.h> 384eaa4710SRishi Srivatsavai #include <libdlpi.h> 394eaa4710SRishi Srivatsavai #include <libdladm.h> 404eaa4710SRishi Srivatsavai #include <libdllink.h> 414eaa4710SRishi Srivatsavai #include <libdlbridge.h> 424eaa4710SRishi Srivatsavai #include <libdlvlan.h> 434eaa4710SRishi Srivatsavai #include <libdlstat.h> 444eaa4710SRishi Srivatsavai #include <stp_in.h> 454eaa4710SRishi Srivatsavai #include <stp_vectors.h> 464eaa4710SRishi Srivatsavai #include <net/if_types.h> 474eaa4710SRishi Srivatsavai #include <net/bridge.h> 484eaa4710SRishi Srivatsavai #include <sys/ethernet.h> 494eaa4710SRishi Srivatsavai 504eaa4710SRishi Srivatsavai #include "global.h" 514eaa4710SRishi Srivatsavai 524eaa4710SRishi Srivatsavai int refresh_count = 1; /* never zero */ 534eaa4710SRishi Srivatsavai dladm_bridge_prot_t protect = DLADM_BRIDGE_PROT_STP; 544eaa4710SRishi Srivatsavai 554eaa4710SRishi Srivatsavai /* 564eaa4710SRishi Srivatsavai * The 'allports' array is an array of pointers to the struct portdata 574eaa4710SRishi Srivatsavai * structures. We reallocate 'allports' as needed, but the portdata must 584eaa4710SRishi Srivatsavai * remain where it's initially allocated, because libdlpi's notification 594eaa4710SRishi Srivatsavai * mechanism has a copy of a pointer to this structure. 604eaa4710SRishi Srivatsavai */ 614eaa4710SRishi Srivatsavai uint_t nextport; 624eaa4710SRishi Srivatsavai struct portdata **allports; 634eaa4710SRishi Srivatsavai 644eaa4710SRishi Srivatsavai /* Port allocation increment (arbitrary) */ 654eaa4710SRishi Srivatsavai #define ALLOCINCR 10 664eaa4710SRishi Srivatsavai static uint_t numports; 674eaa4710SRishi Srivatsavai 684eaa4710SRishi Srivatsavai static datalink_id_t main_linkid; 694eaa4710SRishi Srivatsavai 704eaa4710SRishi Srivatsavai int control_fd; 714eaa4710SRishi Srivatsavai 724eaa4710SRishi Srivatsavai static void 734eaa4710SRishi Srivatsavai linkdown(void) 744eaa4710SRishi Srivatsavai { 754eaa4710SRishi Srivatsavai (void) dladm_destroy_datalink_id(dlhandle, main_linkid, 764eaa4710SRishi Srivatsavai DLADM_OPT_ACTIVE); 774eaa4710SRishi Srivatsavai } 784eaa4710SRishi Srivatsavai 794eaa4710SRishi Srivatsavai void 804eaa4710SRishi Srivatsavai open_bridge_control(void) 814eaa4710SRishi Srivatsavai { 824eaa4710SRishi Srivatsavai bridge_newbridge_t bnb; 834eaa4710SRishi Srivatsavai dladm_status_t status; 844eaa4710SRishi Srivatsavai char buf[DLADM_STRSIZE]; 854eaa4710SRishi Srivatsavai 864eaa4710SRishi Srivatsavai if ((control_fd = open(BRIDGE_CTLPATH, O_RDWR | O_NONBLOCK)) == -1) { 874eaa4710SRishi Srivatsavai perror(BRIDGE_CTLPATH); 884eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 894eaa4710SRishi Srivatsavai } 904eaa4710SRishi Srivatsavai (void) snprintf(bnb.bnb_name, sizeof (bnb.bnb_name), "%s0", 914eaa4710SRishi Srivatsavai instance_name); 924eaa4710SRishi Srivatsavai status = dladm_name2info(dlhandle, bnb.bnb_name, &bnb.bnb_linkid, NULL, 934eaa4710SRishi Srivatsavai NULL, NULL); 944eaa4710SRishi Srivatsavai if (status != DLADM_STATUS_OK) { 954eaa4710SRishi Srivatsavai (void) fprintf(stderr, "bridged: %s: %s\n", bnb.bnb_name, 964eaa4710SRishi Srivatsavai dladm_status2str(status, buf)); 974eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 984eaa4710SRishi Srivatsavai } 994eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_NEWBRIDGE, &bnb, sizeof (bnb)) == -1) { 1004eaa4710SRishi Srivatsavai perror("NEWBRIDGE"); 1014eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 1024eaa4710SRishi Srivatsavai } 1034eaa4710SRishi Srivatsavai main_linkid = bnb.bnb_linkid; 1044eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_TABLEMAX, &tablemax, 1054eaa4710SRishi Srivatsavai sizeof (tablemax)) == -1) { 1064eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot set table max %lu on bridge %s: %m", 1074eaa4710SRishi Srivatsavai tablemax, instance_name); 1084eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 1094eaa4710SRishi Srivatsavai } 1104eaa4710SRishi Srivatsavai /* 1114eaa4710SRishi Srivatsavai * This covers for any previous incarnation where we might have crashed 1124eaa4710SRishi Srivatsavai * or been SIGKILL'd and failed to take down the datalink. 1134eaa4710SRishi Srivatsavai */ 1144eaa4710SRishi Srivatsavai linkdown(); 1154eaa4710SRishi Srivatsavai (void) atexit(linkdown); 1164eaa4710SRishi Srivatsavai status = dladm_up_datalink_id(dlhandle, bnb.bnb_linkid); 1174eaa4710SRishi Srivatsavai if (status != DLADM_STATUS_OK) { 1184eaa4710SRishi Srivatsavai (void) fprintf(stderr, "bridged: %s link up: %s\n", 1194eaa4710SRishi Srivatsavai bnb.bnb_name, dladm_status2str(status, buf)); 1204eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 1214eaa4710SRishi Srivatsavai } 1224eaa4710SRishi Srivatsavai } 1234eaa4710SRishi Srivatsavai 1244eaa4710SRishi Srivatsavai struct portdata * 1254eaa4710SRishi Srivatsavai find_by_linkid(datalink_id_t linkid) 1264eaa4710SRishi Srivatsavai { 1274eaa4710SRishi Srivatsavai int i; 1284eaa4710SRishi Srivatsavai struct portdata *port; 1294eaa4710SRishi Srivatsavai 1304eaa4710SRishi Srivatsavai for (i = 0; i < nextport; i++) { 1314eaa4710SRishi Srivatsavai port = allports[i]; 1324eaa4710SRishi Srivatsavai if (port->linkid == linkid) 1334eaa4710SRishi Srivatsavai return (port); 1344eaa4710SRishi Srivatsavai } 1354eaa4710SRishi Srivatsavai return (NULL); 1364eaa4710SRishi Srivatsavai } 1374eaa4710SRishi Srivatsavai 1384eaa4710SRishi Srivatsavai /*ARGSUSED2*/ 1394eaa4710SRishi Srivatsavai static int 1404eaa4710SRishi Srivatsavai set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1414eaa4710SRishi Srivatsavai { 1424eaa4710SRishi Srivatsavai struct portdata *port; 1434eaa4710SRishi Srivatsavai dladm_status_t status; 1444eaa4710SRishi Srivatsavai dladm_vlan_attr_t vinfo; 1454eaa4710SRishi Srivatsavai char pointless[DLADM_STRSIZE]; 1464eaa4710SRishi Srivatsavai bridge_vlanenab_t bve; 1474eaa4710SRishi Srivatsavai 1484eaa4710SRishi Srivatsavai status = dladm_vlan_info(handle, linkid, &vinfo, DLADM_OPT_ACTIVE); 1494eaa4710SRishi Srivatsavai if (status != DLADM_STATUS_OK) { 1504eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, "can't get VLAN info on link ID %u: %s", 1514eaa4710SRishi Srivatsavai linkid, dladm_status2str(status, pointless)); 1524eaa4710SRishi Srivatsavai return (DLADM_WALK_CONTINUE); 1534eaa4710SRishi Srivatsavai } 1544eaa4710SRishi Srivatsavai 1554eaa4710SRishi Srivatsavai port = find_by_linkid(vinfo.dv_linkid); 1564eaa4710SRishi Srivatsavai if (port == NULL || !port->kern_added) 1574eaa4710SRishi Srivatsavai return (DLADM_WALK_CONTINUE); 1584eaa4710SRishi Srivatsavai 1594eaa4710SRishi Srivatsavai bve.bve_linkid = port->linkid; 1604eaa4710SRishi Srivatsavai bve.bve_vlan = vinfo.dv_vid; 1614eaa4710SRishi Srivatsavai bve.bve_onoff = B_TRUE; 1624eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) { 1634eaa4710SRishi Srivatsavai syslog(LOG_ERR, "unable to enable VLAN %d on linkid %u: %m", 1644eaa4710SRishi Srivatsavai vinfo.dv_vid, port->linkid); 1654eaa4710SRishi Srivatsavai return (DLADM_WALK_TERMINATE); 1664eaa4710SRishi Srivatsavai } else { 1674eaa4710SRishi Srivatsavai return (DLADM_WALK_CONTINUE); 1684eaa4710SRishi Srivatsavai } 1694eaa4710SRishi Srivatsavai } 1704eaa4710SRishi Srivatsavai 1714eaa4710SRishi Srivatsavai /* 1724eaa4710SRishi Srivatsavai * If the named port already exists, then update its configuration. If it 1734eaa4710SRishi Srivatsavai * doesn't, then create and enable it. 1744eaa4710SRishi Srivatsavai */ 1754eaa4710SRishi Srivatsavai static void 1764eaa4710SRishi Srivatsavai update_port(int vlan_id, const char *portname, datalink_id_t linkid, 1774eaa4710SRishi Srivatsavai datalink_class_t class) 1784eaa4710SRishi Srivatsavai { 1794eaa4710SRishi Srivatsavai int posn; 1804eaa4710SRishi Srivatsavai struct portdata *port; 1814eaa4710SRishi Srivatsavai struct pollfd *fds; 1824eaa4710SRishi Srivatsavai int port_index; 1834eaa4710SRishi Srivatsavai struct { 1844eaa4710SRishi Srivatsavai datalink_id_t linkid; 1854eaa4710SRishi Srivatsavai char linkname[MAXLINKNAMELEN]; 1864eaa4710SRishi Srivatsavai } adddata; 1874eaa4710SRishi Srivatsavai bridge_setpvid_t bsv; 1884eaa4710SRishi Srivatsavai uint_t propval, valcnt; 1894eaa4710SRishi Srivatsavai dladm_status_t status; 1904eaa4710SRishi Srivatsavai 1914eaa4710SRishi Srivatsavai for (posn = 0; posn < nextport; posn++) { 1924eaa4710SRishi Srivatsavai if (allports[posn]->linkid == linkid) 1934eaa4710SRishi Srivatsavai break; 1944eaa4710SRishi Srivatsavai } 1954eaa4710SRishi Srivatsavai 1964eaa4710SRishi Srivatsavai /* If we need to allocate more array space, then do so in chunks. */ 1974eaa4710SRishi Srivatsavai if (posn >= numports) { 1984eaa4710SRishi Srivatsavai struct portdata **newarr; 1994eaa4710SRishi Srivatsavai 2004eaa4710SRishi Srivatsavai newarr = realloc(allports, 2014eaa4710SRishi Srivatsavai sizeof (*newarr) * (nextport + ALLOCINCR)); 2024eaa4710SRishi Srivatsavai if (newarr != NULL) 2034eaa4710SRishi Srivatsavai allports = newarr; 2044eaa4710SRishi Srivatsavai fds = realloc(fdarray, 2054eaa4710SRishi Srivatsavai sizeof (*fds) * (nextport + ALLOCINCR + FDOFFSET)); 2064eaa4710SRishi Srivatsavai if (fds != NULL) 2074eaa4710SRishi Srivatsavai fdarray = fds; 2084eaa4710SRishi Srivatsavai if (newarr == NULL || fds == NULL) { 2094eaa4710SRishi Srivatsavai syslog(LOG_ERR, "unable to add %s; no memory", 2104eaa4710SRishi Srivatsavai portname); 2114eaa4710SRishi Srivatsavai return; 2124eaa4710SRishi Srivatsavai } 2134eaa4710SRishi Srivatsavai numports = nextport + ALLOCINCR; 2144eaa4710SRishi Srivatsavai } 2154eaa4710SRishi Srivatsavai 2164eaa4710SRishi Srivatsavai port_index = posn + 1; 2174eaa4710SRishi Srivatsavai fds = fdarray + posn + FDOFFSET; 2184eaa4710SRishi Srivatsavai 2194eaa4710SRishi Srivatsavai /* If our linkid search ran to the end, then this is a new port. */ 2204eaa4710SRishi Srivatsavai if (posn == nextport) { 2214eaa4710SRishi Srivatsavai if ((port = calloc(1, sizeof (*port))) == NULL) { 2224eaa4710SRishi Srivatsavai syslog(LOG_ERR, "unable to add %s; no memory", 2234eaa4710SRishi Srivatsavai portname); 2244eaa4710SRishi Srivatsavai return; 2254eaa4710SRishi Srivatsavai } 2264eaa4710SRishi Srivatsavai allports[posn] = port; 2274eaa4710SRishi Srivatsavai port->vlan_id = vlan_id; 2284eaa4710SRishi Srivatsavai port->linkid = linkid; 2294eaa4710SRishi Srivatsavai port->port_index = port_index; 2304eaa4710SRishi Srivatsavai port->phys_status = B_TRUE; 2314eaa4710SRishi Srivatsavai port->admin_status = B_TRUE; 2324eaa4710SRishi Srivatsavai port->state = BLS_BLOCKLISTEN; 2334eaa4710SRishi Srivatsavai nextport++; 2344eaa4710SRishi Srivatsavai } else { 2354eaa4710SRishi Srivatsavai /* Located port by linkid; we're just updating existing data */ 2364eaa4710SRishi Srivatsavai port = allports[posn]; 2374eaa4710SRishi Srivatsavai 2384eaa4710SRishi Srivatsavai /* 2394eaa4710SRishi Srivatsavai * If it changed name, then close and reopen so we log under 2404eaa4710SRishi Srivatsavai * the most current name for this port. 2414eaa4710SRishi Srivatsavai */ 2424eaa4710SRishi Srivatsavai if (port->name != NULL && strcmp(portname, port->name) != 0) { 2434eaa4710SRishi Srivatsavai if (port->dlpi != NULL) 2444eaa4710SRishi Srivatsavai dlpi_close(port->dlpi); 2454eaa4710SRishi Srivatsavai port->dlpi = NULL; 2464eaa4710SRishi Srivatsavai port->name = NULL; 2474eaa4710SRishi Srivatsavai fds->fd = -1; 2484eaa4710SRishi Srivatsavai fds->events = 0; 2494eaa4710SRishi Srivatsavai } 2504eaa4710SRishi Srivatsavai } 2514eaa4710SRishi Srivatsavai 2524eaa4710SRishi Srivatsavai /* 2534eaa4710SRishi Srivatsavai * If the port is not yet attached to the bridge in the kernel, then do 2544eaa4710SRishi Srivatsavai * that now. 2554eaa4710SRishi Srivatsavai */ 2564eaa4710SRishi Srivatsavai if (!port->kern_added) { 2574eaa4710SRishi Srivatsavai adddata.linkid = linkid; 2584eaa4710SRishi Srivatsavai (void) strlcpy(adddata.linkname, portname, 2594eaa4710SRishi Srivatsavai sizeof (adddata.linkname)); 2604eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_ADDLINK, &adddata, 2614eaa4710SRishi Srivatsavai sizeof (adddata.linkid) + strlen(adddata.linkname)) == -1) { 2624eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot bridge %s: %m", portname); 2634eaa4710SRishi Srivatsavai goto failure; 2644eaa4710SRishi Srivatsavai } 2654eaa4710SRishi Srivatsavai port->kern_added = B_TRUE; 2664eaa4710SRishi Srivatsavai } 2674eaa4710SRishi Srivatsavai 2684eaa4710SRishi Srivatsavai port->referenced = B_TRUE; 2694eaa4710SRishi Srivatsavai 2704eaa4710SRishi Srivatsavai valcnt = 1; 2714eaa4710SRishi Srivatsavai status = dladm_get_linkprop_values(dlhandle, linkid, 2724eaa4710SRishi Srivatsavai DLADM_PROP_VAL_PERSISTENT, "forward", &propval, &valcnt); 2734eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK) 2744eaa4710SRishi Srivatsavai port->admin_status = propval; 2754eaa4710SRishi Srivatsavai 2764eaa4710SRishi Srivatsavai bsv.bsv_vlan = 1; 2774eaa4710SRishi Srivatsavai status = dladm_get_linkprop_values(dlhandle, linkid, 2784eaa4710SRishi Srivatsavai DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt); 2794eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK) 2804eaa4710SRishi Srivatsavai bsv.bsv_vlan = propval; 2814eaa4710SRishi Srivatsavai 2824eaa4710SRishi Srivatsavai bsv.bsv_linkid = linkid; 2834eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_SETPVID, &bsv, sizeof (bsv)) == -1) { 2844eaa4710SRishi Srivatsavai syslog(LOG_ERR, "can't set PVID on %s: %m", portname); 2854eaa4710SRishi Srivatsavai goto failure; 2864eaa4710SRishi Srivatsavai } 2874eaa4710SRishi Srivatsavai 2884eaa4710SRishi Srivatsavai if (port->dlpi == NULL) { 2894eaa4710SRishi Srivatsavai if (!port_dlpi_open(portname, port, class)) 2904eaa4710SRishi Srivatsavai goto failure; 2914eaa4710SRishi Srivatsavai fds->fd = dlpi_fd(port->dlpi); 2924eaa4710SRishi Srivatsavai fds->events = POLLIN; 2934eaa4710SRishi Srivatsavai } 2944eaa4710SRishi Srivatsavai 2954eaa4710SRishi Srivatsavai if (rstp_add_port(port)) 2964eaa4710SRishi Srivatsavai return; 2974eaa4710SRishi Srivatsavai 2984eaa4710SRishi Srivatsavai failure: 2994eaa4710SRishi Srivatsavai if (port->dlpi != NULL) { 3004eaa4710SRishi Srivatsavai dlpi_close(port->dlpi); 3014eaa4710SRishi Srivatsavai port->dlpi = NULL; 3024eaa4710SRishi Srivatsavai port->name = NULL; 3034eaa4710SRishi Srivatsavai fds->fd = -1; 3044eaa4710SRishi Srivatsavai fds->events = 0; 3054eaa4710SRishi Srivatsavai } 3064eaa4710SRishi Srivatsavai if (port->kern_added) { 3074eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_REMLINK, &port->linkid, 3084eaa4710SRishi Srivatsavai sizeof (port->linkid)) == -1) 3094eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot remove from bridge %s: %m", 3104eaa4710SRishi Srivatsavai portname); 3114eaa4710SRishi Srivatsavai else 3124eaa4710SRishi Srivatsavai port->kern_added = B_FALSE; 3134eaa4710SRishi Srivatsavai } 3144eaa4710SRishi Srivatsavai if (posn + 1 == nextport) { 3154eaa4710SRishi Srivatsavai free(port); 3164eaa4710SRishi Srivatsavai nextport--; 3174eaa4710SRishi Srivatsavai } 3184eaa4710SRishi Srivatsavai } 3194eaa4710SRishi Srivatsavai 3204eaa4710SRishi Srivatsavai /*ARGSUSED2*/ 3214eaa4710SRishi Srivatsavai static int 3224eaa4710SRishi Srivatsavai update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg) 3234eaa4710SRishi Srivatsavai { 3244eaa4710SRishi Srivatsavai dladm_status_t status; 3254eaa4710SRishi Srivatsavai char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN]; 3264eaa4710SRishi Srivatsavai char pointless[DLADM_STRSIZE]; 3274eaa4710SRishi Srivatsavai datalink_class_t class; 3284eaa4710SRishi Srivatsavai 3294eaa4710SRishi Srivatsavai status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)); 3304eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK && strcmp(bridge, instance_name) == 0) { 3314eaa4710SRishi Srivatsavai status = dladm_datalink_id2info(handle, linkid, NULL, &class, 3324eaa4710SRishi Srivatsavai NULL, linkname, sizeof (linkname)); 3334eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK) { 3344eaa4710SRishi Srivatsavai update_port(0, linkname, linkid, class); 3354eaa4710SRishi Srivatsavai } else { 3364eaa4710SRishi Srivatsavai syslog(LOG_ERR, "unable to get link info for ID %u: %s", 3374eaa4710SRishi Srivatsavai linkid, dladm_status2str(status, pointless)); 3384eaa4710SRishi Srivatsavai } 3394eaa4710SRishi Srivatsavai } else if (debugging) { 3404eaa4710SRishi Srivatsavai if (status != DLADM_STATUS_OK) 3414eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, 3424eaa4710SRishi Srivatsavai "unable to get bridge data for ID %u: %s", 3434eaa4710SRishi Srivatsavai linkid, dladm_status2str(status, pointless)); 3444eaa4710SRishi Srivatsavai else 3454eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, "link ID %u is on bridge %s, not %s", 3464eaa4710SRishi Srivatsavai linkid, bridge, instance_name); 3474eaa4710SRishi Srivatsavai } 3484eaa4710SRishi Srivatsavai return (DLADM_WALK_CONTINUE); 3494eaa4710SRishi Srivatsavai } 3504eaa4710SRishi Srivatsavai 3514eaa4710SRishi Srivatsavai /* 3524eaa4710SRishi Srivatsavai * Refresh action - reread configuration properties. 3534eaa4710SRishi Srivatsavai */ 3544eaa4710SRishi Srivatsavai static void 3554eaa4710SRishi Srivatsavai handle_refresh(int sigfd) 3564eaa4710SRishi Srivatsavai { 3574eaa4710SRishi Srivatsavai int i; 3584eaa4710SRishi Srivatsavai struct portdata *pdp; 3594eaa4710SRishi Srivatsavai struct pollfd *fdp; 3604eaa4710SRishi Srivatsavai char buf[16]; 3614eaa4710SRishi Srivatsavai dladm_status_t status; 3624eaa4710SRishi Srivatsavai boolean_t new_debug; 3634eaa4710SRishi Srivatsavai uint32_t new_tablemax; 3644eaa4710SRishi Srivatsavai 3654eaa4710SRishi Srivatsavai /* Drain signal events from pipe */ 3664eaa4710SRishi Srivatsavai if (sigfd != -1) 3674eaa4710SRishi Srivatsavai (void) read(sigfd, buf, sizeof (buf)); 3684eaa4710SRishi Srivatsavai 3694eaa4710SRishi Srivatsavai status = dladm_bridge_get_privprop(instance_name, &new_debug, 3704eaa4710SRishi Srivatsavai &new_tablemax); 3714eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK) { 3724eaa4710SRishi Srivatsavai if (debugging && !new_debug) 3734eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, "disabling debugging"); 3744eaa4710SRishi Srivatsavai debugging = new_debug; 3754eaa4710SRishi Srivatsavai if (new_tablemax != tablemax) { 3764eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, "changed tablemax from %lu to %lu", 3774eaa4710SRishi Srivatsavai tablemax, new_tablemax); 3784eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_TABLEMAX, &new_tablemax, 3794eaa4710SRishi Srivatsavai sizeof (tablemax)) == -1) 3804eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot set table max " 3814eaa4710SRishi Srivatsavai "%lu on bridge %s: %m", tablemax, 3824eaa4710SRishi Srivatsavai instance_name); 3834eaa4710SRishi Srivatsavai else 3844eaa4710SRishi Srivatsavai tablemax = new_tablemax; 3854eaa4710SRishi Srivatsavai } 3864eaa4710SRishi Srivatsavai } else { 3874eaa4710SRishi Srivatsavai syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s", 3884eaa4710SRishi Srivatsavai instance_name, dladm_status2str(status, buf)); 3894eaa4710SRishi Srivatsavai } 3904eaa4710SRishi Srivatsavai 3914eaa4710SRishi Srivatsavai rstp_refresh(); 3924eaa4710SRishi Srivatsavai 3934eaa4710SRishi Srivatsavai for (i = 0; i < nextport; i++) 3944eaa4710SRishi Srivatsavai allports[i]->referenced = B_FALSE; 3954eaa4710SRishi Srivatsavai 3964eaa4710SRishi Srivatsavai /* 3974eaa4710SRishi Srivatsavai * libdladm doesn't guarantee anything about link ordering in a walk, 3984eaa4710SRishi Srivatsavai * so we do this walk twice: once to pick up the ports, and a second 3994eaa4710SRishi Srivatsavai * time to get the enabled VLANs on all ports. 4004eaa4710SRishi Srivatsavai */ 4014eaa4710SRishi Srivatsavai (void) dladm_walk_datalink_id(update_link, dlhandle, NULL, 4024eaa4710SRishi Srivatsavai DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4034eaa4710SRishi Srivatsavai 4044eaa4710SRishi Srivatsavai (void) dladm_walk_datalink_id(set_vlan, dlhandle, NULL, 4054eaa4710SRishi Srivatsavai DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4064eaa4710SRishi Srivatsavai 4074eaa4710SRishi Srivatsavai /* 4084eaa4710SRishi Srivatsavai * If any ports now show up as unreferenced, then they've been removed 4094eaa4710SRishi Srivatsavai * from the configuration. 4104eaa4710SRishi Srivatsavai */ 4114eaa4710SRishi Srivatsavai for (i = 0; i < nextport; i++) { 4124eaa4710SRishi Srivatsavai pdp = allports[i]; 4134eaa4710SRishi Srivatsavai fdp = fdarray + i + FDOFFSET; 4144eaa4710SRishi Srivatsavai if (!pdp->referenced) { 4154eaa4710SRishi Srivatsavai if (pdp->stp_added) { 4164eaa4710SRishi Srivatsavai (void) STP_IN_port_remove(pdp->vlan_id, 4174eaa4710SRishi Srivatsavai pdp->port_index); 4184eaa4710SRishi Srivatsavai pdp->stp_added = B_FALSE; 4194eaa4710SRishi Srivatsavai } 4204eaa4710SRishi Srivatsavai if (pdp->dlpi != NULL) { 4214eaa4710SRishi Srivatsavai dlpi_close(pdp->dlpi); 4224eaa4710SRishi Srivatsavai pdp->dlpi = NULL; 4234eaa4710SRishi Srivatsavai pdp->name = NULL; 4244eaa4710SRishi Srivatsavai fdp->fd = -1; 4254eaa4710SRishi Srivatsavai fdp->events = 0; 4264eaa4710SRishi Srivatsavai } 4274eaa4710SRishi Srivatsavai if (pdp->kern_added) { 4284eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_REMLINK, 4294eaa4710SRishi Srivatsavai &pdp->linkid, sizeof (pdp->linkid)) == -1) 4304eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot remove linkid " 4314eaa4710SRishi Srivatsavai "%u from bridge %s: %m", 4324eaa4710SRishi Srivatsavai pdp->linkid, instance_name); 4334eaa4710SRishi Srivatsavai pdp->kern_added = B_FALSE; 4344eaa4710SRishi Srivatsavai } 4354eaa4710SRishi Srivatsavai } 4364eaa4710SRishi Srivatsavai } 4374eaa4710SRishi Srivatsavai 4384eaa4710SRishi Srivatsavai if (++refresh_count == 0) 4394eaa4710SRishi Srivatsavai refresh_count = 1; 4404eaa4710SRishi Srivatsavai } 4414eaa4710SRishi Srivatsavai 4424eaa4710SRishi Srivatsavai /* 4434eaa4710SRishi Srivatsavai * Handle messages on the common control stream. This currently just deals 4444eaa4710SRishi Srivatsavai * with port SDU mismatches. 4454eaa4710SRishi Srivatsavai */ 4464eaa4710SRishi Srivatsavai static void 4474eaa4710SRishi Srivatsavai handle_control(void) 4484eaa4710SRishi Srivatsavai { 4494eaa4710SRishi Srivatsavai bridge_ctl_t bc; 4504eaa4710SRishi Srivatsavai ssize_t retv; 4514eaa4710SRishi Srivatsavai struct portdata *port; 4524eaa4710SRishi Srivatsavai int rc; 4534eaa4710SRishi Srivatsavai 4544eaa4710SRishi Srivatsavai retv = read(control_fd, &bc, sizeof (bc)); 4554eaa4710SRishi Srivatsavai if (retv != sizeof (bc)) 4564eaa4710SRishi Srivatsavai return; 4574eaa4710SRishi Srivatsavai if ((port = find_by_linkid(bc.bc_linkid)) == NULL) 4584eaa4710SRishi Srivatsavai return; 4594eaa4710SRishi Srivatsavai if (port->sdu_failed == bc.bc_failed) 4604eaa4710SRishi Srivatsavai return; 4614eaa4710SRishi Srivatsavai port->sdu_failed = bc.bc_failed; 4624eaa4710SRishi Srivatsavai if (!port->phys_status || !port->admin_status || 4634eaa4710SRishi Srivatsavai protect != DLADM_BRIDGE_PROT_STP) 4644eaa4710SRishi Srivatsavai return; 4654eaa4710SRishi Srivatsavai if (port->admin_non_stp) { 4664eaa4710SRishi Srivatsavai bridge_setstate_t bss; 4674eaa4710SRishi Srivatsavai 4684eaa4710SRishi Srivatsavai bss.bss_linkid = port->linkid; 4694eaa4710SRishi Srivatsavai bss.bss_state = !port->sdu_failed && !port->bpdu_protect ? 4704eaa4710SRishi Srivatsavai BLS_FORWARDING : BLS_BLOCKLISTEN; 4714eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_SETSTATE, &bss, 4724eaa4710SRishi Srivatsavai sizeof (bss)) == -1) { 4734eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot set STP state on %s: %m", 4744eaa4710SRishi Srivatsavai port->name); 4754eaa4710SRishi Srivatsavai } 4764eaa4710SRishi Srivatsavai } 4774eaa4710SRishi Srivatsavai if ((rc = STP_IN_enable_port(port->port_index, !bc.bc_failed)) != 0) 4784eaa4710SRishi Srivatsavai syslog(LOG_ERR, "STP can't %s port %s for SDU failure: %s", 4794eaa4710SRishi Srivatsavai port->name, bc.bc_failed ? "disable" : "enable", 4804eaa4710SRishi Srivatsavai STP_IN_get_error_explanation(rc)); 4814eaa4710SRishi Srivatsavai } 4824eaa4710SRishi Srivatsavai 4834eaa4710SRishi Srivatsavai static void 4844eaa4710SRishi Srivatsavai receive_packet(struct portdata *port) 4854eaa4710SRishi Srivatsavai { 4864eaa4710SRishi Srivatsavai int rc; 4874eaa4710SRishi Srivatsavai size_t buflen; 4884eaa4710SRishi Srivatsavai uint16_t buffer[ETHERMAX / sizeof (uint16_t)]; 4894eaa4710SRishi Srivatsavai struct ether_header *eh; 4904eaa4710SRishi Srivatsavai char sender[ETHERADDRL * 3]; 4914eaa4710SRishi Srivatsavai 4924eaa4710SRishi Srivatsavai buflen = sizeof (buffer); 4934eaa4710SRishi Srivatsavai rc = dlpi_recv(port->dlpi, NULL, NULL, buffer, &buflen, 1, NULL); 4944eaa4710SRishi Srivatsavai if (rc != DLPI_SUCCESS) { 4954eaa4710SRishi Srivatsavai if (rc != DLPI_ETIMEDOUT) 4964eaa4710SRishi Srivatsavai syslog(LOG_ERR, "receive failure on %s: %s", port->name, 4974eaa4710SRishi Srivatsavai dlpi_strerror(rc)); 4984eaa4710SRishi Srivatsavai return; 4994eaa4710SRishi Srivatsavai } 5004eaa4710SRishi Srivatsavai 5014eaa4710SRishi Srivatsavai /* 5024eaa4710SRishi Srivatsavai * If we're administratively disabled, then don't deliver packets to 5034eaa4710SRishi Srivatsavai * the STP state machine. It will re-enable the port because it uses 5044eaa4710SRishi Srivatsavai * the same variable for both link status and administrative state. 5054eaa4710SRishi Srivatsavai */ 5064eaa4710SRishi Srivatsavai if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP) { 5074eaa4710SRishi Srivatsavai if (debugging) 5084eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, 5094eaa4710SRishi Srivatsavai "discard BPDU on non-forwarding interface %s", 5104eaa4710SRishi Srivatsavai port->name); 5114eaa4710SRishi Srivatsavai return; 5124eaa4710SRishi Srivatsavai } 5134eaa4710SRishi Srivatsavai 5144eaa4710SRishi Srivatsavai /* 5154eaa4710SRishi Srivatsavai * There's a mismatch between the librstp and libdlpi expectations on 5164eaa4710SRishi Srivatsavai * receive. librstp wants the packet to start with the 802 length 5174eaa4710SRishi Srivatsavai * field, not the destination address. 5184eaa4710SRishi Srivatsavai */ 5194eaa4710SRishi Srivatsavai eh = (struct ether_header *)buffer; 5204eaa4710SRishi Srivatsavai rc = STP_IN_check_bpdu_header((BPDU_T *)&eh->ether_type, buflen); 5214eaa4710SRishi Srivatsavai 5224eaa4710SRishi Srivatsavai /* 5234eaa4710SRishi Srivatsavai * Note that we attempt to avoid calling the relatively expensive 5244eaa4710SRishi Srivatsavai * _link_ntoa function unless we're going to use the result. In normal 5254eaa4710SRishi Srivatsavai * usage, we don't need this string. 5264eaa4710SRishi Srivatsavai */ 5274eaa4710SRishi Srivatsavai if (rc == 0) { 5284eaa4710SRishi Srivatsavai if (port->admin_non_stp && !port->bpdu_protect) { 5294eaa4710SRishi Srivatsavai bridge_setstate_t bss; 5304eaa4710SRishi Srivatsavai 5314eaa4710SRishi Srivatsavai (void) _link_ntoa(eh->ether_shost.ether_addr_octet, 5324eaa4710SRishi Srivatsavai sender, ETHERADDRL, IFT_OTHER); 5334eaa4710SRishi Srivatsavai syslog(LOG_WARNING, "unexpected BPDU on %s from %s; " 5344eaa4710SRishi Srivatsavai "forwarding disabled", port->name, sender); 5354eaa4710SRishi Srivatsavai port->bpdu_protect = B_TRUE; 5364eaa4710SRishi Srivatsavai bss.bss_linkid = port->linkid; 5374eaa4710SRishi Srivatsavai bss.bss_state = BLS_BLOCKLISTEN; 5384eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_SETSTATE, &bss, 5394eaa4710SRishi Srivatsavai sizeof (bss)) == -1) { 5404eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot set STP state on " 5414eaa4710SRishi Srivatsavai "%s: %m", port->name); 5424eaa4710SRishi Srivatsavai } 5434eaa4710SRishi Srivatsavai return; 5444eaa4710SRishi Srivatsavai } 5454eaa4710SRishi Srivatsavai if (debugging) { 5464eaa4710SRishi Srivatsavai (void) _link_ntoa(eh->ether_shost.ether_addr_octet, 5474eaa4710SRishi Srivatsavai sender, ETHERADDRL, IFT_OTHER); 5484eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, "got BPDU from %s on %s; %d bytes", 5494eaa4710SRishi Srivatsavai sender, port->name, buflen); 5504eaa4710SRishi Srivatsavai } 5514eaa4710SRishi Srivatsavai rc = STP_IN_rx_bpdu(port->vlan_id, port->port_index, 5524eaa4710SRishi Srivatsavai (BPDU_T *)&eh->ether_type, buflen); 5534eaa4710SRishi Srivatsavai } 5544eaa4710SRishi Srivatsavai if (rc != 0) { 5554eaa4710SRishi Srivatsavai (void) _link_ntoa(eh->ether_shost.ether_addr_octet, sender, 5564eaa4710SRishi Srivatsavai ETHERADDRL, IFT_OTHER); 5574eaa4710SRishi Srivatsavai syslog(LOG_DEBUG, 5584eaa4710SRishi Srivatsavai "discarded malformed packet on %s from %s: %s", 5594eaa4710SRishi Srivatsavai port->name, sender, STP_IN_get_error_explanation(rc)); 5604eaa4710SRishi Srivatsavai } 5614eaa4710SRishi Srivatsavai } 5624eaa4710SRishi Srivatsavai 5634eaa4710SRishi Srivatsavai void 5644eaa4710SRishi Srivatsavai get_dladm_speed(struct portdata *port) 5654eaa4710SRishi Srivatsavai { 5664eaa4710SRishi Srivatsavai dladm_status_t status; 5674eaa4710SRishi Srivatsavai uint64_t ifspeed; 5684eaa4710SRishi Srivatsavai 5694eaa4710SRishi Srivatsavai status = dladm_get_single_mac_stat(dlhandle, port->linkid, "ifspeed", 5704eaa4710SRishi Srivatsavai KSTAT_DATA_UINT64, &ifspeed); 5714eaa4710SRishi Srivatsavai if (status == DLADM_STATUS_OK && ifspeed != 0) 5724eaa4710SRishi Srivatsavai port->speed = ifspeed / 1000000; 5734eaa4710SRishi Srivatsavai else 5744eaa4710SRishi Srivatsavai port->speed = 10UL; 5754eaa4710SRishi Srivatsavai } 5764eaa4710SRishi Srivatsavai 5774eaa4710SRishi Srivatsavai void 5784eaa4710SRishi Srivatsavai enable_forwarding(struct portdata *port) 5794eaa4710SRishi Srivatsavai { 5804eaa4710SRishi Srivatsavai bridge_setstate_t bss; 5814eaa4710SRishi Srivatsavai 5824eaa4710SRishi Srivatsavai bss.bss_linkid = port->linkid; 5834eaa4710SRishi Srivatsavai bss.bss_state = BLS_FORWARDING; 5844eaa4710SRishi Srivatsavai if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) 5854eaa4710SRishi Srivatsavai syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name); 5864eaa4710SRishi Srivatsavai } 5874eaa4710SRishi Srivatsavai 5884eaa4710SRishi Srivatsavai void 5894eaa4710SRishi Srivatsavai event_loop(void) 5904eaa4710SRishi Srivatsavai { 5914eaa4710SRishi Srivatsavai int i; 5924eaa4710SRishi Srivatsavai hrtime_t last_time, now; 5934eaa4710SRishi Srivatsavai int tout; 5944eaa4710SRishi Srivatsavai 5954eaa4710SRishi Srivatsavai if (lock_engine() != 0) { 5964eaa4710SRishi Srivatsavai syslog(LOG_ERR, "mutex lock"); 5974eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 5984eaa4710SRishi Srivatsavai } 5994eaa4710SRishi Srivatsavai 6004eaa4710SRishi Srivatsavai /* Bootstrap configuration */ 6014eaa4710SRishi Srivatsavai handle_refresh(-1); 6024eaa4710SRishi Srivatsavai 6034eaa4710SRishi Srivatsavai last_time = gethrtime(); 6044eaa4710SRishi Srivatsavai while (!shutting_down) { 6054eaa4710SRishi Srivatsavai now = gethrtime(); 6064eaa4710SRishi Srivatsavai if (now - last_time >= 1000000000ll) { 6074eaa4710SRishi Srivatsavai (void) STP_IN_one_second(); 6084eaa4710SRishi Srivatsavai tout = 1000; 6094eaa4710SRishi Srivatsavai last_time = now; 6104eaa4710SRishi Srivatsavai } else { 6114eaa4710SRishi Srivatsavai tout = 1000 - (now - last_time) / 1000000ll; 6124eaa4710SRishi Srivatsavai } 6134eaa4710SRishi Srivatsavai unlock_engine(); 6144eaa4710SRishi Srivatsavai (void) poll(fdarray, nextport + FDOFFSET, tout); 6154eaa4710SRishi Srivatsavai if (lock_engine() != 0) { 6164eaa4710SRishi Srivatsavai syslog(LOG_ERR, "mutex lock"); 6174eaa4710SRishi Srivatsavai exit(EXIT_FAILURE); 6184eaa4710SRishi Srivatsavai } 6194eaa4710SRishi Srivatsavai if (fdarray[0].revents & POLLIN) 6204eaa4710SRishi Srivatsavai handle_refresh(fdarray[0].fd); 6214eaa4710SRishi Srivatsavai if (fdarray[1].revents & POLLIN) 6224eaa4710SRishi Srivatsavai handle_control(); 6234eaa4710SRishi Srivatsavai for (i = 0; i < nextport; i++) { 6244eaa4710SRishi Srivatsavai if (fdarray[i + FDOFFSET].revents & POLLIN) 6254eaa4710SRishi Srivatsavai receive_packet(allports[i]); 6264eaa4710SRishi Srivatsavai } 6274eaa4710SRishi Srivatsavai } 6284eaa4710SRishi Srivatsavai unlock_engine(); 6294eaa4710SRishi Srivatsavai } 630