summaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2005-05-24 02:45:19 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2005-05-24 02:45:19 +0000
commitebfdc55d0bccbe344e8950b89a5a0730ccc9253b (patch)
tree0d112ec2b177d5ac16483cb270c2af3be371991e /sys/net
parentc31238d30ac7cb511a14c4a4bc25106b4f89d822 (diff)
initial import of a trunking (link aggregation and link failover)
implementation. it currently supports round robin mode with link state checking, additional modes will be added later. ok brad@, deraadt@
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/if.c11
-rw-r--r--sys/net/if.h3
-rw-r--r--sys/net/if_ethersubr.c23
-rw-r--r--sys/net/if_trunk.c790
-rw-r--r--sys/net/if_trunk.h131
5 files changed, 955 insertions, 3 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index 14cf8d65d2b..06b3f2fafdf 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.c,v 1.114 2005/05/24 02:26:04 henning Exp $ */
+/* $OpenBSD: if.c,v 1.115 2005/05/24 02:45:17 reyk Exp $ */
/* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */
/*
@@ -105,6 +105,10 @@
#include <net/bpf.h>
#endif
+#if NTRUNK > 0
+#include <net/if_trunk.h>
+#endif
+
#if NBRIDGE > 0
#include <net/if_bridge.h>
#endif
@@ -496,6 +500,11 @@ if_detach(struct ifnet *ifp)
ifp->if_init = if_detached_init;
ifp->if_watchdog = if_detached_watchdog;
+#if NTRUNK > 0
+ if (ifp->if_type == IFT_IEEE8023ADLAG)
+ trunk_port_ifdetach(ifp);
+#endif
+
#if NBRIDGE > 0
/* Remove the interface from any bridge it is part of. */
if (ifp->if_bridge)
diff --git a/sys/net/if.h b/sys/net/if.h
index f31c5c3c112..20227989e4e 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.h,v 1.68 2005/05/24 02:26:04 henning Exp $ */
+/* $OpenBSD: if.h,v 1.69 2005/05/24 02:45:17 reyk Exp $ */
/* $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $ */
/*
@@ -181,6 +181,7 @@ struct ifnet { /* and the entries */
int if_pcount; /* number of promiscuous listeners */
caddr_t if_bpf; /* packet filter structure */
caddr_t if_bridge; /* bridge structure */
+ caddr_t if_tp; /* used by trunk ports */
caddr_t if_pf_kif; /* pf interface abstraction */
union {
caddr_t carp_s; /* carp structure (used by !carp ifs) */
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 6b5c799a57c..4b440cb2dd1 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_ethersubr.c,v 1.89 2005/04/25 01:34:27 brad Exp $ */
+/* $OpenBSD: if_ethersubr.c,v 1.90 2005/05/24 02:45:17 reyk Exp $ */
/* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */
/*
@@ -94,6 +94,7 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <net/route.h>
#include <net/if_llc.h>
#include <net/if_dl.h>
+#include <net/if_media.h>
#include <net/if_types.h>
#include <netinet/in.h>
@@ -127,6 +128,11 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
#include <net/if_pppoe.h>
#endif
+#include "trunk.h"
+#if NTRUNK > 0
+#include <net/if_trunk.h>
+#endif
+
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
@@ -256,6 +262,11 @@ ether_output(ifp0, m0, dst, rt0)
short mflags;
struct ifnet *ifp = ifp0;
+#if NTRUNK > 0
+ if (ifp->if_type == IFT_IEEE8023ADLAG)
+ senderr(EBUSY);
+#endif
+
#if NCARP > 0
if (ifp->if_type == IFT_CARP) {
struct ifaddr *ifa;
@@ -608,6 +619,16 @@ ether_input(ifp, eh, m)
struct ether_header *eh_tmp;
#endif
+#if NTRUNK > 0
+ /* Handle input from a trunk port */
+ if (ifp->if_type == IFT_IEEE8023ADLAG) {
+ if (trunk_input(ifp, eh, m) != 0)
+ return;
+ /* Has been set to the trunk interface */
+ ifp = m->m_pkthdr.rcvif;
+ }
+#endif
+
if ((ifp->if_flags & IFF_UP) == 0) {
m_freem(m);
return;
diff --git a/sys/net/if_trunk.c b/sys/net/if_trunk.c
new file mode 100644
index 00000000000..e0c7290b63f
--- /dev/null
+++ b/sys/net/if_trunk.c
@@ -0,0 +1,790 @@
+/* $OpenBSD: if_trunk.c,v 1.1 2005/05/24 02:45:17 reyk Exp $ */
+
+/*
+ * Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bpfilter.h"
+#include "trunk.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_llc.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net/if_trunk.h>
+
+SLIST_HEAD(__trhead, trunk_softc) trunk_list; /* list of trunks */
+
+extern struct ifaddr **ifnet_addrs;
+extern int ifqmaxlen;
+
+void trunkattach(int);
+int trunk_clone_create(struct if_clone *, int);
+int trunk_clone_destroy(struct ifnet *);
+void trunk_lladdr(struct trunk_softc *, u_int8_t *);
+int trunk_port_create(struct trunk_softc *, struct ifnet *);
+int trunk_port_destroy(struct trunk_port *);
+void trunk_port_watchdog(struct ifnet *);
+int trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
+struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
+void trunk_port2req(struct trunk_port *, struct trunk_reqport *);
+int trunk_ioctl(struct ifnet *, u_long, caddr_t);
+void trunk_start(struct ifnet *);
+void trunk_watchdog(struct ifnet *);
+int trunk_media_change(struct ifnet *);
+void trunk_media_status(struct ifnet *, struct ifmediareq *);
+struct trunk_port *trunk_link_active(struct trunk_softc *,
+ struct trunk_port *);
+
+struct if_clone trunk_cloner =
+ IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
+
+/* Simple round robin */
+int trunk_rr_attach(struct trunk_softc *);
+int trunk_rr_detach(struct trunk_softc *);
+int trunk_rr_start(struct trunk_softc *, struct mbuf *);
+int trunk_rr_watchdog(struct trunk_softc *);
+int trunk_rr_input(struct trunk_softc *, struct trunk_port *,
+ struct ether_header *, struct mbuf *);
+
+/* Trunk protocol table */
+static const struct {
+ enum trunk_proto ti_proto;
+ int (*ti_attach)(struct trunk_softc *);
+} trunk_protos[] = {
+ { TRUNK_PROTO_ROUNDROBIN, trunk_rr_attach },
+ { TRUNK_PROTO_NONE, }
+};
+
+void
+trunkattach(int count)
+{
+ SLIST_INIT(&trunk_list);
+ if_clone_attach(&trunk_cloner);
+}
+
+int
+trunk_clone_create(struct if_clone *ifc, int unit)
+{
+ struct trunk_softc *tr;
+ struct ifnet *ifp;
+ int i, error = 0;
+
+ if ((tr = malloc(sizeof(struct trunk_softc),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+
+ bzero(tr, sizeof(struct trunk_softc));
+
+ tr->tr_unit = unit;
+ tr->tr_proto = TRUNK_PROTO_NONE;
+ for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
+ if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
+ tr->tr_proto = trunk_protos[i].ti_proto;
+ if ((error = trunk_protos[i].ti_attach(tr)) != 0)
+ return (error);
+ break;
+ }
+ }
+ SLIST_INIT(&tr->tr_ports);
+
+ /* Initialise pseudo media types */
+ ifmedia_init(&tr->tr_media, 0, trunk_media_change,
+ trunk_media_status);
+ ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
+
+ ifp = &tr->tr_ac.ac_if;
+ ifp->if_carp = NULL;
+ ifp->if_type = IFT_ETHER;
+ ifp->if_softc = tr;
+ ifp->if_start = trunk_start;
+ ifp->if_watchdog = trunk_watchdog;
+ ifp->if_ioctl = trunk_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
+ ifc->ifc_name, unit);
+
+ /*
+ * Attach as an ordinary ethernet device, childs will be attached
+ * as special device IFT_IEEE8023ADLAG.
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ /* Insert into the global list of trunks */
+ SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
+
+ return (0);
+}
+
+int
+trunk_clone_destroy(struct ifnet *ifp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+ struct trunk_port *tp;
+ int error;
+
+ /* Shutdown and remove trunk ports, return on error */
+ while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL)
+ if ((error = trunk_port_destroy(tp)) != 0)
+ return (error);
+
+ ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
+#if NBPFILTER > 0
+ bpfdetach(ifp);
+#endif
+ ether_ifdetach(ifp);
+ if_detach(ifp);
+
+ SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
+ free(tr, M_DEVBUF);
+
+ return (0);
+}
+
+void
+trunk_lladdr(struct trunk_softc *tr, u_int8_t *lladdr)
+{
+ struct ifnet *ifp = &tr->tr_ac.ac_if;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+
+ ifa = ifnet_addrs[ifp->if_index];
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
+ bcopy(lladdr, tr->tr_ac.ac_enaddr, ETHER_ADDR_LEN);
+}
+
+int
+trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
+{
+ struct trunk_port *tp;
+ int error = 0;
+
+ /* Limit the maximal number of trunk ports */
+ if (tr->tr_count >= TRUNK_MAX_PORTS)
+ return (ENOSPC);
+
+ /* New trunk port has to be in an idle state */
+ if (ifp->if_flags & IFF_OACTIVE)
+ return (EBUSY);
+
+ /* Check if port has already been associated to a trunk */
+ if (trunk_port_get(NULL, ifp) != NULL)
+ return (EBUSY);
+
+ /* XXX Disallow non-ethernet interfaces (this should be any of 802) */
+ if (ifp->if_type != IFT_ETHER)
+ return (EPROTONOSUPPORT);
+
+ if ((error = ifpromisc(ifp, 1)) != 0)
+ return (error);
+
+ if ((tp = malloc(sizeof(struct trunk_port),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+
+ bzero(tp, sizeof(struct trunk_port));
+
+ /* Change the interface type */
+ tp->tp_iftype = ifp->if_type;
+ ifp->if_type = IFT_IEEE8023ADLAG;
+ ifp->if_tp = (caddr_t)tp;
+ tp->tp_watchdog = ifp->if_watchdog;
+ ifp->if_watchdog = trunk_port_watchdog;
+ tp->tp_ioctl = ifp->if_ioctl;
+ ifp->if_ioctl = trunk_port_ioctl;
+
+ tp->tp_if = ifp;
+ tp->tp_trunk = (caddr_t)tr;
+
+ if (SLIST_EMPTY(&tr->tr_ports)) {
+ tr->tr_primary = tp;
+ tp->tp_flags = TRUNK_PORT_MASTER;
+ trunk_lladdr(tr, ((struct arpcom *)ifp)->ac_enaddr);
+ }
+
+ /* Insert into the global list of trunks */
+ SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
+ tr->tr_count++;
+
+ return (0);
+}
+
+int
+trunk_port_destroy(struct trunk_port *tp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+ struct trunk_port *tp_ptr;
+ struct ifnet *ifp = tp->tp_if;
+
+ /* Port has to be down */
+ if (ifp->if_flags & IFF_UP)
+ if_down(ifp);
+
+ ifpromisc(ifp, 0);
+
+ /* Restore interface */
+ ifp->if_type = tp->tp_iftype;
+ ifp->if_watchdog = tp->tp_watchdog;
+ ifp->if_ioctl = tp->tp_ioctl;
+ ifp->if_tp = NULL;
+
+ /* Finally, remove the port from the trunk */
+ SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
+ tr->tr_count--;
+
+ /* Update the primary interface */
+ if (tp == tr->tr_primary) {
+ if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
+ u_int8_t lladdr[ETHER_ADDR_LEN];
+ bzero(&lladdr, ETHER_ADDR_LEN);
+ trunk_lladdr(tr, lladdr);
+ } else {
+ trunk_lladdr(tr,
+ ((struct arpcom *)tp_ptr->tp_if)->ac_enaddr);
+ tp_ptr->tp_flags = TRUNK_PORT_MASTER;
+ }
+ tr->tr_primary = tp_ptr;
+ }
+
+ free(tp, M_DEVBUF);
+
+ return (0);
+}
+
+void
+trunk_port_watchdog(struct ifnet *ifp)
+{
+ struct trunk_softc *tr;
+ struct trunk_port *tp;
+
+ /* Should be checked by the caller */
+ if (ifp->if_type != IFT_IEEE8023ADLAG)
+ return;
+ if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
+ (tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
+ return;
+
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s\n", __func__);
+
+ if (tp->tp_watchdog != NULL)
+ (*tp->tp_watchdog)(ifp);
+}
+
+
+int
+trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct trunk_reqport *rp = (struct trunk_reqport *)data;
+ struct trunk_softc *tr;
+ struct trunk_port *tp;
+ int s, error = 0;
+
+ s = splimp();
+
+ /* Should be checked by the caller */
+ if (ifp->if_type != IFT_IEEE8023ADLAG ||
+ (tp = (struct trunk_port *)ifp->if_tp) == NULL ||
+ (tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
+ goto fallback;
+
+ switch (cmd) {
+ case SIOCGTRUNKPORT:
+ if (rp->rp_portname[0] == '\0' ||
+ ifunit(rp->rp_portname) != ifp) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Search in all trunks if the global flag is set */
+ if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
+ NULL : tr, ifp)) == NULL) {
+ error = ENOENT;
+ break;
+ }
+
+ trunk_port2req(tp, rp);
+ break;
+ default:
+ goto fallback;
+ }
+
+ splx(s);
+ return (error);
+
+ fallback:
+ splx(s);
+ return ((*tp->tp_ioctl)(ifp, cmd, data));
+}
+
+void
+trunk_port_ifdetach(struct ifnet *ifp)
+{
+ struct trunk_port *tp;
+
+ if (SLIST_FIRST(&trunk_list) == NULL)
+ return;
+ if ((tp = trunk_port_get(NULL, ifp)) == NULL)
+ return;
+
+ trunk_port_destroy(tp);
+}
+
+struct trunk_port *
+trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
+{
+ struct trunk_port *tp;
+ struct trunk_softc *tr_ptr;
+
+ if (tr != NULL) {
+ /* Search port in specified trunk */
+ SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
+ if (tp->tp_if == ifp)
+ return (tp);
+ }
+ } else {
+ /* Search all trunks for the selected port */
+ SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
+ SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
+ if (tp->tp_if == ifp)
+ return (tp);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+void
+trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+ strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
+ strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
+ rp->rp_flags = tp->tp_flags;
+}
+
+int
+trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+ struct trunk_reqall *ra = (struct trunk_reqall *)data;
+ struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct trunk_port *tp;
+ struct ifnet *tpif;
+ int s, i, error = 0;
+
+ s = splimp();
+
+ if ((error = ether_ioctl(ifp, &tr->tr_ac, cmd, data)) > 0) {
+ splx(s);
+ return (error);
+ }
+
+ bzero(&rpbuf, sizeof(rpbuf));
+
+ switch (cmd) {
+ case SIOCGTRUNK:
+ ra->ra_proto = tr->tr_proto;
+ ra->ra_ports = i = 0;
+ tp = SLIST_FIRST(&tr->tr_ports);
+ while (tp && ra->ra_size >=
+ i + sizeof(struct trunk_reqport)) {
+ trunk_port2req(tp, &rpbuf);
+ error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
+ sizeof(struct trunk_reqport));
+ if (error)
+ break;
+ i += sizeof(struct trunk_reqport);
+ ra->ra_ports++;
+ tp = SLIST_NEXT(tp, tp_entries);
+ }
+ break;
+ case SIOCSTRUNK:
+ if ((error = suser(curproc, 0)) != 0) {
+ error = EPERM;
+ break;
+ }
+ if (ra->ra_proto >= TRUNK_PROTO_MAX) {
+ error = EPROTONOSUPPORT;
+ break;
+ }
+ if (tr->tr_proto != TRUNK_PROTO_NONE)
+ error = tr->tr_detach(tr);
+ if (error != 0)
+ break;
+ for (i = 0; i < (sizeof(trunk_protos) /
+ sizeof(trunk_protos[0])); i++) {
+ if (trunk_protos[i].ti_proto == ra->ra_proto) {
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s: using proto %u\n",
+ tr->tr_ifname,
+ trunk_protos[i].ti_proto);
+ tr->tr_proto = trunk_protos[i].ti_proto;
+ if (tr->tr_proto != TRUNK_PROTO_NONE)
+ error = trunk_protos[i].ti_attach(tr);
+ goto out;
+ }
+ }
+ error = EPROTONOSUPPORT;
+ break;
+ case SIOCGTRUNKPORT:
+ if (rp->rp_portname[0] == '\0' ||
+ (tpif = ifunit(rp->rp_portname)) == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Search in all trunks if the global flag is set */
+ if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
+ NULL : tr, tpif)) == NULL) {
+ error = ENOENT;
+ break;
+ }
+
+ trunk_port2req(tp, rp);
+ break;
+ case SIOCSTRUNKPORT:
+ if ((error = suser(curproc, 0)) != 0) {
+ error = EPERM;
+ break;
+ }
+ if (rp->rp_portname[0] == '\0' ||
+ (tpif = ifunit(rp->rp_portname)) == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = trunk_port_create(tr, tpif);
+ break;
+ case SIOCSTRUNKDELPORT:
+ if ((error = suser(curproc, 0)) != 0) {
+ error = EPERM;
+ break;
+ }
+ if (rp->rp_portname[0] == '\0' ||
+ (tpif = ifunit(rp->rp_portname)) == NULL) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Search in all trunks if the global flag is set */
+ if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
+ NULL : tr, tpif)) == NULL) {
+ error = ENOENT;
+ break;
+ }
+
+ error = trunk_port_destroy(tp);
+ break;
+ case SIOCSIFADDR:
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ arp_ifinit(&tr->tr_ac, ifa);
+ }
+#endif /* INET */
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ break;
+ }
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP)
+ ifp->if_flags |= IFF_RUNNING;
+ else
+ ifp->if_flags &= ~IFF_RUNNING;
+ break;
+ case SIOCADDMULTI:
+ error = ether_addmulti(ifr, &tr->tr_ac);
+ break;
+ case SIOCDELMULTI:
+ error = ether_delmulti(ifr, &tr->tr_ac);
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ out:
+ splx(s);
+
+ return (error);
+}
+
+void
+trunk_start(struct ifnet *ifp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+ struct mbuf *m;
+ int error = 0;
+
+ if (ifp->if_flags & IFF_DEBUG)
+ printf("%s: start\n", ifp->if_xname);
+
+ for (;; error = 0) {
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ if (tr->tr_proto != TRUNK_PROTO_NONE)
+ error = (*tr->tr_start)(tr, m);
+ else
+ m_free(m);
+ if (error == 0)
+ ifp->if_opackets++;
+ else
+ ifp->if_oerrors++;
+ }
+
+ return;
+}
+
+void
+trunk_watchdog(struct ifnet *ifp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+
+ if (tr->tr_proto != TRUNK_PROTO_NONE &&
+ (*tr->tr_watchdog)(tr) != 0) {
+ ifp->if_oerrors++;
+ }
+
+}
+
+int
+trunk_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
+{
+ struct trunk_softc *tr;
+ struct trunk_port *tp;
+ struct ifnet *trifp;
+ int error = 0;
+
+ /* Should be checked by the caller */
+ if (ifp->if_type != IFT_IEEE8023ADLAG) {
+ error = EPROTONOSUPPORT;
+ goto bad;
+ }
+ if ((tp = (struct trunk_port *)ifp->if_tp) == NULL ||
+ (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
+ error = ENOENT;
+ goto bad;
+ }
+ if (tr->tr_proto == TRUNK_PROTO_NONE)
+ goto bad;
+ trifp = &tr->tr_ac.ac_if;
+
+#if NBPFILTER > 0
+ if (trifp->if_bpf) {
+ struct mbuf m0;
+
+ m0.m_flags = 0;
+ m0.m_next = m;
+ m0.m_len = ETHER_HDR_LEN;
+ m0.m_data = (char *)eh;
+ bpf_mtap(trifp->if_bpf, &m0);
+ }
+#endif
+
+ error = (*tr->tr_input)(tr, tp, eh, m);
+ if (error != 0)
+ goto bad;
+
+ trifp->if_ipackets++;
+
+ return (0);
+
+ bad:
+ trifp->if_ierrors++;
+ if (m)
+ m_free(m);
+ return (error);
+}
+
+int
+trunk_media_change(struct ifnet *ifp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s\n", __func__);
+
+ /* Ignore */
+ return (0);
+}
+
+void
+trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
+ struct trunk_port *tp;
+
+ imr->ifm_status = IFM_AVALID;
+ imr->ifm_active = IFM_ETHER | IFM_AUTO;
+
+ tp = tr->tr_primary;
+ if (tp != NULL && tp->tp_if->if_flags & IFF_UP)
+ imr->ifm_status |= IFM_ACTIVE;
+}
+
+struct trunk_port *
+trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
+{
+ struct trunk_port *tp_next;
+
+ /*
+ * Search a port which reports an active link state.
+ * Normally, this should be LINK_STATE_UP but not all
+ * drivers seem to report this correctly so we assume
+ * that LINK_STATE_DOWN is the opposite from
+ * LINK_STATE_UNKNOWN and LINK_STATE_UP.
+ */
+
+ if (tp == NULL)
+ goto search;
+ if (tp->tp_link_state != LINK_STATE_DOWN)
+ return (tp);
+ if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
+ tp_next->tp_link_state != LINK_STATE_DOWN)
+ return (tp_next);
+
+ search:
+ SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
+ if (tp_next->tp_link_state != LINK_STATE_DOWN)
+ return (tp_next);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Simple round robin trunking
+ */
+
+int
+trunk_rr_attach(struct trunk_softc *tr)
+{
+ struct trunk_port *tp;
+
+ tr->tr_detach = trunk_rr_detach;
+ tr->tr_start = trunk_rr_start;
+ tr->tr_input = trunk_rr_input;
+
+ tp = SLIST_FIRST(&tr->tr_ports);
+ tr->tr_psc = (caddr_t)tp;
+
+ return (0);
+}
+
+int
+trunk_rr_detach(struct trunk_softc *tr)
+{
+ tr->tr_psc = NULL;
+ return (0);
+}
+
+int
+trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
+{
+ struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
+ struct ifnet *ifp;
+ int error = 0;
+
+ if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL)
+ return (ENOENT);
+
+ /* Send mbuf */
+ ifp = tp->tp_if;
+ IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
+ if (error)
+ return (error);
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ (*ifp->if_start)(ifp);
+
+ ifp->if_obytes += m->m_pkthdr.len;
+ if (m->m_flags & M_MCAST)
+ ifp->if_omcasts++;
+
+ /* Get next active port */
+ tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
+ tr->tr_psc = (caddr_t)tp_next;
+
+ return (error);
+}
+
+int
+trunk_rr_watchdog(struct trunk_softc *tr)
+{
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s\n", __func__);
+
+ return (0);
+}
+
+int
+trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp,
+ struct ether_header *eh, struct mbuf *m)
+{
+ struct ifnet *ifp = &tr->tr_ac.ac_if;
+
+ /* Just pass in the packet to our trunk device */
+ m->m_pkthdr.rcvif = ifp;
+
+ return (0);
+}
diff --git a/sys/net/if_trunk.h b/sys/net/if_trunk.h
new file mode 100644
index 00000000000..42c6d5fa264
--- /dev/null
+++ b/sys/net/if_trunk.h
@@ -0,0 +1,131 @@
+/* $OpenBSD: if_trunk.h,v 1.1 2005/05/24 02:45:17 reyk Exp $ */
+
+/*
+ * Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _NET_TRUNK_H
+#define _NET_TRUNK_H
+
+/*
+ * Global definitions
+ */
+
+#define TRUNK_MAX_PORTS 32 /* logically */
+#define TRUNK_MAX_NAMESIZE 32 /* name of a protocol */
+
+/* Port flags */
+#define TRUNK_PORT_SLAVE 0x00000000 /* normal enslaved port */
+#define TRUNK_PORT_MASTER 0x00000001 /* primary port */
+#define TRUNK_PORT_GLOBAL 0x80000000 /* IOCTL: global flag */
+#define TRUNK_PORT_BITS "\20\01MASTER"
+
+/* Supported trunk PROTOs */
+enum trunk_proto {
+ TRUNK_PROTO_NONE = 0, /* no trunk protocol defined */
+ TRUNK_PROTO_ROUNDROBIN = 1, /* simple round robin */
+ TRUNK_PROTO_MAX = 3,
+};
+
+struct trunk_protos {
+ const char *tpr_name;
+ enum trunk_proto tpr_proto;
+};
+
+#define TRUNK_PROTO_DEFAULT TRUNK_PROTO_ROUNDROBIN
+#define TRUNK_PROTOS { \
+ { "roundrobin", TRUNK_PROTO_ROUNDROBIN }, \
+ { "none", TRUNK_PROTO_NONE }, \
+ { "default", TRUNK_PROTO_DEFAULT } \
+}
+
+/*
+ * Trunk ioctls.
+ */
+
+/* Trunk port settings */
+struct trunk_reqport {
+ char rp_ifname[IFNAMSIZ]; /* name of the trunk */
+ char rp_portname[IFNAMSIZ]; /* name of the port */
+ u_int32_t rp_flags; /* port flags */
+};
+
+#define SIOCGTRUNKPORT _IOWR('i', 140, struct trunk_reqport)
+#define SIOCSTRUNKPORT _IOW('i', 141, struct trunk_reqport)
+#define SIOCSTRUNKDELPORT _IOW('i', 142, struct trunk_reqport)
+
+/* Trunk, ports and options */
+struct trunk_reqall {
+ char ra_ifname[IFNAMSIZ]; /* name of the trunk */
+ u_int ra_proto; /* trunk protocol */
+
+ size_t ra_size; /* size of buffer */
+ struct trunk_reqport *ra_port; /* allocated buffer */
+ int ra_ports; /* total port count */
+};
+
+#define SIOCGTRUNK _IOWR('i', 143, struct trunk_reqall)
+#define SIOCSTRUNK _IOW('i', 144, struct trunk_reqall)
+
+#ifdef _KERNEL
+/*
+ * Internal kernel part
+ */
+
+struct trunk_port {
+ struct ifnet *tp_if; /* physical interface */
+ caddr_t tp_trunk; /* parent trunk */
+
+ u_char tp_iftype; /* interface type */
+ u_int32_t tp_flags; /* port flags */
+
+ /* Redirected callbacks */
+ void (*tp_watchdog)(struct ifnet *);
+ int (*tp_ioctl)(struct ifnet *, u_long, caddr_t);
+
+ SLIST_ENTRY(trunk_port) tp_entries;
+};
+
+#define tp_ifname tp_if->if_xname /* interface name */
+#define tp_link_state tp_if->if_link_state /* link state */
+
+struct trunk_softc {
+ struct arpcom tr_ac; /* virtual interface */
+ int tr_unit; /* trunk unit */
+ enum trunk_proto tr_proto; /* trunk protocol */
+ u_int tr_count; /* number of ports */
+ struct trunk_port *tr_primary; /* primary port */
+ struct ifmedia tr_media; /* media config */
+ caddr_t tr_psc; /* protocol data */
+
+ SLIST_HEAD(__tplhd, trunk_port) tr_ports; /* list of interfaces */
+ SLIST_ENTRY(trunk_softc) tr_entries;
+
+ /* Trunk protocol callbacks */
+ int (*tr_detach)(struct trunk_softc *);
+ int (*tr_start)(struct trunk_softc *, struct mbuf *);
+ int (*tr_watchdog)(struct trunk_softc *);
+ int (*tr_input)(struct trunk_softc *, struct trunk_port *,
+ struct ether_header *, struct mbuf *);
+};
+
+#define tr_ifflags tr_ac.ac_if.if_flags /* interface flags */
+#define tr_ifname tr_ac.ac_if.if_xname /* interface name */
+
+void trunk_port_ifdetach(struct ifnet *);
+int trunk_input(struct ifnet *, struct ether_header *, struct mbuf *);
+#endif /* _KERNEL */
+
+#endif /* _NET_TRUNK_H */