diff options
-rw-r--r-- | sbin/ifconfig/ifbridge.c | 533 | ||||
-rw-r--r-- | share/man/man4/if_bridge.4 | 169 | ||||
-rw-r--r-- | sys/net/bridgestp.c | 1174 | ||||
-rw-r--r-- | sys/net/if_bridge.c | 2408 | ||||
-rw-r--r-- | sys/net/if_bridgevar.h | 357 |
5 files changed, 4641 insertions, 0 deletions
diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c new file mode 100644 index 000000000000..bf41ea8af8b8 --- /dev/null +++ b/sbin/ifconfig/ifbridge.c @@ -0,0 +1,533 @@ +/*- + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_bridgevar.h> +#include <net/route.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#include <errno.h> + +#include "ifconfig.h" + +static int +get_val(const char *cp, u_long *valp) +{ + char *endptr; + u_long val; + + errno = 0; + val = strtoul(cp, &endptr, 0); + if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) + return (-1); + + *valp = val; + return (0); +} + +static int +do_cmd(int sock, u_long op, void *arg, size_t argsize, int set) +{ + struct ifdrv ifd; + + memset(&ifd, 0, sizeof(ifd)); + + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = op; + ifd.ifd_len = argsize; + ifd.ifd_data = arg; + + return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); +} + +static void +do_bridgeflag(int sock, const char *ifs, int flag, int set) +{ + struct ifbreq req; + + strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); + + if (do_cmd(sock, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) + err(1, "unable to get bridge flags"); + + if (set) + req.ifbr_ifsflags |= flag; + else + req.ifbr_ifsflags &= ~flag; + + if (do_cmd(sock, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) + err(1, "unable to set bridge flags"); +} + +static void +bridge_interfaces(int s, const char *prefix, int flags) +{ + static const char *stpstates[] = { + "disabled", + "listening", + "learning", + "forwarding", + "blocking", + }; + struct ifbifconf bifc; + struct ifbreq *req; + char *inbuf = NULL, *ninbuf; + int i, len = 8192; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate interface buffer"); + bifc.ifbic_len = len; + bifc.ifbic_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGGIFS, &bifc, sizeof(bifc), 0) < 0) + err(1, "unable to get interface list"); + if ((bifc.ifbic_len + sizeof(*req)) < len) + break; + len *= 2; + } + + for (i = 0; i < bifc.ifbic_len / sizeof(*req); i++) { + req = bifc.ifbic_req + i; + printf("%s%s ", prefix, req->ifbr_ifsname); + printb("flags", req->ifbr_ifsflags, IFBIFBITS); + printf("\n"); + + if (!flags) continue; + + printf("%s\t", prefix); + printf("port %u priority %u", + req->ifbr_portno, req->ifbr_priority); + if (req->ifbr_ifsflags & IFBIF_STP) { + printf(" path cost %u", req->ifbr_path_cost); + if (req->ifbr_state < + sizeof(stpstates) / sizeof(stpstates[0])) + printf(" %s", stpstates[req->ifbr_state]); + else + printf(" <unknown state %d>", + req->ifbr_state); + } + printf("\n"); + } + + free(inbuf); +} + +static void +bridge_addresses(int s, const char *prefix) +{ + struct ifbaconf ifbac; + struct ifbareq *ifba; + char *inbuf = NULL, *ninbuf; + int i, len = 8192; + struct ether_addr ea; + + for (;;) { + ninbuf = realloc(inbuf, len); + if (ninbuf == NULL) + err(1, "unable to allocate address buffer"); + ifbac.ifbac_len = len; + ifbac.ifbac_buf = inbuf = ninbuf; + if (do_cmd(s, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) + err(1, "unable to get address cache"); + if ((ifbac.ifbac_len + sizeof(*ifba)) < len) + break; + len *= 2; + } + + for (i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { + ifba = ifbac.ifbac_req + i; + memcpy(ea.octet, ifba->ifba_dst, + sizeof(ea.octet)); + printf("%s%s %s %lu ", prefix, ether_ntoa(&ea), + ifba->ifba_ifsname, ifba->ifba_expire); + printb("flags", ifba->ifba_flags, IFBAFBITS); + printf("\n"); + } + + free(inbuf); +} + +static void +bridge_status(int s) +{ + struct ifbrparam param; + u_int16_t pri; + u_int8_t ht, fd, ma; + + if (do_cmd(s, BRDGGPRI, ¶m, sizeof(param), 0) < 0) + return; + pri = param.ifbrp_prio; + + if (do_cmd(s, BRDGGHT, ¶m, sizeof(param), 0) < 0) + return; + ht = param.ifbrp_hellotime; + + if (do_cmd(s, BRDGGFD, ¶m, sizeof(param), 0) < 0) + return; + fd = param.ifbrp_fwddelay; + + if (do_cmd(s, BRDGGMA, ¶m, sizeof(param), 0) < 0) + return; + ma = param.ifbrp_maxage; + + printf("\tpriority %u hellotime %u fwddelay %u maxage %u\n", + pri, ht, fd, ma); + + bridge_interfaces(s, "\tmember: ", 0); + + return; + +} + +static void +setbridge_add(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGADD, &req, sizeof(req), 1) < 0) + err(1, "BRDGADD %s", val); +} + +static void +setbridge_delete(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); + if (do_cmd(s, BRDGDEL, &req, sizeof(req), 1) < 0) + err(1, "BRDGDEL %s", val); +} + +static void +setbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 1); +} + +static void +unsetbridge_discover(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_DISCOVER, 0); +} + +static void +setbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 1); +} + +static void +unsetbridge_learn(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_LEARNING, 0); +} + +static void +setbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 1); +} + +static void +unsetbridge_stp(const char *val, int d, int s, const struct afswtch *afp) +{ + + do_bridgeflag(s, val, IFBIF_STP, 0); +} + +static void +setbridge_flush(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHDYN; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_flushall(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + req.ifbr_ifsflags = IFBF_FLUSHALL; + if (do_cmd(s, BRDGFLUSH, &req, sizeof(req), 1) < 0) + err(1, "BRDGFLUSH"); +} + +static void +setbridge_static(const char *val, const char *mac, int s, + const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname)); + + ea = ether_aton(mac); + if (ea == NULL) + errx(1, "%s: invalid address: %s", val, mac); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + req.ifba_flags = IFBAF_STATIC; + + if (do_cmd(s, BRDGSADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGSADDR %s", val); +} + +static void +setbridge_deladdr(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifbareq req; + struct ether_addr *ea; + + memset(&req, 0, sizeof(req)); + + ea = ether_aton(val); + if (ea == NULL) + errx(1, "invalid address: %s", val); + + memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); + + if (do_cmd(s, BRDGDADDR, &req, sizeof(req), 1) < 0) + err(1, "BRDGDADDR %s", val); +} + +static void +setbridge_addr(const char *val, int d, int s, const struct afswtch *afp) +{ + + bridge_addresses(s, ""); +} + +static void +setbridge_maxaddr(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_csize = val & 0xffffffff; + + if (do_cmd(s, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSCACHE %s", arg); +} + +static void +setbridge_hellotime(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_hellotime = val & 0xff; + + if (do_cmd(s, BRDGSHT, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSHT %s", arg); +} + +static void +setbridge_fwddelay(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_fwddelay = val & 0xff; + + if (do_cmd(s, BRDGSFD, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSFD %s", arg); +} + +static void +setbridge_maxage(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_maxage = val & 0xff; + + if (do_cmd(s, BRDGSMA, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSMA %s", arg); +} + +static void +setbridge_priority(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_prio = val & 0xffff; + + if (do_cmd(s, BRDGSPRI, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSPRI %s", arg); +} + +static void +setbridge_ifpriority(const char *ifn, const char *pri, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", pri); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_priority = val & 0xff; + + if (do_cmd(s, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPRIO %s", pri); +} + +static void +setbridge_ifpathcost(const char *ifn, const char *cost, int s, + const struct afswtch *afp) +{ + struct ifbreq req; + u_long val; + + memset(&req, 0, sizeof(req)); + + if (get_val(cost, &val) < 0 || (val & ~0xff) != 0) + errx(1, "invalid value: %s", cost); + + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + req.ifbr_path_cost = val & 0xffff; + + if (do_cmd(s, BRDGSIFCOST, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFCOST %s", cost); +} + +static void +setbridge_timeout(const char *arg, int d, int s, const struct afswtch *afp) +{ + struct ifbrparam param; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) + errx(1, "invalid value: %s", arg); + + param.ifbrp_ctime = val & 0xffffffff; + + if (do_cmd(s, BRDGSTO, ¶m, sizeof(param), 1) < 0) + err(1, "BRDGSTO %s", arg); +} + +static struct cmd bridge_cmds[] = { + DEF_CMD_ARG("addm", setbridge_add), + DEF_CMD_ARG("deletem", setbridge_delete), + DEF_CMD_ARG("discover", setbridge_discover), + DEF_CMD_ARG("-discover", unsetbridge_discover), + DEF_CMD_ARG("learn", setbridge_learn), + DEF_CMD_ARG("-learn", unsetbridge_learn), + DEF_CMD_ARG("stp", setbridge_stp), + DEF_CMD_ARG("-stp", unsetbridge_stp), + DEF_CMD("flush", 0, setbridge_flush), + DEF_CMD("flushall", 0, setbridge_flushall), + DEF_CMD_ARG2("static", setbridge_static), + DEF_CMD_ARG("deladdr", setbridge_deladdr), + DEF_CMD("addr", 1, setbridge_addr), + DEF_CMD_ARG("maxaddr", setbridge_maxaddr), + DEF_CMD_ARG("hellotime", setbridge_hellotime), + DEF_CMD_ARG("fwddelay", setbridge_fwddelay), + DEF_CMD_ARG("maxage", setbridge_maxage), + DEF_CMD_ARG("priority", setbridge_priority), + DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), + DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), + DEF_CMD_ARG("timeout", setbridge_timeout), +}; +static struct afswtch af_bridge = { + .af_name = "af_bridge", + .af_af = AF_UNSPEC, + .af_other_status = bridge_status, +}; + +static __constructor void +bridge_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(bridge_cmds); i++) + cmd_register(&bridge_cmds[i]); + af_register(&af_bridge); +#undef N +} diff --git a/share/man/man4/if_bridge.4 b/share/man/man4/if_bridge.4 new file mode 100644 index 000000000000..54fa1aa021c7 --- /dev/null +++ b/share/man/man4/if_bridge.4 @@ -0,0 +1,169 @@ +.\" $NetBSD: bridge.4,v 1.5 2004/01/31 20:14:11 jdc Exp $ +.\" +.\" Copyright 2001 Wasabi Systems, Inc. +.\" All rights reserved. +.\" +.\" Written by Jason R. Thorpe for Wasabi Systems, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project by +.\" Wasabi Systems, Inc. +.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.Dd January 4, 2004 +.Dt IF_BRIDGE 4 +.Os +.Sh NAME +.Nm if_bridge +.Nd network bridge device +.Sh SYNOPSIS +.Cd device if_bridge +.Sh DESCRIPTION +The +.Nm +driver creates a logical link between two or more IEEE 802 networks +that use the same (or +.Dq similar enough ) +framing format. +For example, it is possible to bridge Ethernet and 802.11 networks together, +but it is not possible to bridge Ethernet and Token Ring together. +.Pp +Each +.Nm +interface is created at runtime using interface cloning. +This is +most easily done with the +.Xr ifconfig 8 +.Cm create +command or using the +.Va cloned_interfaces +variable in +.Xr rc.conf 5 . +.Pp +A bridge can be used to provide several services, such as a simple +802.11-to-Ethernet bridge for wireless hosts, and traffic isolation. +.Pp +A bridge works like a hub, forwarding traffic from one interface +to another. +Multicast and broadcast packets are always forwarded to all +interfaces that are part of the bridge. +For unicast traffic, the bridge learns which MAC addresses are associated +with which interfaces and will forward the traffic selectively. +.Pp +The +.Nm +driver implements the IEEE 802.1D Spanning Tree protocol (STP). +Spanning Tree is used to detect and remove loops in a network topology. +.Pp +When filtering is enabled, bridged packets will pass through the filter +inbound on the originating interface, on the bridge interface and outbound on +the appropriate interfaces. This behaviour can be controlled using +.Xr sysctl 8 : +.Bl -tag -width ".Va net.link.bridge.pfil_member" +.It Va net.link.bridge.pfil_member +Set to +.Li 1 +to enable enable filtering on the incoming and outgoing member interfaces, set +to +.Li 0 +to disable it. +.It Va net.link.bridge.pfil_bridge +Set to +.Li 1 +to enable enable filtering on the bridge interface, set +to +.Li 0 +to disable it. +.El +.Pp +ARP and REVARP packets are forwarded without being filtered and others +that are not IP nor IPv6 packets are not forwarded when filtering is +enabled. +.Pp +Note that packets to and from the bridging host will be seen by the +filter on the interface with the appropriate address configured as well +as on the interface on which the packet arrives or departs. +.Sh EXAMPLES +The following then placed in the file +.Pa /etc/rc.conf +will cause the a bridge called +.Sq bridge0 +to be created, and will add the interfaces +.Sq wi0 +and +.Sq fxp0 +to the bridge, and then enable packet forwarding. +Such a configuration could be used to implement a simple +802.11-to-Ethernet bridge (assuming the 802.11 interface is +in ad-hoc mode). +.Bd -literal -offset indent +cloned_interfaces="bridge0" +ifconfig_bridge0="addm wi0 addm fxp0 up" +.Ed +.Pp +Consider a system with two 4-port Ethernet boards. +The following will cause a bridge consisting of all 8 ports with Spanning Tree +enabled to be created: +.Bd -literal -offset indent +iconfig bridge0 create +ifconfig bridge0 \e + addm fxp0 stp fxp0 \e + addm fxp1 stp fxp1 \e + addm fxp2 stp fxp2 \e + addm fxp3 stp fxp3 \e + addm fxp4 stp fxp4 \e + addm fxp5 stp fxp5 \e + addm fxp6 stp fxp6 \e + addm fxp7 stp fxp7 \e + up +.Ed +.Sh SEE ALSO +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 6.0 . +.Sh AUTHORS +The +.Nm bridge +driver was originally written by +.An Jason L. Wright +.Aq jason@thought.net +as part of an undergraduate independent study at the University of +North Carolina at Greensboro. +.Pp +This version of the +.Nm +driver has been heavily modified from the original version by +.An Jason R. Thorpe +.Aq thorpej@wasabisystems.com . +.Sh BUGS +The +.Nm +driver currently supports only Ethernet and Ethernet-like (e.g. 802.11) +network devices, with exactly the same interface MTU size as the bridge device. diff --git a/sys/net/bridgestp.c b/sys/net/bridgestp.c new file mode 100644 index 000000000000..a1a487baa912 --- /dev/null +++ b/sys/net/bridgestp.c @@ -0,0 +1,1174 @@ +/* $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $ */ + +/* + * Copyright (c) 2000 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp + */ + +/* + * Implementation of the spanning tree protocol as defined in + * ISO/IEC Final DIS 15802-3 (IEEE P802.1D/D17), May 25, 1998. + * (In English: IEEE 802.1D, Draft 17, 1998) + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/kernel.h> +#include <sys/callout.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_llc.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <net/if_bridgevar.h> + +#define sc_if ifb_ac.ac_if + +/* BPDU message types */ +#define BSTP_MSGTYPE_CFG 0x00 /* Configuration */ +#define BSTP_MSGTYPE_TCN 0x80 /* Topology chg notification */ + +/* BPDU flags */ +#define BSTP_FLAG_TC 0x01 /* Topology change */ +#define BSTP_FLAG_TCA 0x80 /* Topology change ack */ + +#define BSTP_MESSAGE_AGE_INCR (1 * 256) /* in 256ths of a second */ +#define BSTP_TICK_VAL (1 * 256) /* in 256ths of a second */ + +/* + * Because BPDU's do not make nicely aligned structures, two different + * declarations are used: bstp_?bpdu (wire representation, packed) and + * bstp_*_unit (internal, nicely aligned version). + */ + +/* configuration bridge protocol data unit */ +struct bstp_cbpdu { + uint8_t cbu_dsap; /* LLC: destination sap */ + uint8_t cbu_ssap; /* LLC: source sap */ + uint8_t cbu_ctl; /* LLC: control */ + uint16_t cbu_protoid; /* protocol id */ + uint8_t cbu_protover; /* protocol version */ + uint8_t cbu_bpdutype; /* message type */ + uint8_t cbu_flags; /* flags (below) */ + + /* root id */ + uint16_t cbu_rootpri; /* root priority */ + uint8_t cbu_rootaddr[6]; /* root address */ + + uint32_t cbu_rootpathcost; /* root path cost */ + + /* bridge id */ + uint16_t cbu_bridgepri; /* bridge priority */ + uint8_t cbu_bridgeaddr[6]; /* bridge address */ + + uint16_t cbu_portid; /* port id */ + uint16_t cbu_messageage; /* current message age */ + uint16_t cbu_maxage; /* maximum age */ + uint16_t cbu_hellotime; /* hello time */ + uint16_t cbu_forwarddelay; /* forwarding delay */ +} __attribute__((__packed__)); + +/* topology change notification bridge protocol data unit */ +struct bstp_tbpdu { + uint8_t tbu_dsap; /* LLC: destination sap */ + uint8_t tbu_ssap; /* LLC: source sap */ + uint8_t tbu_ctl; /* LLC: control */ + uint16_t tbu_protoid; /* protocol id */ + uint8_t tbu_protover; /* protocol version */ + uint8_t tbu_bpdutype; /* message type */ +} __attribute__((__packed__)); + +const uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + +void bstp_initialize_port(struct bridge_softc *, struct bridge_iflist *); +void bstp_ifupdstatus(struct bridge_softc *, struct bridge_iflist *); +void bstp_enable_port(struct bridge_softc *, struct bridge_iflist *); +void bstp_disable_port(struct bridge_softc *, struct bridge_iflist *); +void bstp_enable_change_detection(struct bridge_iflist *); +void bstp_disable_change_detection(struct bridge_iflist *); +int bstp_root_bridge(struct bridge_softc *sc); +int bstp_supersedes_port_info(struct bridge_softc *, + struct bridge_iflist *, struct bstp_config_unit *); +int bstp_designated_port(struct bridge_softc *, struct bridge_iflist *); +int bstp_designated_for_some_port(struct bridge_softc *); +void bstp_transmit_config(struct bridge_softc *, struct bridge_iflist *); +void bstp_transmit_tcn(struct bridge_softc *); +void bstp_received_config_bpdu(struct bridge_softc *, + struct bridge_iflist *, struct bstp_config_unit *); +void bstp_received_tcn_bpdu(struct bridge_softc *, struct bridge_iflist *, + struct bstp_tcn_unit *); +void bstp_record_config_information(struct bridge_softc *, + struct bridge_iflist *, struct bstp_config_unit *); +void bstp_record_config_timeout_values(struct bridge_softc *, + struct bstp_config_unit *); +void bstp_config_bpdu_generation(struct bridge_softc *); +void bstp_send_config_bpdu(struct bridge_softc *, struct bridge_iflist *, + struct bstp_config_unit *); +void bstp_configuration_update(struct bridge_softc *); +void bstp_root_selection(struct bridge_softc *); +void bstp_designated_port_selection(struct bridge_softc *); +void bstp_become_designated_port(struct bridge_softc *, + struct bridge_iflist *); +void bstp_port_state_selection(struct bridge_softc *); +void bstp_make_forwarding(struct bridge_softc *, struct bridge_iflist *); +void bstp_make_blocking(struct bridge_softc *, struct bridge_iflist *); +void bstp_set_port_state(struct bridge_iflist *, uint8_t); +void bstp_set_bridge_priority(struct bridge_softc *, uint64_t); +void bstp_set_port_priority(struct bridge_softc *, struct bridge_iflist *, + uint16_t); +void bstp_set_path_cost(struct bridge_softc *, struct bridge_iflist *, + uint32_t); +void bstp_topology_change_detection(struct bridge_softc *); +void bstp_topology_change_acknowledged(struct bridge_softc *); +void bstp_acknowledge_topology_change(struct bridge_softc *, + struct bridge_iflist *); + +void bstp_tick(void *); +void bstp_timer_start(struct bridge_timer *, uint16_t); +void bstp_timer_stop(struct bridge_timer *); +int bstp_timer_expired(struct bridge_timer *, uint16_t); + +void bstp_hold_timer_expiry(struct bridge_softc *, struct bridge_iflist *); +void bstp_message_age_timer_expiry(struct bridge_softc *, + struct bridge_iflist *); +void bstp_forward_delay_timer_expiry(struct bridge_softc *, + struct bridge_iflist *); +void bstp_topology_change_timer_expiry(struct bridge_softc *); +void bstp_tcn_timer_expiry(struct bridge_softc *); +void bstp_hello_timer_expiry(struct bridge_softc *); + +void +bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + if (bif->bif_hold_timer.active) { + bif->bif_config_pending = 1; + return; + } + + bif->bif_config_bpdu.cu_message_type = BSTP_MSGTYPE_CFG; + bif->bif_config_bpdu.cu_rootid = sc->sc_designated_root; + bif->bif_config_bpdu.cu_root_path_cost = sc->sc_root_path_cost; + bif->bif_config_bpdu.cu_bridge_id = sc->sc_bridge_id; + bif->bif_config_bpdu.cu_port_id = bif->bif_port_id; + + if (bstp_root_bridge(sc)) + bif->bif_config_bpdu.cu_message_age = 0; + else + bif->bif_config_bpdu.cu_message_age = + sc->sc_root_port->bif_message_age_timer.value + + BSTP_MESSAGE_AGE_INCR; + + bif->bif_config_bpdu.cu_max_age = sc->sc_max_age; + bif->bif_config_bpdu.cu_hello_time = sc->sc_hello_time; + bif->bif_config_bpdu.cu_forward_delay = sc->sc_forward_delay; + bif->bif_config_bpdu.cu_topology_change_acknowledgment + = bif->bif_topology_change_acknowledge; + bif->bif_config_bpdu.cu_topology_change = sc->sc_topology_change; + + if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age) { + bif->bif_topology_change_acknowledge = 0; + bif->bif_config_pending = 0; + bstp_send_config_bpdu(sc, bif, &bif->bif_config_bpdu); + bstp_timer_start(&bif->bif_hold_timer, 0); + } +} + +void +bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, + struct bstp_config_unit *cu) +{ + struct ifnet *ifp; + struct mbuf *m; + struct ether_header *eh; + struct bstp_cbpdu bpdu; + + BRIDGE_LOCK_ASSERT(sc); + + ifp = bif->bif_ifp; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return; + + eh = mtod(m, struct ether_header *); + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu); + m->m_len = m->m_pkthdr.len; + + bpdu.cbu_ssap = bpdu.cbu_dsap = LLC_8021D_LSAP; + bpdu.cbu_ctl = LLC_UI; + bpdu.cbu_protoid = htons(0); + bpdu.cbu_protover = 0; + bpdu.cbu_bpdutype = cu->cu_message_type; + bpdu.cbu_flags = (cu->cu_topology_change ? BSTP_FLAG_TC : 0) | + (cu->cu_topology_change_acknowledgment ? BSTP_FLAG_TCA : 0); + + bpdu.cbu_rootpri = htons(cu->cu_rootid >> 48); + bpdu.cbu_rootaddr[0] = cu->cu_rootid >> 40; + bpdu.cbu_rootaddr[1] = cu->cu_rootid >> 32; + bpdu.cbu_rootaddr[2] = cu->cu_rootid >> 24; + bpdu.cbu_rootaddr[3] = cu->cu_rootid >> 16; + bpdu.cbu_rootaddr[4] = cu->cu_rootid >> 8; + bpdu.cbu_rootaddr[5] = cu->cu_rootid >> 0; + + bpdu.cbu_rootpathcost = htonl(cu->cu_root_path_cost); + + bpdu.cbu_bridgepri = htons(cu->cu_rootid >> 48); + bpdu.cbu_bridgeaddr[0] = cu->cu_rootid >> 40; + bpdu.cbu_bridgeaddr[1] = cu->cu_rootid >> 32; + bpdu.cbu_bridgeaddr[2] = cu->cu_rootid >> 24; + bpdu.cbu_bridgeaddr[3] = cu->cu_rootid >> 16; + bpdu.cbu_bridgeaddr[4] = cu->cu_rootid >> 8; + bpdu.cbu_bridgeaddr[5] = cu->cu_rootid >> 0; + + bpdu.cbu_portid = htons(cu->cu_port_id); + bpdu.cbu_messageage = htons(cu->cu_message_age); + bpdu.cbu_maxage = htons(cu->cu_max_age); + bpdu.cbu_hellotime = htons(cu->cu_hello_time); + bpdu.cbu_forwarddelay = htons(cu->cu_forward_delay); + + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); + eh->ether_type = htons(sizeof(bpdu)); + + memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu)); + + /* XXX: safe here?!? */ + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, ifp, m, 0); + BRIDGE_LOCK(sc); +} + +int +bstp_root_bridge(struct bridge_softc *sc) +{ + return (sc->sc_designated_root == sc->sc_bridge_id); +} + +int +bstp_supersedes_port_info(struct bridge_softc *sc, struct bridge_iflist *bif, + struct bstp_config_unit *cu) +{ + if (cu->cu_rootid < bif->bif_designated_root) + return (1); + if (cu->cu_rootid > bif->bif_designated_root) + return (0); + + if (cu->cu_root_path_cost < bif->bif_designated_cost) + return (1); + if (cu->cu_root_path_cost > bif->bif_designated_cost) + return (0); + + if (cu->cu_bridge_id < bif->bif_designated_bridge) + return (1); + if (cu->cu_bridge_id > bif->bif_designated_bridge) + return (0); + + if (sc->sc_bridge_id != cu->cu_bridge_id) + return (1); + if (cu->cu_port_id <= bif->bif_designated_port) + return (1); + return (0); +} + +void +bstp_record_config_information(struct bridge_softc *sc, + struct bridge_iflist *bif, struct bstp_config_unit *cu) +{ + bif->bif_designated_root = cu->cu_rootid; + bif->bif_designated_cost = cu->cu_root_path_cost; + bif->bif_designated_bridge = cu->cu_bridge_id; + bif->bif_designated_port = cu->cu_port_id; + bstp_timer_start(&bif->bif_message_age_timer, cu->cu_message_age); +} + +void +bstp_record_config_timeout_values(struct bridge_softc *sc, + struct bstp_config_unit *config) +{ + sc->sc_max_age = config->cu_max_age; + sc->sc_hello_time = config->cu_hello_time; + sc->sc_forward_delay = config->cu_forward_delay; + sc->sc_topology_change = config->cu_topology_change; +} + +void +bstp_config_bpdu_generation(struct bridge_softc *sc) +{ + struct bridge_iflist *bif; + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_designated_port(sc, bif) && + (bif->bif_state != BSTP_IFSTATE_DISABLED)) + bstp_transmit_config(sc, bif); + } +} + +int +bstp_designated_port(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + return ((bif->bif_designated_bridge == sc->sc_bridge_id) + && (bif->bif_designated_port == bif->bif_port_id)); +} + +void +bstp_transmit_tcn(struct bridge_softc *sc) +{ + struct bstp_tbpdu bpdu; + struct bridge_iflist *bif = sc->sc_root_port; + struct ifnet *ifp = bif->bif_ifp; + struct ether_header *eh; + struct mbuf *m; + + BRIDGE_LOCK_ASSERT(sc); + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return; + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu); + m->m_len = m->m_pkthdr.len; + + eh = mtod(m, struct ether_header *); + + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); + eh->ether_type = htons(sizeof(bpdu)); + + bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP; + bpdu.tbu_ctl = LLC_UI; + bpdu.tbu_protoid = 0; + bpdu.tbu_protover = 0; + bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN; + + memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu)); + + /* XXX: safe here?!? */ + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, ifp, m, 0); + BRIDGE_LOCK(sc); +} + +void +bstp_configuration_update(struct bridge_softc *sc) +{ + BRIDGE_LOCK_ASSERT(sc); + + bstp_root_selection(sc); + bstp_designated_port_selection(sc); +} + +void +bstp_root_selection(struct bridge_softc *sc) +{ + struct bridge_iflist *root_port = NULL, *bif; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_designated_port(sc, bif)) + continue; + if (bif->bif_state == BSTP_IFSTATE_DISABLED) + continue; + if (bif->bif_designated_root >= sc->sc_bridge_id) + continue; + if (root_port == NULL) + goto set_port; + + if (bif->bif_designated_root < root_port->bif_designated_root) + goto set_port; + if (bif->bif_designated_root > root_port->bif_designated_root) + continue; + + if ((bif->bif_designated_cost + bif->bif_path_cost) < + (root_port->bif_designated_cost + root_port->bif_path_cost)) + goto set_port; + if ((bif->bif_designated_cost + bif->bif_path_cost) > + (root_port->bif_designated_cost + root_port->bif_path_cost)) + continue; + + if (bif->bif_designated_bridge < + root_port->bif_designated_bridge) + goto set_port; + if (bif->bif_designated_bridge > + root_port->bif_designated_bridge) + continue; + + if (bif->bif_designated_port < root_port->bif_designated_port) + goto set_port; + if (bif->bif_designated_port > root_port->bif_designated_port) + continue; + + if (bif->bif_port_id >= root_port->bif_port_id) + continue; +set_port: + root_port = bif; + } + + sc->sc_root_port = root_port; + if (root_port == NULL) { + sc->sc_designated_root = sc->sc_bridge_id; + sc->sc_root_path_cost = 0; + } else { + sc->sc_designated_root = root_port->bif_designated_root; + sc->sc_root_path_cost = root_port->bif_designated_cost + + root_port->bif_path_cost; + } +} + +void +bstp_designated_port_selection(struct bridge_softc *sc) +{ + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_designated_port(sc, bif)) + goto designated; + if (bif->bif_designated_root != sc->sc_designated_root) + goto designated; + + if (sc->sc_root_path_cost < bif->bif_designated_cost) + goto designated; + if (sc->sc_root_path_cost > bif->bif_designated_cost) + continue; + + if (sc->sc_bridge_id < bif->bif_designated_bridge) + goto designated; + if (sc->sc_bridge_id > bif->bif_designated_bridge) + continue; + + if (bif->bif_port_id > bif->bif_designated_port) + continue; +designated: + bstp_become_designated_port(sc, bif); + } +} + +void +bstp_become_designated_port(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + bif->bif_designated_root = sc->sc_designated_root; + bif->bif_designated_cost = sc->sc_root_path_cost; + bif->bif_designated_bridge = sc->sc_bridge_id; + bif->bif_designated_port = bif->bif_port_id; +} + +void +bstp_port_state_selection(struct bridge_softc *sc) +{ + struct bridge_iflist *bif; + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bif == sc->sc_root_port) { + bif->bif_config_pending = 0; + bif->bif_topology_change_acknowledge = 0; + bstp_make_forwarding(sc, bif); + } else if (bstp_designated_port(sc, bif)) { + bstp_timer_stop(&bif->bif_message_age_timer); + bstp_make_forwarding(sc, bif); + } else { + bif->bif_config_pending = 0; + bif->bif_topology_change_acknowledge = 0; + bstp_make_blocking(sc, bif); + } + } +} + +void +bstp_make_forwarding(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + if (bif->bif_state == BSTP_IFSTATE_BLOCKING) { + bstp_set_port_state(bif, BSTP_IFSTATE_LISTENING); + bstp_timer_start(&bif->bif_forward_delay_timer, 0); + } +} + +void +bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + BRIDGE_LOCK_ASSERT(sc); + + if ((bif->bif_state != BSTP_IFSTATE_DISABLED) && + (bif->bif_state != BSTP_IFSTATE_BLOCKING)) { + if ((bif->bif_state == BSTP_IFSTATE_FORWARDING) || + (bif->bif_state == BSTP_IFSTATE_LEARNING)) { + if (bif->bif_change_detection_enabled) { + bstp_topology_change_detection(sc); + } + } + bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); + bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); + bstp_timer_stop(&bif->bif_forward_delay_timer); + } +} + +void +bstp_set_port_state(struct bridge_iflist *bif, uint8_t state) +{ + bif->bif_state = state; +} + +void +bstp_topology_change_detection(struct bridge_softc *sc) +{ + if (bstp_root_bridge(sc)) { + sc->sc_topology_change = 1; + bstp_timer_start(&sc->sc_topology_change_timer, 0); + } else if (!sc->sc_topology_change_detected) { + bstp_transmit_tcn(sc); + bstp_timer_start(&sc->sc_tcn_timer, 0); + } + sc->sc_topology_change_detected = 1; +} + +void +bstp_topology_change_acknowledged(struct bridge_softc *sc) +{ + sc->sc_topology_change_detected = 0; + bstp_timer_stop(&sc->sc_tcn_timer); +} + +void +bstp_acknowledge_topology_change(struct bridge_softc *sc, + struct bridge_iflist *bif) +{ + bif->bif_topology_change_acknowledge = 1; + bstp_transmit_config(sc, bif); +} + +struct mbuf * +bstp_input(struct ifnet *ifp, struct mbuf *m) +{ + struct bridge_softc *sc = ifp->if_bridge; + struct bridge_iflist *bif = NULL; + struct ether_header *eh; + struct bstp_tbpdu tpdu; + struct bstp_cbpdu cpdu; + struct bstp_config_unit cu; + struct bstp_tcn_unit tu; + uint16_t len; + + BRIDGE_LOCK_ASSERT(sc); + + eh = mtod(m, struct ether_header *); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bif->bif_ifp == ifp) + break; + } + if (bif == NULL) + goto out; + + len = ntohs(eh->ether_type); + if (len < sizeof(tpdu)) + goto out; + + m_adj(m, ETHER_HDR_LEN); + + if (m->m_pkthdr.len > len) + m_adj(m, len - m->m_pkthdr.len); + if (m->m_len < sizeof(tpdu) && + (m = m_pullup(m, sizeof(tpdu))) == NULL) + goto out; + + memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu)); + + if (tpdu.tbu_dsap != LLC_8021D_LSAP || + tpdu.tbu_ssap != LLC_8021D_LSAP || + tpdu.tbu_ctl != LLC_UI) + goto out; + if (tpdu.tbu_protoid != 0 || tpdu.tbu_protover != 0) + goto out; + + switch (tpdu.tbu_bpdutype) { + case BSTP_MSGTYPE_TCN: + tu.tu_message_type = tpdu.tbu_bpdutype; + bstp_received_tcn_bpdu(sc, bif, &tu); + break; + case BSTP_MSGTYPE_CFG: + if (m->m_len < sizeof(cpdu) && + (m = m_pullup(m, sizeof(cpdu))) == NULL) + goto out; + memcpy(&cpdu, mtod(m, caddr_t), sizeof(cpdu)); + + cu.cu_rootid = + (((uint64_t)ntohs(cpdu.cbu_rootpri)) << 48) | + (((uint64_t)cpdu.cbu_rootaddr[0]) << 40) | + (((uint64_t)cpdu.cbu_rootaddr[1]) << 32) | + (((uint64_t)cpdu.cbu_rootaddr[2]) << 24) | + (((uint64_t)cpdu.cbu_rootaddr[3]) << 16) | + (((uint64_t)cpdu.cbu_rootaddr[4]) << 8) | + (((uint64_t)cpdu.cbu_rootaddr[5]) << 0); + + cu.cu_bridge_id = + (((uint64_t)ntohs(cpdu.cbu_bridgepri)) << 48) | + (((uint64_t)cpdu.cbu_bridgeaddr[0]) << 40) | + (((uint64_t)cpdu.cbu_bridgeaddr[1]) << 32) | + (((uint64_t)cpdu.cbu_bridgeaddr[2]) << 24) | + (((uint64_t)cpdu.cbu_bridgeaddr[3]) << 16) | + (((uint64_t)cpdu.cbu_bridgeaddr[4]) << 8) | + (((uint64_t)cpdu.cbu_bridgeaddr[5]) << 0); + + cu.cu_root_path_cost = ntohl(cpdu.cbu_rootpathcost); + cu.cu_message_age = ntohs(cpdu.cbu_messageage); + cu.cu_max_age = ntohs(cpdu.cbu_maxage); + cu.cu_hello_time = ntohs(cpdu.cbu_hellotime); + cu.cu_forward_delay = ntohs(cpdu.cbu_forwarddelay); + cu.cu_port_id = ntohs(cpdu.cbu_portid); + cu.cu_message_type = cpdu.cbu_bpdutype; + cu.cu_topology_change_acknowledgment = + (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0; + cu.cu_topology_change = + (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0; + bstp_received_config_bpdu(sc, bif, &cu); + break; + default: + goto out; + } + + out: + if (m) + m_freem(m); + return (NULL); +} + +void +bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, + struct bstp_config_unit *cu) +{ + int root; + + BRIDGE_LOCK_ASSERT(sc); + + root = bstp_root_bridge(sc); + + if (bif->bif_state != BSTP_IFSTATE_DISABLED) { + if (bstp_supersedes_port_info(sc, bif, cu)) { + bstp_record_config_information(sc, bif, cu); + bstp_configuration_update(sc); + bstp_port_state_selection(sc); + + if ((bstp_root_bridge(sc) == 0) && root) { + bstp_timer_stop(&sc->sc_hello_timer); + + if (sc->sc_topology_change_detected) { + bstp_timer_stop( + &sc->sc_topology_change_timer); + bstp_transmit_tcn(sc); + bstp_timer_start(&sc->sc_tcn_timer, 0); + } + } + + if (bif == sc->sc_root_port) { + bstp_record_config_timeout_values(sc, cu); + bstp_config_bpdu_generation(sc); + + if (cu->cu_topology_change_acknowledgment) + bstp_topology_change_acknowledged(sc); + } + } else if (bstp_designated_port(sc, bif)) + bstp_transmit_config(sc, bif); + } +} + +void +bstp_received_tcn_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, + struct bstp_tcn_unit *tcn) +{ + if (bif->bif_state != BSTP_IFSTATE_DISABLED && + bstp_designated_port(sc, bif)) { + bstp_topology_change_detection(sc); + bstp_acknowledge_topology_change(sc, bif); + } +} + +void +bstp_hello_timer_expiry(struct bridge_softc *sc) +{ + bstp_config_bpdu_generation(sc); + bstp_timer_start(&sc->sc_hello_timer, 0); +} + +void +bstp_message_age_timer_expiry(struct bridge_softc *sc, + struct bridge_iflist *bif) +{ + int root; + + root = bstp_root_bridge(sc); + bstp_become_designated_port(sc, bif); + bstp_configuration_update(sc); + bstp_port_state_selection(sc); + + if ((bstp_root_bridge(sc)) && (root == 0)) { + sc->sc_max_age = sc->sc_bridge_max_age; + sc->sc_hello_time = sc->sc_bridge_hello_time; + sc->sc_forward_delay = sc->sc_bridge_forward_delay; + + bstp_topology_change_detection(sc); + bstp_timer_stop(&sc->sc_tcn_timer); + bstp_config_bpdu_generation(sc); + bstp_timer_start(&sc->sc_hello_timer, 0); + } +} + +void +bstp_forward_delay_timer_expiry(struct bridge_softc *sc, + struct bridge_iflist *bif) +{ + if (bif->bif_state == BSTP_IFSTATE_LISTENING) { + bstp_set_port_state(bif, BSTP_IFSTATE_LEARNING); + bstp_timer_start(&bif->bif_forward_delay_timer, 0); + } else if (bif->bif_state == BSTP_IFSTATE_LEARNING) { + bstp_set_port_state(bif, BSTP_IFSTATE_FORWARDING); + if (bstp_designated_for_some_port(sc) && + bif->bif_change_detection_enabled) + bstp_topology_change_detection(sc); + } +} + +int +bstp_designated_for_some_port(struct bridge_softc *sc) +{ + + struct bridge_iflist *bif; + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bif->bif_designated_bridge == sc->sc_bridge_id) + return (1); + } + return (0); +} + +void +bstp_tcn_timer_expiry(struct bridge_softc *sc) +{ + bstp_transmit_tcn(sc); + bstp_timer_start(&sc->sc_tcn_timer, 0); +} + +void +bstp_topology_change_timer_expiry(struct bridge_softc *sc) +{ + sc->sc_topology_change_detected = 0; + sc->sc_topology_change = 0; +} + +void +bstp_hold_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + if (bif->bif_config_pending) + bstp_transmit_config(sc, bif); +} + +void +bstp_initialization(struct bridge_softc *sc) +{ + struct bridge_iflist *bif, *mif; + + mif = NULL; + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bif->bif_ifp->if_type != IFT_ETHER) + continue; + bif->bif_port_id = (bif->bif_priority << 8) | + (bif->bif_ifp->if_index & 0xff); + + if (mif == NULL) { + mif = bif; + continue; + } + if (memcmp(IF_LLADDR(bif->bif_ifp), + IF_LLADDR(mif->bif_ifp), ETHER_ADDR_LEN) < 0) { + mif = bif; + continue; + } + } + if (mif == NULL) { + bstp_stop(sc); + return; + } + + sc->sc_bridge_id = + (((uint64_t)sc->sc_bridge_priority) << 48) | + (((uint64_t)IF_LLADDR(mif->bif_ifp)[0]) << 40) | + (((uint64_t)IF_LLADDR(mif->bif_ifp)[1]) << 32) | + (IF_LLADDR(mif->bif_ifp)[2] << 24) | + (IF_LLADDR(mif->bif_ifp)[3] << 16) | + (IF_LLADDR(mif->bif_ifp)[4] << 8) | + (IF_LLADDR(mif->bif_ifp)[5]); + + sc->sc_designated_root = sc->sc_bridge_id; + sc->sc_root_path_cost = 0; + sc->sc_root_port = NULL; + + sc->sc_max_age = sc->sc_bridge_max_age; + sc->sc_hello_time = sc->sc_bridge_hello_time; + sc->sc_forward_delay = sc->sc_bridge_forward_delay; + sc->sc_topology_change_detected = 0; + sc->sc_topology_change = 0; + bstp_timer_stop(&sc->sc_tcn_timer); + bstp_timer_stop(&sc->sc_topology_change_timer); + + if (callout_pending(&sc->sc_bstpcallout) == 0) + callout_reset(&sc->sc_bstpcallout, hz, + bstp_tick, sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (bif->bif_flags & IFBIF_STP) + bstp_enable_port(sc, bif); + else + bstp_disable_port(sc, bif); + } + + bstp_port_state_selection(sc); + bstp_config_bpdu_generation(sc); + bstp_timer_start(&sc->sc_hello_timer, 0); +} + +void +bstp_stop(struct bridge_softc *sc) +{ + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED); + bstp_timer_stop(&bif->bif_hold_timer); + bstp_timer_stop(&bif->bif_message_age_timer); + bstp_timer_stop(&bif->bif_forward_delay_timer); + } + + callout_stop(&sc->sc_bstpcallout); + + bstp_timer_stop(&sc->sc_topology_change_timer); + bstp_timer_stop(&sc->sc_tcn_timer); + bstp_timer_stop(&sc->sc_hello_timer); + +} + +void +bstp_initialize_port(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + bstp_become_designated_port(sc, bif); + bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); + bif->bif_topology_change_acknowledge = 0; + bif->bif_config_pending = 0; + bif->bif_change_detection_enabled = 1; + bstp_timer_stop(&bif->bif_message_age_timer); + bstp_timer_stop(&bif->bif_forward_delay_timer); + bstp_timer_stop(&bif->bif_hold_timer); +} + +void +bstp_enable_port(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + bstp_initialize_port(sc, bif); + bstp_port_state_selection(sc); +} + +void +bstp_disable_port(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + int root; + + BRIDGE_LOCK_ASSERT(sc); + + root = bstp_root_bridge(sc); + bstp_become_designated_port(sc, bif); + bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED); + bif->bif_topology_change_acknowledge = 0; + bif->bif_config_pending = 0; + bstp_timer_stop(&bif->bif_message_age_timer); + bstp_timer_stop(&bif->bif_forward_delay_timer); + bstp_configuration_update(sc); + bstp_port_state_selection(sc); + bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); + + if (bstp_root_bridge(sc) && (root == 0)) { + sc->sc_max_age = sc->sc_bridge_max_age; + sc->sc_hello_time = sc->sc_bridge_hello_time; + sc->sc_forward_delay = sc->sc_bridge_forward_delay; + + bstp_topology_change_detection(sc); + bstp_timer_stop(&sc->sc_tcn_timer); + bstp_config_bpdu_generation(sc); + bstp_timer_start(&sc->sc_hello_timer, 0); + } +} + +void +bstp_set_bridge_priority(struct bridge_softc *sc, uint64_t new_bridge_id) +{ + struct bridge_iflist *bif; + int root; + + BRIDGE_LOCK_ASSERT(sc); + + root = bstp_root_bridge(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_designated_port(sc, bif)) + bif->bif_designated_bridge = new_bridge_id; + } + + sc->sc_bridge_id = new_bridge_id; + + bstp_configuration_update(sc); + bstp_port_state_selection(sc); + + if (bstp_root_bridge(sc) && (root == 0)) { + sc->sc_max_age = sc->sc_bridge_max_age; + sc->sc_hello_time = sc->sc_bridge_hello_time; + sc->sc_forward_delay = sc->sc_bridge_forward_delay; + + bstp_topology_change_detection(sc); + bstp_timer_stop(&sc->sc_tcn_timer); + bstp_config_bpdu_generation(sc); + bstp_timer_start(&sc->sc_hello_timer, 0); + } +} + +void +bstp_set_port_priority(struct bridge_softc *sc, struct bridge_iflist *bif, + uint16_t new_port_id) +{ + if (bstp_designated_port(sc, bif)) + bif->bif_designated_port = new_port_id; + + bif->bif_port_id = new_port_id; + + if ((sc->sc_bridge_id == bif->bif_designated_bridge) && + (bif->bif_port_id < bif->bif_designated_port)) { + bstp_become_designated_port(sc, bif); + bstp_port_state_selection(sc); + } +} + +void +bstp_set_path_cost(struct bridge_softc *sc, struct bridge_iflist *bif, + uint32_t path_cost) +{ + bif->bif_path_cost = path_cost; + bstp_configuration_update(sc); + bstp_port_state_selection(sc); +} + +void +bstp_enable_change_detection(struct bridge_iflist *bif) +{ + bif->bif_change_detection_enabled = 1; +} + +void +bstp_disable_change_detection(struct bridge_iflist *bif) +{ + bif->bif_change_detection_enabled = 0; +} + +void +bstp_linkstate(struct ifnet *ifp, int state) +{ + struct bridge_softc *sc; + struct bridge_iflist *bif; + + sc = ifp->if_bridge; + BRIDGE_LOCK(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + + if (bif->bif_ifp == ifp) { + bstp_ifupdstatus(sc, bif); + break; + } + } + + BRIDGE_UNLOCK(sc); +} + +void +bstp_ifupdstatus(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + struct ifnet *ifp = bif->bif_ifp; + struct ifmediareq ifmr; + int error = 0; + + BRIDGE_LOCK_ASSERT(sc); + + bzero((char *)&ifmr, sizeof(ifmr)); + error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); + + if ((error == 0) && (ifp->if_flags & IFF_UP)) { + if (ifmr.ifm_status & IFM_ACTIVE) { + if (bif->bif_state == BSTP_IFSTATE_DISABLED) + bstp_enable_port(sc, bif); + + } else { + if (bif->bif_state != BSTP_IFSTATE_DISABLED) + bstp_disable_port(sc, bif); + } + return; + } + + if (bif->bif_state != BSTP_IFSTATE_DISABLED) + bstp_disable_port(sc, bif); +} + +void +bstp_tick(void *arg) +{ + struct bridge_softc *sc = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK(sc); + +#if 0 + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + /* + * XXX This can cause a lag in "link does away" + * XXX and "spanning tree gets updated". We need + * XXX come sort of callback from the link state + * XXX update code to kick spanning tree. + * XXX --thorpej@NetBSD.org + */ + bstp_ifupdstatus(sc, bif); + } +#endif + + if (bstp_timer_expired(&sc->sc_hello_timer, sc->sc_hello_time)) + bstp_hello_timer_expiry(sc); + + if (bstp_timer_expired(&sc->sc_tcn_timer, sc->sc_bridge_hello_time)) + bstp_tcn_timer_expiry(sc); + + if (bstp_timer_expired(&sc->sc_topology_change_timer, + sc->sc_topology_change_time)) + bstp_topology_change_timer_expiry(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_timer_expired(&bif->bif_message_age_timer, + sc->sc_max_age)) + bstp_message_age_timer_expiry(sc, bif); + } + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if ((bif->bif_flags & IFBIF_STP) == 0) + continue; + if (bstp_timer_expired(&bif->bif_forward_delay_timer, + sc->sc_forward_delay)) + bstp_forward_delay_timer_expiry(sc, bif); + + if (bstp_timer_expired(&bif->bif_hold_timer, + sc->sc_hold_time)) + bstp_hold_timer_expiry(sc, bif); + } + + if (sc->sc_if.if_flags & IFF_RUNNING) + callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc); + + BRIDGE_UNLOCK(sc); +} + +void +bstp_timer_start(struct bridge_timer *t, uint16_t v) +{ + t->value = v; + t->active = 1; +} + +void +bstp_timer_stop(struct bridge_timer *t) +{ + t->value = 0; + t->active = 0; +} + +int +bstp_timer_expired(struct bridge_timer *t, uint16_t v) +{ + if (t->active == 0) + return (0); + t->value += BSTP_TICK_VAL; + if (t->value >= v) { + bstp_timer_stop(t); + return (1); + } + return (0); + +} diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c new file mode 100644 index 000000000000..04fa6aabd475 --- /dev/null +++ b/sys/net/if_bridge.c @@ -0,0 +1,2408 @@ +/* $NetBSD: if_bridge.c,v 1.24 2004/04/21 19:10:31 itojun Exp $ */ + +/* + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp + */ + +/* + * Network interface bridge support. + * + * TODO: + * + * - Currently only supports Ethernet-like interfaces (Ethernet, + * 802.11, VLANs on Ethernet, etc.) Figure out a nice way + * to bridge other types of interfaces (FDDI-FDDI, and maybe + * consider heterogenous bridges). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/protosw.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/socket.h> /* for net/if.h */ +#include <sys/sockio.h> +#include <sys/ctype.h> /* string functions */ +#include <sys/kernel.h> +#include <sys/random.h> +#include <sys/sysctl.h> +#include <vm/uma.h> +#include <sys/module.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/condvar.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_clone.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_var.h> +#include <net/pfil.h> + +#include <netinet/in.h> /* for struct arpcom */ +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#ifdef INET6 +#include <netinet/ip6.h> +#include <netinet6/ip6_var.h> +#endif +#include <machine/in_cksum.h> +#include <netinet/if_ether.h> /* for struct arpcom */ +#include <net/if_bridgevar.h> +#include <net/if_llc.h> + +#include <net/route.h> +#include <netinet/ip_fw.h> +#include <netinet/ip_dummynet.h> +#include <net/bridge.h> + +#define sc_if ifb_ac.ac_if +/* + * Size of the route hash table. Must be a power of two. + */ +#ifndef BRIDGE_RTHASH_SIZE +#define BRIDGE_RTHASH_SIZE 1024 +#endif + +#define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) + +/* + * Maximum number of addresses to cache. + */ +#ifndef BRIDGE_RTABLE_MAX +#define BRIDGE_RTABLE_MAX 100 +#endif + +/* + * Spanning tree defaults. + */ +#define BSTP_DEFAULT_MAX_AGE (20 * 256) +#define BSTP_DEFAULT_HELLO_TIME (2 * 256) +#define BSTP_DEFAULT_FORWARD_DELAY (15 * 256) +#define BSTP_DEFAULT_HOLD_TIME (1 * 256) +#define BSTP_DEFAULT_BRIDGE_PRIORITY 0x8000 +#define BSTP_DEFAULT_PORT_PRIORITY 0x80 +#define BSTP_DEFAULT_PATH_COST 55 + +/* + * Timeout (in seconds) for entries learned dynamically. + */ +#ifndef BRIDGE_RTABLE_TIMEOUT +#define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ +#endif + +/* + * Number of seconds between walks of the route list. + */ +#ifndef BRIDGE_RTABLE_PRUNE_PERIOD +#define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) +#endif + +static struct mtx bridge_list_mtx; + +extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *); +extern int (*bridge_output_p)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); + +int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; + +uma_zone_t bridge_rtnode_zone; + +int bridge_clone_create(struct if_clone *, int); +void bridge_clone_destroy(struct ifnet *); + +int bridge_ioctl(struct ifnet *, u_long, caddr_t); +static void bridge_init(void *); +void bridge_stop(struct ifnet *, int); +void bridge_start(struct ifnet *); + +void bridge_forward(struct bridge_softc *, struct mbuf *m); + +void bridge_timer(void *); + +void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *); + +int bridge_rtupdate(struct bridge_softc *, const uint8_t *, + struct ifnet *, int, uint8_t); +struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *); +void bridge_rttrim(struct bridge_softc *); +void bridge_rtage(struct bridge_softc *); +void bridge_rtflush(struct bridge_softc *, int); +int bridge_rtdaddr(struct bridge_softc *, const uint8_t *); + +int bridge_rtable_init(struct bridge_softc *); +void bridge_rtable_fini(struct bridge_softc *); + +struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, + const uint8_t *); +int bridge_rtnode_insert(struct bridge_softc *, struct bridge_rtnode *); +void bridge_rtnode_destroy(struct bridge_softc *, struct bridge_rtnode *); + +struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, + const char *name); +struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, + struct ifnet *ifp); +void bridge_delete_member(struct bridge_softc *, struct bridge_iflist *); + +int bridge_ioctl_add(struct bridge_softc *, void *); +int bridge_ioctl_del(struct bridge_softc *, void *); +int bridge_ioctl_gifflags(struct bridge_softc *, void *); +int bridge_ioctl_sifflags(struct bridge_softc *, void *); +int bridge_ioctl_scache(struct bridge_softc *, void *); +int bridge_ioctl_gcache(struct bridge_softc *, void *); +int bridge_ioctl_gifs(struct bridge_softc *, void *); +int bridge_ioctl_rts(struct bridge_softc *, void *); +int bridge_ioctl_saddr(struct bridge_softc *, void *); +int bridge_ioctl_sto(struct bridge_softc *, void *); +int bridge_ioctl_gto(struct bridge_softc *, void *); +int bridge_ioctl_daddr(struct bridge_softc *, void *); +int bridge_ioctl_flush(struct bridge_softc *, void *); +int bridge_ioctl_gpri(struct bridge_softc *, void *); +int bridge_ioctl_spri(struct bridge_softc *, void *); +int bridge_ioctl_ght(struct bridge_softc *, void *); +int bridge_ioctl_sht(struct bridge_softc *, void *); +int bridge_ioctl_gfd(struct bridge_softc *, void *); +int bridge_ioctl_sfd(struct bridge_softc *, void *); +int bridge_ioctl_gma(struct bridge_softc *, void *); +int bridge_ioctl_sma(struct bridge_softc *, void *); +int bridge_ioctl_sifprio(struct bridge_softc *, void *); +int bridge_ioctl_sifcost(struct bridge_softc *, void *); +static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); +static int bridge_ip_checkbasic(struct mbuf **mp); +# ifdef INET6 +static int bridge_ip6_checkbasic(struct mbuf **mp); +# endif /* INET6 */ + +SYSCTL_DECL(_net_link); +SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge"); + +static int pfil_bridge = 1; /* run pfil hooks on the bridge interface */ +static int pfil_member = 1; /* run pfil hooks on the member interface */ +SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RW, + &pfil_bridge, 0, "Packet filter on the bridge interface"); +SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RW, + &pfil_member, 0, "Packet filter on the member interface"); + +struct bridge_control { + int (*bc_func)(struct bridge_softc *, void *); + int bc_argsize; + int bc_flags; +}; + +#define BC_F_COPYIN 0x01 /* copy arguments in */ +#define BC_F_COPYOUT 0x02 /* copy arguments out */ +#define BC_F_SUSER 0x04 /* do super-user check */ + +const struct bridge_control bridge_control_table[] = { + { bridge_ioctl_add, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_del, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gifflags, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_COPYOUT }, + { bridge_ioctl_sifflags, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_scache, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_gcache, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + + { bridge_ioctl_gifs, sizeof(struct ifbifconf), + BC_F_COPYIN|BC_F_COPYOUT }, + { bridge_ioctl_rts, sizeof(struct ifbaconf), + BC_F_COPYIN|BC_F_COPYOUT }, + + { bridge_ioctl_saddr, sizeof(struct ifbareq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sto, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + { bridge_ioctl_gto, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + + { bridge_ioctl_daddr, sizeof(struct ifbareq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_flush, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gpri, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_spri, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_ght, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sht, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gfd, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sfd, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_gma, sizeof(struct ifbrparam), + BC_F_COPYOUT }, + { bridge_ioctl_sma, sizeof(struct ifbrparam), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sifprio, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, + + { bridge_ioctl_sifcost, sizeof(struct ifbreq), + BC_F_COPYIN|BC_F_SUSER }, +}; +const int bridge_control_table_size = + sizeof(bridge_control_table) / sizeof(bridge_control_table[0]); + +LIST_HEAD(, bridge_softc) bridge_list; + +IFC_SIMPLE_DECLARE(bridge, 0); + +static int +bridge_modevent(module_t mod, int type, void *data) +{ + + switch (type) { + case MOD_LOAD: + mtx_init(&bridge_list_mtx, "if_bridge list", NULL, MTX_DEF); + if_clone_attach(&bridge_cloner); + bridge_rtnode_zone = uma_zcreate("bridge_rtnode", + sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + LIST_INIT(&bridge_list); + bridge_input_p = bridge_input; + bridge_output_p = bridge_output; + bstp_linkstate_p = bstp_linkstate; + break; + case MOD_UNLOAD: + if_clone_detach(&bridge_cloner); + while (!LIST_EMPTY(&bridge_list)) + bridge_clone_destroy(&LIST_FIRST(&bridge_list)->sc_if); + uma_zdestroy(bridge_rtnode_zone); + bridge_input_p = NULL; + bridge_output_p = NULL; + bstp_linkstate_p = NULL; + mtx_destroy(&bridge_list_mtx); + break; + default: + return EOPNOTSUPP; + } + return 0; +} + +static moduledata_t bridge_mod = { + "if_bridge", + bridge_modevent, + 0 +}; + +DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); + + +/* + * bridge_clone_create: + * + * Create a new bridge instance. + */ +int +bridge_clone_create(struct if_clone *ifc, int unit) +{ + struct bridge_softc *sc; + struct ifnet *ifp; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); + BRIDGE_LOCK_INIT(sc); + ifp = &sc->sc_if; + + sc->sc_brtmax = BRIDGE_RTABLE_MAX; + sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; + sc->sc_bridge_max_age = BSTP_DEFAULT_MAX_AGE; + sc->sc_bridge_hello_time = BSTP_DEFAULT_HELLO_TIME; + sc->sc_bridge_forward_delay = BSTP_DEFAULT_FORWARD_DELAY; + sc->sc_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY; + sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME; + + /* Initialize our routing table. */ + bridge_rtable_init(sc); + + callout_init(&sc->sc_brcallout, 0); + callout_init(&sc->sc_bstpcallout, 0); + + LIST_INIT(&sc->sc_iflist); + + ifp->if_softc = sc; + if_initname(ifp, ifc->ifc_name, unit); + ifp->if_mtu = ETHERMTU; + ifp->if_ioctl = bridge_ioctl; + ifp->if_output = bridge_output; + ifp->if_start = bridge_start; + ifp->if_init = bridge_init; + ifp->if_type = IFT_BRIDGE; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + ifp->if_hdrlen = ETHER_HDR_LEN; + + /* + * Generate a random ethernet address and use the private AC:DE:48 + * OUI code. + */ + arc4rand( &sc->ifb_ac.ac_enaddr, ETHER_ADDR_LEN, 1); + sc->ifb_ac.ac_enaddr[0] = 0xAC; + sc->ifb_ac.ac_enaddr[1] = 0xDE; + sc->ifb_ac.ac_enaddr[2] = 0x48; + + ether_ifattach(ifp, sc->ifb_ac.ac_enaddr); + /* Now undo some of the damage... */ + ifp->if_baudrate = 0; + ifp->if_type = IFT_BRIDGE; + + mtx_lock(&bridge_list_mtx); + LIST_INSERT_HEAD(&bridge_list, sc, sc_list); + mtx_unlock(&bridge_list_mtx); + + return (0); +} + +/* + * bridge_clone_destroy: + * + * Destroy a bridge instance. + */ +void +bridge_clone_destroy(struct ifnet *ifp) +{ + struct bridge_softc *sc = ifp->if_softc; + struct bridge_iflist *bif; + + BRIDGE_LOCK(sc); + + bridge_stop(ifp, 1); + + while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) + bridge_delete_member(sc, bif); + + BRIDGE_UNLOCK(sc); + + mtx_lock(&bridge_list_mtx); + LIST_REMOVE(sc, sc_list); + mtx_unlock(&bridge_list_mtx); + + ether_ifdetach(ifp); + + /* Tear down the routing table. */ + bridge_rtable_fini(sc); + + BRIDGE_LOCK_DESTROY(sc); + free(sc, M_DEVBUF); +} + +/* + * bridge_ioctl: + * + * Handle a control request from the operator. + */ +int +bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct bridge_softc *sc = ifp->if_softc; + struct thread *td = curthread; + union { + struct ifbreq ifbreq; + struct ifbifconf ifbifconf; + struct ifbareq ifbareq; + struct ifbaconf ifbaconf; + struct ifbrparam ifbrparam; + } args; + struct ifdrv *ifd = (struct ifdrv *) data; + const struct bridge_control *bc; + int error = 0; + + BRIDGE_LOCK(sc); + + switch (cmd) { + + case SIOCGDRVSPEC: + case SIOCSDRVSPEC: + if (ifd->ifd_cmd >= bridge_control_table_size) { + error = EINVAL; + break; + } + bc = &bridge_control_table[ifd->ifd_cmd]; + + if (cmd == SIOCGDRVSPEC && + (bc->bc_flags & BC_F_COPYOUT) == 0) { + error = EINVAL; + break; + } + else if (cmd == SIOCSDRVSPEC && + (bc->bc_flags & BC_F_COPYOUT) != 0) { + error = EINVAL; + break; + } + + if (bc->bc_flags & BC_F_SUSER) { + error = suser(td); + if (error) + break; + } + + if (ifd->ifd_len != bc->bc_argsize || + ifd->ifd_len > sizeof(args)) { + error = EINVAL; + break; + } + + if (bc->bc_flags & BC_F_COPYIN) { + error = copyin(ifd->ifd_data, &args, ifd->ifd_len); + if (error) + break; + } + + error = (*bc->bc_func)(sc, &args); + if (error) + break; + + if (bc->bc_flags & BC_F_COPYOUT) + error = copyout(&args, ifd->ifd_data, ifd->ifd_len); + + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_RUNNING) { + /* + * If interface is marked down and it is running, + * then stop and disable it. + */ + bridge_stop(ifp, 1); + } else if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == IFF_UP) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + (*ifp->if_init)(ifp); + } + break; + + case SIOCSIFMTU: + /* Do not allow the MTU to be changed on the bridge */ + error = EINVAL; + break; + + default: + /* + * drop the lock as ether_ioctl() will call bridge_start() and + * cause the lock to be recursed. + */ + BRIDGE_UNLOCK(sc); + error = ether_ioctl(ifp, cmd, data); + break; + } + + if (BRIDGE_LOCKED(sc)) + BRIDGE_UNLOCK(sc); + + return (error); +} + +/* + * bridge_lookup_member: + * + * Lookup a bridge member interface. + */ +struct bridge_iflist * +bridge_lookup_member(struct bridge_softc *sc, const char *name) +{ + struct bridge_iflist *bif; + struct ifnet *ifp; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + ifp = bif->bif_ifp; + if (strcmp(ifp->if_xname, name) == 0) + return (bif); + } + + return (NULL); +} + +/* + * bridge_lookup_member_if: + * + * Lookup a bridge member interface by ifnet*. + */ +struct bridge_iflist * +bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) +{ + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (bif->bif_ifp == member_ifp) + return (bif); + } + + return (NULL); +} + +/* + * bridge_delete_member: + * + * Delete the specified member interface. + */ +void +bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + struct ifnet *ifs = bif->bif_ifp; + + BRIDGE_LOCK_ASSERT(sc); + + switch (ifs->if_type) { + case IFT_ETHER: + /* + * Take the interface out of promiscuous mode. + */ + (void) ifpromisc(ifs, 0); + break; + + default: +#ifdef DIAGNOSTIC + panic("bridge_delete_member: impossible"); +#endif + break; + } + + ifs->if_bridge = NULL; + BRIDGE_XLOCK(sc); + LIST_REMOVE(bif, bif_next); + BRIDGE_XDROP(sc); + + bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); + + free(bif, M_DEVBUF); + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); +} + +int +bridge_ioctl_add(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif = NULL; + struct ifnet *ifs; + int error = 0; + + BRIDGE_LOCK_ASSERT(sc); + + ifs = ifunit(req->ifbr_ifsname); + if (ifs == NULL) + return (ENOENT); + + if (sc->sc_if.if_mtu != ifs->if_mtu) + return (EINVAL); + + if (ifs->if_bridge == sc) + return (EEXIST); + + if (ifs->if_bridge != NULL) + return (EBUSY); + + bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT); + if (bif == NULL) + return (ENOMEM); + + switch (ifs->if_type) { + case IFT_ETHER: + case IFT_L2VLAN: + /* + * Place the interface into promiscuous mode. + */ + error = ifpromisc(ifs, 1); + if (error) + goto out; + break; + + default: + error = EINVAL; + goto out; + } + + bif->bif_ifp = ifs; + bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; + bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY; + bif->bif_path_cost = BSTP_DEFAULT_PATH_COST; + + ifs->if_bridge = sc; + /* + * XXX: XLOCK HERE!?! + * + * NOTE: insert_***HEAD*** should be safe for the traversals. + */ + LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + else + bstp_stop(sc); + + out: + if (error) { + if (bif != NULL) + free(bif, M_DEVBUF); + } + return (error); +} + +int +bridge_ioctl_del(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bridge_delete_member(sc, bif); + + return (0); +} + +int +bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + req->ifbr_ifsflags = bif->bif_flags; + req->ifbr_state = bif->bif_state; + req->ifbr_priority = bif->bif_priority; + req->ifbr_path_cost = bif->bif_path_cost; + req->ifbr_portno = bif->bif_ifp->if_index & 0xff; + + return (0); +} + +int +bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + if (req->ifbr_ifsflags & IFBIF_STP) { + switch (bif->bif_ifp->if_type) { + case IFT_ETHER: + /* These can do spanning tree. */ + break; + + default: + /* Nothing else can. */ + return (EINVAL); + } + } + + bif->bif_flags = req->ifbr_ifsflags; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_scache(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_brtmax = param->ifbrp_csize; + bridge_rttrim(sc); + + return (0); +} + +int +bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_csize = sc->sc_brtmax; + + return (0); +} + +int +bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) +{ + struct ifbifconf *bifc = arg; + struct bridge_iflist *bif; + struct ifbreq breq; + int count, len, error = 0; + + BRIDGE_LOCK_ASSERT(sc); + + count = 0; + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) + count++; + + if (bifc->ifbic_len == 0) { + bifc->ifbic_len = sizeof(breq) * count; + return (0); + } + + count = 0; + len = bifc->ifbic_len; + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (len < sizeof(breq)) + break; + + strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, + sizeof(breq.ifbr_ifsname)); + breq.ifbr_ifsflags = bif->bif_flags; + breq.ifbr_state = bif->bif_state; + breq.ifbr_priority = bif->bif_priority; + breq.ifbr_path_cost = bif->bif_path_cost; + breq.ifbr_portno = bif->bif_ifp->if_index & 0xff; + error = copyout(&breq, bifc->ifbic_req + count, sizeof(breq)); + if (error) + break; + count++; + len -= sizeof(breq); + } + + bifc->ifbic_len = sizeof(breq) * count; + return (error); +} + +int +bridge_ioctl_rts(struct bridge_softc *sc, void *arg) +{ + struct ifbaconf *bac = arg; + struct bridge_rtnode *brt; + struct ifbareq bareq; + struct timeval tv; + int count = 0, error = 0, len; + + BRIDGE_LOCK_ASSERT(sc); + + if (bac->ifbac_len == 0) + return (0); + + getmicrotime(&tv); + + len = bac->ifbac_len; + LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { + if (len < sizeof(bareq)) + goto out; + strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, + sizeof(bareq.ifba_ifsname)); + memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && + tv.tv_sec < brt->brt_expire) + bareq.ifba_expire = brt->brt_expire - tv.tv_sec; + else + bareq.ifba_expire = 0; + bareq.ifba_flags = brt->brt_flags; + + error = copyout(&bareq, bac->ifbac_req + count, sizeof(bareq)); + if (error) + goto out; + count++; + len -= sizeof(bareq); + } + out: + bac->ifbac_len = sizeof(bareq) * count; + return (error); +} + +int +bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) +{ + struct ifbareq *req = arg; + struct bridge_iflist *bif; + int error; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifba_ifsname); + if (bif == NULL) + return (ENOENT); + + error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1, + req->ifba_flags); + + return (error); +} + +int +bridge_ioctl_sto(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_brttimeout = param->ifbrp_ctime; + + return (0); +} + +int +bridge_ioctl_gto(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_ctime = sc->sc_brttimeout; + + return (0); +} + +int +bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) +{ + struct ifbareq *req = arg; + + BRIDGE_LOCK_ASSERT(sc); + + return (bridge_rtdaddr(sc, req->ifba_dst)); +} + +int +bridge_ioctl_flush(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + + BRIDGE_LOCK_ASSERT(sc); + + bridge_rtflush(sc, req->ifbr_ifsflags); + + return (0); +} + +int +bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_prio = sc->sc_bridge_priority; + + return (0); +} + +int +bridge_ioctl_spri(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + sc->sc_bridge_priority = param->ifbrp_prio; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_ght(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_hellotime = sc->sc_bridge_hello_time >> 8; + + return (0); +} + +int +bridge_ioctl_sht(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_hellotime == 0) + return (EINVAL); + sc->sc_bridge_hello_time = param->ifbrp_hellotime << 8; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_fwddelay = sc->sc_bridge_forward_delay >> 8; + + return (0); +} + +int +bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_fwddelay == 0) + return (EINVAL); + sc->sc_bridge_forward_delay = param->ifbrp_fwddelay << 8; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_gma(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + param->ifbrp_maxage = sc->sc_bridge_max_age >> 8; + + return (0); +} + +int +bridge_ioctl_sma(struct bridge_softc *sc, void *arg) +{ + struct ifbrparam *param = arg; + + BRIDGE_LOCK_ASSERT(sc); + + if (param->ifbrp_maxage == 0) + return (EINVAL); + sc->sc_bridge_max_age = param->ifbrp_maxage << 8; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bif->bif_priority = req->ifbr_priority; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +int +bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) +{ + struct ifbreq *req = arg; + struct bridge_iflist *bif; + + BRIDGE_LOCK_ASSERT(sc); + + bif = bridge_lookup_member(sc, req->ifbr_ifsname); + if (bif == NULL) + return (ENOENT); + + bif->bif_path_cost = req->ifbr_path_cost; + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return (0); +} + +/* + * bridge_ifdetach: + * + * Detach an interface from a bridge. Called when a member + * interface is detaching. + */ +void +bridge_ifdetach(struct ifnet *ifp) +{ + struct bridge_softc *sc = ifp->if_bridge; + struct ifbreq breq; + + BRIDGE_LOCK_ASSERT(sc); + + memset(&breq, 0, sizeof(breq)); + snprintf(breq.ifbr_ifsname, sizeof(breq.ifbr_ifsname), ifp->if_xname); + + (void) bridge_ioctl_del(sc, &breq); +} + +/* + * bridge_init: + * + * Initialize a bridge interface. + */ +static void +bridge_init(void *xsc) +{ + struct bridge_softc *sc = (struct bridge_softc *)xsc; + struct ifnet *ifp = &sc->sc_if; + + if (ifp->if_flags & IFF_RUNNING) + return; + + callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, + bridge_timer, sc); + + ifp->if_flags |= IFF_RUNNING; + bstp_initialization(sc); + return; +} + +/* + * bridge_stop: + * + * Stop the bridge interface. + */ +void +bridge_stop(struct ifnet *ifp, int disable) +{ + struct bridge_softc *sc = ifp->if_softc; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + callout_stop(&sc->sc_brcallout); + bstp_stop(sc); + + bridge_rtflush(sc, IFBF_FLUSHDYN); + + ifp->if_flags &= ~IFF_RUNNING; +} + +/* + * bridge_enqueue: + * + * Enqueue a packet on a bridge member interface. + * + */ +__inline void +bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, + int runfilt) +{ + int len, err; + short mflags; + + /* + * Clear any in-bound checksum flags for this packet. + */ + m->m_pkthdr.csum_flags = 0; + + if (runfilt && inet_pfil_hook.ph_busy_count >= 0) { + if (bridge_pfil(&m, &sc->sc_if, dst_ifp, PFIL_OUT) != 0) + return; + } + if (m == NULL) + return; + + len = m->m_pkthdr.len; + mflags = m->m_flags; + IFQ_ENQUEUE(&dst_ifp->if_snd, m, err); + if (err == 0) { + + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + + dst_ifp->if_obytes += len; + + if (mflags & M_MCAST) { + sc->sc_if.if_omcasts++; + dst_ifp->if_omcasts++; + } + } + + if ((dst_ifp->if_flags & IFF_OACTIVE) == 0) + (*dst_ifp->if_start)(dst_ifp); +} + +/* + * bridge_output: + * + * Send output from a bridge member interface. This + * performs the bridging function for locally originated + * packets. + * + * The mbuf has the Ethernet header already attached. We must + * enqueue or free the mbuf before returning. + */ +int +bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, + struct rtentry *rt) +{ + struct ether_header *eh; + struct ifnet *dst_if; + struct bridge_softc *sc; + + if (m->m_len < ETHER_HDR_LEN) { + m = m_pullup(m, ETHER_HDR_LEN); + if (m == NULL) + return (0); + } + + eh = mtod(m, struct ether_header *); + sc = ifp->if_bridge; + + BRIDGE_LOCK(sc); + + /* + * If bridge is down, but the original output interface is up, + * go ahead and send out that interface. Otherwise, the packet + * is dropped below. + */ + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { + dst_if = ifp; + goto sendunicast; + } + + /* + * If the packet is a multicast, or we don't know a better way to + * get there, send to all interfaces. + */ + if (ETHER_IS_MULTICAST(eh->ether_dhost)) + dst_if = NULL; + else + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + if (dst_if == NULL) { + struct bridge_iflist *bif; + struct mbuf *mc; + int error = 0, used = 0; + + BRIDGE_LOCK2REF(sc, error); + if (error) { + m_freem(m); + return (0); + } + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + dst_if = bif->bif_ifp; + if ((dst_if->if_flags & IFF_RUNNING) == 0) + continue; + + /* + * If this is not the original output interface, + * and the interface is participating in spanning + * tree, make sure the port is in a state that + * allows forwarding. + */ + if (dst_if != ifp && + (bif->bif_flags & IFBIF_STP) != 0) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + continue; + } + } + + if (LIST_NEXT(bif, bif_next) == NULL) { + used = 1; + mc = m; + } else { + mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT); + if (mc == NULL) { + sc->sc_if.if_oerrors++; + continue; + } + } + + bridge_enqueue(sc, dst_if, mc, 0); + } + if (used == 0) + m_freem(m); + BRIDGE_UNREF(sc); + return (0); + } + + sendunicast: + /* + * XXX Spanning tree consideration here? + */ + + if ((dst_if->if_flags & IFF_RUNNING) == 0) { + m_freem(m); + BRIDGE_UNLOCK(sc); + return (0); + } + + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, dst_if, m, 0); + return (0); +} + +/* + * bridge_start: + * + * Start output on a bridge. + * + */ +void +bridge_start(struct ifnet *ifp) +{ + struct bridge_softc *sc; + struct mbuf *m; + struct ether_header *eh; + struct ifnet *dst_if; + + sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + for (;;) { + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; + BPF_MTAP(ifp, m); + + eh = mtod(m, struct ether_header *); + dst_if = NULL; + + BRIDGE_LOCK(sc); + if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + } + + if (dst_if == NULL) + bridge_broadcast(sc, ifp, m); + else { + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, dst_if, m, 1); + } + } + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +/* + * bridge_forward: + * + * The forwarding function of the bridge. + * + * NOTE: Releases the lock on return. + */ +void +bridge_forward(struct bridge_softc *sc, struct mbuf *m) +{ + struct bridge_iflist *bif; + struct ifnet *src_if, *dst_if, *ifp; + struct ether_header *eh; + + src_if = m->m_pkthdr.rcvif; + BRIDGE_LOCK_ASSERT(sc); + ifp = &sc->sc_if; + + sc->sc_if.if_ipackets++; + sc->sc_if.if_ibytes += m->m_pkthdr.len; + + /* + * Look up the bridge_iflist. + */ + bif = bridge_lookup_member_if(sc, src_if); + if (bif == NULL) { + /* Interface is not a bridge member (anymore?) */ + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } + + eh = mtod(m, struct ether_header *); + + /* + * If the interface is learning, and the source + * address is valid and not multicast, record + * the address. + */ + if ((bif->bif_flags & IFBIF_LEARNING) != 0 && + ETHER_IS_MULTICAST(eh->ether_shost) == 0 && + (eh->ether_shost[0] == 0 && + eh->ether_shost[1] == 0 && + eh->ether_shost[2] == 0 && + eh->ether_shost[3] == 0 && + eh->ether_shost[4] == 0 && + eh->ether_shost[5] == 0) == 0) { + (void) bridge_rtupdate(sc, eh->ether_shost, + src_if, 0, IFBAF_DYNAMIC); + } + + if ((bif->bif_flags & IFBIF_STP) != 0 && + bif->bif_state == BSTP_IFSTATE_LEARNING) { + m_freem(m); + BRIDGE_UNLOCK(sc); + return; + } + + /* + * At this point, the port either doesn't participate + * in spanning tree or it is in the forwarding state. + */ + + /* + * If the packet is unicast, destined for someone on + * "this" side of the bridge, drop it. + */ + if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + if (src_if == dst_if) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } else { + /* ...forward it to all interfaces. */ + sc->sc_if.if_imcasts++; + dst_if = NULL; + } + + /* run the packet filter */ + if (inet_pfil_hook.ph_busy_count >= 0) { + BRIDGE_UNLOCK(sc); + if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) + return; + BRIDGE_LOCK(sc); + } + if (m == NULL) { + BRIDGE_UNLOCK(sc); + return; + } + + if (dst_if == NULL) { + /* tap off packets passing the bridge */ + BPF_MTAP(ifp, m); + + bridge_broadcast(sc, src_if, m); + return; + } + + /* + * At this point, we're dealing with a unicast frame + * going to a different interface. + */ + if ((dst_if->if_flags & IFF_RUNNING) == 0) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + bif = bridge_lookup_member_if(sc, dst_if); + if (bif == NULL) { + /* Not a member of the bridge (anymore?) */ + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_DISABLED: + case BSTP_IFSTATE_BLOCKING: + BRIDGE_UNLOCK(sc); + m_freem(m); + return; + } + } + + /* tap off packets passing the bridge */ + BPF_MTAP(ifp, m); + + BRIDGE_UNLOCK(sc); + bridge_enqueue(sc, dst_if, m, 1); +} + +/* + * bridge_input: + * + * Receive input from a member interface. Queue the packet for + * bridging if it is not for us. + */ +struct mbuf * +bridge_input(struct ifnet *ifp, struct mbuf *m) +{ + struct bridge_softc *sc = ifp->if_bridge; + struct bridge_iflist *bif; + struct ether_header *eh; + struct mbuf *mc; + + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) + return (m); + + BRIDGE_LOCK(sc); + bif = bridge_lookup_member_if(sc, ifp); + if (bif == NULL) { + BRIDGE_UNLOCK(sc); + return (m); + } + + eh = mtod(m, struct ether_header *); + + if (memcmp(eh->ether_dhost, sc->ifb_ac.ac_enaddr, + ETHER_ADDR_LEN) == 0) { + /* + * If the packet is for us, set the packets source as the + * bridge, and return the packet back to ether_input for + * local processing. + */ + + /* XXX Do we tap the packet for the member interface too? + * BPF_MTAP(&m->m_pkthdr.rcvif, m); + */ + + /* Mark the packet as arriving on the bridge interface */ + m->m_pkthdr.rcvif = &sc->sc_if; + BPF_MTAP(&sc->sc_if, m); + sc->sc_if.if_ipackets++; + + BRIDGE_UNLOCK(sc); + return (m); + } + + if (m->m_flags & (M_BCAST|M_MCAST)) { + /* Tap off 802.1D packets; they do not get forwarded. */ + if (memcmp(eh->ether_dhost, bstp_etheraddr, + ETHER_ADDR_LEN) == 0) { + m = bstp_input(ifp, m); + if (m == NULL) { + BRIDGE_UNLOCK(sc); + return (NULL); + } + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + return (m); + } + } + + /* + * Make a deep copy of the packet and enqueue the copy + * for bridge processing; return the original packet for + * local processing. + */ + mc = m_dup(m, M_DONTWAIT); + if (mc == NULL) { + BRIDGE_UNLOCK(sc); + return (m); + } + + /* Perform the bridge forwarding function with the copy. */ + bridge_forward(sc, mc); + + /* Return the original packet for local processing. */ + return (m); + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + BRIDGE_UNLOCK(sc); + return (m); + } + } + + /* + * Unicast. Make sure it's not for us. + */ + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + /* It is destined for us. */ + if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost, + ETHER_ADDR_LEN) == 0) { + if (bif->bif_flags & IFBIF_LEARNING) + (void) bridge_rtupdate(sc, + eh->ether_shost, ifp, 0, IFBAF_DYNAMIC); + m->m_pkthdr.rcvif = bif->bif_ifp; + BRIDGE_UNLOCK(sc); + return (m); + } + + /* We just received a packet that we sent out. */ + if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_shost, + ETHER_ADDR_LEN) == 0) { + BRIDGE_UNLOCK(sc); + m_freem(m); + return (NULL); + } + } + + /* Perform the bridge forwarding function. */ + bridge_forward(sc, m); + + return (NULL); +} + +/* + * bridge_broadcast: + * + * Send a frame to all interfaces that are members of + * the bridge, except for the one on which the packet + * arrived. + * + * NOTE: Releases the lock on return. + */ +void +bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, + struct mbuf *m) +{ + struct bridge_iflist *bif; + struct mbuf *mc; + struct ifnet *dst_if; + int error = 0, used = 0; + + BRIDGE_LOCK_ASSERT(sc); + BRIDGE_LOCK2REF(sc, error); + if (error) { + m_freem(m); + return; + } + + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + dst_if = bif->bif_ifp; + if (dst_if == src_if) + continue; + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_DISABLED: + continue; + } + } + + if ((bif->bif_flags & IFBIF_DISCOVER) == 0 && + (m->m_flags & (M_BCAST|M_MCAST)) == 0) + continue; + + if ((dst_if->if_flags & IFF_RUNNING) == 0) + continue; + + if (LIST_NEXT(bif, bif_next) == NULL) { + mc = m; + used = 1; + } else { + mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT); + if (mc == NULL) { + sc->sc_if.if_oerrors++; + continue; + } + } + + bridge_enqueue(sc, dst_if, mc, 1); + } + if (used == 0) + m_freem(m); + + BRIDGE_UNREF(sc); +} + +/* + * bridge_rtupdate: + * + * Add a bridge routing entry. + */ +int +bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, + struct ifnet *dst_if, int setflags, uint8_t flags) +{ + struct bridge_rtnode *brt; + struct timeval tv; + int error; + + BRIDGE_LOCK_ASSERT(sc); + + /* + * A route for this destination might already exist. If so, + * update it, otherwise create a new one. + */ + getmicrotime(&tv); + if ((brt = bridge_rtnode_lookup(sc, dst)) == NULL) { + if (sc->sc_brtcnt >= sc->sc_brtmax) + return (ENOSPC); + + /* + * Allocate a new bridge forwarding node, and + * initialize the expiration time and Ethernet + * address. + */ + brt = uma_zalloc(bridge_rtnode_zone, M_NOWAIT | M_ZERO); + if (brt == NULL) + return (ENOMEM); + + brt->brt_expire = tv.tv_sec + sc->sc_brttimeout; + brt->brt_flags = IFBAF_DYNAMIC; + memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); + + if ((error = bridge_rtnode_insert(sc, brt)) != 0) { + uma_zfree(bridge_rtnode_zone, brt); + return (error); + } + } + + brt->brt_ifp = dst_if; + if (setflags) { + brt->brt_flags = flags; + brt->brt_expire = (flags & IFBAF_STATIC) ? 0 : + tv.tv_sec + sc->sc_brttimeout; + } + + return (0); +} + +/* + * bridge_rtlookup: + * + * Lookup the destination interface for an address. + */ +struct ifnet * +bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + + BRIDGE_LOCK_ASSERT(sc); + + if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) + return (NULL); + + return (brt->brt_ifp); +} + +/* + * bridge_rttrim: + * + * Trim the routine table so that we have a number + * of routing entries less than or equal to the + * maximum number. + */ +void +bridge_rttrim(struct bridge_softc *sc) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + /* Make sure we actually need to do this. */ + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + + /* Force an aging cycle; this might trim enough addresses. */ + bridge_rtage(sc); + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { + bridge_rtnode_destroy(sc, brt); + if (sc->sc_brtcnt <= sc->sc_brtmax) + return; + } + } +} + +/* + * bridge_timer: + * + * Aging timer for the bridge. + */ +void +bridge_timer(void *arg) +{ + struct bridge_softc *sc = arg; + + BRIDGE_LOCK(sc); + bridge_rtage(sc); + BRIDGE_UNLOCK(sc); + + if (sc->sc_if.if_flags & IFF_RUNNING) + callout_reset(&sc->sc_brcallout, + bridge_rtable_prune_period * hz, bridge_timer, sc); +} + +/* + * bridge_rtage: + * + * Perform an aging cycle. + */ +void +bridge_rtage(struct bridge_softc *sc) +{ + struct bridge_rtnode *brt, *nbrt; + struct timeval tv; + + BRIDGE_LOCK_ASSERT(sc); + + getmicrotime(&tv); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { + if (tv.tv_sec >= brt->brt_expire) + bridge_rtnode_destroy(sc, brt); + } + } +} + +/* + * bridge_rtflush: + * + * Remove all dynamic addresses from the bridge. + */ +void +bridge_rtflush(struct bridge_softc *sc, int full) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) + bridge_rtnode_destroy(sc, brt); + } +} + +/* + * bridge_rtdaddr: + * + * Remove an address from the table. + */ +int +bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + + BRIDGE_LOCK_ASSERT(sc); + + if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) + return (ENOENT); + + bridge_rtnode_destroy(sc, brt); + return (0); +} + +/* + * bridge_rtdelete: + * + * Delete routes to a speicifc member interface. + */ +void +bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) +{ + struct bridge_rtnode *brt, *nbrt; + + BRIDGE_LOCK_ASSERT(sc); + + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { + nbrt = LIST_NEXT(brt, brt_list); + if (brt->brt_ifp == ifp && (full || + (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) + bridge_rtnode_destroy(sc, brt); + } +} + +/* + * bridge_rtable_init: + * + * Initialize the route table for this bridge. + */ +int +bridge_rtable_init(struct bridge_softc *sc) +{ + int i; + + sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, + M_DEVBUF, M_NOWAIT); + if (sc->sc_rthash == NULL) + return (ENOMEM); + + for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) + LIST_INIT(&sc->sc_rthash[i]); + + sc->sc_rthash_key = arc4random(); + + LIST_INIT(&sc->sc_rtlist); + + return (0); +} + +/* + * bridge_rtable_fini: + * + * Deconstruct the route table for this bridge. + */ +void +bridge_rtable_fini(struct bridge_softc *sc) +{ + + free(sc->sc_rthash, M_DEVBUF); +} + +/* + * The following hash function is adapted from "Hash Functions" by Bob Jenkins + * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). + */ +#define mix(a, b, c) \ +do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ +} while (/*CONSTCOND*/0) + +static __inline uint32_t +bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) +{ + uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; + + b += addr[5] << 8; + b += addr[4]; + a += addr[3] << 24; + a += addr[2] << 16; + a += addr[1] << 8; + a += addr[0]; + + mix(a, b, c); + + return (c & BRIDGE_RTHASH_MASK); +} + +#undef mix + +/* + * bridge_rtnode_lookup: + * + * Look up a bridge route node for the specified destination. + */ +struct bridge_rtnode * +bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr) +{ + struct bridge_rtnode *brt; + uint32_t hash; + int dir; + + BRIDGE_LOCK_ASSERT(sc); + + hash = bridge_rthash(sc, addr); + LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { + dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN); + if (dir == 0) + return (brt); + if (dir > 0) + return (NULL); + } + + return (NULL); +} + +/* + * bridge_rtnode_insert: + * + * Insert the specified bridge node into the route table. We + * assume the entry is not already in the table. + */ +int +bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) +{ + struct bridge_rtnode *lbrt; + uint32_t hash; + int dir; + + BRIDGE_LOCK_ASSERT(sc); + + hash = bridge_rthash(sc, brt->brt_addr); + + lbrt = LIST_FIRST(&sc->sc_rthash[hash]); + if (lbrt == NULL) { + LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); + goto out; + } + + do { + dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); + if (dir == 0) + return (EEXIST); + if (dir > 0) { + LIST_INSERT_BEFORE(lbrt, brt, brt_hash); + goto out; + } + if (LIST_NEXT(lbrt, brt_hash) == NULL) { + LIST_INSERT_AFTER(lbrt, brt, brt_hash); + goto out; + } + lbrt = LIST_NEXT(lbrt, brt_hash); + } while (lbrt != NULL); + +#ifdef DIAGNOSTIC + panic("bridge_rtnode_insert: impossible"); +#endif + + out: + LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); + sc->sc_brtcnt++; + + return (0); +} + +/* + * bridge_rtnode_destroy: + * + * Destroy a bridge rtnode. + */ +void +bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) +{ + BRIDGE_LOCK_ASSERT(sc); + + LIST_REMOVE(brt, brt_hash); + + LIST_REMOVE(brt, brt_list); + sc->sc_brtcnt--; + uma_zfree(bridge_rtnode_zone, brt); +} + +/* + * Send bridge packets through pfil if they are one of the types pfil can deal + * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without + * question.) + */ +static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, + struct ifnet *ifp, int dir) +{ + int snap, error; + struct ether_header *eh1, eh2; + struct ip *ip; + struct llc llc; + u_int16_t ether_type; + + snap = 0; + error = -1; /* Default error if not error == 0 */ + eh1 = mtod(*mp, struct ether_header *); + ether_type = ntohs(eh1->ether_type); + + /* + * Check for SNAP/LLC. + */ + if (ether_type < ETHERMTU) { + struct llc *llc = (struct llc *)(eh1 + 1); + + if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && + llc->llc_dsap == LLC_SNAP_LSAP && + llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI) { + ether_type = htons(llc->llc_un.type_snap.ether_type); + snap = 1; + } + } + + /* + * If we're trying to filter bridge traffic, don't look at anything + * other than IP and ARP traffic. If the filter doesn't understand + * IPv6, don't allow IPv6 through the bridge either. This is lame + * since if we really wanted, say, an AppleTalk filter, we are hosed, + * but of course we don't have an AppleTalk filter to begin with. + * (Note that since pfil doesn't understand ARP it will pass *ALL* + * ARP traffic.) + */ + switch (ether_type) { + case ETHERTYPE_ARP: + case ETHERTYPE_REVARP: + return 0; /* Automatically pass */ + case ETHERTYPE_IP: +# ifdef INET6 + case ETHERTYPE_IPV6: +# endif /* INET6 */ + break; + default: + goto bad; + } + + /* Strip off the Ethernet header and keep a copy. */ + m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2); + m_adj(*mp, ETHER_HDR_LEN); + + /* Strip off snap header, if present */ + if (snap) { + m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc); + m_adj(*mp, sizeof(struct llc)); + } + + /* + * Check basic packet sanity and run pfil through pfil. + */ + switch (ether_type) + { + case ETHERTYPE_IP : + error = (dir == PFIL_IN) ? bridge_ip_checkbasic(mp) : 0; + /* + * before calling the firewall, swap fields the same as + * IP does. here we assume the header is contiguous + */ + if (error == 0) { + ip = mtod(*mp, struct ip *); + + ip->ip_len = ntohs(ip->ip_len); + ip->ip_off = ntohs(ip->ip_off); + } else { + error = -1; + break; + } + + /* + * Run pfil on the member interface and the bridge, both can + * be skipped by clearing pfil_member or pfil_bridge. + * + * Keep the order: + * in_if -> bridge_if -> out_if + */ + if (error == 0 && pfil_bridge && dir == PFIL_OUT) + error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, + dir, NULL); + + if (error == 0 && pfil_member) + error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, + dir, NULL); + + if (error == 0 && pfil_bridge && dir == PFIL_IN) + error = pfil_run_hooks(&inet_pfil_hook, mp, bifp, + dir, NULL); + + /* Restore ip and the fields ntohs()'d. */ + if (*mp != NULL && error == 0) { + ip = mtod(*mp, struct ip *); + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + } + + break; +# ifdef INET6 + case ETHERTYPE_IPV6 : + error = (dir == PFIL_IN) ? bridge_ip6_checkbasic(mp) : 0; + if (error == 0) + error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, + dir, NULL); + break; +# endif + default : + error = 0; + break; + } + + if (*mp == NULL) + return error; + if (error != 0) + goto bad; + + error = -1; + + /* + * Finally, put everything back the way it was and return + */ + if (snap) { + M_PREPEND(*mp, sizeof(struct llc), M_DONTWAIT); + if (*mp == NULL) + return error; + bcopy(&llc, mtod(*mp, caddr_t), sizeof(struct llc)); + } + + M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT); + if (*mp == NULL) + return error; + bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); + + return 0; + + bad: + m_freem(*mp); + *mp = NULL; + return error; +} + +/* + * Perform basic checks on header size since + * pfil assumes ip_input has already processed + * it for it. Cut-and-pasted from ip_input.c. + * Given how simple the IPv6 version is, + * does the IPv4 version really need to be + * this complicated? + * + * XXX Should we update ipstat here, or not? + * XXX Right now we update ipstat but not + * XXX csum_counter. + */ +static int +bridge_ip_checkbasic(struct mbuf **mp) +{ + struct mbuf *m = *mp; + struct ip *ip; + int len, hlen; + u_short sum; + + if (*mp == NULL) + return -1; + + if (__predict_false(m->m_len < sizeof (struct ip))) { + if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { + ipstat.ips_toosmall++; + goto bad; + } + } + ip = mtod(m, struct ip *); + if (ip == NULL) goto bad; + + if (ip->ip_v != IPVERSION) { + ipstat.ips_badvers++; + goto bad; + } + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { /* minimum header length */ + ipstat.ips_badhlen++; + goto bad; + } + if (hlen > m->m_len) { + if ((m = m_pullup(m, hlen)) == 0) { + ipstat.ips_badhlen++; + goto bad; + } + ip = mtod(m, struct ip *); + if (ip == NULL) goto bad; + } + + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { + sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); + } else { + if (hlen == sizeof(struct ip)) { + sum = in_cksum_hdr(ip); + } else { + sum = in_cksum(m, hlen); + } + } + if (sum) { + ipstat.ips_badsum++; + goto bad; + } + + /* Retrieve the packet length. */ + len = ntohs(ip->ip_len); + + /* + * Check for additional length bogosity + */ + if (len < hlen) { + ipstat.ips_badlen++; + goto bad; + } + + /* + * Check that the amount of data in the buffers + * is as at least much as the IP header would have us expect. + * Drop packet if shorter than we expect. + */ + if (m->m_pkthdr.len < len) { + ipstat.ips_tooshort++; + goto bad; + } + + /* Checks out, proceed */ + *mp = m; + return 0; + + bad: + *mp = m; + return -1; +} + +# ifdef INET6 +/* + * Same as above, but for IPv6. + * Cut-and-pasted from ip6_input.c. + * XXX Should we update ip6stat, or not? + */ +static int +bridge_ip6_checkbasic(struct mbuf **mp) +{ + struct mbuf *m = *mp; + struct ip6_hdr *ip6; + + /* + * If the IPv6 header is not aligned, slurp it up into a new + * mbuf with space for link headers, in the event we forward + * it. Otherwise, if it is aligned, make sure the entire base + * IPv6 header is in the first mbuf of the chain. + + if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { + struct ifnet *inifp = m->m_pkthdr.rcvif; + if ((m = m_copyup(m, sizeof(struct ip6_hdr), + (max_linkhdr + 3) & ~3)) == NULL) { + * XXXJRT new stat, please * + ip6stat.ip6s_toosmall++; + in6_ifstat_inc(inifp, ifs6_in_hdrerr); + goto bad; + } + } else */ + if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { + struct ifnet *inifp = m->m_pkthdr.rcvif; + if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { + ip6stat.ip6s_toosmall++; + in6_ifstat_inc(inifp, ifs6_in_hdrerr); + goto bad; + } + } + + ip6 = mtod(m, struct ip6_hdr *); + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + ip6stat.ip6s_badvers++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + goto bad; + } + + /* Checks out, proceed */ + *mp = m; + return 0; + + bad: + *mp = m; + return -1; +} +# endif /* INET6 */ diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h new file mode 100644 index 000000000000..959a84445206 --- /dev/null +++ b/sys/net/if_bridgevar.h @@ -0,0 +1,357 @@ +/* $NetBSD: if_bridgevar.h,v 1.4 2003/07/08 07:13:50 itojun Exp $ */ + +/* + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * OpenBSD: if_bridge.h,v 1.14 2001/03/22 03:48:29 jason Exp + * + * $FreeBSD$ + */ + +/* + * Data structure and control definitions for bridge interfaces. + */ + +#include <sys/callout.h> +#include <sys/queue.h> + +/* + * Commands used in the SIOCSDRVSPEC ioctl. Note the lookup of the + * bridge interface itself is keyed off the ifdrv structure. + */ +#define BRDGADD 0 /* add bridge member (ifbreq) */ +#define BRDGDEL 1 /* delete bridge member (ifbreq) */ +#define BRDGGIFFLGS 2 /* get member if flags (ifbreq) */ +#define BRDGSIFFLGS 3 /* set member if flags (ifbreq) */ +#define BRDGSCACHE 4 /* set cache size (ifbrparam) */ +#define BRDGGCACHE 5 /* get cache size (ifbrparam) */ +#define BRDGGIFS 6 /* get member list (ifbifconf) */ +#define BRDGRTS 7 /* get address list (ifbaconf) */ +#define BRDGSADDR 8 /* set static address (ifbareq) */ +#define BRDGSTO 9 /* set cache timeout (ifbrparam) */ +#define BRDGGTO 10 /* get cache timeout (ifbrparam) */ +#define BRDGDADDR 11 /* delete address (ifbareq) */ +#define BRDGFLUSH 12 /* flush address cache (ifbreq) */ + +#define BRDGGPRI 13 /* get priority (ifbrparam) */ +#define BRDGSPRI 14 /* set priority (ifbrparam) */ +#define BRDGGHT 15 /* get hello time (ifbrparam) */ +#define BRDGSHT 16 /* set hello time (ifbrparam) */ +#define BRDGGFD 17 /* get forward delay (ifbrparam) */ +#define BRDGSFD 18 /* set forward delay (ifbrparam) */ +#define BRDGGMA 19 /* get max age (ifbrparam) */ +#define BRDGSMA 20 /* set max age (ifbrparam) */ +#define BRDGSIFPRIO 21 /* set if priority (ifbreq) */ +#define BRDGSIFCOST 22 /* set if path cost (ifbreq) */ + +/* + * Generic bridge control request. + */ +struct ifbreq { + char ifbr_ifsname[IFNAMSIZ]; /* member if name */ + uint32_t ifbr_ifsflags; /* member if flags */ + uint8_t ifbr_state; /* member if STP state */ + uint8_t ifbr_priority; /* member if STP priority */ + uint8_t ifbr_path_cost; /* member if STP cost */ + uint8_t ifbr_portno; /* member if port number */ +}; + +/* BRDGGIFFLAGS, BRDGSIFFLAGS */ +#define IFBIF_LEARNING 0x01 /* if can learn */ +#define IFBIF_DISCOVER 0x02 /* if sends packets w/ unknown dest. */ +#define IFBIF_STP 0x04 /* if participates in spanning tree */ + +#define IFBIFBITS "\020\1LEARNING\2DISCOVER\3STP" + +/* BRDGFLUSH */ +#define IFBF_FLUSHDYN 0x00 /* flush learned addresses only */ +#define IFBF_FLUSHALL 0x01 /* flush all addresses */ + +/* STP port states */ +#define BSTP_IFSTATE_DISABLED 0 +#define BSTP_IFSTATE_LISTENING 1 +#define BSTP_IFSTATE_LEARNING 2 +#define BSTP_IFSTATE_FORWARDING 3 +#define BSTP_IFSTATE_BLOCKING 4 + +/* + * Interface list structure. + */ +struct ifbifconf { + uint32_t ifbic_len; /* buffer size */ + union { + caddr_t ifbicu_buf; + struct ifbreq *ifbicu_req; + } ifbic_ifbicu; +#define ifbic_buf ifbic_ifbicu.ifbicu_buf +#define ifbic_req ifbic_ifbicu.ifbicu_req +}; + +/* + * Bridge address request. + */ +struct ifbareq { + char ifba_ifsname[IFNAMSIZ]; /* member if name */ + unsigned long ifba_expire; /* address expire time */ + uint8_t ifba_flags; /* address flags */ + uint8_t ifba_dst[ETHER_ADDR_LEN];/* destination address */ +}; + +#define IFBAF_TYPEMASK 0x03 /* address type mask */ +#define IFBAF_DYNAMIC 0x00 /* dynamically learned address */ +#define IFBAF_STATIC 0x01 /* static address */ + +#define IFBAFBITS "\020\1STATIC" + +/* + * Address list structure. + */ +struct ifbaconf { + uint32_t ifbac_len; /* buffer size */ + union { + caddr_t ifbacu_buf; + struct ifbareq *ifbacu_req; + } ifbac_ifbacu; +#define ifbac_buf ifbac_ifbacu.ifbacu_buf +#define ifbac_req ifbac_ifbacu.ifbacu_req +}; + +/* + * Bridge parameter structure. + */ +struct ifbrparam { + union { + uint32_t ifbrpu_int32; + uint16_t ifbrpu_int16; + uint8_t ifbrpu_int8; + } ifbrp_ifbrpu; +}; +#define ifbrp_csize ifbrp_ifbrpu.ifbrpu_int32 /* cache size */ +#define ifbrp_ctime ifbrp_ifbrpu.ifbrpu_int32 /* cache time (sec) */ +#define ifbrp_prio ifbrp_ifbrpu.ifbrpu_int16 /* bridge priority */ +#define ifbrp_hellotime ifbrp_ifbrpu.ifbrpu_int8 /* hello time (sec) */ +#define ifbrp_fwddelay ifbrp_ifbrpu.ifbrpu_int8 /* fwd time (sec) */ +#define ifbrp_maxage ifbrp_ifbrpu.ifbrpu_int8 /* max age (sec) */ + +#ifdef _KERNEL +/* + * Timekeeping structure used in spanning tree code. + */ +struct bridge_timer { + uint16_t active; + uint16_t value; +}; + +struct bstp_config_unit { + uint64_t cu_rootid; + uint64_t cu_bridge_id; + uint32_t cu_root_path_cost; + uint16_t cu_message_age; + uint16_t cu_max_age; + uint16_t cu_hello_time; + uint16_t cu_forward_delay; + uint16_t cu_port_id; + uint8_t cu_message_type; + uint8_t cu_topology_change_acknowledgment; + uint8_t cu_topology_change; +}; + +struct bstp_tcn_unit { + uint8_t tu_message_type; +}; + +/* + * Bridge interface list entry. + */ +struct bridge_iflist { + LIST_ENTRY(bridge_iflist) bif_next; + uint64_t bif_designated_root; + uint64_t bif_designated_bridge; + uint32_t bif_path_cost; + uint32_t bif_designated_cost; + struct bridge_timer bif_hold_timer; + struct bridge_timer bif_message_age_timer; + struct bridge_timer bif_forward_delay_timer; + struct bstp_config_unit bif_config_bpdu; + uint16_t bif_port_id; + uint16_t bif_designated_port; + uint8_t bif_state; + uint8_t bif_topology_change_acknowledge; + uint8_t bif_config_pending; + uint8_t bif_change_detection_enabled; + uint8_t bif_priority; + struct ifnet *bif_ifp; /* member if */ + uint32_t bif_flags; /* member if flags */ +}; + +/* + * Bridge route node. + */ +struct bridge_rtnode { + LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ + LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ + struct ifnet *brt_ifp; /* destination if */ + unsigned long brt_expire; /* expiration time */ + uint8_t brt_flags; /* address flags */ + uint8_t brt_addr[ETHER_ADDR_LEN]; +}; + +/* + * Software state for each bridge. + */ +struct bridge_softc { + struct arpcom ifb_ac; /* make this an interface */ + LIST_ENTRY(bridge_softc) sc_list; + struct mtx sc_mtx; + struct cv sc_cv; + uint64_t sc_designated_root; + uint64_t sc_bridge_id; + struct bridge_iflist *sc_root_port; + uint32_t sc_root_path_cost; + uint16_t sc_max_age; + uint16_t sc_hello_time; + uint16_t sc_forward_delay; + uint16_t sc_bridge_max_age; + uint16_t sc_bridge_hello_time; + uint16_t sc_bridge_forward_delay; + uint16_t sc_topology_change_time; + uint16_t sc_hold_time; + uint16_t sc_bridge_priority; + uint8_t sc_topology_change_detected; + uint8_t sc_topology_change; + struct bridge_timer sc_hello_timer; + struct bridge_timer sc_topology_change_timer; + struct bridge_timer sc_tcn_timer; + uint32_t sc_brtmax; /* max # of addresses */ + uint32_t sc_brtcnt; /* cur. # of addresses */ + uint32_t sc_brttimeout; /* rt timeout in seconds */ + struct callout sc_brcallout; /* bridge callout */ + struct callout sc_bstpcallout; /* STP callout */ + uint32_t sc_iflist_ref; /* refcount for sc_iflist */ + uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ + LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ + LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ + LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ + uint32_t sc_rthash_key; /* key for hash */ +}; + +#define BRIDGE_LOCK_INIT(_sc) do { \ + mtx_init(&(_sc)->sc_mtx, "if_bridge", NULL, MTX_DEF); \ + cv_init(&(_sc)->sc_cv, "if_bridge_cv"); \ +} while (0) +#define BRIDGE_LOCK_DESTROY(_sc) do { \ + mtx_destroy(&(_sc)->sc_mtx); \ + cv_destroy(&(_sc)->sc_cv); \ +} while (0) +#define BRIDGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define BRIDGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define BRIDGE_LOCKED(_sc) mtx_owned(&(_sc)->sc_mtx) +#define BRIDGE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define BRIDGE_LOCK2REF(_sc, _err) do { \ + mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ + if ((_sc)->sc_iflist_xcnt > 0) \ + (_err) = EBUSY; \ + else \ + (_sc)->sc_iflist_ref++; \ + mtx_unlock(&(_sc)->sc_mtx); \ +} while (0) +#define BRIDGE_UNREF(_sc) do { \ + mtx_lock(&(_sc)->sc_mtx); \ + (_sc)->sc_iflist_ref--; \ + if (((_sc)->sc_iflist_xcnt > 0) && ((_sc)->sc_iflist_ref == 0)) \ + cv_broadcast(&(_sc)->sc_cv); \ + mtx_unlock(&(_sc)->sc_mtx); \ +} while (0) +#define BRIDGE_XLOCK(_sc) do { \ + mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ + (_sc)->sc_iflist_xcnt++; \ + while ((_sc)->sc_iflist_ref > 0) \ + cv_wait(&(_sc)->sc_cv, &(_sc)->sc_mtx); \ +} while (0) +#define BRIDGE_XDROP(_sc) do { \ + mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ + (_sc)->sc_iflist_xcnt--; \ +} while (0) + +extern const uint8_t bstp_etheraddr[]; + +void bridge_ifdetach(struct ifnet *); +void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); + +int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +struct mbuf *bridge_input(struct ifnet *, struct mbuf *); + +extern void (*bstp_linkstate_p)(struct ifnet *ifp, int state); + +void bstp_initialization(struct bridge_softc *); +void bstp_linkstate(struct ifnet *, int); +void bstp_stop(struct bridge_softc *); +struct mbuf *bstp_input(struct ifnet *, struct mbuf *); + +void bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *, + int); + +#endif /* _KERNEL */ |