the failover protocol is supported due to limitations in the IPoIB architecture. Refer to the lagg(4) manual page for how to configure and use this new feature. A new network interface type, IFT_INFINIBANDLAG, has been added, similar to the existing IFT_IEEE8023ADLAG . ifconfig(8) has been updated to accept a new laggtype argument when creating lagg(4) network interfaces. This new argument is used to distinguish between ethernet and infiniband type of lagg(4) network interface. The laggtype argument is optional and defaults to ethernet. The lagg(4) command line syntax is backwards compatible. Differential Revision: https://reviews.freebsd.org/D26254 Reviewed by: melifaro@ MFC after: 1 week Sponsored by: Mellanox Technologies // NVIDIA Networking
368 lines
9.1 KiB
C
368 lines
9.1 KiB
C
/*-
|
|
*/
|
|
|
|
#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_lagg.h>
|
|
#include <net/ieee8023ad_lacp.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 struct iflaggparam params = {
|
|
.lagg_type = LAGG_TYPE_DEFAULT,
|
|
};
|
|
|
|
static char lacpbuf[120]; /* LACP peer '[(a,a,a),(p,p,p)]' */
|
|
|
|
static void
|
|
setlaggport(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqport rp;
|
|
|
|
bzero(&rp, sizeof(rp));
|
|
strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname));
|
|
strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname));
|
|
|
|
/*
|
|
* Do not exit with an error here. Doing so permits a
|
|
* failed NIC to take down an entire lagg.
|
|
*
|
|
* Don't error at all if the port is already in the lagg.
|
|
*/
|
|
if (ioctl(s, SIOCSLAGGPORT, &rp) && errno != EEXIST) {
|
|
warnx("%s %s: SIOCSLAGGPORT: %s",
|
|
name, val, strerror(errno));
|
|
exit_code = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
unsetlaggport(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqport rp;
|
|
|
|
bzero(&rp, sizeof(rp));
|
|
strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname));
|
|
strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname));
|
|
|
|
if (ioctl(s, SIOCSLAGGDELPORT, &rp))
|
|
err(1, "SIOCSLAGGDELPORT");
|
|
}
|
|
|
|
static void
|
|
setlaggproto(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_protos lpr[] = LAGG_PROTOS;
|
|
struct lagg_reqall ra;
|
|
int i;
|
|
|
|
bzero(&ra, sizeof(ra));
|
|
ra.ra_proto = LAGG_PROTO_MAX;
|
|
|
|
for (i = 0; i < nitems(lpr); i++) {
|
|
if (strcmp(val, lpr[i].lpr_name) == 0) {
|
|
ra.ra_proto = lpr[i].lpr_proto;
|
|
break;
|
|
}
|
|
}
|
|
if (ra.ra_proto == LAGG_PROTO_MAX)
|
|
errx(1, "Invalid aggregation protocol: %s", val);
|
|
|
|
strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
|
|
if (ioctl(s, SIOCSLAGG, &ra) != 0)
|
|
err(1, "SIOCSLAGG");
|
|
}
|
|
|
|
static void
|
|
setlaggflowidshift(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqopts ro;
|
|
|
|
bzero(&ro, sizeof(ro));
|
|
ro.ro_opts = LAGG_OPT_FLOWIDSHIFT;
|
|
strlcpy(ro.ro_ifname, name, sizeof(ro.ro_ifname));
|
|
ro.ro_flowid_shift = (int)strtol(val, NULL, 10);
|
|
if (ro.ro_flowid_shift & ~LAGG_OPT_FLOWIDSHIFT_MASK)
|
|
errx(1, "Invalid flowid_shift option: %s", val);
|
|
|
|
if (ioctl(s, SIOCSLAGGOPTS, &ro) != 0)
|
|
err(1, "SIOCSLAGGOPTS");
|
|
}
|
|
|
|
static void
|
|
setlaggrr_limit(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqopts ro;
|
|
|
|
bzero(&ro, sizeof(ro));
|
|
strlcpy(ro.ro_ifname, name, sizeof(ro.ro_ifname));
|
|
ro.ro_opts = LAGG_OPT_RR_LIMIT;
|
|
ro.ro_bkt = (uint32_t)strtoul(val, NULL, 10);
|
|
if (ro.ro_bkt == 0)
|
|
errx(1, "Invalid round-robin stride: %s", val);
|
|
|
|
if (ioctl(s, SIOCSLAGGOPTS, &ro) != 0)
|
|
err(1, "SIOCSLAGGOPTS");
|
|
}
|
|
|
|
static void
|
|
setlaggsetopt(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqopts ro;
|
|
|
|
bzero(&ro, sizeof(ro));
|
|
ro.ro_opts = d;
|
|
switch (ro.ro_opts) {
|
|
case LAGG_OPT_USE_FLOWID:
|
|
case -LAGG_OPT_USE_FLOWID:
|
|
case LAGG_OPT_USE_NUMA:
|
|
case -LAGG_OPT_USE_NUMA:
|
|
case LAGG_OPT_LACP_STRICT:
|
|
case -LAGG_OPT_LACP_STRICT:
|
|
case LAGG_OPT_LACP_TXTEST:
|
|
case -LAGG_OPT_LACP_TXTEST:
|
|
case LAGG_OPT_LACP_RXTEST:
|
|
case -LAGG_OPT_LACP_RXTEST:
|
|
case LAGG_OPT_LACP_FAST_TIMO:
|
|
case -LAGG_OPT_LACP_FAST_TIMO:
|
|
break;
|
|
default:
|
|
err(1, "Invalid lagg option");
|
|
}
|
|
strlcpy(ro.ro_ifname, name, sizeof(ro.ro_ifname));
|
|
|
|
if (ioctl(s, SIOCSLAGGOPTS, &ro) != 0)
|
|
err(1, "SIOCSLAGGOPTS");
|
|
}
|
|
|
|
static void
|
|
setlagghash(const char *val, int d, int s, const struct afswtch *afp)
|
|
{
|
|
struct lagg_reqflags rf;
|
|
char *str, *tmp, *tok;
|
|
|
|
|
|
rf.rf_flags = 0;
|
|
str = tmp = strdup(val);
|
|
while ((tok = strsep(&tmp, ",")) != NULL) {
|
|
if (strcmp(tok, "l2") == 0)
|
|
rf.rf_flags |= LAGG_F_HASHL2;
|
|
else if (strcmp(tok, "l3") == 0)
|
|
rf.rf_flags |= LAGG_F_HASHL3;
|
|
else if (strcmp(tok, "l4") == 0)
|
|
rf.rf_flags |= LAGG_F_HASHL4;
|
|
else
|
|
errx(1, "Invalid lagghash option: %s", tok);
|
|
}
|
|
free(str);
|
|
if (rf.rf_flags == 0)
|
|
errx(1, "No lagghash options supplied");
|
|
|
|
strlcpy(rf.rf_ifname, name, sizeof(rf.rf_ifname));
|
|
if (ioctl(s, SIOCSLAGGHASH, &rf))
|
|
err(1, "SIOCSLAGGHASH");
|
|
}
|
|
|
|
static char *
|
|
lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen)
|
|
{
|
|
snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X",
|
|
(int)mac[0], (int)mac[1], (int)mac[2], (int)mac[3],
|
|
(int)mac[4], (int)mac[5]);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
static char *
|
|
lacp_format_peer(struct lacp_opreq *req, const char *sep)
|
|
{
|
|
char macbuf1[20];
|
|
char macbuf2[20];
|
|
|
|
snprintf(lacpbuf, sizeof(lacpbuf),
|
|
"[(%04X,%s,%04X,%04X,%04X),%s(%04X,%s,%04X,%04X,%04X)]",
|
|
req->actor_prio,
|
|
lacp_format_mac(req->actor_mac, macbuf1, sizeof(macbuf1)),
|
|
req->actor_key, req->actor_portprio, req->actor_portno, sep,
|
|
req->partner_prio,
|
|
lacp_format_mac(req->partner_mac, macbuf2, sizeof(macbuf2)),
|
|
req->partner_key, req->partner_portprio, req->partner_portno);
|
|
|
|
return(lacpbuf);
|
|
}
|
|
|
|
static void
|
|
lagg_status(int s)
|
|
{
|
|
struct lagg_protos lpr[] = LAGG_PROTOS;
|
|
struct lagg_reqport rpbuf[LAGG_MAX_PORTS];
|
|
struct lagg_reqall ra;
|
|
struct lagg_reqopts ro;
|
|
struct lagg_reqflags rf;
|
|
struct lacp_opreq *lp;
|
|
const char *proto = "<unknown>";
|
|
int i;
|
|
|
|
bzero(&ra, sizeof(ra));
|
|
bzero(&ro, sizeof(ro));
|
|
|
|
strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname));
|
|
ra.ra_size = sizeof(rpbuf);
|
|
ra.ra_port = rpbuf;
|
|
|
|
strlcpy(ro.ro_ifname, name, sizeof(ro.ro_ifname));
|
|
ioctl(s, SIOCGLAGGOPTS, &ro);
|
|
|
|
strlcpy(rf.rf_ifname, name, sizeof(rf.rf_ifname));
|
|
if (ioctl(s, SIOCGLAGGFLAGS, &rf) != 0)
|
|
rf.rf_flags = 0;
|
|
|
|
if (ioctl(s, SIOCGLAGG, &ra) == 0) {
|
|
lp = (struct lacp_opreq *)&ra.ra_lacpreq;
|
|
|
|
for (i = 0; i < nitems(lpr); i++) {
|
|
if (ra.ra_proto == lpr[i].lpr_proto) {
|
|
proto = lpr[i].lpr_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("\tlaggproto %s", proto);
|
|
if (rf.rf_flags & LAGG_F_HASHMASK) {
|
|
const char *sep = "";
|
|
|
|
printf(" lagghash ");
|
|
if (rf.rf_flags & LAGG_F_HASHL2) {
|
|
printf("%sl2", sep);
|
|
sep = ",";
|
|
}
|
|
if (rf.rf_flags & LAGG_F_HASHL3) {
|
|
printf("%sl3", sep);
|
|
sep = ",";
|
|
}
|
|
if (rf.rf_flags & LAGG_F_HASHL4) {
|
|
printf("%sl4", sep);
|
|
sep = ",";
|
|
}
|
|
}
|
|
putchar('\n');
|
|
if (verbose) {
|
|
printf("\tlagg options:\n");
|
|
printb("\t\tflags", ro.ro_opts, LAGG_OPT_BITS);
|
|
putchar('\n');
|
|
printf("\t\tflowid_shift: %d\n", ro.ro_flowid_shift);
|
|
if (ra.ra_proto == LAGG_PROTO_ROUNDROBIN)
|
|
printf("\t\trr_limit: %d\n", ro.ro_bkt);
|
|
printf("\tlagg statistics:\n");
|
|
printf("\t\tactive ports: %d\n", ro.ro_active);
|
|
printf("\t\tflapping: %u\n", ro.ro_flapping);
|
|
if (ra.ra_proto == LAGG_PROTO_LACP) {
|
|
printf("\tlag id: %s\n",
|
|
lacp_format_peer(lp, "\n\t\t "));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ra.ra_ports; i++) {
|
|
lp = (struct lacp_opreq *)&rpbuf[i].rp_lacpreq;
|
|
printf("\tlaggport: %s ", rpbuf[i].rp_portname);
|
|
printb("flags", rpbuf[i].rp_flags, LAGG_PORT_BITS);
|
|
if (verbose && ra.ra_proto == LAGG_PROTO_LACP)
|
|
printb(" state", lp->actor_state,
|
|
LACP_STATE_BITS);
|
|
putchar('\n');
|
|
if (verbose && ra.ra_proto == LAGG_PROTO_LACP)
|
|
printf("\t\t%s\n",
|
|
lacp_format_peer(lp, "\n\t\t "));
|
|
}
|
|
|
|
if (0 /* XXX */) {
|
|
printf("\tsupported aggregation protocols:\n");
|
|
for (i = 0; i < nitems(lpr); i++)
|
|
printf("\t\tlaggproto %s\n", lpr[i].lpr_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
DECL_CMD_FUNC(setlaggtype, arg, d)
|
|
{
|
|
static const struct lagg_types lt[] = LAGG_TYPES;
|
|
int i;
|
|
|
|
for (i = 0; i < nitems(lt); i++) {
|
|
if (strcmp(arg, lt[i].lt_name) == 0) {
|
|
params.lagg_type = lt[i].lt_value;
|
|
return;
|
|
}
|
|
}
|
|
errx(1, "invalid lagg type: %s", arg);
|
|
}
|
|
|
|
static void
|
|
lagg_create(int s, struct ifreq *ifr)
|
|
{
|
|
ifr->ifr_data = (caddr_t) ¶ms;
|
|
if (ioctl(s, SIOCIFCREATE2, ifr) < 0)
|
|
err(1, "SIOCIFCREATE2");
|
|
}
|
|
|
|
static struct cmd lagg_cmds[] = {
|
|
DEF_CLONE_CMD_ARG("laggtype", setlaggtype),
|
|
DEF_CMD_ARG("laggport", setlaggport),
|
|
DEF_CMD_ARG("-laggport", unsetlaggport),
|
|
DEF_CMD_ARG("laggproto", setlaggproto),
|
|
DEF_CMD_ARG("lagghash", setlagghash),
|
|
DEF_CMD("use_flowid", LAGG_OPT_USE_FLOWID, setlaggsetopt),
|
|
DEF_CMD("-use_flowid", -LAGG_OPT_USE_FLOWID, setlaggsetopt),
|
|
DEF_CMD("use_numa", LAGG_OPT_USE_NUMA, setlaggsetopt),
|
|
DEF_CMD("-use_numa", -LAGG_OPT_USE_NUMA, setlaggsetopt),
|
|
DEF_CMD("lacp_strict", LAGG_OPT_LACP_STRICT, setlaggsetopt),
|
|
DEF_CMD("-lacp_strict", -LAGG_OPT_LACP_STRICT, setlaggsetopt),
|
|
DEF_CMD("lacp_txtest", LAGG_OPT_LACP_TXTEST, setlaggsetopt),
|
|
DEF_CMD("-lacp_txtest", -LAGG_OPT_LACP_TXTEST, setlaggsetopt),
|
|
DEF_CMD("lacp_rxtest", LAGG_OPT_LACP_RXTEST, setlaggsetopt),
|
|
DEF_CMD("-lacp_rxtest", -LAGG_OPT_LACP_RXTEST, setlaggsetopt),
|
|
DEF_CMD("lacp_fast_timeout", LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt),
|
|
DEF_CMD("-lacp_fast_timeout", -LAGG_OPT_LACP_FAST_TIMO, setlaggsetopt),
|
|
DEF_CMD_ARG("flowid_shift", setlaggflowidshift),
|
|
DEF_CMD_ARG("rr_limit", setlaggrr_limit),
|
|
};
|
|
static struct afswtch af_lagg = {
|
|
.af_name = "af_lagg",
|
|
.af_af = AF_UNSPEC,
|
|
.af_other_status = lagg_status,
|
|
};
|
|
|
|
static __constructor void
|
|
lagg_ctor(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nitems(lagg_cmds); i++)
|
|
cmd_register(&lagg_cmds[i]);
|
|
af_register(&af_lagg);
|
|
clone_setdefcallback("lagg", lagg_create);
|
|
}
|