summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/trunk.435
-rw-r--r--sys/net/if_trunk.c229
-rw-r--r--sys/net/if_trunk.h21
3 files changed, 261 insertions, 24 deletions
diff --git a/share/man/man4/trunk.4 b/share/man/man4/trunk.4
index f332e7f42ef..c979f207096 100644
--- a/share/man/man4/trunk.4
+++ b/share/man/man4/trunk.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: trunk.4,v 1.12 2006/05/20 05:46:21 reyk Exp $
+.\" $OpenBSD: trunk.4,v 1.13 2006/05/23 04:35:52 reyk Exp $
.\"
.\" Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -48,25 +48,34 @@ The driver currently supports the trunk protocols
.Ic roundrobin
(the default),
.Ic failover ,
+.Ic loadbalance ,
and
.Ic none .
-The
-.Ic roundrobin
-protocol distributes outgoing traffic using a round-robin scheduler
-through all active ports and accepts incoming traffic from
-any active port;
-the
-.Ic failover
-protocol sends outgoing and receives incoming traffic only through the
-master or the next active port.
+The protocols will choose the active ports for outgoing traffic
+and if they accept incoming traffic from specific ports.
The interface link state is used to validate if the port is active or
not.
-The
-.Ic none
-protocol is intended to do nothing: it disables any traffic without
+.Pp
+.Bl -tag -width loadbalance
+.It Ic roundrobin
+Distributes outgoing traffic using a round-robin scheduler
+through all active ports and accepts incoming traffic from
+any active port.
+.It Ic failover
+Sends and receives traffic only through the master or the next active
+port.
+.It Ic loadbalance
+Balances outgoing traffic across the active ports based on hashed
+protocol header information and accepts incoming traffic from
+any active port.
+The hash includes the Ethernet source and destination address, and, if
+available, the VLAN tag, and the IP source and destination address.
+.It Ic none
+This protocol is intended to do nothing: it disables any traffic without
disabling the
.Nm
interface itself.
+.El
.Pp
The configuration can be done at runtime or by setting up a
.Xr hostname.if 5
diff --git a/sys/net/if_trunk.c b/sys/net/if_trunk.c
index f4e5d6a206c..60e552af177 100644
--- a/sys/net/if_trunk.c
+++ b/sys/net/if_trunk.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_trunk.c,v 1.26 2006/05/20 22:03:24 reyk Exp $ */
+/* $OpenBSD: if_trunk.c,v 1.27 2006/05/23 04:35:52 reyk Exp $ */
/*
* Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
@@ -29,6 +29,9 @@
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/proc.h>
+#include <sys/hash.h>
+
+#include <dev/rndvar.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -42,9 +45,16 @@
#ifdef INET
#include <netinet/in.h>
+#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
#endif
+#include <net/if_vlan_var.h>
#include <net/if_trunk.h>
SLIST_HEAD(__trhead, trunk_softc) trunk_list; /* list of trunks */
@@ -97,14 +107,26 @@ int trunk_fail_start(struct trunk_softc *, struct mbuf *);
int trunk_fail_input(struct trunk_softc *, struct trunk_port *,
struct ether_header *, struct mbuf *);
+/* Loadbalancing */
+int trunk_lb_attach(struct trunk_softc *);
+int trunk_lb_detach(struct trunk_softc *);
+int trunk_lb_port_create(struct trunk_port *);
+void trunk_lb_port_destroy(struct trunk_port *);
+int trunk_lb_start(struct trunk_softc *, struct mbuf *);
+int trunk_lb_input(struct trunk_softc *, struct trunk_port *,
+ struct ether_header *, struct mbuf *);
+int trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
+const void *trunk_lb_gethdr(struct mbuf *, u_int, u_int, void *);
+
/* 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_FAILOVER, trunk_fail_attach },
- { TRUNK_PROTO_NONE, }
+ { TRUNK_PROTO_ROUNDROBIN, trunk_rr_attach },
+ { TRUNK_PROTO_FAILOVER, trunk_fail_attach },
+ { TRUNK_PROTO_LOADBALANCE, trunk_lb_attach },
+ { TRUNK_PROTO_NONE, NULL }
};
void
@@ -253,7 +275,7 @@ trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
/* Reset the port to update the lladdr */
if (ifp->if_flags & IFF_UP) {
- int s = splnet();
+ int s = splnet();
ifp->if_flags &= ~IFF_UP;
(*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
ifp->if_flags |= IFF_UP;
@@ -1185,3 +1207,200 @@ trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp,
return (-1);
}
+
+/*
+ * Loadbalancing
+ */
+
+int
+trunk_lb_attach(struct trunk_softc *tr)
+{
+ struct trunk_lb *lb;
+
+ if ((lb = (struct trunk_lb *)malloc(sizeof(struct trunk_lb),
+ M_DEVBUF, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ bzero(lb, sizeof(struct trunk_lb));
+
+ tr->tr_detach = trunk_lb_detach;
+ tr->tr_start = trunk_lb_start;
+ tr->tr_input = trunk_lb_input;
+ tr->tr_port_create = trunk_lb_port_create;
+ tr->tr_port_destroy = trunk_lb_port_destroy;
+
+ lb->lb_key = arc4random();
+ tr->tr_psc = (caddr_t)lb;
+
+ return (0);
+}
+
+int
+trunk_lb_detach(struct trunk_softc *tr)
+{
+ struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
+ if (lb != NULL)
+ free(lb, M_DEVBUF);
+ return (0);
+}
+
+int
+trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
+{
+ struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
+ struct trunk_port *tp_next;
+ int i = 0;
+
+ bzero(&lb->lb_ports, sizeof(lb->lb_ports));
+ SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
+ if (tp_next == tp)
+ continue;
+ if (i >= TRUNK_MAX_PORTS)
+ return (EINVAL);
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s: port %s at index %d\n",
+ tr->tr_ifname, tp_next->tp_ifname, i);
+ lb->lb_ports[i++] = tp_next;
+ }
+
+ return (0);
+}
+
+int
+trunk_lb_port_create(struct trunk_port *tp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+ return (trunk_lb_porttable(tr, NULL));
+}
+
+void
+trunk_lb_port_destroy(struct trunk_port *tp)
+{
+ struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
+ trunk_lb_porttable(tr, tp);
+}
+
+const void *
+trunk_lb_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
+{
+ if (m->m_pkthdr.len < (off + len)) {
+ return (NULL);
+ } else if (m->m_len < (off + len)) {
+ m_copydata(m, off, len, buf);
+ return (buf);
+ }
+ return (mtod(m, const void *) + off);
+}
+
+int
+trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
+{
+ struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
+ struct trunk_port *tp = NULL;
+ u_int16_t etype;
+ struct ifnet *ifp;
+ u_int32_t p = 0;
+ u_int16_t *vlan, vlanbuf[2];
+ int error = 0, idx, off;
+ struct ether_header *eh;
+#ifdef INET
+ struct ip *ip, ipbuf;
+#endif
+#ifdef INET6
+ struct ip6_hdr *ip6, ip6buf;
+#endif
+
+ off = sizeof(*eh);
+ if (m->m_len < off)
+ goto send;
+ eh = mtod(m, struct ether_header *);
+ etype = ntohs(eh->ether_type);
+ p = hash32_buf(&eh->ether_shost, ETHER_ADDR_LEN, lb->lb_key);
+ p = hash32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
+
+ /* Special handling for encapsulating VLAN frames */
+ if (etype == ETHERTYPE_VLAN) {
+ if ((vlan = (u_int16_t *)
+ trunk_lb_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL) {
+ if (m == NULL)
+ goto merr;
+ goto portidx;
+ }
+ p = hash32_buf(vlan, sizeof(*vlan), p);
+ etype = ntohs(vlan[1]);
+ off += EVL_ENCAPLEN;
+ }
+
+ switch (etype) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ if ((ip = (struct ip *)
+ trunk_lb_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL) {
+ if (m == NULL)
+ goto merr;
+ goto portidx;
+ }
+ p = hash32_buf(&ip->ip_src, sizeof(struct in_addr), p);
+ p = hash32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
+ break;
+#endif
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ if ((ip6 = (struct ip6_hdr *)
+ trunk_lb_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL) {
+ if (m == NULL)
+ goto merr;
+ goto portidx;
+ }
+ p = hash32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
+ p = hash32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
+ break;
+#endif
+ }
+
+ portidx:
+ /* Finally get the physical port */
+ if ((idx = p % tr->tr_count) >= TRUNK_MAX_PORTS)
+ return (EINVAL);
+ tp = lb->lb_ports[idx];
+
+ if (tr->tr_ifflags & IFF_DEBUG)
+ printf("%s: %d: port %u\n", __func__, __LINE__, p);
+
+ send:
+ /*
+ * Check the port's link state. This will return the next active
+ * port if the link is down or the port is NULL.
+ */
+ if ((tp = trunk_link_active(tr, tp)) == 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++;
+
+ return (error);
+
+ merr:
+ m = NULL;
+ return (ENOBUFS);
+}
+
+int
+trunk_lb_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
index 72cb55013b4..6049b0db17c 100644
--- a/sys/net/if_trunk.h
+++ b/sys/net/if_trunk.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_trunk.h,v 1.8 2006/05/20 22:03:24 reyk Exp $ */
+/* $OpenBSD: if_trunk.h,v 1.9 2006/05/23 04:35:52 reyk Exp $ */
/*
* Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
@@ -40,7 +40,8 @@ enum trunk_proto {
TRUNK_PROTO_NONE = 0, /* no trunk protocol defined */
TRUNK_PROTO_ROUNDROBIN = 1, /* simple round robin */
TRUNK_PROTO_FAILOVER = 2, /* active failover */
- TRUNK_PROTO_MAX = 3
+ TRUNK_PROTO_LOADBALANCE = 3, /* loadbalance */
+ TRUNK_PROTO_MAX = 4
};
struct trunk_protos {
@@ -50,10 +51,11 @@ struct trunk_protos {
#define TRUNK_PROTO_DEFAULT TRUNK_PROTO_ROUNDROBIN
#define TRUNK_PROTOS { \
- { "roundrobin", TRUNK_PROTO_ROUNDROBIN }, \
- { "failover", TRUNK_PROTO_FAILOVER }, \
- { "none", TRUNK_PROTO_NONE }, \
- { "default", TRUNK_PROTO_DEFAULT } \
+ { "roundrobin", TRUNK_PROTO_ROUNDROBIN }, \
+ { "failover", TRUNK_PROTO_FAILOVER }, \
+ { "loadbalance", TRUNK_PROTO_LOADBALANCE }, \
+ { "none", TRUNK_PROTO_NONE }, \
+ { "default", TRUNK_PROTO_DEFAULT } \
}
/*
@@ -160,6 +162,13 @@ struct trunk_softc {
#define tr_ifname tr_ac.ac_if.if_xname /* name */
#define tr_capabilities tr_ac.ac_if.if_capabilities /* capabilities */
+/* Private data used by the loadbalancing protocol */
+#define TRUNK_LB_MAXKEYS 8
+struct trunk_lb {
+ u_int32_t lb_key;
+ struct trunk_port *lb_ports[TRUNK_MAX_PORTS];
+};
+
void trunk_port_ifdetach(struct ifnet *);
int trunk_input(struct ifnet *, struct ether_header *, struct mbuf *);
#endif /* _KERNEL */