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
linkdown(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
open_bridge_control(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 *
find_by_linkid(datalink_id_t linkid)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
set_vlan(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
update_port(int vlan_id,const char * portname,datalink_id_t linkid,datalink_class_t class)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
update_link(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
handle_refresh(int sigfd)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,
4034eaa4710SRishi Srivatsavai 
4044eaa4710SRishi Srivatsavai 	(void) dladm_walk_datalink_id(set_vlan, dlhandle, NULL,
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
handle_control(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
receive_packet(struct portdata * port)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
get_dladm_speed(struct portdata * port)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
enable_forwarding(struct portdata * port)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
event_loop(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 }