-.\" $OpenBSD: ifconfig.8,v 1.315 2018/08/12 12:40:25 jmc Exp $
+.\" $OpenBSD: ifconfig.8,v 1.316 2018/08/12 23:50:31 ccardenas Exp $
.\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $
.\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $
.\"
.Xr trunk 4
interface:
.Bl -tag -width Ds
+.It Cm lacpmode Cm active Ns | Ns Cm passive
+Set the LACP trunk mode to either
+.Ar active
+or
+.Ar passive .
+.It Cm lacptimeout Cm fast Ns | Ns Cm slow
+Set the LACP timeout speed to either
+.Ar fast
+or
+.Ar slow .
.It Cm trunkport Ar child-iface
Add
.Ar child-iface
-/* $OpenBSD: ifconfig.c,v 1.374 2018/08/12 18:33:55 stsp Exp $ */
+/* $OpenBSD: ifconfig.c,v 1.375 2018/08/12 23:50:31 ccardenas Exp $ */
/* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */
/*
int shownet80211nodes;
int showclasses;
-struct ifencap;
+struct ifencap;
+
+const char *lacpmodeactive = "active";
+const char *lacpmodepassive = "passive";
+const char *lacptimeoutfast = "fast";
+const char *lacptimeoutslow = "slow";
void notealias(const char *, int);
void setifaddr(const char *, int);
void settrunkport(const char *, int);
void unsettrunkport(const char *, int);
void settrunkproto(const char *, int);
+void settrunklacpmode(const char *, int);
+void settrunklacptimeout(const char *, int);
void trunk_status(void);
void list_cloners(void);
{ "trunkport", NEXTARG, 0, settrunkport },
{ "-trunkport", NEXTARG, 0, unsettrunkport },
{ "trunkproto", NEXTARG, 0, settrunkproto },
+ { "lacpmode", NEXTARG, 0, settrunklacpmode },
+ { "lacptimeout", NEXTARG, 0, settrunklacptimeout },
{ "anycast", IN6_IFF_ANYCAST, 0, setia6flags },
{ "-anycast", -IN6_IFF_ANYCAST, 0, setia6flags },
{ "tentative", IN6_IFF_TENTATIVE, 0, setia6flags },
err(1, "SIOCSTRUNK");
}
+void
+settrunklacpmode(const char *val, int d)
+{
+ struct trunk_reqall ra;
+ struct trunk_opts tops;
+
+ bzero(&ra, sizeof(ra));
+ strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
+
+ if (ioctl(s, SIOCGTRUNK, &ra) != 0)
+ err(1, "SIOCGTRUNK");
+
+ if (ra.ra_proto != TRUNK_PROTO_LACP)
+ errx(1, "Invalid option for trunk: %s", name);
+
+ if (strcmp(val, lacpmodeactive) != 0 &&
+ strcmp(val, lacpmodepassive) != 0)
+ errx(1, "Invalid lacpmode option for trunk: %s", name);
+
+ bzero(&tops, sizeof(tops));
+ strlcpy(tops.to_ifname, name, sizeof(tops.to_ifname));
+ tops.to_proto = TRUNK_PROTO_LACP;
+ tops.to_opts |= TRUNK_OPT_LACP_MODE;
+
+ if (strcmp(val, lacpmodeactive) == 0)
+ tops.to_lacpopts.lacp_mode = 1;
+ else
+ tops.to_lacpopts.lacp_mode = 0;
+
+ if (ioctl(s, SIOCSTRUNKOPTS, &tops) != 0)
+ err(1, "SIOCSTRUNKOPTS");
+}
+
+void
+settrunklacptimeout(const char *val, int d)
+{
+ struct trunk_reqall ra;
+ struct trunk_opts tops;
+
+ bzero(&ra, sizeof(ra));
+ strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
+
+ if (ioctl(s, SIOCGTRUNK, &ra) != 0)
+ err(1, "SIOCGTRUNK");
+
+ if (ra.ra_proto != TRUNK_PROTO_LACP)
+ errx(1, "Invalid option for trunk: %s", name);
+
+ if (strcmp(val, lacptimeoutfast) != 0 &&
+ strcmp(val, lacptimeoutslow) != 0)
+ errx(1, "Invalid lacptimeout option for trunk: %s", name);
+
+ bzero(&tops, sizeof(tops));
+ strlcpy(tops.to_ifname, name, sizeof(tops.to_ifname));
+ tops.to_proto = TRUNK_PROTO_LACP;
+ tops.to_opts |= TRUNK_OPT_LACP_TIMEOUT;
+
+ if (strcmp(val, lacptimeoutfast) == 0)
+ tops.to_lacpopts.lacp_timeout = 1;
+ else
+ tops.to_lacpopts.lacp_timeout = 0;
+
+ if (ioctl(s, SIOCSTRUNKOPTS, &tops) != 0)
+ err(1, "SIOCSTRUNKOPTS");
+}
+
void
trunk_status(void)
{
-.\" $OpenBSD: trunk.4,v 1.29 2015/03/13 19:58:41 jmc Exp $
+.\" $OpenBSD: trunk.4,v 1.30 2018/08/12 23:50:31 ccardenas Exp $
.\"
.\" Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
.\"
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: March 13 2015 $
+.Dd $Mdocdate: August 12 2018 $
.Dt TRUNK 4
.Os
.Sh NAME
The first interface added is the master port;
any interfaces added after that are used as failover devices.
.It Ic lacp
-Uses the IEEE 802.3ad Link Aggregation Control Protocol (LACP)
+Uses the IEEE 802.3ad (renamed to 802.1AX in 2014)
+Link Aggregation Control Protocol (LACP)
and the Marker Protocol
to increase link speed and provide redundancy.
LACP trunk groups are composed of ports of the same speed,
set to full-duplex operation.
This protocol requires a switch which supports LACP.
+By default, the LACP implementation uses active-mode LACP,
+slow timeout, and 0x8000 (medium) priority as system and port
+priorities.
.It Ic loadbalance
Distributes outgoing traffic through all active ports
and accepts incoming traffic from any active port.
duplicate address detection (DAD)
cannot properly deal with duplicate packets.
.Pp
-There is no way to configure LACP administrative variables, including
-system and port priorities.
-The current implementation always performs active-mode LACP and uses
-0x8000 as system and port priorities.
-.Pp
The
.Nm
interface takes its MTU from the first
-/* $OpenBSD: if_trunk.c,v 1.136 2018/02/19 08:59:52 mpi Exp $ */
+/* $OpenBSD: if_trunk.c,v 1.137 2018/08/12 23:50:31 ccardenas Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
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 trunk_opts *tro = (struct trunk_opts *)data;
struct ifreq *ifr = (struct ifreq *)data;
+ struct lacp_softc *lsc;
struct trunk_port *tp;
+ struct lacp_port *lp;
struct ifnet *tpif;
int i, error = 0;
}
error = EPROTONOSUPPORT;
break;
+ case SIOCGTRUNKOPTS:
+ /* Only LACP trunks have options atm */
+ if (tro->to_proto != TRUNK_PROTO_LACP) {
+ error = EPROTONOSUPPORT;
+ break;
+ }
+ lsc = LACP_SOFTC(tr);
+ tro->to_lacpopts.lacp_mode = lsc->lsc_mode;
+ tro->to_lacpopts.lacp_timeout = lsc->lsc_timeout;
+ tro->to_lacpopts.lacp_prio = lsc->lsc_sys_prio;
+ tro->to_lacpopts.lacp_portprio = lsc->lsc_port_prio;
+ tro->to_lacpopts.lacp_ifqprio = lsc->lsc_ifq_prio;
+ break;
+ case SIOCSTRUNKOPTS:
+ if ((error = suser(curproc)) != 0) {
+ error = EPERM;
+ break;
+ }
+ /* Only LACP trunks have options atm */
+ if (tro->to_proto != TRUNK_PROTO_LACP) {
+ error = EPROTONOSUPPORT;
+ break;
+ }
+ lsc = LACP_SOFTC(tr);
+ switch(tro->to_opts) {
+ case TRUNK_OPT_LACP_MODE:
+ /*
+ * Ensure mode changes occur immediately
+ * on all ports
+ */
+ lsc->lsc_mode = tro->to_lacpopts.lacp_mode;
+ if (lsc->lsc_mode == 0) {
+ LIST_FOREACH(lp, &lsc->lsc_ports,
+ lp_next)
+ lp->lp_state &=
+ ~LACP_STATE_ACTIVITY;
+ } else {
+ LIST_FOREACH(lp, &lsc->lsc_ports,
+ lp_next)
+ lp->lp_state |=
+ LACP_STATE_ACTIVITY;
+ }
+ break;
+ case TRUNK_OPT_LACP_TIMEOUT:
+ /*
+ * Ensure timeout changes occur immediately
+ * on all ports
+ */
+ lsc->lsc_timeout =
+ tro->to_lacpopts.lacp_timeout;
+ if (lsc->lsc_timeout == 0) {
+ LIST_FOREACH(lp, &lsc->lsc_ports,
+ lp_next)
+ lp->lp_state &=
+ ~LACP_STATE_TIMEOUT;
+ } else {
+ LIST_FOREACH(lp, &lsc->lsc_ports,
+ lp_next)
+ lp->lp_state |=
+ LACP_STATE_TIMEOUT;
+ }
+ break;
+ case TRUNK_OPT_LACP_SYS_PRIO:
+ if (tro->to_lacpopts.lacp_prio == 0) {
+ error = EINVAL;
+ break;
+ }
+ lsc->lsc_sys_prio = tro->to_lacpopts.lacp_prio;
+ break;
+ case TRUNK_OPT_LACP_PORT_PRIO:
+ if (tro->to_lacpopts.lacp_portprio == 0) {
+ error = EINVAL;
+ break;
+ }
+ lsc->lsc_port_prio =
+ tro->to_lacpopts.lacp_portprio;
+ break;
+ case TRUNK_OPT_LACP_IFQ_PRIO:
+ if (tro->to_lacpopts.lacp_ifqprio >
+ IFQ_MAXPRIO) {
+ error = EINVAL;
+ break;
+ }
+ lsc->lsc_ifq_prio =
+ tro->to_lacpopts.lacp_ifqprio;
+ break;
+ }
+ break;
case SIOCGTRUNKPORT:
if (rp->rp_portname[0] == '\0' ||
(tpif = ifunit(rp->rp_portname)) == NULL) {
-/* $OpenBSD: if_trunk.h,v 1.25 2015/09/23 12:40:12 mikeb Exp $ */
+/* $OpenBSD: if_trunk.h,v 1.26 2018/08/12 23:50:31 ccardenas Exp $ */
/*
* Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
#define SIOCGTRUNK _IOWR('i', 143, struct trunk_reqall)
#define SIOCSTRUNK _IOW('i', 144, struct trunk_reqall)
+/* LACP administrative options */
+struct lacp_adminopts {
+ u_int8_t lacp_mode; /* active or passive */
+ u_int8_t lacp_timeout; /* fast or slow */
+ u_int16_t lacp_prio; /* system priority */
+ u_int16_t lacp_portprio; /* port priority */
+ u_int8_t lacp_ifqprio; /* ifq priority */
+};
+
+/* Trunk administrative options */
+struct trunk_opts {
+ char to_ifname[IFNAMSIZ]; /* name of the trunk */
+ u_int to_proto; /* trunk protocol */
+ int to_opts; /* option bitmap */
+#define TRUNK_OPT_NONE 0x00
+#define TRUNK_OPT_LACP_MODE 0x01 /* set active bit */
+#define TRUNK_OPT_LACP_TIMEOUT 0x02 /* set timeout bit */
+#define TRUNK_OPT_LACP_SYS_PRIO 0x04 /* set sys_prio bit */
+#define TRUNK_OPT_LACP_PORT_PRIO 0x08 /* set port_prio bit */
+#define TRUNK_OPT_LACP_IFQ_PRIO 0x10 /* set ifq_prio bit */
+
+ union {
+ struct lacp_adminopts rpsc_lacp;
+ } to_psc;
+#define to_lacpopts to_psc.rpsc_lacp
+};
+
+#define SIOCGTRUNKOPTS _IOWR('i', 145, struct trunk_opts)
+#define SIOCSTRUNKOPTS _IOW('i', 146, struct trunk_opts)
+
#ifdef _KERNEL
/*
* Internal kernel part
-/* $OpenBSD: trunklacp.c,v 1.29 2017/01/24 10:08:30 krw Exp $ */
+/* $OpenBSD: trunklacp.c,v 1.30 2018/08/12 23:50:31 ccardenas Exp $ */
/* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */
/* $FreeBSD:ieee8023ad_lacp.c,v 1.15 2008/03/16 19:25:30 thompsa Exp $ */
#include <net/bpf.h>
#endif
-/*
- * actor system priority and port priority.
- * XXX should be configurable.
- */
-#define LACP_SYSTEM_PRIO 0x8000
-#define LACP_PORT_PRIO 0x8000
-#define LACP_IFQ_PRIO 6
-
const u_int8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] =
{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
typedef void (*lacp_timer_func_t)(struct lacp_port *);
+void lacp_default_partner(struct lacp_softc *,
+ struct lacp_peerinfo *);
void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *);
void lacp_fill_markerinfo(struct lacp_port *,
struct lacp_markerinfo *);
#define LACP_DPRINTF(a) /* nothing */
#endif
-/*
- * partner administration variables.
- * XXX should be configurable.
- */
-
-const struct lacp_peerinfo lacp_partner_admin = {
- { 0xffff }, /* lip_systemid.lsi_prio */
- 0, /* lip_key */
- { 0xffff }, /* lip_portid.lpi_prio */
-#if 1
- /* optimistic lip_state */
- LACP_STATE_SYNC | LACP_STATE_AGGREGATION |
- LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING
-#else
- /* pessimistic lip_state */
- 0
-#endif
-};
-
const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = {
[LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer,
[LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer,
[LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer,
};
+void
+lacp_default_partner(struct lacp_softc *lsc, struct lacp_peerinfo *peer)
+{
+ peer->lip_systemid.lsi_prio = lsc->lsc_sys_prio;
+ peer->lip_key = 0;
+ peer->lip_portid.lpi_prio = lsc->lsc_port_prio;
+ peer->lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION |
+ LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING;
+}
+
int
lacp_input(struct trunk_port *tp, struct mbuf *m)
{
void
lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info)
{
+ struct lacp_softc *lsc = lp->lp_lsc;
struct trunk_port *tp = lp->lp_trunk;
struct trunk_softc *sc = tp->tp_trunk;
- info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO);
+ info->lip_systemid.lsi_prio = htons(lsc->lsc_sys_prio);
memcpy(&info->lip_systemid.lsi_mac,
sc->tr_ac.ac_enaddr, ETHER_ADDR_LEN);
- info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO);
+ info->lip_portid.lpi_prio = htons(lsc->lsc_port_prio);
info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index);
info->lip_state = lp->lp_state;
}
int
lacp_xmit_lacpdu(struct lacp_port *lp)
{
+ struct lacp_softc *lsc = lp->lp_lsc;
struct trunk_port *tp = lp->lp_trunk;
struct mbuf *m;
struct lacpdu *du;
if (m == NULL)
return (ENOMEM);
m->m_len = m->m_pkthdr.len = sizeof(*du);
- m->m_pkthdr.pf.prio = LACP_IFQ_PRIO;
+ m->m_pkthdr.pf.prio = lsc->lsc_ifq_prio;
du = mtod(m, struct lacpdu *);
memset(du, 0, sizeof(*du));
int
lacp_xmit_marker(struct lacp_port *lp)
{
+ struct lacp_softc *lsc = lp->lp_lsc;
struct trunk_port *tp = lp->lp_trunk;
struct mbuf *m;
struct markerdu *mdu;
if (m == NULL)
return (ENOMEM);
m->m_len = m->m_pkthdr.len = sizeof(*mdu);
- m->m_pkthdr.pf.prio = LACP_IFQ_PRIO;
+ m->m_pkthdr.pf.prio = lsc->lsc_ifq_prio;
mdu = mtod(m, struct markerdu *);
memset(mdu, 0, sizeof(*mdu));
struct ifreq ifr;
int error;
- int active = 1; /* XXX should be configurable */
- int fast = 0; /* XXX should be configurable */
-
bzero(&ifr, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_UNSPEC;
ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
lacp_fill_actorinfo(lp, &lp->lp_actor);
lacp_fill_markerinfo(lp, &lp->lp_marker);
lp->lp_state =
- (active ? LACP_STATE_ACTIVITY : 0) |
- (fast ? LACP_STATE_TIMEOUT : 0);
+ (lsc->lsc_mode ? LACP_STATE_ACTIVITY : 0) |
+ (lsc->lsc_timeout ? LACP_STATE_TIMEOUT : 0);
lp->lp_aggregator = NULL;
lacp_sm_rx_set_expired(lp);
TAILQ_INIT(&lsc->lsc_aggregators);
LIST_INIT(&lsc->lsc_ports);
+ /* set default admin values */
+ lsc->lsc_mode = LACP_DEFAULT_MODE;
+ lsc->lsc_timeout = LACP_DEFAULT_TIMEOUT;
+ lsc->lsc_sys_prio = LACP_DEFAULT_SYSTEM_PRIO;
+ lsc->lsc_port_prio = LACP_DEFAULT_PORT_PRIO;
+ lsc->lsc_ifq_prio = LACP_DEFAULT_IFQ_PRIO;
+
timeout_set(&lsc->lsc_transit_callout, lacp_transit_expire, lsc);
timeout_set(&lsc->lsc_callout, lacp_tick, lsc);
task_set(&lsc->lsc_input, lacp_input_process, lsc);
void
lacp_sm_rx_record_default(struct lacp_port *lp)
{
+ struct lacp_softc *lsc;
u_int8_t oldpstate;
+ lsc = lp->lp_lsc;
+
/* LACP_DPRINTF((lp, "%s\n", __func__)); */
oldpstate = lp->lp_partner.lip_state;
- lp->lp_partner = lacp_partner_admin;
+ lacp_default_partner(lsc, &(lp->lp_partner));
lp->lp_state |= LACP_STATE_DEFAULTED;
lacp_sm_ptx_update_timeout(lp, oldpstate);
}
void
lacp_sm_rx_update_default_selected(struct lacp_port *lp)
{
+ struct lacp_softc *lsc;
+ struct lacp_peerinfo peer;
+
+ lsc = lp->lp_lsc;
+ lacp_default_partner(lsc, &peer);
/* LACP_DPRINTF((lp, "%s\n", __func__)); */
- lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin);
+ lacp_sm_rx_update_selected_from_peerinfo(lp, &peer);
}
/* transmit machine */
-/* $OpenBSD: trunklacp.h,v 1.13 2018/05/12 02:02:34 ccardenas Exp $ */
+/* $OpenBSD: trunklacp.h,v 1.14 2018/08/12 23:50:31 ccardenas Exp $ */
/* $NetBSD: ieee8023ad_impl.h,v 1.2 2005/12/10 23:21:39 elad Exp $ */
/*
#define SLOWPROTOCOLS_SUBTYPE_LACP 1
#define SLOWPROTOCOLS_SUBTYPE_MARKER 2
+/*
+ * default administrative values
+ */
+#define LACP_DEFAULT_MODE 1 /* Active Mode */
+#define LACP_DEFAULT_TIMEOUT 0 /* Slow Timeout */
+#define LACP_DEFAULT_SYSTEM_PRIO 0x8000 /* Medium Priority */
+#define LACP_LOW_SYSTEM_PRIO 0xffff
+#define LACP_HIGH_SYSTEM_PRIO 0x0001
+#define LACP_DEFAULT_PORT_PRIO 0x8000 /* Medium Priority */
+#define LACP_LOW_PORT_PRIO 0xffff
+#define LACP_HIGH_PORT_PRIO 0x0001
+#define LACP_DEFAULT_IFQ_PRIO 6
+
struct slowprothdr {
u_int8_t sph_subtype;
u_int8_t sph_version;
int la_pending; /* number of ports in wait_while */
};
+struct lacp_admin_def {
+ u_int8_t lad_mode; /* active or passive */
+ u_int8_t lad_timeout; /* fast or slow */
+ u_int16_t lad_prio; /* system priority */
+ u_int16_t lad_portprio; /* port priority */
+ u_int8_t lad_ifqprio; /* ifq priority */
+};
+
struct lacp_softc {
struct trunk_softc *lsc_softc;
struct lacp_aggregator *lsc_active_aggregator;
volatile u_int lsc_activemap;
SIPHASH_KEY lsc_hashkey;
struct task lsc_input;
+ struct lacp_admin_def lsc_admin_defaults;
+#define lsc_mode lsc_admin_defaults.lad_mode
+#define lsc_timeout lsc_admin_defaults.lad_timeout
+#define lsc_sys_prio lsc_admin_defaults.lad_prio
+#define lsc_port_prio lsc_admin_defaults.lad_portprio
+#define lsc_ifq_prio lsc_admin_defaults.lad_ifqprio
};
#define LACP_TYPE_ACTORINFO 1