summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2005-09-10 22:40:37 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2005-09-10 22:40:37 +0000
commitf4a29f27f10f769aa96175eedfb93b6fd630b886 (patch)
treeb3b087c993b12c693973459773f19474ff79f365 /sys
parent57a6ae151f8522fd757545b3431245c991b8c315 (diff)
update the trunk(4) driver
- add multicast support by passing multicast addresses to the ports. this is a requirement for carp(4) over trunk(4). - support the smallest common interface capabilities. ie., this adds support for VLAN MTUs if all attached ports have this capability. - add a port_destroy callback to the trunk protocol. this fixes a potential crash if the master port has been detached while running. discussed with deraadt@, brad@ and some others.
Diffstat (limited to 'sys')
-rw-r--r--sys/net/if_trunk.c207
-rw-r--r--sys/net/if_trunk.h33
2 files changed, 229 insertions, 11 deletions
diff --git a/sys/net/if_trunk.c b/sys/net/if_trunk.c
index 84b896281b7..c7bc1473e25 100644
--- a/sys/net/if_trunk.c
+++ b/sys/net/if_trunk.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_trunk.c,v 1.4 2005/07/31 03:52:18 pascoe Exp $ */
+/* $OpenBSD: if_trunk.c,v 1.5 2005/09/10 22:40:36 reyk Exp $ */
/*
* Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net>
@@ -56,6 +56,7 @@ 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_capabilities(struct trunk_softc *);
int trunk_port_create(struct trunk_softc *, struct ifnet *);
int trunk_port_destroy(struct trunk_port *);
void trunk_port_watchdog(struct ifnet *);
@@ -64,6 +65,11 @@ struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
int trunk_port_checkstacking(struct trunk_softc *);
void trunk_port2req(struct trunk_port *, struct trunk_reqport *);
int trunk_ioctl(struct ifnet *, u_long, caddr_t);
+int trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
+int trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
+void trunk_ether_purgemulti(struct trunk_softc *);
+int trunk_ether_cmdmulti(struct trunk_port *, u_long);
+int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
void trunk_start(struct ifnet *);
void trunk_watchdog(struct ifnet *);
int trunk_media_change(struct ifnet *);
@@ -77,6 +83,7 @@ struct if_clone trunk_cloner =
/* Simple round robin */
int trunk_rr_attach(struct trunk_softc *);
int trunk_rr_detach(struct trunk_softc *);
+void trunk_rr_port_destroy(struct trunk_port *);
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 *,
@@ -140,6 +147,7 @@ trunk_clone_create(struct if_clone *ifc, int unit)
ifp->if_ioctl = trunk_ioctl;
ifp->if_output = ether_output;
ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ ifp->if_capabilities = trunk_capabilities(tr);
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
IFQ_SET_READY(&ifp->if_snd);
@@ -167,6 +175,9 @@ trunk_clone_destroy(struct ifnet *ifp)
struct trunk_port *tp;
int error, s;
+ /* Remove any multicast groups that we may have joined. */
+ trunk_ether_purgemulti(tr);
+
s = splnet();
/* Shutdown and remove trunk ports, return on error */
@@ -208,6 +219,23 @@ trunk_lladdr(struct trunk_softc *tr, u_int8_t *lladdr)
}
int
+trunk_capabilities(struct trunk_softc *tr)
+{
+ struct trunk_port *tp;
+ int cap = ~0;
+
+ SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
+ cap &= tp->tp_capabilities;
+
+ if (tr->tr_ifflags & IFF_DEBUG) {
+ printf("%s: capabilities 0x%08x\n",
+ tr->tr_ifname, cap == ~0 ? 0 : cap);
+ }
+
+ return (cap == ~0 ? 0 : cap);
+}
+
+int
trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
{
struct trunk_softc *tr_ptr;
@@ -273,7 +301,16 @@ trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
tr->tr_count++;
- return (0);
+ /* Update trunk capabilities */
+ tr->tr_capabilities = trunk_capabilities(tr);
+
+ /* Add multicast addresses to this port */
+ trunk_ether_cmdmulti(tp, SIOCADDMULTI);
+
+ if (tr->tr_port_create != NULL)
+ error = (*tr->tr_port_create)(tp);
+
+ return (error);
}
int
@@ -282,7 +319,7 @@ trunk_port_checkstacking(struct trunk_softc *tr)
struct trunk_softc *tr_ptr;
struct trunk_port *tp;
int m = 0;
-
+
SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
if (tp->tp_flags & TRUNK_PORT_STACK) {
tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
@@ -300,6 +337,12 @@ trunk_port_destroy(struct trunk_port *tp)
struct trunk_port *tp_ptr;
struct ifnet *ifp = tp->tp_if;
+ if (tr->tr_port_destroy != NULL)
+ (*tr->tr_port_destroy)(tp);
+
+ /* Remove multicast addresses from this port */
+ trunk_ether_cmdmulti(tp, SIOCDELMULTI);
+
/* Port has to be down */
if (ifp->if_flags & IFF_UP)
if_down(ifp);
@@ -332,6 +375,9 @@ trunk_port_destroy(struct trunk_port *tp)
free(tp, M_DEVBUF);
+ /* Update trunk capabilities */
+ tr->tr_capabilities = trunk_capabilities(tr);
+
return (0);
}
@@ -581,10 +627,10 @@ trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
ifp->if_flags &= ~IFF_RUNNING;
break;
case SIOCADDMULTI:
- error = ether_addmulti(ifr, &tr->tr_ac);
+ error = trunk_ether_addmulti(tr, ifr);
break;
case SIOCDELMULTI:
- error = ether_delmulti(ifr, &tr->tr_ac);
+ error = trunk_ether_delmulti(tr, ifr);
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
@@ -601,6 +647,143 @@ trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
return (error);
}
+int
+trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
+{
+ struct trunk_mc *mc;
+ u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
+ int error;
+
+ /* Ignore ENETRESET error code */
+ if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
+ return (error);
+
+ if ((mc = (struct trunk_mc *)malloc(sizeof(struct trunk_mc),
+ M_DEVBUF, M_NOWAIT)) == NULL) {
+ error = ENOMEM;
+ goto failed;
+ }
+
+ ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
+ bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
+ SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
+
+ if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
+ (caddr_t)ifr)) != 0) {
+ trunk_ether_delmulti(tr, ifr);
+ return (error);
+ }
+
+ return (error);
+
+ failed:
+ ether_delmulti(ifr, &tr->tr_ac);
+
+ return (error);
+}
+
+int
+trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
+{
+ struct ether_multi *enm;
+ struct trunk_mc *mc;
+ u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
+ int error;
+
+ if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
+ return (error);
+ ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
+
+ if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
+ return (error);
+
+ if ((error = trunk_ioctl_allports(tr, SIOCDELMULTI,
+ (caddr_t)ifr)) != 0) {
+ /* XXX At least one port failed to remove the address */
+ if (tr->tr_ifflags & IFF_DEBUG) {
+ printf("%s: failed to remove multicast address "
+ "on all ports\n", tr->tr_ifname);
+ }
+ }
+
+ SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
+ if (mc->mc_enm == enm) {
+ SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
+ free(mc, M_DEVBUF);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+void
+trunk_ether_purgemulti(struct trunk_softc *tr)
+{
+ struct trunk_mc *mc;
+ struct trunk_ifreq ifs;
+ struct ifreq *ifr = &ifs.ifreq.ifreq;
+
+ while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
+ bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
+
+ /* Try to remove multicast address on all ports */
+ trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
+
+ SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
+ free(mc, M_DEVBUF);
+ }
+}
+
+int
+trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+ struct trunk_mc *mc;
+ struct trunk_ifreq ifs;
+ struct ifreq *ifr = &ifs.ifreq.ifreq;
+ int ret, error = 0;
+
+ bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
+ SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
+ bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
+
+ if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
+ if (tr->tr_ifflags & IFF_DEBUG) {
+ printf("%s: ioctl %lu failed on %s: %d\n",
+ tr->tr_ifname, cmd, tp->tp_ifname, ret);
+ }
+ /* Store last known error and continue */
+ error = ret;
+ }
+ }
+
+ return (error);
+}
+
+int
+trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
+{
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct trunk_port *tp;
+ int ret, error = 0;
+
+ SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
+ bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
+ if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
+ if (tr->tr_ifflags & IFF_DEBUG) {
+ printf("%s: ioctl %lu failed on %s: %d\n",
+ tr->tr_ifname, cmd, tp->tp_ifname, ret);
+ }
+ /* Store last known error and continue */
+ error = ret;
+ }
+ }
+
+ return (error);
+}
+
void
trunk_start(struct ifnet *ifp)
{
@@ -608,9 +791,6 @@ trunk_start(struct ifnet *ifp)
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)
@@ -754,6 +934,8 @@ trunk_rr_attach(struct trunk_softc *tr)
tr->tr_detach = trunk_rr_detach;
tr->tr_start = trunk_rr_start;
tr->tr_input = trunk_rr_input;
+ tr->tr_port_create = NULL;
+ tr->tr_port_destroy = trunk_rr_port_destroy;
tp = SLIST_FIRST(&tr->tr_ports);
tr->tr_psc = (caddr_t)tp;
@@ -768,6 +950,15 @@ trunk_rr_detach(struct trunk_softc *tr)
return (0);
}
+void
+trunk_rr_port_destroy(struct trunk_port *tp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+
+ if (tp == (struct trunk_port *)tr->tr_psc)
+ tr->tr_psc = NULL;
+}
+
int
trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
{
diff --git a/sys/net/if_trunk.h b/sys/net/if_trunk.h
index d41497e5d1e..b67e3533d83 100644
--- a/sys/net/if_trunk.h
+++ b/sys/net/if_trunk.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_trunk.h,v 1.2 2005/05/24 07:51:53 reyk Exp $ */
+/* $OpenBSD: if_trunk.h,v 1.3 2005/09/10 22:40:36 reyk Exp $ */
/*
* Copyright (c) 2005 Reyk Floeter <reyk@vantronix.net>
@@ -102,6 +102,28 @@ struct trunk_port {
#define tp_ifname tp_if->if_xname /* interface name */
#define tp_link_state tp_if->if_link_state /* link state */
+#define tp_capabilities tp_if->if_capabilities /* capabilities */
+
+struct trunk_mc {
+ union {
+ struct ether_multi *mcu_enm;
+ } mc_u;
+ struct sockaddr_storage mc_addr;
+
+ SLIST_ENTRY(trunk_mc) mc_entries;
+};
+
+#define mc_enm mc_u.mcu_enm
+
+struct trunk_ifreq {
+ union {
+ struct ifreq ifreq;
+ struct {
+ char ifr_name[IFNAMSIZ];
+ struct sockaddr_storage ifr_ss;
+ } ifreq_storage;
+ } ifreq;
+};
struct trunk_softc {
struct arpcom tr_ac; /* virtual interface */
@@ -115,16 +137,21 @@ struct trunk_softc {
SLIST_HEAD(__tplhd, trunk_port) tr_ports; /* list of interfaces */
SLIST_ENTRY(trunk_softc) tr_entries;
+ SLIST_HEAD(__mclhd, trunk_mc) tr_mc_head; /* multicast addresses */
+
/* 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 *);
+ int (*tr_port_create)(struct trunk_port *);
+ void (*tr_port_destroy)(struct trunk_port *);
};
-#define tr_ifflags tr_ac.ac_if.if_flags /* interface flags */
-#define tr_ifname tr_ac.ac_if.if_xname /* interface name */
+#define tr_ifflags tr_ac.ac_if.if_flags /* flags */
+#define tr_ifname tr_ac.ac_if.if_xname /* name */
+#define tr_capabilities tr_ac.ac_if.if_capabilities /* capabilities */
void trunk_port_ifdetach(struct ifnet *);
int trunk_input(struct ifnet *, struct ether_header *, struct mbuf *);