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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * bridged - bridging control daemon. This module handles events and general
28 * port-related operations.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <syslog.h>
38 #include <libdlpi.h>
39 #include <libdladm.h>
40 #include <libdllink.h>
41 #include <libdlbridge.h>
42 #include <libdlvlan.h>
43 #include <libdlstat.h>
44 #include <stp_in.h>
45 #include <stp_vectors.h>
46 #include <net/if_types.h>
47 #include <net/bridge.h>
48 #include <sys/ethernet.h>
49
50 #include "global.h"
51
52 int refresh_count = 1; /* never zero */
53 dladm_bridge_prot_t protect = DLADM_BRIDGE_PROT_STP;
54
55 /*
56 * The 'allports' array is an array of pointers to the struct portdata
57 * structures. We reallocate 'allports' as needed, but the portdata must
58 * remain where it's initially allocated, because libdlpi's notification
59 * mechanism has a copy of a pointer to this structure.
60 */
61 uint_t nextport;
62 struct portdata **allports;
63
64 /* Port allocation increment (arbitrary) */
65 #define ALLOCINCR 10
66 static uint_t numports;
67
68 static datalink_id_t main_linkid;
69
70 int control_fd;
71
72 static void
linkdown(void)73 linkdown(void)
74 {
75 (void) dladm_destroy_datalink_id(dlhandle, main_linkid,
76 DLADM_OPT_ACTIVE);
77 }
78
79 void
open_bridge_control(void)80 open_bridge_control(void)
81 {
82 bridge_newbridge_t bnb;
83 dladm_status_t status;
84 char buf[DLADM_STRSIZE];
85
86 if ((control_fd = open(BRIDGE_CTLPATH, O_RDWR | O_NONBLOCK)) == -1) {
87 perror(BRIDGE_CTLPATH);
88 exit(EXIT_FAILURE);
89 }
90 (void) snprintf(bnb.bnb_name, sizeof (bnb.bnb_name), "%s0",
91 instance_name);
92 status = dladm_name2info(dlhandle, bnb.bnb_name, &bnb.bnb_linkid, NULL,
93 NULL, NULL);
94 if (status != DLADM_STATUS_OK) {
95 (void) fprintf(stderr, "bridged: %s: %s\n", bnb.bnb_name,
96 dladm_status2str(status, buf));
97 exit(EXIT_FAILURE);
98 }
99 if (strioctl(control_fd, BRIOC_NEWBRIDGE, &bnb, sizeof (bnb)) == -1) {
100 perror("NEWBRIDGE");
101 exit(EXIT_FAILURE);
102 }
103 main_linkid = bnb.bnb_linkid;
104 if (strioctl(control_fd, BRIOC_TABLEMAX, &tablemax,
105 sizeof (tablemax)) == -1) {
106 syslog(LOG_ERR, "cannot set table max %lu on bridge %s: %m",
107 tablemax, instance_name);
108 exit(EXIT_FAILURE);
109 }
110 /*
111 * This covers for any previous incarnation where we might have crashed
112 * or been SIGKILL'd and failed to take down the datalink.
113 */
114 linkdown();
115 (void) atexit(linkdown);
116 status = dladm_up_datalink_id(dlhandle, bnb.bnb_linkid);
117 if (status != DLADM_STATUS_OK) {
118 (void) fprintf(stderr, "bridged: %s link up: %s\n",
119 bnb.bnb_name, dladm_status2str(status, buf));
120 exit(EXIT_FAILURE);
121 }
122 }
123
124 struct portdata *
find_by_linkid(datalink_id_t linkid)125 find_by_linkid(datalink_id_t linkid)
126 {
127 int i;
128 struct portdata *port;
129
130 for (i = 0; i < nextport; i++) {
131 port = allports[i];
132 if (port->linkid == linkid)
133 return (port);
134 }
135 return (NULL);
136 }
137
138 /*ARGSUSED2*/
139 static int
set_vlan(dladm_handle_t handle,datalink_id_t linkid,void * arg)140 set_vlan(dladm_handle_t handle, datalink_id_t linkid, void *arg)
141 {
142 struct portdata *port;
143 dladm_status_t status;
144 dladm_vlan_attr_t vinfo;
145 char pointless[DLADM_STRSIZE];
146 bridge_vlanenab_t bve;
147
148 status = dladm_vlan_info(handle, linkid, &vinfo, DLADM_OPT_ACTIVE);
149 if (status != DLADM_STATUS_OK) {
150 syslog(LOG_DEBUG, "can't get VLAN info on link ID %u: %s",
151 linkid, dladm_status2str(status, pointless));
152 return (DLADM_WALK_CONTINUE);
153 }
154
155 port = find_by_linkid(vinfo.dv_linkid);
156 if (port == NULL || !port->kern_added)
157 return (DLADM_WALK_CONTINUE);
158
159 bve.bve_linkid = port->linkid;
160 bve.bve_vlan = vinfo.dv_vid;
161 bve.bve_onoff = B_TRUE;
162 if (strioctl(control_fd, BRIOC_VLANENAB, &bve, sizeof (bve)) == -1) {
163 syslog(LOG_ERR, "unable to enable VLAN %d on linkid %u: %m",
164 vinfo.dv_vid, port->linkid);
165 return (DLADM_WALK_TERMINATE);
166 } else {
167 return (DLADM_WALK_CONTINUE);
168 }
169 }
170
171 /*
172 * If the named port already exists, then update its configuration. If it
173 * doesn't, then create and enable it.
174 */
175 static void
update_port(int vlan_id,const char * portname,datalink_id_t linkid,datalink_class_t class)176 update_port(int vlan_id, const char *portname, datalink_id_t linkid,
177 datalink_class_t class)
178 {
179 int posn;
180 struct portdata *port;
181 struct pollfd *fds;
182 int port_index;
183 struct {
184 datalink_id_t linkid;
185 char linkname[MAXLINKNAMELEN];
186 } adddata;
187 bridge_setpvid_t bsv;
188 uint_t propval, valcnt;
189 dladm_status_t status;
190
191 for (posn = 0; posn < nextport; posn++) {
192 if (allports[posn]->linkid == linkid)
193 break;
194 }
195
196 /* If we need to allocate more array space, then do so in chunks. */
197 if (posn >= numports) {
198 struct portdata **newarr;
199
200 newarr = realloc(allports,
201 sizeof (*newarr) * (nextport + ALLOCINCR));
202 if (newarr != NULL)
203 allports = newarr;
204 fds = realloc(fdarray,
205 sizeof (*fds) * (nextport + ALLOCINCR + FDOFFSET));
206 if (fds != NULL)
207 fdarray = fds;
208 if (newarr == NULL || fds == NULL) {
209 syslog(LOG_ERR, "unable to add %s; no memory",
210 portname);
211 return;
212 }
213 numports = nextport + ALLOCINCR;
214 }
215
216 port_index = posn + 1;
217 fds = fdarray + posn + FDOFFSET;
218
219 /* If our linkid search ran to the end, then this is a new port. */
220 if (posn == nextport) {
221 if ((port = calloc(1, sizeof (*port))) == NULL) {
222 syslog(LOG_ERR, "unable to add %s; no memory",
223 portname);
224 return;
225 }
226 allports[posn] = port;
227 port->vlan_id = vlan_id;
228 port->linkid = linkid;
229 port->port_index = port_index;
230 port->phys_status = B_TRUE;
231 port->admin_status = B_TRUE;
232 port->state = BLS_BLOCKLISTEN;
233 nextport++;
234 } else {
235 /* Located port by linkid; we're just updating existing data */
236 port = allports[posn];
237
238 /*
239 * If it changed name, then close and reopen so we log under
240 * the most current name for this port.
241 */
242 if (port->name != NULL && strcmp(portname, port->name) != 0) {
243 if (port->dlpi != NULL)
244 dlpi_close(port->dlpi);
245 port->dlpi = NULL;
246 port->name = NULL;
247 fds->fd = -1;
248 fds->events = 0;
249 }
250 }
251
252 /*
253 * If the port is not yet attached to the bridge in the kernel, then do
254 * that now.
255 */
256 if (!port->kern_added) {
257 adddata.linkid = linkid;
258 (void) strlcpy(adddata.linkname, portname,
259 sizeof (adddata.linkname));
260 if (strioctl(control_fd, BRIOC_ADDLINK, &adddata,
261 sizeof (adddata.linkid) + strlen(adddata.linkname)) == -1) {
262 syslog(LOG_ERR, "cannot bridge %s: %m", portname);
263 goto failure;
264 }
265 port->kern_added = B_TRUE;
266 }
267
268 port->referenced = B_TRUE;
269
270 valcnt = 1;
271 status = dladm_get_linkprop_values(dlhandle, linkid,
272 DLADM_PROP_VAL_PERSISTENT, "forward", &propval, &valcnt);
273 if (status == DLADM_STATUS_OK)
274 port->admin_status = propval;
275
276 bsv.bsv_vlan = 1;
277 status = dladm_get_linkprop_values(dlhandle, linkid,
278 DLADM_PROP_VAL_PERSISTENT, "default_tag", &propval, &valcnt);
279 if (status == DLADM_STATUS_OK)
280 bsv.bsv_vlan = propval;
281
282 bsv.bsv_linkid = linkid;
283 if (strioctl(control_fd, BRIOC_SETPVID, &bsv, sizeof (bsv)) == -1) {
284 syslog(LOG_ERR, "can't set PVID on %s: %m", portname);
285 goto failure;
286 }
287
288 if (port->dlpi == NULL) {
289 if (!port_dlpi_open(portname, port, class))
290 goto failure;
291 fds->fd = dlpi_fd(port->dlpi);
292 fds->events = POLLIN;
293 }
294
295 if (rstp_add_port(port))
296 return;
297
298 failure:
299 if (port->dlpi != NULL) {
300 dlpi_close(port->dlpi);
301 port->dlpi = NULL;
302 port->name = NULL;
303 fds->fd = -1;
304 fds->events = 0;
305 }
306 if (port->kern_added) {
307 if (strioctl(control_fd, BRIOC_REMLINK, &port->linkid,
308 sizeof (port->linkid)) == -1)
309 syslog(LOG_ERR, "cannot remove from bridge %s: %m",
310 portname);
311 else
312 port->kern_added = B_FALSE;
313 }
314 if (posn + 1 == nextport) {
315 free(port);
316 nextport--;
317 }
318 }
319
320 /*ARGSUSED2*/
321 static int
update_link(dladm_handle_t handle,datalink_id_t linkid,void * arg)322 update_link(dladm_handle_t handle, datalink_id_t linkid, void *arg)
323 {
324 dladm_status_t status;
325 char bridge[MAXLINKNAMELEN], linkname[MAXLINKNAMELEN];
326 char pointless[DLADM_STRSIZE];
327 datalink_class_t class;
328
329 status = dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge));
330 if (status == DLADM_STATUS_OK && strcmp(bridge, instance_name) == 0) {
331 status = dladm_datalink_id2info(handle, linkid, NULL, &class,
332 NULL, linkname, sizeof (linkname));
333 if (status == DLADM_STATUS_OK) {
334 update_port(0, linkname, linkid, class);
335 } else {
336 syslog(LOG_ERR, "unable to get link info for ID %u: %s",
337 linkid, dladm_status2str(status, pointless));
338 }
339 } else if (debugging) {
340 if (status != DLADM_STATUS_OK)
341 syslog(LOG_DEBUG,
342 "unable to get bridge data for ID %u: %s",
343 linkid, dladm_status2str(status, pointless));
344 else
345 syslog(LOG_DEBUG, "link ID %u is on bridge %s, not %s",
346 linkid, bridge, instance_name);
347 }
348 return (DLADM_WALK_CONTINUE);
349 }
350
351 /*
352 * Refresh action - reread configuration properties.
353 */
354 static void
handle_refresh(int sigfd)355 handle_refresh(int sigfd)
356 {
357 int i;
358 struct portdata *pdp;
359 struct pollfd *fdp;
360 char buf[16];
361 dladm_status_t status;
362 boolean_t new_debug;
363 uint32_t new_tablemax;
364
365 /* Drain signal events from pipe */
366 if (sigfd != -1)
367 (void) read(sigfd, buf, sizeof (buf));
368
369 status = dladm_bridge_get_privprop(instance_name, &new_debug,
370 &new_tablemax);
371 if (status == DLADM_STATUS_OK) {
372 if (debugging && !new_debug)
373 syslog(LOG_DEBUG, "disabling debugging");
374 debugging = new_debug;
375 if (new_tablemax != tablemax) {
376 syslog(LOG_DEBUG, "changed tablemax from %lu to %lu",
377 tablemax, new_tablemax);
378 if (strioctl(control_fd, BRIOC_TABLEMAX, &new_tablemax,
379 sizeof (tablemax)) == -1)
380 syslog(LOG_ERR, "cannot set table max "
381 "%lu on bridge %s: %m", tablemax,
382 instance_name);
383 else
384 tablemax = new_tablemax;
385 }
386 } else {
387 syslog(LOG_ERR, "%s: unable to refresh bridge properties: %s",
388 instance_name, dladm_status2str(status, buf));
389 }
390
391 rstp_refresh();
392
393 for (i = 0; i < nextport; i++)
394 allports[i]->referenced = B_FALSE;
395
396 /*
397 * libdladm doesn't guarantee anything about link ordering in a walk,
398 * so we do this walk twice: once to pick up the ports, and a second
399 * time to get the enabled VLANs on all ports.
400 */
401 (void) dladm_walk_datalink_id(update_link, dlhandle, NULL,
402 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
403
404 (void) dladm_walk_datalink_id(set_vlan, dlhandle, NULL,
405 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
406
407 /*
408 * If any ports now show up as unreferenced, then they've been removed
409 * from the configuration.
410 */
411 for (i = 0; i < nextport; i++) {
412 pdp = allports[i];
413 fdp = fdarray + i + FDOFFSET;
414 if (!pdp->referenced) {
415 if (pdp->stp_added) {
416 (void) STP_IN_port_remove(pdp->vlan_id,
417 pdp->port_index);
418 pdp->stp_added = B_FALSE;
419 }
420 if (pdp->dlpi != NULL) {
421 dlpi_close(pdp->dlpi);
422 pdp->dlpi = NULL;
423 pdp->name = NULL;
424 fdp->fd = -1;
425 fdp->events = 0;
426 }
427 if (pdp->kern_added) {
428 if (strioctl(control_fd, BRIOC_REMLINK,
429 &pdp->linkid, sizeof (pdp->linkid)) == -1)
430 syslog(LOG_ERR, "cannot remove linkid "
431 "%u from bridge %s: %m",
432 pdp->linkid, instance_name);
433 pdp->kern_added = B_FALSE;
434 }
435 }
436 }
437
438 if (++refresh_count == 0)
439 refresh_count = 1;
440 }
441
442 /*
443 * Handle messages on the common control stream. This currently just deals
444 * with port SDU mismatches.
445 */
446 static void
handle_control(void)447 handle_control(void)
448 {
449 bridge_ctl_t bc;
450 ssize_t retv;
451 struct portdata *port;
452 int rc;
453
454 retv = read(control_fd, &bc, sizeof (bc));
455 if (retv != sizeof (bc))
456 return;
457 if ((port = find_by_linkid(bc.bc_linkid)) == NULL)
458 return;
459 if (port->sdu_failed == bc.bc_failed)
460 return;
461 port->sdu_failed = bc.bc_failed;
462 if (!port->phys_status || !port->admin_status ||
463 protect != DLADM_BRIDGE_PROT_STP)
464 return;
465 if (port->admin_non_stp) {
466 bridge_setstate_t bss;
467
468 bss.bss_linkid = port->linkid;
469 bss.bss_state = !port->sdu_failed && !port->bpdu_protect ?
470 BLS_FORWARDING : BLS_BLOCKLISTEN;
471 if (strioctl(control_fd, BRIOC_SETSTATE, &bss,
472 sizeof (bss)) == -1) {
473 syslog(LOG_ERR, "cannot set STP state on %s: %m",
474 port->name);
475 }
476 }
477 if ((rc = STP_IN_enable_port(port->port_index, !bc.bc_failed)) != 0)
478 syslog(LOG_ERR, "STP can't %s port %s for SDU failure: %s",
479 port->name, bc.bc_failed ? "disable" : "enable",
480 STP_IN_get_error_explanation(rc));
481 }
482
483 static void
receive_packet(struct portdata * port)484 receive_packet(struct portdata *port)
485 {
486 int rc;
487 size_t buflen;
488 uint16_t buffer[ETHERMAX / sizeof (uint16_t)];
489 struct ether_header *eh;
490 char sender[ETHERADDRL * 3];
491
492 buflen = sizeof (buffer);
493 rc = dlpi_recv(port->dlpi, NULL, NULL, buffer, &buflen, 1, NULL);
494 if (rc != DLPI_SUCCESS) {
495 if (rc != DLPI_ETIMEDOUT)
496 syslog(LOG_ERR, "receive failure on %s: %s", port->name,
497 dlpi_strerror(rc));
498 return;
499 }
500
501 /*
502 * If we're administratively disabled, then don't deliver packets to
503 * the STP state machine. It will re-enable the port because it uses
504 * the same variable for both link status and administrative state.
505 */
506 if (!port->admin_status || protect != DLADM_BRIDGE_PROT_STP) {
507 if (debugging)
508 syslog(LOG_DEBUG,
509 "discard BPDU on non-forwarding interface %s",
510 port->name);
511 return;
512 }
513
514 /*
515 * There's a mismatch between the librstp and libdlpi expectations on
516 * receive. librstp wants the packet to start with the 802 length
517 * field, not the destination address.
518 */
519 eh = (struct ether_header *)buffer;
520 rc = STP_IN_check_bpdu_header((BPDU_T *)&eh->ether_type, buflen);
521
522 /*
523 * Note that we attempt to avoid calling the relatively expensive
524 * _link_ntoa function unless we're going to use the result. In normal
525 * usage, we don't need this string.
526 */
527 if (rc == 0) {
528 if (port->admin_non_stp && !port->bpdu_protect) {
529 bridge_setstate_t bss;
530
531 (void) _link_ntoa(eh->ether_shost.ether_addr_octet,
532 sender, ETHERADDRL, IFT_OTHER);
533 syslog(LOG_WARNING, "unexpected BPDU on %s from %s; "
534 "forwarding disabled", port->name, sender);
535 port->bpdu_protect = B_TRUE;
536 bss.bss_linkid = port->linkid;
537 bss.bss_state = BLS_BLOCKLISTEN;
538 if (strioctl(control_fd, BRIOC_SETSTATE, &bss,
539 sizeof (bss)) == -1) {
540 syslog(LOG_ERR, "cannot set STP state on "
541 "%s: %m", port->name);
542 }
543 return;
544 }
545 if (debugging) {
546 (void) _link_ntoa(eh->ether_shost.ether_addr_octet,
547 sender, ETHERADDRL, IFT_OTHER);
548 syslog(LOG_DEBUG, "got BPDU from %s on %s; %d bytes",
549 sender, port->name, buflen);
550 }
551 rc = STP_IN_rx_bpdu(port->vlan_id, port->port_index,
552 (BPDU_T *)&eh->ether_type, buflen);
553 }
554 if (rc != 0) {
555 (void) _link_ntoa(eh->ether_shost.ether_addr_octet, sender,
556 ETHERADDRL, IFT_OTHER);
557 syslog(LOG_DEBUG,
558 "discarded malformed packet on %s from %s: %s",
559 port->name, sender, STP_IN_get_error_explanation(rc));
560 }
561 }
562
563 void
get_dladm_speed(struct portdata * port)564 get_dladm_speed(struct portdata *port)
565 {
566 dladm_status_t status;
567 uint64_t ifspeed;
568
569 status = dladm_get_single_mac_stat(dlhandle, port->linkid, "ifspeed",
570 KSTAT_DATA_UINT64, &ifspeed);
571 if (status == DLADM_STATUS_OK && ifspeed != 0)
572 port->speed = ifspeed / 1000000;
573 else
574 port->speed = 10UL;
575 }
576
577 void
enable_forwarding(struct portdata * port)578 enable_forwarding(struct portdata *port)
579 {
580 bridge_setstate_t bss;
581
582 bss.bss_linkid = port->linkid;
583 bss.bss_state = BLS_FORWARDING;
584 if (strioctl(control_fd, BRIOC_SETSTATE, &bss, sizeof (bss)) == -1)
585 syslog(LOG_ERR, "cannot set STP state on %s: %m", port->name);
586 }
587
588 void
event_loop(void)589 event_loop(void)
590 {
591 int i;
592 hrtime_t last_time, now;
593 int tout;
594
595 if (lock_engine() != 0) {
596 syslog(LOG_ERR, "mutex lock");
597 exit(EXIT_FAILURE);
598 }
599
600 /* Bootstrap configuration */
601 handle_refresh(-1);
602
603 last_time = gethrtime();
604 while (!shutting_down) {
605 now = gethrtime();
606 if (now - last_time >= 1000000000ll) {
607 (void) STP_IN_one_second();
608 tout = 1000;
609 last_time = now;
610 } else {
611 tout = 1000 - (now - last_time) / 1000000ll;
612 }
613 unlock_engine();
614 (void) poll(fdarray, nextport + FDOFFSET, tout);
615 if (lock_engine() != 0) {
616 syslog(LOG_ERR, "mutex lock");
617 exit(EXIT_FAILURE);
618 }
619 if (fdarray[0].revents & POLLIN)
620 handle_refresh(fdarray[0].fd);
621 if (fdarray[1].revents & POLLIN)
622 handle_control();
623 for (i = 0; i < nextport; i++) {
624 if (fdarray[i + FDOFFSET].revents & POLLIN)
625 receive_packet(allports[i]);
626 }
627 }
628 unlock_engine();
629 }
630