1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * bridged - bridging control daemon. This module provides DLPI-specific 29 * functions for interface to libdlpi. 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <syslog.h> 38 #include <stropts.h> 39 #include <stp_in.h> 40 #include <net/if_types.h> 41 #include <net/if_dl.h> 42 #include <sys/ethernet.h> 43 #include <sys/pfmod.h> 44 45 #include "global.h" 46 47 static const uchar_t bridge_group_address[] = BRIDGE_GROUP_ADDRESS; 48 49 static const ushort_t bpdu_filter[] = { 50 ENF_PUSHWORD | 0, /* check for 1:80:c2:0:0:0 dest. */ 51 ENF_PUSHLIT | ENF_CAND, 52 #ifdef _BIG_ENDIAN 53 0x0180, 54 #else 55 0x8001, 56 #endif 57 ENF_PUSHWORD | 1, 58 ENF_PUSHLIT | ENF_CAND, 59 #ifdef _BIG_ENDIAN 60 0xC200, 61 #else 62 0x00C2, 63 #endif 64 ENF_PUSHWORD | 2, 65 ENF_PUSHZERO | ENF_CAND, 66 ENF_PUSHWORD | 7, /* check for SSAP/DSAP 42 42 */ 67 ENF_PUSHLIT | ENF_CAND, 68 0x4242, 69 }; 70 71 /* 72 * Because we're called by dlpi_recv(), we're called with the engine lock held. 73 */ 74 /*ARGSUSED*/ 75 static void 76 dlpi_notify(dlpi_handle_t dlpi, dlpi_notifyinfo_t *info, void *arg) 77 { 78 struct portdata *port = arg; 79 int rc; 80 81 switch (info->dni_note) { 82 case DL_NOTE_SPEED: 83 /* libdlpi gives us Kbps, and we want Mbps */ 84 if (port->speed == info->dni_speed / 1000) 85 break; 86 port->speed = info->dni_speed / 1000; 87 if ((rc = STP_IN_changed_port_speed(port->port_index, 88 port->speed)) != 0) 89 syslog(LOG_ERR, "STP can't change port speed on %s: %s", 90 port->name, STP_IN_get_error_explanation(rc)); 91 break; 92 93 case DL_NOTE_PHYS_ADDR: 94 if (memcmp(info->dni_physaddr, port->mac_addr, ETHERADDRL) != 0) 95 rstp_change_mac(port, info->dni_physaddr); 96 break; 97 98 case DL_NOTE_LINK_DOWN: 99 if (!port->phys_status) 100 break; 101 port->phys_status = B_FALSE; 102 if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP || 103 port->sdu_failed) 104 break; 105 if ((rc = STP_IN_enable_port(port->port_index, False)) != 0) 106 syslog(LOG_ERR, "STP can't disable port %s: %s", 107 port->name, STP_IN_get_error_explanation(rc)); 108 break; 109 110 case DL_NOTE_LINK_UP: 111 if (port->phys_status) 112 break; 113 port->phys_status = B_TRUE; 114 if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP || 115 port->sdu_failed) { 116 port->bpdu_protect = B_FALSE; 117 break; 118 } 119 /* 120 * If we're not running STP, and the link state has just come 121 * up, then clear out any protection shutdown state, and allow 122 * us to forward again. 123 */ 124 if (port->admin_non_stp && port->bpdu_protect) { 125 port->bpdu_protect = B_FALSE; 126 enable_forwarding(port); 127 } 128 if ((rc = STP_IN_enable_port(port->port_index, True)) != 0) 129 syslog(LOG_ERR, "STP can't enable port %s: %s", 130 port->name, STP_IN_get_error_explanation(rc)); 131 break; 132 } 133 } 134 135 boolean_t 136 port_dlpi_open(const char *portname, struct portdata *port, 137 datalink_class_t class) 138 { 139 uchar_t addrbuf[DLPI_PHYSADDR_MAX]; 140 size_t alen = DLPI_PHYSADDR_MAX; 141 int rc; 142 char addrstr[ETHERADDRL * 3]; 143 144 /* 145 * We use DLPI 'raw' mode so that we get access to the received 146 * Ethernet 802 length field. libdlpi otherwise eats this value. Note 147 * that 'raw' mode support is required in order to use snoop, so it's 148 * expected to be common, even if it's not documented. 149 */ 150 rc = dlpi_open(portname, &port->dlpi, DLPI_RAW); 151 if (rc != DLPI_SUCCESS) { 152 syslog(LOG_ERR, "can't open %s: %s", portname, 153 dlpi_strerror(rc)); 154 return (B_FALSE); 155 } 156 157 port->phys_status = B_TRUE; 158 port->sdu_failed = B_FALSE; 159 port->bpdu_protect = B_FALSE; 160 161 /* 162 * Now that the driver is open, we can get at least the initial value 163 * of the interface speed. We need to do this before establishing the 164 * notify callback, so that it can update us later. 165 */ 166 get_dladm_speed(port); 167 168 /* 169 * Save off the libdlpi port name, as it's dynamically allocated, and 170 * the name we're passed is not. 171 */ 172 port->name = dlpi_linkname(port->dlpi); 173 174 /* 175 * We can't bind SAP 0 or enable multicast on an etherstub. It's ok, 176 * though, because there's no real hardware involved. 177 */ 178 if (class != DATALINK_CLASS_ETHERSTUB) { 179 if ((rc = dlpi_bind(port->dlpi, 0, NULL)) != DLPI_SUCCESS) { 180 syslog(LOG_ERR, "can't bind %s: %s", portname, 181 dlpi_strerror(rc)); 182 return (B_FALSE); 183 } 184 if ((rc = dlpi_enabmulti(port->dlpi, bridge_group_address, 185 sizeof (bridge_group_address))) != DLPI_SUCCESS) { 186 syslog(LOG_ERR, "can't enable multicast on %s: %s", 187 portname, dlpi_strerror(rc)); 188 return (B_FALSE); 189 } 190 } 191 192 if ((rc = dlpi_enabnotify(port->dlpi, 193 DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_DOWN | DL_NOTE_LINK_UP | 194 DL_NOTE_SPEED, dlpi_notify, port, &port->notifyid)) != 195 DLPI_SUCCESS) { 196 syslog(LOG_WARNING, "no DLPI notification on %s: %s", portname, 197 dlpi_strerror(rc)); 198 } 199 200 rc = dlpi_get_physaddr(port->dlpi, DL_CURR_PHYS_ADDR, addrbuf, &alen); 201 if (rc != DLPI_SUCCESS) { 202 syslog(LOG_ERR, "unable to get MAC address on %s: %s", 203 port->name, dlpi_strerror(rc)); 204 return (B_FALSE); 205 } 206 if (alen != ETHERADDRL) { 207 syslog(LOG_ERR, "bad MAC address length %d on %s", 208 alen, port->name); 209 return (B_FALSE); 210 } 211 (void) memcpy(port->mac_addr, addrbuf, ETHERADDRL); 212 213 if (class != DATALINK_CLASS_ETHERSTUB) { 214 int fd = dlpi_fd(port->dlpi); 215 int lowflag = 1; 216 217 if (strioctl(fd, DLIOCLOWLINK, &lowflag, sizeof (lowflag)) != 0) 218 syslog(LOG_WARNING, "low-link notify failed on %s: %m", 219 portname); 220 if (ioctl(fd, I_PUSH, "pfmod") == 0) { 221 struct packetfilt pf; 222 223 pf.Pf_Priority = 0; 224 pf.Pf_FilterLen = sizeof (bpdu_filter) / 225 sizeof (*bpdu_filter); 226 (void) memcpy(pf.Pf_Filter, bpdu_filter, 227 sizeof (bpdu_filter)); 228 if (strioctl(fd, PFIOCSETF, &pf, sizeof (pf)) == -1) 229 syslog(LOG_WARNING, 230 "pfil ioctl failed on %s: %m", portname); 231 } else { 232 syslog(LOG_WARNING, "pfil push failed on %s: %m", 233 portname); 234 } 235 } 236 237 if (debugging) { 238 (void) _link_ntoa(port->mac_addr, addrstr, ETHERADDRL, 239 IFT_OTHER); 240 syslog(LOG_DEBUG, "got MAC address %s on %s", addrstr, 241 port->name); 242 } 243 244 return (B_TRUE); 245 } 246