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
dlpi_notify(dlpi_handle_t dlpi,dlpi_notifyinfo_t * info,void * arg)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
port_dlpi_open(const char * portname,struct portdata * port,datalink_class_t class)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