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 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 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 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 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 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 * 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 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 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 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 * 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 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 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 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 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 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 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 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