diff options
author | sjg <sjg@FreeBSD.org> | 2014-11-19 01:07:58 +0000 |
---|---|---|
committer | sjg <sjg@FreeBSD.org> | 2014-11-19 01:07:58 +0000 |
commit | b137080f19736ee33fede2e88bb54438604cf86b (patch) | |
tree | 377ac0ac449528621eb192cd245adadb5fd53668 /sbin/ifconfig | |
parent | ab21a29eb607d4dfe389b965fbdee27558e791aa (diff) | |
parent | 4a8d07956d121238d006d34ffe7d6269744e8b1a (diff) | |
download | FreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.zip FreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.tar.gz |
Merge from head@274682
Diffstat (limited to 'sbin/ifconfig')
-rw-r--r-- | sbin/ifconfig/Makefile | 5 | ||||
-rw-r--r-- | sbin/ifconfig/af_inet6.c | 4 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.8 | 129 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.c | 5 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.h | 3 | ||||
-rw-r--r-- | sbin/ifconfig/ifgre.c | 61 | ||||
-rw-r--r-- | sbin/ifconfig/iflagg.c | 80 | ||||
-rw-r--r-- | sbin/ifconfig/ifvxlan.c | 648 | ||||
-rw-r--r-- | sbin/ifconfig/sfp.c | 827 |
9 files changed, 1732 insertions, 30 deletions
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index aae6724..8aba6b4 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -30,9 +30,14 @@ SRCS+= ifmac.c # MAC support SRCS+= ifmedia.c # SIOC[GS]IFMEDIA support SRCS+= iffib.c # non-default FIB support SRCS+= ifvlan.c # SIOC[GS]ETVLAN support +SRCS+= ifvxlan.c # VXLAN support SRCS+= ifgre.c # GRE keys etc SRCS+= ifgif.c # GIF reversed header workaround +SRCS+= sfp.c # SFP/SFP+ information +DPADD+= ${LIBM} +LDADD+= -lm + SRCS+= ifieee80211.c regdomain.c # SIOC[GS]IEEE80211 support DPADD+= ${LIBBSDXML} ${LIBSBUF} LDADD+= -lbsdxml -lsbuf diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index d2e65e4..0f8688a 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -254,6 +254,8 @@ in6_status(int s __unused, const struct ifaddrs *ifa) printf("autoconf "); if ((flags6 & IN6_IFF_TEMPORARY) != 0) printf("temporary "); + if ((flags6 & IN6_IFF_PREFER_SOURCE) != 0) + printf("prefer_source "); if (((struct sockaddr_in6 *)(ifa->ifa_addr))->sin6_scope_id) printf("scopeid 0x%x ", @@ -465,6 +467,8 @@ static struct cmd inet6_cmds[] = { DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED, setip6flags), DEF_CMD("autoconf", IN6_IFF_AUTOCONF, setip6flags), DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF, setip6flags), + DEF_CMD("prefer_source",IN6_IFF_PREFER_SOURCE, setip6flags), + DEF_CMD("-prefer_source",-IN6_IFF_PREFER_SOURCE,setip6flags), DEF_CMD("accept_rtadv", ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("-accept_rtadv",-ND6_IFF_ACCEPT_RTADV, setnd6flags), DEF_CMD("no_radr", ND6_IFF_NO_RADR, setnd6flags), diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 5bf590d..064a62d 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -28,7 +28,7 @@ .\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 .\" $FreeBSD$ .\" -.Dd June 5, 2014 +.Dd October 20, 2014 .Dt IFCONFIG 8 .Os .Sh NAME @@ -679,7 +679,7 @@ Set a flag to enable Neighbor Unreachability Detection. Clear a flag .Cm nud . .It Cm no_prefer_iface -Set a flag to not honor rule 5 of source address selection in RFC 3484. +Set a flag to not honor rule 5 of source address selection in RFC 3484. In practice this means the address on the outgoing interface will not be preferred, effectively yielding the decision to the address selection policy table, configurable with @@ -689,6 +689,19 @@ Clear a flag .Cm no_prefer_iface . .El .Pp +The following parameters are specific for IPv6 addresses. +Note that the address family keyword +.Dq Li inet6 +is needed for them: +.Bl -tag -width indent +.It Cm prefer_source +Set a flag to prefer address as a candidate of the source address for +outgoing packets. +.It Cm -prefer_source +Clear a flag +.Cm prefer_source . +.El +.Pp The following parameters are specific to cloning IEEE 802.11 wireless interfaces with the .Cm create @@ -2318,9 +2331,16 @@ Remove the interface named by from the aggregation interface. .It Cm laggproto Ar proto Set the aggregation protocol. -The default is failover. -The available options are failover, fec, lacp, loadbalance, roundrobin and -none. +The default is +.Li failover . +The available options are +.Li failover , +.Li lacp , +.Li loadbalance , +.Li roundrobin , +.Li broadcast +and +.Li none . .It Cm lagghash Ar option Ns Oo , Ns Ar option Oc Set the packet layers to hash for aggregation protocols which load balance. The default is @@ -2335,6 +2355,34 @@ src/dst address for IPv4 or IPv6. .It Cm l4 src/dst port for TCP/UDP/SCTP. .El +.It Cm use_flowid +Enable local hash computation for RSS hash on the interface. +The +.Li loadbalance +and +.Li lacp +modes will use the RSS hash from the network card if available +to avoid computing one, this may give poor traffic distribution +if the hash is invalid or uses less of the protocol header information. +.Cm use_flowid +disables use of RSS hash from the network card. +The default value can be set via the +.Va net.link.lagg.default_use_flowid +.Xr sysctl 8 +variable. +.Li 0 +means +.Dq disabled +and +.Li 1 +means +.Dq enabled . +.It Cm -use_flowid +Disable local hash computation for RSS hash on the interface. +.It Cm flowid_shift Ar number +Set a shift parameter for RSS local hash computation. +Hash is calculated by using flowid bits in a packet header mbuf +which are shifted by the number of this parameter. .El .Pp The following parameters are specific to IP tunnel interfaces, @@ -2493,6 +2541,76 @@ argument is useless and hence deprecated. .El .Pp The following parameters are used to configure +.Xr vxlan 4 +interfaces. +.Bl -tag -width indent +.It Cm vni Ar identifier +This value is a 24-bit VXLAN Network Identifier (VNI) that identifies the +virtual network segment membership of the interface. +.It Cm local Ar address +The source address used in the encapsulating IPv4/IPv6 header. +The address should already be assigned to an existing interface. +When the interface is configured in unicast mode, the listening socket +is bound to this address. +.It Cm remote Ar address +The interface can be configured in a unicast, or point-to-point, mode +to create a tunnel between two hosts. +This is the IP address of the remote end of the tunnel. +.It Cm group Ar address +The interface can be configured in a multicast mode +to create a virtual network of hosts. +This is the IP multicast group address the interface will join. +.It Cm localport Ar port +The port number the interface will listen on. +The default port number is 4789. +.It Cm remoteport Ar port +The destination port number used in the encapsulating IPv4/IPv6 header. +The remote host should be listening on this port. +The default port number is 4789. +Note some other implementations, such as Linux, +do not default to the IANA assigned port, +but instead listen on port 8472. +.It Cm portrange Ar low high +The range of source ports used in the encapsulating IPv4/IPv6 header. +The port selected within the range is based on a hash of the inner frame. +A range is useful to provide entropy within the outer IP header +for more effective load balancing. +The default range is between the +.Xr sysctl 8 +variables +.Va net.inet.ip.portrange.first +and +.Va net.inet.ip.portrange.last +.It Cm timeout Ar timeout +The maximum time, in seconds, before an entry in the forwarding table +is pruned. +The default is 1200 seconds (20 minutes). +.It Cm maxaddr Ar max +The maximum number of entries in the forwarding table. +The default is 2000. +.It Cm vxlandev Ar dev +When the interface is configured in multicast mode, the +.Cm dev +interface is used to transmit IP multicast packets. +.It Cm ttl Ar ttl +The TTL used in the encapsulating IPv4/IPv6 header. +The default is 64. +.It Cm learn +The source IP address and inner source Ethernet MAC address of +received packets are used to dynamically populate the forwarding table. +When in multicast mode, an entry in the forwarding table allows the +interface to send the frame directly to the remote host instead of +broadcasting the frame to the multicast group. +This is the default. +.It Fl learn +The forwarding table is not populated by recevied packets. +.It Cm flush +Delete all dynamically-learned addresses from the forwarding table. +.It Cm flushall +Delete all addresses, including static addresses, from the forwarding table. +.El +.Pp +The following parameters are used to configure .Xr carp 4 protocol on an interface: .Bl -tag -width indent @@ -2697,6 +2815,7 @@ tried to alter an interface's configuration. .Xr pfsync 4 , .Xr polling 4 , .Xr vlan 4 , +.Xr vxlan 4 , .Xr devd.conf 5 , .\" .Xr eon 5 , .Xr devd 8 , diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index d2ddeca..61fd155 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -78,7 +78,7 @@ static const char rcsid[] = /* * Since "struct ifreq" is composed of various union members, callers - * should pay special attention to interprete the value. + * should pay special attention to interpret the value. * (.e.g. little/big endian difference in the structure.) */ struct ifreq ifr; @@ -1011,6 +1011,9 @@ status(const struct afswtch *afp, const struct sockaddr_dl *sdl, if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) printf("%s", ifs.ascii); + if (verbose > 0) + sfp_status(s, &ifr, verbose); + close(s); return; } diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index ea21db5..6df9acf 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -74,6 +74,7 @@ void callback_register(callback_func *, void *); #define DEF_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func }, 0, NULL } #define DEF_CLONE_CMD(name, param, func) { name, param, { .c_func = func }, 1, NULL } #define DEF_CLONE_CMD_ARG(name, func) { name, NEXTARG, { .c_func = func }, 1, NULL } +#define DEF_CLONE_CMD_ARG2(name, func) { name, NEXTARG2, { .c_func2 = func }, 1, NULL } struct ifaddrs; struct addrinfo; @@ -143,6 +144,8 @@ void ifmaybeload(const char *name); typedef void clone_callback_func(int, struct ifreq *); void clone_setdefcallback(const char *, clone_callback_func *); +void sfp_status(int s, struct ifreq *ifr, int verbose); + /* * XXX expose this so modules that neeed to know of any pending * operations on ifmedia can avoid cmd line ordering confusion. diff --git a/sbin/ifconfig/ifgre.c b/sbin/ifconfig/ifgre.c index 8bf7ede..3ac7454 100644 --- a/sbin/ifconfig/ifgre.c +++ b/sbin/ifconfig/ifgre.c @@ -23,52 +23,50 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); #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_gre.h> -#include <net/route.h> #include <ctype.h> +#include <limits.h> #include <stdio.h> -#include <string.h> #include <stdlib.h> -#include <unistd.h> +#include <string.h> #include <err.h> -#include <errno.h> #include "ifconfig.h" +#define GREBITS "\020\01ENABLE_CSUM\02ENABLE_SEQ" + static void gre_status(int s); static void gre_status(int s) { - int grekey = 0; + uint32_t opts = 0; - ifr.ifr_data = (caddr_t)&grekey; + ifr.ifr_data = (caddr_t)&opts; if (ioctl(s, GREGKEY, &ifr) == 0) - if (grekey != 0) - printf("\tgrekey: %d\n", grekey); + if (opts != 0) + printf("\tgrekey: 0x%x (%u)\n", opts, opts); + opts = 0; + if (ioctl(s, GREGOPTS, &ifr) != 0 || opts == 0) + return; + printb("\toptions", opts, GREBITS); + putchar('\n'); } static void setifgrekey(const char *val, int dummy __unused, int s, const struct afswtch *afp) { - uint32_t grekey = atol(val); + uint32_t grekey = strtol(val, NULL, 0); strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); ifr.ifr_data = (caddr_t)&grekey; @@ -76,8 +74,35 @@ setifgrekey(const char *val, int dummy __unused, int s, warn("ioctl (set grekey)"); } +static void +setifgreopts(const char *val, int d, int s, const struct afswtch *afp) +{ + uint32_t opts; + + ifr.ifr_data = (caddr_t)&opts; + if (ioctl(s, GREGOPTS, &ifr) == -1) { + warn("ioctl(GREGOPTS)"); + return; + } + + if (d < 0) + opts &= ~(-d); + else + opts |= d; + + if (ioctl(s, GRESOPTS, &ifr) == -1) { + warn("ioctl(GIFSOPTS)"); + return; + } +} + + static struct cmd gre_cmds[] = { DEF_CMD_ARG("grekey", setifgrekey), + DEF_CMD("enable_csum", GRE_ENABLE_CSUM, setifgreopts), + DEF_CMD("-enable_csum",-GRE_ENABLE_CSUM,setifgreopts), + DEF_CMD("enable_seq", GRE_ENABLE_SEQ, setifgreopts), + DEF_CMD("-enable_seq",-GRE_ENABLE_SEQ, setifgreopts), }; static struct afswtch af_gre = { .af_name = "af_gre", diff --git a/sbin/ifconfig/iflagg.c b/sbin/ifconfig/iflagg.c index 29b8574..51a6faa 100644 --- a/sbin/ifconfig/iflagg.c +++ b/sbin/ifconfig/iflagg.c @@ -17,6 +17,7 @@ static const char rcsid[] = #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> @@ -68,7 +69,7 @@ setlaggproto(const char *val, int d, int s, const struct afswtch *afp) bzero(&ra, sizeof(ra)); ra.ra_proto = LAGG_PROTO_MAX; - for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) { + for (i = 0; i < nitems(lpr); i++) { if (strcmp(val, lpr[i].lpr_name) == 0) { ra.ra_proto = lpr[i].lpr_proto; break; @@ -83,6 +84,48 @@ setlaggproto(const char *val, int d, int s, const struct afswtch *afp) } 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 +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_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: + 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; @@ -144,6 +187,7 @@ lagg_status(int s) struct lagg_protos lpr[] = LAGG_PROTOS; struct lagg_reqport rp, rpbuf[LAGG_MAX_PORTS]; struct lagg_reqall ra; + struct lagg_reqopts ro; struct lagg_reqflags rf; struct lacp_opreq *lp; const char *proto = "<unknown>"; @@ -151,6 +195,7 @@ lagg_status(int s) bzero(&rp, sizeof(rp)); bzero(&ra, sizeof(ra)); + bzero(&ro, sizeof(ro)); strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); strlcpy(rp.rp_portname, name, sizeof(rp.rp_portname)); @@ -162,6 +207,9 @@ lagg_status(int s) 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; @@ -169,7 +217,7 @@ lagg_status(int s) if (ioctl(s, SIOCGLAGG, &ra) == 0) { lp = (struct lacp_opreq *)&ra.ra_lacpreq; - for (i = 0; i < (sizeof(lpr) / sizeof(lpr[0])); i++) { + for (i = 0; i < nitems(lpr); i++) { if (ra.ra_proto == lpr[i].lpr_proto) { proto = lpr[i].lpr_name; break; @@ -197,16 +245,27 @@ lagg_status(int s) if (isport) printf(" laggdev %s", rp.rp_ifname); putchar('\n'); - if (verbose && ra.ra_proto == LAGG_PROTO_LACP) - printf("\tlag id: %s\n", - lacp_format_peer(lp, "\n\t\t ")); + 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); + 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) - printf(" state=%X", lp->actor_state); + printb(" state", lp->actor_state, + LACP_STATE_BITS); putchar('\n'); if (verbose && ra.ra_proto == LAGG_PROTO_LACP) printf("\t\t%s\n", @@ -226,6 +285,15 @@ static struct cmd lagg_cmds[] = { 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("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_ARG("flowid_shift", setlaggflowidshift), }; static struct afswtch af_lagg = { .af_name = "af_lagg", diff --git a/sbin/ifconfig/ifvxlan.c b/sbin/ifconfig/ifvxlan.c new file mode 100644 index 0000000..7234667 --- /dev/null +++ b/sbin/ifconfig/ifvxlan.c @@ -0,0 +1,648 @@ +/*- + * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <netdb.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_vxlan.h> +#include <net/route.h> +#include <netinet/in.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 ifvxlanparam params = { + .vxlp_vni = VXLAN_VNI_MAX, +}; + +static int +get_val(const char *cp, u_long *valp) +{ + char *endptr; + u_long val; + + errno = 0; + val = strtoul(cp, &endptr, 0); + if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) + return (-1); + + *valp = val; + return (0); +} + +static int +do_cmd(int sock, u_long op, void *arg, size_t argsize, int set) +{ + struct ifdrv ifd; + + bzero(&ifd, sizeof(ifd)); + + strlcpy(ifd.ifd_name, ifr.ifr_name, sizeof(ifd.ifd_name)); + ifd.ifd_cmd = op; + ifd.ifd_len = argsize; + ifd.ifd_data = arg; + + return (ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); +} + +static int +vxlan_exists(int sock) +{ + struct ifvxlancfg cfg; + + bzero(&cfg, sizeof(cfg)); + + return (do_cmd(sock, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) != -1); +} + +static void +vxlan_status(int s) +{ + struct ifvxlancfg cfg; + char src[NI_MAXHOST], dst[NI_MAXHOST]; + char srcport[NI_MAXSERV], dstport[NI_MAXSERV]; + struct sockaddr *lsa, *rsa; + int vni, mc, ipv6; + + bzero(&cfg, sizeof(cfg)); + + if (do_cmd(s, VXLAN_CMD_GET_CONFIG, &cfg, sizeof(cfg), 0) < 0) + return; + + vni = cfg.vxlc_vni; + lsa = &cfg.vxlc_local_sa.sa; + rsa = &cfg.vxlc_remote_sa.sa; + ipv6 = rsa->sa_family == AF_INET6; + + /* Just report nothing if the network identity isn't set yet. */ + if (vni >= VXLAN_VNI_MAX) + return; + + if (getnameinfo(lsa, lsa->sa_len, src, sizeof(src), + srcport, sizeof(srcport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) + src[0] = srcport[0] = '\0'; + if (getnameinfo(rsa, rsa->sa_len, dst, sizeof(dst), + dstport, sizeof(dstport), NI_NUMERICHOST | NI_NUMERICSERV) != 0) + dst[0] = dstport[0] = '\0'; + + if (!ipv6) { + struct sockaddr_in *sin = (struct sockaddr_in *)rsa; + mc = IN_MULTICAST(ntohl(sin->sin_addr.s_addr)); + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rsa; + mc = IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr); + } + + printf("\tvxlan vni %d", vni); + printf(" local %s%s%s:%s", ipv6 ? "[" : "", src, ipv6 ? "]" : "", + srcport); + printf(" %s %s%s%s:%s", mc ? "group" : "remote", ipv6 ? "[" : "", + dst, ipv6 ? "]" : "", dstport); + + if (verbose) { + printf("\n\t\tconfig: "); + printf("%slearning portrange %d-%d ttl %d", + cfg.vxlc_learn ? "" : "no", cfg.vxlc_port_min, + cfg.vxlc_port_max, cfg.vxlc_ttl); + printf("\n\t\tftable: "); + printf("cnt %d max %d timeout %d", + cfg.vxlc_ftable_cnt, cfg.vxlc_ftable_max, + cfg.vxlc_ftable_timeout); + } + + putchar('\n'); +} + +#define _LOCAL_ADDR46 \ + (VXLAN_PARAM_WITH_LOCAL_ADDR4 | VXLAN_PARAM_WITH_LOCAL_ADDR6) +#define _REMOTE_ADDR46 \ + (VXLAN_PARAM_WITH_REMOTE_ADDR4 | VXLAN_PARAM_WITH_REMOTE_ADDR6) + +static void +vxlan_check_params(void) +{ + + if ((params.vxlp_with & _LOCAL_ADDR46) == _LOCAL_ADDR46) + errx(1, "cannot specify both local IPv4 and IPv6 addresses"); + if ((params.vxlp_with & _REMOTE_ADDR46) == _REMOTE_ADDR46) + errx(1, "cannot specify both remote IPv4 and IPv6 addresses"); + if ((params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR4 && + params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR6) || + (params.vxlp_with & VXLAN_PARAM_WITH_LOCAL_ADDR6 && + params.vxlp_with & VXLAN_PARAM_WITH_REMOTE_ADDR4)) + errx(1, "cannot mix IPv4 and IPv6 addresses"); +} + +#undef _LOCAL_ADDR46 +#undef _REMOTE_ADDR46 + +static void +vxlan_cb(int s, void *arg) +{ + +} + +static void +vxlan_create(int s, struct ifreq *ifr) +{ + + vxlan_check_params(); + + ifr->ifr_data = (caddr_t) ¶ms; + if (ioctl(s, SIOCIFCREATE2, ifr) < 0) + err(1, "SIOCIFCREATE2"); +} + +static +DECL_CMD_FUNC(setvxlan_vni, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || val >= VXLAN_VNI_MAX) + errx(1, "invalid network identifier: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_VNI; + params.vxlp_vni = val; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_vni = val; + + if (do_cmd(s, VXLAN_CMD_SET_VNI, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_VNI"); +} + +static +DECL_CMD_FUNC(setvxlan_local, addr, d) +{ + struct ifvxlancmd cmd; + struct addrinfo *ai; + struct sockaddr *sa; + int error; + + bzero(&cmd, sizeof(cmd)); + + if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) + errx(1, "error in parsing local address string: %s", + gai_strerror(error)); + + sa = ai->ai_addr; + + switch (ai->ai_family) { +#ifdef INET + case AF_INET: { + struct in_addr addr = ((struct sockaddr_in *) sa)->sin_addr; + + if (IN_MULTICAST(ntohl(addr.s_addr))) + errx(1, "local address cannot be multicast"); + + cmd.vxlcmd_sa.in4.sin_family = AF_INET; + cmd.vxlcmd_sa.in4.sin_addr = addr; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + + if (IN6_IS_ADDR_MULTICAST(addr)) + errx(1, "local address cannot be multicast"); + + cmd.vxlcmd_sa.in6.sin6_family = AF_INET6; + cmd.vxlcmd_sa.in6.sin6_addr = *addr; + break; + } +#endif + default: + errx(1, "local address %s not supported", addr); + } + + freeaddrinfo(ai); + + if (!vxlan_exists(s)) { + if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { + params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR4; + params.vxlp_local_in4 = cmd.vxlcmd_sa.in4.sin_addr; + } else { + params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_ADDR6; + params.vxlp_local_in6 = cmd.vxlcmd_sa.in6.sin6_addr; + } + return; + } + + if (do_cmd(s, VXLAN_CMD_SET_LOCAL_ADDR, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_LOCAL_ADDR"); +} + +static +DECL_CMD_FUNC(setvxlan_remote, addr, d) +{ + struct ifvxlancmd cmd; + struct addrinfo *ai; + struct sockaddr *sa; + int error; + + bzero(&cmd, sizeof(cmd)); + + if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) + errx(1, "error in parsing remote address string: %s", + gai_strerror(error)); + + sa = ai->ai_addr; + + switch (ai->ai_family) { +#ifdef INET + case AF_INET: { + struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr; + + if (IN_MULTICAST(ntohl(addr.s_addr))) + errx(1, "remote address cannot be multicast"); + + cmd.vxlcmd_sa.in4.sin_family = AF_INET; + cmd.vxlcmd_sa.in4.sin_addr = addr; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + + if (IN6_IS_ADDR_MULTICAST(addr)) + errx(1, "remote address cannot be multicast"); + + cmd.vxlcmd_sa.in6.sin6_family = AF_INET6; + cmd.vxlcmd_sa.in6.sin6_addr = *addr; + break; + } +#endif + default: + errx(1, "remote address %s not supported", addr); + } + + freeaddrinfo(ai); + + if (!vxlan_exists(s)) { + if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { + params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4; + params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr; + } else { + params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6; + params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr; + } + return; + } + + if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_REMOTE_ADDR"); +} + +static +DECL_CMD_FUNC(setvxlan_group, addr, d) +{ + struct ifvxlancmd cmd; + struct addrinfo *ai; + struct sockaddr *sa; + int error; + + bzero(&cmd, sizeof(cmd)); + + if ((error = getaddrinfo(addr, NULL, NULL, &ai)) != 0) + errx(1, "error in parsing group address string: %s", + gai_strerror(error)); + + sa = ai->ai_addr; + + switch (ai->ai_family) { +#ifdef INET + case AF_INET: { + struct in_addr addr = ((struct sockaddr_in *)sa)->sin_addr; + + if (!IN_MULTICAST(ntohl(addr.s_addr))) + errx(1, "group address must be multicast"); + + cmd.vxlcmd_sa.in4.sin_family = AF_INET; + cmd.vxlcmd_sa.in4.sin_addr = addr; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct in6_addr *addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + + if (!IN6_IS_ADDR_MULTICAST(addr)) + errx(1, "group address must be multicast"); + + cmd.vxlcmd_sa.in6.sin6_family = AF_INET6; + cmd.vxlcmd_sa.in6.sin6_addr = *addr; + break; + } +#endif + default: + errx(1, "group address %s not supported", addr); + } + + freeaddrinfo(ai); + + if (!vxlan_exists(s)) { + if (cmd.vxlcmd_sa.sa.sa_family == AF_INET) { + params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR4; + params.vxlp_remote_in4 = cmd.vxlcmd_sa.in4.sin_addr; + } else { + params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_ADDR6; + params.vxlp_remote_in6 = cmd.vxlcmd_sa.in6.sin6_addr; + } + return; + } + + if (do_cmd(s, VXLAN_CMD_SET_REMOTE_ADDR, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_REMOTE_ADDR"); +} + +static +DECL_CMD_FUNC(setvxlan_local_port, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || val >= UINT16_MAX) + errx(1, "invalid local port: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_LOCAL_PORT; + params.vxlp_local_port = val; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_port = val; + + if (do_cmd(s, VXLAN_CMD_SET_LOCAL_PORT, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_LOCAL_PORT"); +} + +static +DECL_CMD_FUNC(setvxlan_remote_port, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || val >= UINT16_MAX) + errx(1, "invalid remote port: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_REMOTE_PORT; + params.vxlp_remote_port = val; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_port = val; + + if (do_cmd(s, VXLAN_CMD_SET_REMOTE_PORT, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_REMOTE_PORT"); +} + +static +DECL_CMD_FUNC2(setvxlan_port_range, arg1, arg2) +{ + struct ifvxlancmd cmd; + u_long min, max; + + if (get_val(arg1, &min) < 0 || min >= UINT16_MAX) + errx(1, "invalid port range minimum: %s", arg1); + if (get_val(arg2, &max) < 0 || max >= UINT16_MAX) + errx(1, "invalid port range maximum: %s", arg2); + if (max < min) + errx(1, "invalid port range"); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_PORT_RANGE; + params.vxlp_min_port = min; + params.vxlp_max_port = max; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_port_min = min; + cmd.vxlcmd_port_max = max; + + if (do_cmd(s, VXLAN_CMD_SET_PORT_RANGE, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_PORT_RANGE"); +} + +static +DECL_CMD_FUNC(setvxlan_timeout, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0) + errx(1, "invalid timeout value: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_TIMEOUT; + params.vxlp_ftable_timeout = val & 0xFFFFFFFF; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_ftable_timeout = val & 0xFFFFFFFF; + + if (do_cmd(s, VXLAN_CMD_SET_FTABLE_TIMEOUT, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_FTABLE_TIMEOUT"); +} + +static +DECL_CMD_FUNC(setvxlan_maxaddr, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || (val & ~0xFFFFFFFF) != 0) + errx(1, "invalid maxaddr value: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_FTABLE_MAX; + params.vxlp_ftable_max = val & 0xFFFFFFFF; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_ftable_max = val & 0xFFFFFFFF; + + if (do_cmd(s, VXLAN_CMD_SET_FTABLE_MAX, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_FTABLE_MAX"); +} + +static +DECL_CMD_FUNC(setvxlan_dev, arg, d) +{ + struct ifvxlancmd cmd; + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_MULTICAST_IF; + strlcpy(params.vxlp_mc_ifname, arg, + sizeof(params.vxlp_mc_ifname)); + return; + } + + bzero(&cmd, sizeof(cmd)); + strlcpy(cmd.vxlcmd_ifname, arg, sizeof(cmd.vxlcmd_ifname)); + + if (do_cmd(s, VXLAN_CMD_SET_MULTICAST_IF, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_MULTICAST_IF"); +} + +static +DECL_CMD_FUNC(setvxlan_ttl, arg, d) +{ + struct ifvxlancmd cmd; + u_long val; + + if (get_val(arg, &val) < 0 || val > 256) + errx(1, "invalid TTL value: %s", arg); + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_TTL; + params.vxlp_ttl = val; + return; + } + + bzero(&cmd, sizeof(cmd)); + cmd.vxlcmd_ttl = val; + + if (do_cmd(s, VXLAN_CMD_SET_TTL, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_TTL"); +} + +static +DECL_CMD_FUNC(setvxlan_learn, arg, d) +{ + struct ifvxlancmd cmd; + + if (!vxlan_exists(s)) { + params.vxlp_with |= VXLAN_PARAM_WITH_LEARN; + params.vxlp_learn = d; + return; + } + + bzero(&cmd, sizeof(cmd)); + if (d != 0) + cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_LEARN; + + if (do_cmd(s, VXLAN_CMD_SET_LEARN, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_SET_LEARN"); +} + +static void +setvxlan_flush(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifvxlancmd cmd; + + bzero(&cmd, sizeof(cmd)); + if (d != 0) + cmd.vxlcmd_flags |= VXLAN_CMD_FLAG_FLUSH_ALL; + + if (do_cmd(s, VXLAN_CMD_FLUSH, &cmd, sizeof(cmd), 1) < 0) + err(1, "VXLAN_CMD_FLUSH"); +} + +static struct cmd vxlan_cmds[] = { + + DEF_CLONE_CMD_ARG("vni", setvxlan_vni), + DEF_CLONE_CMD_ARG("local", setvxlan_local), + DEF_CLONE_CMD_ARG("remote", setvxlan_remote), + DEF_CLONE_CMD_ARG("group", setvxlan_group), + DEF_CLONE_CMD_ARG("localport", setvxlan_local_port), + DEF_CLONE_CMD_ARG("remoteport", setvxlan_remote_port), + DEF_CLONE_CMD_ARG2("portrange", setvxlan_port_range), + DEF_CLONE_CMD_ARG("timeout", setvxlan_timeout), + DEF_CLONE_CMD_ARG("maxaddr", setvxlan_maxaddr), + DEF_CLONE_CMD_ARG("vxlandev", setvxlan_dev), + DEF_CLONE_CMD_ARG("ttl", setvxlan_ttl), + DEF_CLONE_CMD("learn", 1, setvxlan_learn), + DEF_CLONE_CMD("-learn", 0, setvxlan_learn), + + DEF_CMD_ARG("vni", setvxlan_vni), + DEF_CMD_ARG("local", setvxlan_local), + DEF_CMD_ARG("remote", setvxlan_remote), + DEF_CMD_ARG("group", setvxlan_group), + DEF_CMD_ARG("localport", setvxlan_local_port), + DEF_CMD_ARG("remoteport", setvxlan_remote_port), + DEF_CMD_ARG2("portrange", setvxlan_port_range), + DEF_CMD_ARG("timeout", setvxlan_timeout), + DEF_CMD_ARG("maxaddr", setvxlan_maxaddr), + DEF_CMD_ARG("vxlandev", setvxlan_dev), + DEF_CMD_ARG("ttl", setvxlan_ttl), + DEF_CMD("learn", 1, setvxlan_learn), + DEF_CMD("-learn", 0, setvxlan_learn), + + DEF_CMD("flush", 0, setvxlan_flush), + DEF_CMD("flushall", 1, setvxlan_flush), +}; + +static struct afswtch af_vxlan = { + .af_name = "af_vxlan", + .af_af = AF_UNSPEC, + .af_other_status = vxlan_status, +}; + +static __constructor void +vxlan_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + size_t i; + + for (i = 0; i < N(vxlan_cmds); i++) + cmd_register(&vxlan_cmds[i]); + af_register(&af_vxlan); + callback_register(vxlan_cb, NULL); + clone_setdefcallback("vxlan", vxlan_create); +#undef N +} diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c new file mode 100644 index 0000000..d4da8c3 --- /dev/null +++ b/sbin/ifconfig/sfp.c @@ -0,0 +1,827 @@ +/*- + * Copyright (c) 2014 Alexander V. Chernikov. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/sff8436.h> +#include <net/sff8472.h> + +#include <math.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ifconfig.h" + +struct i2c_info; +typedef int (read_i2c)(struct i2c_info *ii, uint8_t addr, uint8_t off, + uint8_t len, caddr_t buf); + +struct i2c_info { + int s; + int error; + int bshift; + int qsfp; + int do_diag; + struct ifreq *ifr; + read_i2c *f; + char *textbuf; + size_t bufsize; + int cfd; + int port_id; + int chip_id; +}; + +static void dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off, + uint8_t len); + +struct _nv { + int v; + const char *n; +}; + +const char *find_value(struct _nv *x, int value); +const char *find_zero_bit(struct _nv *x, int value, int sz); + +/* SFF-8472 Rev. 11.4 table 3.4: Connector values */ +static struct _nv conn[] = { + { 0x00, "Unknown" }, + { 0x01, "SC" }, + { 0x02, "Fibre Channel Style 1 copper" }, + { 0x03, "Fibre Channel Style 2 copper" }, + { 0x04, "BNC/TNC" }, + { 0x05, "Fibre Channel coaxial" }, + { 0x06, "FiberJack" }, + { 0x07, "LC" }, + { 0x08, "MT-RJ" }, + { 0x09, "MU" }, + { 0x0A, "SG" }, + { 0x0B, "Optical pigtail" }, + { 0x0C, "MPO Parallel Optic" }, + { 0x20, "HSSDC II" }, + { 0x21, "Copper pigtail" }, + { 0x22, "RJ45" }, + { 0x23, "No separate connector" }, /* SFF-8436 */ + { 0, NULL } +}; + +/* SFF-8472 Rev. 11.4 table 3.5: Transceiver codes */ +/* 10G Ethernet/IB compliance codes, byte 3 */ +static struct _nv eth_10g[] = { + { 0x80, "10G Base-ER" }, + { 0x40, "10G Base-LRM" }, + { 0x20, "10G Base-LR" }, + { 0x10, "10G Base-SR" }, + { 0x08, "1X SX" }, + { 0x04, "1X LX" }, + { 0x02, "1X Copper Active" }, + { 0x01, "1X Copper Passive" }, + { 0, NULL } +}; + +/* Ethernet compliance codes, byte 6 */ +static struct _nv eth_compat[] = { + { 0x80, "BASE-PX" }, + { 0x40, "BASE-BX10" }, + { 0x20, "100BASE-FX" }, + { 0x10, "100BASE-LX/LX10" }, + { 0x08, "1000BASE-T" }, + { 0x04, "1000BASE-CX" }, + { 0x02, "1000BASE-LX" }, + { 0x01, "1000BASE-SX" }, + { 0, NULL } +}; + +/* FC link length, byte 7 */ +static struct _nv fc_len[] = { + { 0x80, "very long distance" }, + { 0x40, "short distance" }, + { 0x20, "intermediate distance" }, + { 0x10, "long distance" }, + { 0x08, "medium distance" }, + { 0, NULL } +}; + +/* Channel/Cable technology, byte 7-8 */ +static struct _nv cab_tech[] = { + { 0x0400, "Shortwave laser (SA)" }, + { 0x0200, "Longwave laser (LC)" }, + { 0x0100, "Electrical inter-enclosure (EL)" }, + { 0x80, "Electrical intra-enclosure (EL)" }, + { 0x40, "Shortwave laser (SN)" }, + { 0x20, "Shortwave laser (SL)" }, + { 0x10, "Longwave laser (LL)" }, + { 0x08, "Active Cable" }, + { 0x04, "Passive Cable" }, + { 0, NULL } +}; + +/* FC Transmission media, byte 9 */ +static struct _nv fc_media[] = { + { 0x80, "Twin Axial Pair" }, + { 0x40, "Twisted Pair" }, + { 0x20, "Miniature Coax" }, + { 0x10, "Viao Coax" }, + { 0x08, "Miltimode, 62.5um" }, + { 0x04, "Multimode, 50um" }, + { 0x02, "" }, + { 0x01, "Single Mode" }, + { 0, NULL } +}; + +/* FC Speed, byte 10 */ +static struct _nv fc_speed[] = { + { 0x80, "1200 MBytes/sec" }, + { 0x40, "800 MBytes/sec" }, + { 0x20, "1600 MBytes/sec" }, + { 0x10, "400 MBytes/sec" }, + { 0x08, "3200 MBytes/sec" }, + { 0x04, "200 MBytes/sec" }, + { 0x01, "100 MBytes/sec" }, + { 0, NULL } +}; + +/* SFF-8436 Rev. 4.8 table 33: Specification compliance */ + +/* 10/40G Ethernet compliance codes, byte 128 + 3 */ +static struct _nv eth_1040g[] = { + { 0x80, "Reserved" }, + { 0x40, "10GBASE-LRM" }, + { 0x20, "10GBASE-LR" }, + { 0x10, "10GBASE-SR" }, + { 0x08, "40GBASE-CR4" }, + { 0x04, "40GBASE-SR4" }, + { 0x02, "40GBASE-LR4" }, + { 0x01, "40G Active Cable" }, + { 0, NULL } +}; + +const char * +find_value(struct _nv *x, int value) +{ + for (; x->n != NULL; x++) + if (x->v == value) + return (x->n); + return (NULL); +} + +const char * +find_zero_bit(struct _nv *x, int value, int sz) +{ + int v, m; + const char *s; + + v = 1; + for (v = 1, m = 1 << (8 * sz); v < m; v *= 2) { + if ((value & v) == 0) + continue; + if ((s = find_value(x, value & v)) != NULL) { + value &= ~v; + return (s); + } + } + + return (NULL); +} + +static void +convert_sff_identifier(char *buf, size_t size, uint8_t value) +{ + const char *x; + + x = NULL; + if (value <= SFF_8024_ID_LAST) + x = sff_8024_id[value]; + else { + if (value > 0x80) + x = "Vendor specific"; + else + x = "Reserved"; + } + + snprintf(buf, size, "%s", x); +} + +static void +convert_sff_connector(char *buf, size_t size, uint8_t value) +{ + const char *x; + + if ((x = find_value(conn, value)) == NULL) { + if (value >= 0x0D && value <= 0x1F) + x = "Unallocated"; + else if (value >= 0x24 && value <= 0x7F) + x = "Unallocated"; + else + x = "Vendor specific"; + } + + snprintf(buf, size, "%s", x); +} + +static void +get_sfp_identifier(struct i2c_info *ii, char *buf, size_t size) +{ + uint8_t data; + + ii->f(ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&data); + convert_sff_identifier(buf, size, data); +} + +static void +get_sfp_connector(struct i2c_info *ii, char *buf, size_t size) +{ + uint8_t data; + + ii->f(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, (caddr_t)&data); + convert_sff_connector(buf, size, data); +} + +static void +get_qsfp_identifier(struct i2c_info *ii, char *buf, size_t size) +{ + uint8_t data; + + ii->f(ii, SFF_8436_BASE, SFF_8436_ID, 1, (caddr_t)&data); + convert_sff_identifier(buf, size, data); +} + +static void +get_qsfp_connector(struct i2c_info *ii, char *buf, size_t size) +{ + uint8_t data; + + ii->f(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, (caddr_t)&data); + convert_sff_connector(buf, size, data); +} + +static void +printf_sfp_transceiver_descr(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[12]; + const char *tech_class, *tech_len, *tech_tech, *tech_media, *tech_speed; + + tech_class = NULL; + tech_len = NULL; + tech_tech = NULL; + tech_media = NULL; + tech_speed = NULL; + + /* Read bytes 3-10 at once */ + ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, &xbuf[3]); + + /* Check 10G ethernet first */ + tech_class = find_zero_bit(eth_10g, xbuf[3], 1); + if (tech_class == NULL) { + /* No match. Try 1G */ + tech_class = find_zero_bit(eth_compat, xbuf[6], 1); + } + + tech_len = find_zero_bit(fc_len, xbuf[7], 1); + tech_tech = find_zero_bit(cab_tech, xbuf[7] << 8 | xbuf[8], 2); + tech_media = find_zero_bit(fc_media, xbuf[9], 1); + tech_speed = find_zero_bit(fc_speed, xbuf[10], 1); + + printf("Class: %s\n", tech_class); + printf("Length: %s\n", tech_len); + printf("Tech: %s\n", tech_tech); + printf("Media: %s\n", tech_media); + printf("Speed: %s\n", tech_speed); +} + +static void +get_sfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size) +{ + const char *tech_class; + uint8_t code; + + unsigned char qbuf[8]; + ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, (caddr_t)qbuf); + + /* Check 10G Ethernet/IB first */ + ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, (caddr_t)&code); + tech_class = find_zero_bit(eth_10g, code, 1); + if (tech_class == NULL) { + /* No match. Try Ethernet 1G */ + ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3, + 1, (caddr_t)&code); + tech_class = find_zero_bit(eth_compat, code, 1); + } + + if (tech_class == NULL) + tech_class = "Unknown"; + + snprintf(buf, size, "%s", tech_class); +} + +static void +get_qsfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size) +{ + const char *tech_class; + uint8_t code; + + /* Check 10/40G Ethernet class only */ + ii->f(ii, SFF_8436_BASE, SFF_8436_CODE_E1040G, 1, (caddr_t)&code); + tech_class = find_zero_bit(eth_1040g, code, 1); + if (tech_class == NULL) + tech_class = "Unknown"; + + snprintf(buf, size, "%s", tech_class); +} + +/* + * Print SFF-8472/SFF-8436 string to supplied buffer. + * All (vendor-specific) strings are padded right with '0x20'. + */ +static void +convert_sff_name(char *buf, size_t size, char *xbuf) +{ + char *p; + + for (p = &xbuf[16]; *(p - 1) == 0x20; p--) + ; + *p = '\0'; + snprintf(buf, size, "%s", xbuf); +} + +static void +convert_sff_date(char *buf, size_t size, char *xbuf) +{ + + snprintf(buf, size, "20%c%c-%c%c-%c%c", xbuf[0], xbuf[1], + xbuf[2], xbuf[3], xbuf[4], xbuf[5]); +} + +static void +get_sfp_vendor_name(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_sfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_BASE, SFF_8472_PN_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_sfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_BASE, SFF_8472_SN_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_sfp_vendor_date(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[6]; + + memset(xbuf, 0, sizeof(xbuf)); + /* Date code, see Table 3.8 for description */ + ii->f(ii, SFF_8472_BASE, SFF_8472_DATE_START, 6, xbuf); + convert_sff_date(buf, size, xbuf); +} + +static void +get_qsfp_vendor_name(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_qsfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_PN_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_qsfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[17]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_SN_START, 16, xbuf); + convert_sff_name(buf, size, xbuf); +} + +static void +get_qsfp_vendor_date(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[6]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_DATE_START, 6, xbuf); + convert_sff_date(buf, size, xbuf); +} + +static void +print_sfp_vendor(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[80]; + + memset(xbuf, 0, sizeof(xbuf)); + if (ii->qsfp != 0) { + get_qsfp_vendor_name(ii, xbuf, 20); + get_qsfp_vendor_pn(ii, &xbuf[20], 20); + get_qsfp_vendor_sn(ii, &xbuf[40], 20); + get_qsfp_vendor_date(ii, &xbuf[60], 20); + } else { + get_sfp_vendor_name(ii, xbuf, 20); + get_sfp_vendor_pn(ii, &xbuf[20], 20); + get_sfp_vendor_sn(ii, &xbuf[40], 20); + get_sfp_vendor_date(ii, &xbuf[60], 20); + } + + snprintf(buf, size, "vendor: %s PN: %s SN: %s DATE: %s", + xbuf, &xbuf[20], &xbuf[40], &xbuf[60]); +} + +/* + * Converts internal templerature (SFF-8472, SFF-8436) + * 16-bit unsigned value to human-readable representation: + * + * Internally measured Module temperature are represented + * as a 16-bit signed twos complement value in increments of + * 1/256 degrees Celsius, yielding a total range of –128C to +128C + * that is considered valid between –40 and +125C. + * + */ +static void +convert_sff_temp(char *buf, size_t size, char *xbuf) +{ + double d; + + d = (double)(int8_t)xbuf[0]; + d += (double)(uint8_t)xbuf[1] / 256; + + snprintf(buf, size, "%.2f C", d); +} + +/* + * Retrieves supplied voltage (SFF-8472, SFF-8436). + * 16-bit usigned value, treated as range 0..+6.55 Volts + */ +static void +convert_sff_voltage(char *buf, size_t size, char *xbuf) +{ + double d; + + d = (double)(((uint8_t)xbuf[0] << 8) | (uint8_t)xbuf[1]); + snprintf(buf, size, "%.2f Volts", d / 10000); +} + +/* + * Converts value in @xbuf to both milliwats and dBm + * human representation. + */ +static void +convert_sff_power(struct i2c_info *ii, char *buf, size_t size, char *xbuf) +{ + uint16_t mW; + double dbm; + + mW = ((uint8_t)xbuf[0] << 8) + (uint8_t)xbuf[1]; + + /* Convert mw to dbm */ + dbm = 10.0 * log10(1.0 * mW / 10000); + + /* + * Assume internally-calibrated data. + * This is always true for SFF-8346, and explicitly + * checked for SFF-8472. + */ + + /* Table 3.9, bit 5 is set, internally calibrated */ + snprintf(buf, size, "%d.%02d mW (%.2f dBm)", + mW / 10000, (mW % 10000) / 100, dbm); +} + +static void +get_sfp_temp(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_DIAG, SFF_8472_TEMP, 2, xbuf); + convert_sff_temp(buf, size, xbuf); +} + +static void +get_sfp_voltage(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_DIAG, SFF_8472_VCC, 2, xbuf); + convert_sff_voltage(buf, size, xbuf); +} + +static void +get_qsfp_temp(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_TEMP, 2, xbuf); + convert_sff_temp(buf, size, xbuf); +} + +static void +get_qsfp_voltage(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_VCC, 2, xbuf); + convert_sff_voltage(buf, size, xbuf); +} + +static void +get_sfp_rx_power(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_DIAG, SFF_8472_RX_POWER, 2, xbuf); + convert_sff_power(ii, buf, size, xbuf); +} + +static void +get_sfp_tx_power(struct i2c_info *ii, char *buf, size_t size) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8472_DIAG, SFF_8472_TX_POWER, 2, xbuf); + convert_sff_power(ii, buf, size, xbuf); +} + +static void +get_qsfp_rx_power(struct i2c_info *ii, char *buf, size_t size, int chan) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_RX_CH1_MSB + (chan - 1) * 2, 2, xbuf); + convert_sff_power(ii, buf, size, xbuf); +} + +static void +get_qsfp_tx_power(struct i2c_info *ii, char *buf, size_t size, int chan) +{ + char xbuf[2]; + + memset(xbuf, 0, sizeof(xbuf)); + ii->f(ii, SFF_8436_BASE, SFF_8436_TX_CH1_MSB + (chan -1) * 2, 2, xbuf); + convert_sff_power(ii, buf, size, xbuf); +} + +/* Generic handler */ +static int +read_i2c_generic(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len, + caddr_t buf) +{ + struct ifi2creq req; + int i, l; + + if (ii->error != 0) + return (ii->error); + + ii->ifr->ifr_data = (caddr_t)&req; + + i = 0; + l = 0; + memset(&req, 0, sizeof(req)); + req.dev_addr = addr; + req.offset = off; + req.len = len; + + while (len > 0) { + l = (len > sizeof(req.data)) ? sizeof(req.data) : len; + req.len = l; + if (ioctl(ii->s, SIOCGI2C, ii->ifr) != 0) { + ii->error = errno; + return (errno); + } + + memcpy(&buf[i], req.data, l); + len -= l; + i += l; + req.offset += l; + } + + return (0); +} + +static void +dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len) +{ + unsigned char buf[16]; + int i, read; + + while (len > 0) { + memset(buf, 0, sizeof(buf)); + read = (len > sizeof(buf)) ? sizeof(buf) : len; + ii->f(ii, addr, off, read, buf); + if (ii->error != 0) { + fprintf(stderr, "Error reading i2c info\n"); + return; + } + + printf("\t"); + for (i = 0; i < read; i++) + printf("%02X ", buf[i]); + printf("\n"); + len -= read; + off += read; + } +} + +static void +print_qsfp_status(struct i2c_info *ii, int verbose) +{ + char buf[80], buf2[40], buf3[40]; + uint8_t diag_type; + int i; + + /* Read diagnostic monitoring type */ + ii->f(ii, SFF_8436_BASE, SFF_8436_DIAG_TYPE, 1, (caddr_t)&diag_type); + if (ii->error != 0) + return; + + /* + * Read monitoring data it is supplied. + * XXX: It is not exactly clear from standard + * how one can specify lack of measurements (passive cables case). + */ + if (diag_type != 0) + ii->do_diag = 1; + ii->qsfp = 1; + + /* Transceiver type */ + get_qsfp_identifier(ii, buf, sizeof(buf)); + get_qsfp_transceiver_class(ii, buf2, sizeof(buf2)); + get_qsfp_connector(ii, buf3, sizeof(buf3)); + if (ii->error == 0) + printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3); + print_sfp_vendor(ii, buf, sizeof(buf)); + if (ii->error == 0) + printf("\t%s\n", buf); + + /* Request current measurements if they are provided: */ + if (ii->do_diag != 0) { + get_qsfp_temp(ii, buf, sizeof(buf)); + get_qsfp_voltage(ii, buf2, sizeof(buf2)); + printf("\tmodule temperature: %s voltage: %s\n", buf, buf2); + for (i = 1; i <= 4; i++) { + get_qsfp_rx_power(ii, buf, sizeof(buf), i); + get_qsfp_tx_power(ii, buf2, sizeof(buf2), i); + printf("\tlane %d: RX: %s TX: %s\n", i, buf, buf2); + } + } + + if (verbose > 2) { + printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n"); + dump_i2c_data(ii, SFF_8436_BASE, 128, 128); + printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n"); + dump_i2c_data(ii, SFF_8436_BASE, 0, 82); + } +} + +static void +print_sfp_status(struct i2c_info *ii, int verbose) +{ + char buf[80], buf2[40], buf3[40]; + uint8_t diag_type, flags; + + /* Read diagnostic monitoring type */ + ii->f(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type); + if (ii->error != 0) + return; + + /* + * Read monitoring data IFF it is supplied AND is + * internally calibrated + */ + flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL; + if ((diag_type & flags) == flags) + ii->do_diag = 1; + + /* Transceiver type */ + get_sfp_identifier(ii, buf, sizeof(buf)); + get_sfp_transceiver_class(ii, buf2, sizeof(buf2)); + get_sfp_connector(ii, buf3, sizeof(buf3)); + if (ii->error == 0) + printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3); + print_sfp_vendor(ii, buf, sizeof(buf)); + if (ii->error == 0) + printf("\t%s\n", buf); + + if (verbose > 5) + printf_sfp_transceiver_descr(ii, buf, sizeof(buf)); + /* + * Request current measurements iff they are provided: + */ + if (ii->do_diag != 0) { + get_sfp_temp(ii, buf, sizeof(buf)); + get_sfp_voltage(ii, buf2, sizeof(buf2)); + printf("\tmodule temperature: %s Voltage: %s\n", buf, buf2); + get_sfp_rx_power(ii, buf, sizeof(buf)); + get_sfp_tx_power(ii, buf2, sizeof(buf2)); + printf("\tRX: %s TX: %s\n", buf, buf2); + } + + if (verbose > 2) { + printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n"); + dump_i2c_data(ii, SFF_8472_BASE, 0, 128); + } +} + +void +sfp_status(int s, struct ifreq *ifr, int verbose) +{ + struct i2c_info ii; + uint8_t id_byte; + + memset(&ii, 0, sizeof(ii)); + /* Prepare necessary into to pass to NIC handler */ + ii.s = s; + ii.ifr = ifr; + ii.f = read_i2c_generic; + + /* + * Try to read byte 0 from i2c: + * Both SFF-8472 and SFF-8436 use it as + * 'identification byte'. + * Stop reading status on zero as value - + * this might happen in case of empty transceiver slot. + */ + id_byte = 0; + ii.f(&ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&id_byte); + if (ii.error != 0 || id_byte == 0) + return; + + switch (id_byte) { + case SFF_8024_ID_QSFP: + case SFF_8024_ID_QSFPPLUS: + print_qsfp_status(&ii, verbose); + break; + default: + print_sfp_status(&ii, verbose); + }; +} + |