1*4eaa4710SRishi Srivatsavai /*
2*4eaa4710SRishi Srivatsavai  * CDDL HEADER START
3*4eaa4710SRishi Srivatsavai  *
4*4eaa4710SRishi Srivatsavai  * The contents of this file are subject to the terms of the
5*4eaa4710SRishi Srivatsavai  * Common Development and Distribution License (the "License").
6*4eaa4710SRishi Srivatsavai  * You may not use this file except in compliance with the License.
7*4eaa4710SRishi Srivatsavai  *
8*4eaa4710SRishi Srivatsavai  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4eaa4710SRishi Srivatsavai  * or http://www.opensolaris.org/os/licensing.
10*4eaa4710SRishi Srivatsavai  * See the License for the specific language governing permissions
11*4eaa4710SRishi Srivatsavai  * and limitations under the License.
12*4eaa4710SRishi Srivatsavai  *
13*4eaa4710SRishi Srivatsavai  * When distributing Covered Code, include this CDDL HEADER in each
14*4eaa4710SRishi Srivatsavai  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4eaa4710SRishi Srivatsavai  * If applicable, add the following below this CDDL HEADER, with the
16*4eaa4710SRishi Srivatsavai  * fields enclosed by brackets "[]" replaced with your own identifying
17*4eaa4710SRishi Srivatsavai  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4eaa4710SRishi Srivatsavai  *
19*4eaa4710SRishi Srivatsavai  * CDDL HEADER END
20*4eaa4710SRishi Srivatsavai  */
21*4eaa4710SRishi Srivatsavai 
22*4eaa4710SRishi Srivatsavai /*
23*4eaa4710SRishi Srivatsavai  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*4eaa4710SRishi Srivatsavai  * Use is subject to license terms.
25*4eaa4710SRishi Srivatsavai  */
26*4eaa4710SRishi Srivatsavai 
27*4eaa4710SRishi Srivatsavai /*
28*4eaa4710SRishi Srivatsavai  * bridged - bridging control daemon.  This module provides functions related
29*4eaa4710SRishi Srivatsavai  * to the librstp (Rapid Spanning Tree Protocol) library.
30*4eaa4710SRishi Srivatsavai  */
31*4eaa4710SRishi Srivatsavai 
32*4eaa4710SRishi Srivatsavai #include <stdio.h>
33*4eaa4710SRishi Srivatsavai #include <stdlib.h>
34*4eaa4710SRishi Srivatsavai #include <unistd.h>
35*4eaa4710SRishi Srivatsavai #include <stdarg.h>
36*4eaa4710SRishi Srivatsavai #include <string.h>
37*4eaa4710SRishi Srivatsavai #include <sys/types.h>
38*4eaa4710SRishi Srivatsavai #include <syslog.h>
39*4eaa4710SRishi Srivatsavai #include <kstat.h>
40*4eaa4710SRishi Srivatsavai #include <libdlpi.h>
41*4eaa4710SRishi Srivatsavai #include <libdladm.h>
42*4eaa4710SRishi Srivatsavai #include <libdllink.h>
43*4eaa4710SRishi Srivatsavai #include <libdlstat.h>
44*4eaa4710SRishi Srivatsavai #include <stp_in.h>
45*4eaa4710SRishi Srivatsavai #include <stp_vectors.h>
46*4eaa4710SRishi Srivatsavai #include <net/if_types.h>
47*4eaa4710SRishi Srivatsavai #include <net/bridge.h>
48*4eaa4710SRishi Srivatsavai #include <sys/ethernet.h>
49*4eaa4710SRishi Srivatsavai 
50*4eaa4710SRishi Srivatsavai #include "global.h"
51*4eaa4710SRishi Srivatsavai 
52*4eaa4710SRishi Srivatsavai /* current engine configuration; access protected by engine_lock */
53*4eaa4710SRishi Srivatsavai static UID_STP_CFG_T uid_cfg;
54*4eaa4710SRishi Srivatsavai 
55*4eaa4710SRishi Srivatsavai /*
56*4eaa4710SRishi Srivatsavai  * Our implementation doesn't have per-VLAN forwarding entries, so we just
57*4eaa4710SRishi Srivatsavai  * flush by the port.  If port number is zero, then flush entries.
58*4eaa4710SRishi Srivatsavai  */
59*4eaa4710SRishi Srivatsavai /*ARGSUSED1*/
60*4eaa4710SRishi Srivatsavai static int
flush_lt(int port_index,int vlan_id,LT_FLASH_TYPE_T type,char * reason)61*4eaa4710SRishi Srivatsavai flush_lt(int port_index, int vlan_id, LT_FLASH_TYPE_T type, char *reason)
62*4eaa4710SRishi Srivatsavai {
63*4eaa4710SRishi Srivatsavai 	struct portdata *pd;
64*4eaa4710SRishi Srivatsavai 	const char *portname;
65*4eaa4710SRishi Srivatsavai 	bridge_flushfwd_t bff;
66*4eaa4710SRishi Srivatsavai 
67*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index < 0)
68*4eaa4710SRishi Srivatsavai 		return (0);
69*4eaa4710SRishi Srivatsavai 
70*4eaa4710SRishi Srivatsavai 	if (port_index == 0) {
71*4eaa4710SRishi Srivatsavai 		type = LT_FLASH_ONLY_THE_PORT;
72*4eaa4710SRishi Srivatsavai 		portname = "all";
73*4eaa4710SRishi Srivatsavai 		bff.bff_linkid = DATALINK_INVALID_LINKID;
74*4eaa4710SRishi Srivatsavai 	} else {
75*4eaa4710SRishi Srivatsavai 		pd = allports[port_index - 1];
76*4eaa4710SRishi Srivatsavai 		portname = pd->name;
77*4eaa4710SRishi Srivatsavai 		bff.bff_linkid = pd->linkid;
78*4eaa4710SRishi Srivatsavai 	}
79*4eaa4710SRishi Srivatsavai 
80*4eaa4710SRishi Srivatsavai 	if (debugging) {
81*4eaa4710SRishi Srivatsavai 		syslog(LOG_DEBUG, "flush forwarding %s %s: %s",
82*4eaa4710SRishi Srivatsavai 		    type == LT_FLASH_ONLY_THE_PORT ? "to" : "except for",
83*4eaa4710SRishi Srivatsavai 		    portname, reason);
84*4eaa4710SRishi Srivatsavai 	}
85*4eaa4710SRishi Srivatsavai 
86*4eaa4710SRishi Srivatsavai 	bff.bff_exclude = (type == LT_FLASH_ALL_PORTS_EXCLUDE_THIS);
87*4eaa4710SRishi Srivatsavai 
88*4eaa4710SRishi Srivatsavai 	/*
89*4eaa4710SRishi Srivatsavai 	 * If flushing fails, we can't return.  The only safe thing to do is to
90*4eaa4710SRishi Srivatsavai 	 * tear down the bridge so that we're not harming the network.
91*4eaa4710SRishi Srivatsavai 	 */
92*4eaa4710SRishi Srivatsavai 	if (strioctl(control_fd, BRIOC_FLUSHFWD, &bff, sizeof (bff)) == -1) {
93*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "cannot flush forwarding entries on %s %s: %m",
94*4eaa4710SRishi Srivatsavai 		    instance_name, portname);
95*4eaa4710SRishi Srivatsavai 		unlock_engine();
96*4eaa4710SRishi Srivatsavai 		exit(EXIT_FAILURE);
97*4eaa4710SRishi Srivatsavai 	}
98*4eaa4710SRishi Srivatsavai 
99*4eaa4710SRishi Srivatsavai 	return (0);
100*4eaa4710SRishi Srivatsavai }
101*4eaa4710SRishi Srivatsavai 
102*4eaa4710SRishi Srivatsavai static void
get_port_mac(int port_index,unsigned char * mac)103*4eaa4710SRishi Srivatsavai get_port_mac(int port_index, unsigned char *mac)
104*4eaa4710SRishi Srivatsavai {
105*4eaa4710SRishi Srivatsavai 	struct portdata *pd;
106*4eaa4710SRishi Srivatsavai 
107*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
108*4eaa4710SRishi Srivatsavai 		return;
109*4eaa4710SRishi Srivatsavai 
110*4eaa4710SRishi Srivatsavai 	pd = allports[port_index - 1];
111*4eaa4710SRishi Srivatsavai 	(void) memcpy(mac, pd->mac_addr, ETHERADDRL);
112*4eaa4710SRishi Srivatsavai }
113*4eaa4710SRishi Srivatsavai 
114*4eaa4710SRishi Srivatsavai /* Returns speed in megabits per second */
115*4eaa4710SRishi Srivatsavai static unsigned long
get_port_oper_speed(unsigned int port_index)116*4eaa4710SRishi Srivatsavai get_port_oper_speed(unsigned int port_index)
117*4eaa4710SRishi Srivatsavai {
118*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index == 0)
119*4eaa4710SRishi Srivatsavai 		return (1000UL);
120*4eaa4710SRishi Srivatsavai 	else
121*4eaa4710SRishi Srivatsavai 		return (allports[port_index - 1]->speed);
122*4eaa4710SRishi Srivatsavai }
123*4eaa4710SRishi Srivatsavai 
124*4eaa4710SRishi Srivatsavai static int
get_port_link_status(int port_index)125*4eaa4710SRishi Srivatsavai get_port_link_status(int port_index)
126*4eaa4710SRishi Srivatsavai {
127*4eaa4710SRishi Srivatsavai 	struct portdata *pd;
128*4eaa4710SRishi Srivatsavai 
129*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0) {
130*4eaa4710SRishi Srivatsavai 		return (0);
131*4eaa4710SRishi Srivatsavai 	} else {
132*4eaa4710SRishi Srivatsavai 		pd = allports[port_index - 1];
133*4eaa4710SRishi Srivatsavai 		return (pd->phys_status && pd->admin_status &&
134*4eaa4710SRishi Srivatsavai 		    protect == DLADM_BRIDGE_PROT_STP && !pd->sdu_failed ?
135*4eaa4710SRishi Srivatsavai 		    1 : 0);
136*4eaa4710SRishi Srivatsavai 	}
137*4eaa4710SRishi Srivatsavai }
138*4eaa4710SRishi Srivatsavai 
139*4eaa4710SRishi Srivatsavai static int
get_duplex(int port_index)140*4eaa4710SRishi Srivatsavai get_duplex(int port_index)
141*4eaa4710SRishi Srivatsavai {
142*4eaa4710SRishi Srivatsavai 	struct portdata *pd;
143*4eaa4710SRishi Srivatsavai 	link_duplex_t link_duplex;
144*4eaa4710SRishi Srivatsavai 	dladm_status_t status;
145*4eaa4710SRishi Srivatsavai 
146*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
147*4eaa4710SRishi Srivatsavai 		return (False);
148*4eaa4710SRishi Srivatsavai 
149*4eaa4710SRishi Srivatsavai 	pd = allports[port_index - 1];
150*4eaa4710SRishi Srivatsavai 	status = dladm_get_single_mac_stat(dlhandle, pd->linkid, "link_duplex",
151*4eaa4710SRishi Srivatsavai 	    KSTAT_DATA_UINT32, &link_duplex);
152*4eaa4710SRishi Srivatsavai 
153*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK && link_duplex == LINK_DUPLEX_FULL)
154*4eaa4710SRishi Srivatsavai 		return (True);
155*4eaa4710SRishi Srivatsavai 	else
156*4eaa4710SRishi Srivatsavai 		return (False);
157*4eaa4710SRishi Srivatsavai }
158*4eaa4710SRishi Srivatsavai 
159*4eaa4710SRishi Srivatsavai static const char *
bls_state(bridge_state_t bstate)160*4eaa4710SRishi Srivatsavai bls_state(bridge_state_t bstate)
161*4eaa4710SRishi Srivatsavai {
162*4eaa4710SRishi Srivatsavai 	switch (bstate) {
163*4eaa4710SRishi Srivatsavai 	case BLS_LEARNING:
164*4eaa4710SRishi Srivatsavai 		return ("learning");
165*4eaa4710SRishi Srivatsavai 	case BLS_FORWARDING:
166*4eaa4710SRishi Srivatsavai 		return ("forwarding");
167*4eaa4710SRishi Srivatsavai 	default:
168*4eaa4710SRishi Srivatsavai 		return ("block/listen");
169*4eaa4710SRishi Srivatsavai 	}
170*4eaa4710SRishi Srivatsavai }
171*4eaa4710SRishi Srivatsavai 
172*4eaa4710SRishi Srivatsavai /*ARGSUSED1*/
173*4eaa4710SRishi Srivatsavai static int
set_port_state(int port_index,int vlan_id,RSTP_PORT_STATE state)174*4eaa4710SRishi Srivatsavai set_port_state(int port_index, int vlan_id, RSTP_PORT_STATE state)
175*4eaa4710SRishi Srivatsavai {
176*4eaa4710SRishi Srivatsavai 	struct portdata *pd;
177*4eaa4710SRishi Srivatsavai 	bridge_setstate_t bss;
178*4eaa4710SRishi Srivatsavai 
179*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
180*4eaa4710SRishi Srivatsavai 		return (1);
181*4eaa4710SRishi Srivatsavai 
182*4eaa4710SRishi Srivatsavai 	pd = allports[port_index - 1];
183*4eaa4710SRishi Srivatsavai 
184*4eaa4710SRishi Srivatsavai 	if (debugging)
185*4eaa4710SRishi Srivatsavai 		syslog(LOG_DEBUG, "setting port state on port %d (%s) to %d",
186*4eaa4710SRishi Srivatsavai 		    port_index, pd->name, state);
187*4eaa4710SRishi Srivatsavai 	switch (state) {
188*4eaa4710SRishi Srivatsavai 	case UID_PORT_LEARNING:
189*4eaa4710SRishi Srivatsavai 		bss.bss_state = BLS_LEARNING;
190*4eaa4710SRishi Srivatsavai 		break;
191*4eaa4710SRishi Srivatsavai 	case UID_PORT_FORWARDING:
192*4eaa4710SRishi Srivatsavai 		bss.bss_state = BLS_FORWARDING;
193*4eaa4710SRishi Srivatsavai 		break;
194*4eaa4710SRishi Srivatsavai 	default:
195*4eaa4710SRishi Srivatsavai 		bss.bss_state = BLS_BLOCKLISTEN;
196*4eaa4710SRishi Srivatsavai 		break;
197*4eaa4710SRishi Srivatsavai 	}
198*4eaa4710SRishi Srivatsavai 	bss.bss_linkid = pd->linkid;
199*4eaa4710SRishi Srivatsavai 	if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) {
200*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "cannot set STP state on %s from %s to %s: %m",
201*4eaa4710SRishi Srivatsavai 		    pd->name, bls_state(pd->state), bls_state(bss.bss_state));
202*4eaa4710SRishi Srivatsavai 		/*
203*4eaa4710SRishi Srivatsavai 		 * If we've been unsuccessful in disabling forwarding, then the
204*4eaa4710SRishi Srivatsavai 		 * only safe thing to do is to make the daemon exit, so that
205*4eaa4710SRishi Srivatsavai 		 * the kernel will be forced to destroy the bridge state and
206*4eaa4710SRishi Srivatsavai 		 * terminate all forwarding.
207*4eaa4710SRishi Srivatsavai 		 */
208*4eaa4710SRishi Srivatsavai 		if (pd->state == BLS_FORWARDING &&
209*4eaa4710SRishi Srivatsavai 		    bss.bss_state != BLS_FORWARDING) {
210*4eaa4710SRishi Srivatsavai 			unlock_engine();
211*4eaa4710SRishi Srivatsavai 			exit(EXIT_FAILURE);
212*4eaa4710SRishi Srivatsavai 		}
213*4eaa4710SRishi Srivatsavai 	} else {
214*4eaa4710SRishi Srivatsavai 		pd->state = bss.bss_state;
215*4eaa4710SRishi Srivatsavai 	}
216*4eaa4710SRishi Srivatsavai 	return (0);
217*4eaa4710SRishi Srivatsavai }
218*4eaa4710SRishi Srivatsavai 
219*4eaa4710SRishi Srivatsavai /*
220*4eaa4710SRishi Srivatsavai  * Our hardware doesn't actually do anything different when STP is enabled or
221*4eaa4710SRishi Srivatsavai  * disabled, so this function does nothing.  It would be possible to open and
222*4eaa4710SRishi Srivatsavai  * close the DLPI stream here, if such a thing were necessary.
223*4eaa4710SRishi Srivatsavai  */
224*4eaa4710SRishi Srivatsavai static int
set_hardware_mode(int vlan_id,UID_STP_MODE_T mode)225*4eaa4710SRishi Srivatsavai set_hardware_mode(int vlan_id, UID_STP_MODE_T mode)
226*4eaa4710SRishi Srivatsavai {
227*4eaa4710SRishi Srivatsavai 	if (debugging)
228*4eaa4710SRishi Srivatsavai 		syslog(LOG_DEBUG, "setting hardware mode on vlan %d to %d",
229*4eaa4710SRishi Srivatsavai 		    vlan_id, mode);
230*4eaa4710SRishi Srivatsavai 	return (0);
231*4eaa4710SRishi Srivatsavai }
232*4eaa4710SRishi Srivatsavai 
233*4eaa4710SRishi Srivatsavai /*ARGSUSED1*/
234*4eaa4710SRishi Srivatsavai static int
tx_bpdu(int port_index,int vlan_id,unsigned char * bpdu,size_t bpdu_len)235*4eaa4710SRishi Srivatsavai tx_bpdu(int port_index, int vlan_id, unsigned char *bpdu, size_t bpdu_len)
236*4eaa4710SRishi Srivatsavai {
237*4eaa4710SRishi Srivatsavai 	struct portdata *pdp;
238*4eaa4710SRishi Srivatsavai 	int rc;
239*4eaa4710SRishi Srivatsavai 
240*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
241*4eaa4710SRishi Srivatsavai 		return (1);
242*4eaa4710SRishi Srivatsavai 
243*4eaa4710SRishi Srivatsavai 	pdp = allports[port_index - 1];
244*4eaa4710SRishi Srivatsavai 	rc = dlpi_send(pdp->dlpi, NULL, 0, bpdu, bpdu_len, NULL);
245*4eaa4710SRishi Srivatsavai 	if (rc == DLPI_SUCCESS) {
246*4eaa4710SRishi Srivatsavai 		if (debugging)
247*4eaa4710SRishi Srivatsavai 			syslog(LOG_DEBUG, "transmitted %d byte BPDU on %s",
248*4eaa4710SRishi Srivatsavai 			    bpdu_len, pdp->name);
249*4eaa4710SRishi Srivatsavai 		return (0);
250*4eaa4710SRishi Srivatsavai 	} else {
251*4eaa4710SRishi Srivatsavai 		syslog(LOG_WARNING, "failed to send to %s: %s", pdp->name,
252*4eaa4710SRishi Srivatsavai 		    dlpi_strerror(rc));
253*4eaa4710SRishi Srivatsavai 		return (1);
254*4eaa4710SRishi Srivatsavai 	}
255*4eaa4710SRishi Srivatsavai }
256*4eaa4710SRishi Srivatsavai 
257*4eaa4710SRishi Srivatsavai static const char *
get_port_name(int port_index)258*4eaa4710SRishi Srivatsavai get_port_name(int port_index)
259*4eaa4710SRishi Srivatsavai {
260*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
261*4eaa4710SRishi Srivatsavai 		return ("unknown");
262*4eaa4710SRishi Srivatsavai 	else
263*4eaa4710SRishi Srivatsavai 		return (allports[port_index - 1]->name);
264*4eaa4710SRishi Srivatsavai }
265*4eaa4710SRishi Srivatsavai 
266*4eaa4710SRishi Srivatsavai /*ARGSUSED*/
267*4eaa4710SRishi Srivatsavai static int
get_init_stpm_cfg(int vlan_id,UID_STP_CFG_T * cfg)268*4eaa4710SRishi Srivatsavai get_init_stpm_cfg(int vlan_id, UID_STP_CFG_T *cfg)
269*4eaa4710SRishi Srivatsavai {
270*4eaa4710SRishi Srivatsavai 	/* under engine_lock because it's a callback from the engine */
271*4eaa4710SRishi Srivatsavai 	*cfg = uid_cfg;
272*4eaa4710SRishi Srivatsavai 	return (0);
273*4eaa4710SRishi Srivatsavai }
274*4eaa4710SRishi Srivatsavai 
275*4eaa4710SRishi Srivatsavai /*ARGSUSED*/
276*4eaa4710SRishi Srivatsavai static int
get_init_port_cfg(int vlan_id,int port_index,UID_STP_PORT_CFG_T * cfg)277*4eaa4710SRishi Srivatsavai get_init_port_cfg(int vlan_id, int port_index, UID_STP_PORT_CFG_T *cfg)
278*4eaa4710SRishi Srivatsavai {
279*4eaa4710SRishi Srivatsavai 	struct portdata *pdp;
280*4eaa4710SRishi Srivatsavai 	uint_t propval, valcnt;
281*4eaa4710SRishi Srivatsavai 	datalink_id_t linkid;
282*4eaa4710SRishi Srivatsavai 	dladm_status_t status;
283*4eaa4710SRishi Srivatsavai 
284*4eaa4710SRishi Srivatsavai 	if (port_index > nextport || port_index <= 0)
285*4eaa4710SRishi Srivatsavai 		return (1);
286*4eaa4710SRishi Srivatsavai 
287*4eaa4710SRishi Srivatsavai 	pdp = allports[port_index - 1];
288*4eaa4710SRishi Srivatsavai 
289*4eaa4710SRishi Srivatsavai 	cfg->field_mask = 0;
290*4eaa4710SRishi Srivatsavai 	cfg->port_priority = DEF_PORT_PRIO;
291*4eaa4710SRishi Srivatsavai 	cfg->admin_non_stp = DEF_ADMIN_NON_STP;
292*4eaa4710SRishi Srivatsavai 	cfg->admin_edge = DEF_ADMIN_EDGE;
293*4eaa4710SRishi Srivatsavai 	cfg->admin_port_path_cost = ADMIN_PORT_PATH_COST_AUTO;
294*4eaa4710SRishi Srivatsavai 	cfg->admin_point2point = DEF_P2P;
295*4eaa4710SRishi Srivatsavai 
296*4eaa4710SRishi Srivatsavai 	valcnt = 1;
297*4eaa4710SRishi Srivatsavai 	linkid = pdp->linkid;
298*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
299*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp_priority", &propval, &valcnt);
300*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK) {
301*4eaa4710SRishi Srivatsavai 		cfg->port_priority = propval;
302*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_PRIO;
303*4eaa4710SRishi Srivatsavai 	}
304*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
305*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp", &propval, &valcnt);
306*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK) {
307*4eaa4710SRishi Srivatsavai 		cfg->admin_non_stp = !propval;
308*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_NON_STP;
309*4eaa4710SRishi Srivatsavai 	}
310*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
311*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp_edge", &propval, &valcnt);
312*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK) {
313*4eaa4710SRishi Srivatsavai 		cfg->admin_edge = propval;
314*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_EDGE;
315*4eaa4710SRishi Srivatsavai 	}
316*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
317*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp_cost", &propval, &valcnt);
318*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK) {
319*4eaa4710SRishi Srivatsavai 		cfg->admin_port_path_cost = propval;
320*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_COST;
321*4eaa4710SRishi Srivatsavai 	}
322*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
323*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp_p2p", &propval, &valcnt);
324*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK) {
325*4eaa4710SRishi Srivatsavai 		cfg->admin_point2point = propval;
326*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_P2P;
327*4eaa4710SRishi Srivatsavai 	}
328*4eaa4710SRishi Srivatsavai 
329*4eaa4710SRishi Srivatsavai 	/*
330*4eaa4710SRishi Srivatsavai 	 * mcheck is special.  It is actually a command, but the 802 documents
331*4eaa4710SRishi Srivatsavai 	 * define it as a variable that spontaneously resets itself.  We need
332*4eaa4710SRishi Srivatsavai 	 * to handle that behavior here.
333*4eaa4710SRishi Srivatsavai 	 */
334*4eaa4710SRishi Srivatsavai 	status = dladm_get_linkprop_values(dlhandle, linkid,
335*4eaa4710SRishi Srivatsavai 	    DLADM_PROP_VAL_PERSISTENT, "stp_mcheck", &propval, &valcnt);
336*4eaa4710SRishi Srivatsavai 	if (status == DLADM_STATUS_OK && propval != 0) {
337*4eaa4710SRishi Srivatsavai 		char *pval = "0";
338*4eaa4710SRishi Srivatsavai 
339*4eaa4710SRishi Srivatsavai 		cfg->field_mask |= PT_CFG_MCHECK;
340*4eaa4710SRishi Srivatsavai 		(void) dladm_set_linkprop(dlhandle, linkid, "stp_mcheck", &pval,
341*4eaa4710SRishi Srivatsavai 		    1, DLADM_OPT_ACTIVE|DLADM_OPT_PERSIST|DLADM_OPT_NOREFRESH);
342*4eaa4710SRishi Srivatsavai 	}
343*4eaa4710SRishi Srivatsavai 
344*4eaa4710SRishi Srivatsavai 	pdp->admin_non_stp = cfg->admin_non_stp;
345*4eaa4710SRishi Srivatsavai 	if (!pdp->admin_non_stp)
346*4eaa4710SRishi Srivatsavai 		pdp->bpdu_protect = B_FALSE;
347*4eaa4710SRishi Srivatsavai 
348*4eaa4710SRishi Srivatsavai 	return (0);
349*4eaa4710SRishi Srivatsavai }
350*4eaa4710SRishi Srivatsavai 
351*4eaa4710SRishi Srivatsavai static void
trace(const char * fmt,...)352*4eaa4710SRishi Srivatsavai trace(const char *fmt, ...)
353*4eaa4710SRishi Srivatsavai {
354*4eaa4710SRishi Srivatsavai 	va_list ap;
355*4eaa4710SRishi Srivatsavai 
356*4eaa4710SRishi Srivatsavai 	va_start(ap, fmt);
357*4eaa4710SRishi Srivatsavai 	vsyslog(LOG_DEBUG, fmt, ap);
358*4eaa4710SRishi Srivatsavai 	va_end(ap);
359*4eaa4710SRishi Srivatsavai }
360*4eaa4710SRishi Srivatsavai 
361*4eaa4710SRishi Srivatsavai static STP_VECTORS_T stp_vectors = {
362*4eaa4710SRishi Srivatsavai 	flush_lt,
363*4eaa4710SRishi Srivatsavai 	get_port_mac,
364*4eaa4710SRishi Srivatsavai 	get_port_oper_speed,
365*4eaa4710SRishi Srivatsavai 	get_port_link_status,
366*4eaa4710SRishi Srivatsavai 	get_duplex,
367*4eaa4710SRishi Srivatsavai 	set_port_state,
368*4eaa4710SRishi Srivatsavai 	set_hardware_mode,
369*4eaa4710SRishi Srivatsavai 	tx_bpdu,
370*4eaa4710SRishi Srivatsavai 	get_port_name,
371*4eaa4710SRishi Srivatsavai 	get_init_stpm_cfg,
372*4eaa4710SRishi Srivatsavai 	get_init_port_cfg,
373*4eaa4710SRishi Srivatsavai 	trace
374*4eaa4710SRishi Srivatsavai };
375*4eaa4710SRishi Srivatsavai 
376*4eaa4710SRishi Srivatsavai void
rstp_init(void)377*4eaa4710SRishi Srivatsavai rstp_init(void)
378*4eaa4710SRishi Srivatsavai {
379*4eaa4710SRishi Srivatsavai 	dladm_status_t status;
380*4eaa4710SRishi Srivatsavai 	char buf[DLADM_STRSIZE];
381*4eaa4710SRishi Srivatsavai 
382*4eaa4710SRishi Srivatsavai 	STP_IN_init(&stp_vectors);
383*4eaa4710SRishi Srivatsavai 	status = dladm_bridge_get_properties(instance_name, &uid_cfg, &protect);
384*4eaa4710SRishi Srivatsavai 	if (status != DLADM_STATUS_OK) {
385*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "%s: unable to read properties: %s",
386*4eaa4710SRishi Srivatsavai 		    instance_name, dladm_status2str(status, buf));
387*4eaa4710SRishi Srivatsavai 		exit(EXIT_FAILURE);
388*4eaa4710SRishi Srivatsavai 	}
389*4eaa4710SRishi Srivatsavai }
390*4eaa4710SRishi Srivatsavai 
391*4eaa4710SRishi Srivatsavai /*
392*4eaa4710SRishi Srivatsavai  * This is called by a normal refresh operation.  It gets the engine properties
393*4eaa4710SRishi Srivatsavai  * and resets.
394*4eaa4710SRishi Srivatsavai  */
395*4eaa4710SRishi Srivatsavai void
rstp_refresh(void)396*4eaa4710SRishi Srivatsavai rstp_refresh(void)
397*4eaa4710SRishi Srivatsavai {
398*4eaa4710SRishi Srivatsavai 	dladm_status_t status;
399*4eaa4710SRishi Srivatsavai 	int rc;
400*4eaa4710SRishi Srivatsavai 	char buf[DLADM_STRSIZE];
401*4eaa4710SRishi Srivatsavai 	UID_STP_CFG_T new_cfg;
402*4eaa4710SRishi Srivatsavai 	dladm_bridge_prot_t new_prot;
403*4eaa4710SRishi Srivatsavai 
404*4eaa4710SRishi Srivatsavai 	status = dladm_bridge_get_properties(instance_name, &new_cfg,
405*4eaa4710SRishi Srivatsavai 	    &new_prot);
406*4eaa4710SRishi Srivatsavai 	if (status != DLADM_STATUS_OK) {
407*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s",
408*4eaa4710SRishi Srivatsavai 		    instance_name, dladm_status2str(status, buf));
409*4eaa4710SRishi Srivatsavai 	} else {
410*4eaa4710SRishi Srivatsavai 		if (debugging && (protect != new_prot ||
411*4eaa4710SRishi Srivatsavai 		    uid_cfg.stp_enabled != new_cfg.stp_enabled)) {
412*4eaa4710SRishi Srivatsavai 			syslog(LOG_DEBUG, "loop protection %s->%s, STP %d->%d",
413*4eaa4710SRishi Srivatsavai 			    dladm_bridge_prot2str(protect),
414*4eaa4710SRishi Srivatsavai 			    dladm_bridge_prot2str(new_prot),
415*4eaa4710SRishi Srivatsavai 			    uid_cfg.stp_enabled, new_cfg.stp_enabled);
416*4eaa4710SRishi Srivatsavai 		}
417*4eaa4710SRishi Srivatsavai 
418*4eaa4710SRishi Srivatsavai 		/*
419*4eaa4710SRishi Srivatsavai 		 * The engine doesn't take kindly to parameter changes while
420*4eaa4710SRishi Srivatsavai 		 * running.  Disable first if we must do this.
421*4eaa4710SRishi Srivatsavai 		 */
422*4eaa4710SRishi Srivatsavai 		if (uid_cfg.stp_enabled &&
423*4eaa4710SRishi Srivatsavai 		    memcmp(&uid_cfg, &new_cfg, sizeof (uid_cfg)) != 0) {
424*4eaa4710SRishi Srivatsavai 			syslog(LOG_DEBUG, "resetting state machine");
425*4eaa4710SRishi Srivatsavai 			uid_cfg.stp_enabled = STP_DISABLED;
426*4eaa4710SRishi Srivatsavai 			rc = STP_IN_stpm_set_cfg(0, &uid_cfg);
427*4eaa4710SRishi Srivatsavai 			if (rc != 0)
428*4eaa4710SRishi Srivatsavai 				syslog(LOG_ERR, "STP machine reset config: %s",
429*4eaa4710SRishi Srivatsavai 				    STP_IN_get_error_explanation(rc));
430*4eaa4710SRishi Srivatsavai 		}
431*4eaa4710SRishi Srivatsavai 
432*4eaa4710SRishi Srivatsavai 		uid_cfg = new_cfg;
433*4eaa4710SRishi Srivatsavai 		protect = new_prot;
434*4eaa4710SRishi Srivatsavai 		rc = STP_IN_stpm_set_cfg(0, &uid_cfg);
435*4eaa4710SRishi Srivatsavai 		if (rc != 0)
436*4eaa4710SRishi Srivatsavai 			syslog(LOG_ERR, "STP machine set config: %s",
437*4eaa4710SRishi Srivatsavai 			    STP_IN_get_error_explanation(rc));
438*4eaa4710SRishi Srivatsavai 	}
439*4eaa4710SRishi Srivatsavai }
440*4eaa4710SRishi Srivatsavai 
441*4eaa4710SRishi Srivatsavai /*
442*4eaa4710SRishi Srivatsavai  * This is called when a port changes its MAC address.  If it's the main port,
443*4eaa4710SRishi Srivatsavai  * the one that supplies us our bridge ID, then we must choose a new ID, and to
444*4eaa4710SRishi Srivatsavai  * do that we shut the bridge down and bring it back up.
445*4eaa4710SRishi Srivatsavai  */
446*4eaa4710SRishi Srivatsavai void
rstp_change_mac(struct portdata * port,const unsigned char * newaddr)447*4eaa4710SRishi Srivatsavai rstp_change_mac(struct portdata *port, const unsigned char *newaddr)
448*4eaa4710SRishi Srivatsavai {
449*4eaa4710SRishi Srivatsavai 	unsigned short prio;
450*4eaa4710SRishi Srivatsavai 	unsigned char mac[ETHERADDRL];
451*4eaa4710SRishi Srivatsavai 	int rc;
452*4eaa4710SRishi Srivatsavai 	char curid[ETHERADDRL * 3];
453*4eaa4710SRishi Srivatsavai 	char newmac[ETHERADDRL * 3];
454*4eaa4710SRishi Srivatsavai 
455*4eaa4710SRishi Srivatsavai 	(void) _link_ntoa(port->mac_addr, curid, ETHERADDRL, IFT_OTHER);
456*4eaa4710SRishi Srivatsavai 	(void) _link_ntoa(newaddr, newmac, ETHERADDRL, IFT_OTHER);
457*4eaa4710SRishi Srivatsavai 	STP_IN_get_bridge_id(port->vlan_id, &prio, mac);
458*4eaa4710SRishi Srivatsavai 	if (memcmp(port->mac_addr, mac, ETHERADDRL) == 0) {
459*4eaa4710SRishi Srivatsavai 		syslog(LOG_NOTICE, "bridge ID must change: ID %s on %s changed "
460*4eaa4710SRishi Srivatsavai 		    "to %s", curid, port->name, newmac);
461*4eaa4710SRishi Srivatsavai 		uid_cfg.stp_enabled = STP_DISABLED;
462*4eaa4710SRishi Srivatsavai 		if ((rc = STP_IN_stpm_set_cfg(0, &uid_cfg)) != 0)
463*4eaa4710SRishi Srivatsavai 			syslog(LOG_ERR, "STP machine set config: %s",
464*4eaa4710SRishi Srivatsavai 			    STP_IN_get_error_explanation(rc));
465*4eaa4710SRishi Srivatsavai 		(void) memcpy(port->mac_addr, newaddr, ETHERADDRL);
466*4eaa4710SRishi Srivatsavai 		uid_cfg.stp_enabled = STP_ENABLED;
467*4eaa4710SRishi Srivatsavai 		if ((rc = STP_IN_stpm_set_cfg(0, &uid_cfg)) != 0)
468*4eaa4710SRishi Srivatsavai 			syslog(LOG_ERR, "STP machine set config: %s",
469*4eaa4710SRishi Srivatsavai 			    STP_IN_get_error_explanation(rc));
470*4eaa4710SRishi Srivatsavai 	} else {
471*4eaa4710SRishi Srivatsavai 		syslog(LOG_DEBUG,
472*4eaa4710SRishi Srivatsavai 		    "MAC address on %s changed from %s to %s", port->name,
473*4eaa4710SRishi Srivatsavai 		    curid, newmac);
474*4eaa4710SRishi Srivatsavai 		(void) memcpy(port->mac_addr, newaddr, ETHERADDRL);
475*4eaa4710SRishi Srivatsavai 	}
476*4eaa4710SRishi Srivatsavai }
477*4eaa4710SRishi Srivatsavai 
478*4eaa4710SRishi Srivatsavai boolean_t
rstp_add_port(struct portdata * port)479*4eaa4710SRishi Srivatsavai rstp_add_port(struct portdata *port)
480*4eaa4710SRishi Srivatsavai {
481*4eaa4710SRishi Srivatsavai 	int rc;
482*4eaa4710SRishi Srivatsavai 	UID_STP_PORT_CFG_T portcfg;
483*4eaa4710SRishi Srivatsavai 	bridge_vlanenab_t bve;
484*4eaa4710SRishi Srivatsavai 	bridge_setstate_t bss;
485*4eaa4710SRishi Srivatsavai 
486*4eaa4710SRishi Srivatsavai 	if (!port->stp_added &&
487*4eaa4710SRishi Srivatsavai 	    (rc = STP_IN_port_add(port->vlan_id, port->port_index)) != 0) {
488*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "STP add %s %d: %s", port->name,
489*4eaa4710SRishi Srivatsavai 		    port->port_index, STP_IN_get_error_explanation(rc));
490*4eaa4710SRishi Srivatsavai 		return (B_FALSE);
491*4eaa4710SRishi Srivatsavai 	}
492*4eaa4710SRishi Srivatsavai 	port->stp_added = B_TRUE;
493*4eaa4710SRishi Srivatsavai 
494*4eaa4710SRishi Srivatsavai 	/* guaranteed to succeed at this point */
495*4eaa4710SRishi Srivatsavai 	(void) get_init_port_cfg(port->vlan_id, port->port_index, &portcfg);
496*4eaa4710SRishi Srivatsavai 
497*4eaa4710SRishi Srivatsavai 	/*
498*4eaa4710SRishi Srivatsavai 	 * Restore state when reenabling STP engine, set fixed state when
499*4eaa4710SRishi Srivatsavai 	 * disabling.  For TRILL, we don't control forwarding at all, but we
500*4eaa4710SRishi Srivatsavai 	 * need to turn off our controls for TRILL to do its thing.
501*4eaa4710SRishi Srivatsavai 	 */
502*4eaa4710SRishi Srivatsavai 	bss.bss_linkid = port->linkid;
503*4eaa4710SRishi Srivatsavai 	if (protect != DLADM_BRIDGE_PROT_STP) {
504*4eaa4710SRishi Srivatsavai 		bss.bss_state = port->state = BLS_BLOCKLISTEN;
505*4eaa4710SRishi Srivatsavai 	} else if (portcfg.admin_non_stp) {
506*4eaa4710SRishi Srivatsavai 		bss.bss_state = port->admin_status && !port->sdu_failed &&
507*4eaa4710SRishi Srivatsavai 		    !port->bpdu_protect ? BLS_FORWARDING : BLS_BLOCKLISTEN;
508*4eaa4710SRishi Srivatsavai 	} else {
509*4eaa4710SRishi Srivatsavai 		bss.bss_state = port->state;
510*4eaa4710SRishi Srivatsavai 	}
511*4eaa4710SRishi Srivatsavai 	if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1) {
512*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name);
513*4eaa4710SRishi Srivatsavai 		goto failure;
514*4eaa4710SRishi Srivatsavai 	}
515*4eaa4710SRishi Srivatsavai 
516*4eaa4710SRishi Srivatsavai 	rc = STP_IN_enable_port(port->port_index,
517*4eaa4710SRishi Srivatsavai 	    port->admin_status && port->phys_status && !port->sdu_failed &&
518*4eaa4710SRishi Srivatsavai 	    protect == DLADM_BRIDGE_PROT_STP);
519*4eaa4710SRishi Srivatsavai 	if (rc != 0) {
520*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "STP enable %s %d: %s", port->name,
521*4eaa4710SRishi Srivatsavai 		    port->port_index, STP_IN_get_error_explanation(rc));
522*4eaa4710SRishi Srivatsavai 		goto failure;
523*4eaa4710SRishi Srivatsavai 	}
524*4eaa4710SRishi Srivatsavai 
525*4eaa4710SRishi Srivatsavai 	if (debugging) {
526*4eaa4710SRishi Srivatsavai 		rc = STP_IN_dbg_set_port_trace("all", True, 0,
527*4eaa4710SRishi Srivatsavai 		    port->port_index);
528*4eaa4710SRishi Srivatsavai 	} else {
529*4eaa4710SRishi Srivatsavai 		/* return to default debug state */
530*4eaa4710SRishi Srivatsavai 		rc = STP_IN_dbg_set_port_trace("all", False, 0,
531*4eaa4710SRishi Srivatsavai 		    port->port_index);
532*4eaa4710SRishi Srivatsavai 		if (rc == 0)
533*4eaa4710SRishi Srivatsavai 			rc = STP_IN_dbg_set_port_trace("sttrans", True, 0,
534*4eaa4710SRishi Srivatsavai 			    port->port_index);
535*4eaa4710SRishi Srivatsavai 	}
536*4eaa4710SRishi Srivatsavai 	if (rc != 0) {
537*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "STP trace %s %d: %s", port->name,
538*4eaa4710SRishi Srivatsavai 		    port->port_index, STP_IN_get_error_explanation(rc));
539*4eaa4710SRishi Srivatsavai 		goto failure;
540*4eaa4710SRishi Srivatsavai 	}
541*4eaa4710SRishi Srivatsavai 
542*4eaa4710SRishi Srivatsavai 	/* Clear out the kernel's allowed VLAN set; second walk will set */
543*4eaa4710SRishi Srivatsavai 	bve.bve_linkid = port->linkid;
544*4eaa4710SRishi Srivatsavai 	bve.bve_vlan = 0;
545*4eaa4710SRishi Srivatsavai 	bve.bve_onoff = B_FALSE;
546*4eaa4710SRishi Srivatsavai 	if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) {
547*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "unable to disable VLANs on %s: %m",
548*4eaa4710SRishi Srivatsavai 		    port->name);
549*4eaa4710SRishi Srivatsavai 		goto failure;
550*4eaa4710SRishi Srivatsavai 	}
551*4eaa4710SRishi Srivatsavai 
552*4eaa4710SRishi Srivatsavai 	if ((rc = STP_IN_port_set_cfg(0, port->port_index, &portcfg)) != 0) {
553*4eaa4710SRishi Srivatsavai 		syslog(LOG_ERR, "STP port configure %s %d: %s", port->name,
554*4eaa4710SRishi Srivatsavai 		    port->port_index, STP_IN_get_error_explanation(rc));
555*4eaa4710SRishi Srivatsavai 		goto failure;
556*4eaa4710SRishi Srivatsavai 	}
557*4eaa4710SRishi Srivatsavai 
558*4eaa4710SRishi Srivatsavai 	return (B_TRUE);
559*4eaa4710SRishi Srivatsavai 
560*4eaa4710SRishi Srivatsavai failure:
561*4eaa4710SRishi Srivatsavai 	(void) STP_IN_port_remove(port->vlan_id, port->port_index);
562*4eaa4710SRishi Srivatsavai 	port->stp_added = B_FALSE;
563*4eaa4710SRishi Srivatsavai 	return (B_FALSE);
564*4eaa4710SRishi Srivatsavai }
565