10d688beasomers/*
20d688beasomers * Copyright (c) 2017, Spectra Logic Corporation
30d688beasomers * All rights reserved.
40d688beasomers *
50d688beasomers * Redistribution and use in source and binary forms, with or without modification,
60d688beasomers * are permitted provided that the following conditions are met:
70d688beasomers *
80d688beasomers * 1. Redistributions of source code must retain the above copyright notice,
90d688beasomers * thislist of conditions and the following disclaimer.
100d688beasomers *
110d688beasomers * 2. Redistributions in binary form must reproduce the above copyright notice,
120d688beasomers * this list of conditions and the following disclaimer in the documentation and/or
130d688beasomers * other materials provided with the distribution.
140d688beasomers *
150d688beasomers *
160d688beasomers * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
170d688beasomers * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
180d688beasomers * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190d688beasomers * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
200d688beasomers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210d688beasomers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
220d688beasomers * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
230d688beasomers * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
240d688beasomers * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
250d688beasomers * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
260d688beasomers *
270d688beasomers * $FreeBSD$
280d688beasomers */
290d688beasomers
300d688beasomers#include <sys/param.h>
310d688beasomers#include <sys/ioctl.h>
320d688beasomers#include <sys/socket.h>
330d688beasomers
340d688beasomers#include <arpa/inet.h>
350d688beasomers#include <net/ethernet.h>
360d688beasomers#include <net/if.h>
370d688beasomers#include <net/if_dl.h>
380d688beasomers#include <net/if_lagg.h>
390d688beasomers#include <net/if_media.h>
400d688beasomers#include <net/if_types.h>
410d688beasomers#include <netinet/in.h>
420d688beasomers#include <netinet/ip_carp.h>
430d688beasomers#include <netinet6/in6_var.h>
440d688beasomers#include <netinet6/nd6.h>
450d688beasomers
460d688beasomers#include <err.h>
470d688beasomers#include <errno.h>
480d688beasomers#include <ifaddrs.h>
490d688beasomers#include <netdb.h>
500d688beasomers#include <stdio.h>
510d688beasomers#include <stdlib.h>
520d688beasomers#include <string.h>
530d688beasomers#include <libifconfig.h>
540d688beasomers
550d688beasomersstatic const char *carp_states[] = { CARP_STATES };
560d688beasomers
570d688beasomersstatic void
580d688beasomersprint_carp(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
590d688beasomers{
600d688beasomers	struct carpreq carpr[CARP_MAXVHID];
610d688beasomers	int i;
620d688beasomers
630d688beasomers	if (ifconfig_carp_get_info(lifh, ifa->ifa_name, carpr, CARP_MAXVHID)) {
640d688beasomers		return; /* Probably not configured on this interface */
650d688beasomers	}
660d688beasomers	for (i = 0; i < carpr[0].carpr_count; i++) {
670d688beasomers		printf("\tcarp: %s vhid %d advbase %d advskew %d",
680d688beasomers		    carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
690d688beasomers		    carpr[i].carpr_advbase, carpr[i].carpr_advskew);
700d688beasomers		printf("\n");
710d688beasomers	}
720d688beasomers}
730d688beasomers
740d688beasomersstatic void
750d688beasomersprint_inet4_addr(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
760d688beasomers{
770d688beasomers	struct ifconfig_inet_addr addr;
780d688beasomers	char addr_buf[NI_MAXHOST];
790d688beasomers
800d688beasomers	if (ifconfig_inet_get_addrinfo(lifh, ifa->ifa_name, ifa, &addr) != 0) {
810d688beasomers		return;
820d688beasomers	}
830d688beasomers
840d688beasomers	inet_ntop(AF_INET, &addr.sin->sin_addr, addr_buf, sizeof(addr_buf));
850d688beasomers	printf("\tinet %s", addr_buf);
860d688beasomers
870d688beasomers	if (addr.dst) {
880d688beasomers		printf(" --> %s", inet_ntoa(addr.dst->sin_addr));
890d688beasomers	}
900d688beasomers
910d688beasomers	printf(" netmask 0x%x ", ntohl(addr.netmask->sin_addr.s_addr));
920d688beasomers
930d688beasomers	if ((addr.broadcast != NULL) &&
940d688beasomers	    (addr.broadcast->sin_addr.s_addr != 0)) {
950d688beasomers		printf("broadcast %s ", inet_ntoa(addr.broadcast->sin_addr));
960d688beasomers	}
970d688beasomers
980d688beasomers	if (addr.vhid != 0) {
990d688beasomers		printf("vhid %d ", addr.vhid);
1000d688beasomers	}
1010d688beasomers	printf("\n");
1020d688beasomers}
1030d688beasomers
1040d688beasomersstatic void
1050d688beasomersprint_inet6_addr(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
1060d688beasomers{
1070d688beasomers	struct ifconfig_inet6_addr addr;
1080d688beasomers	char addr_buf[NI_MAXHOST];
1090d688beasomers	struct timespec now;
1100d688beasomers
1110d688beasomers	/* Print the address */
1120d688beasomers	if (ifconfig_inet6_get_addrinfo(lifh, ifa->ifa_name, ifa, &addr) != 0) {
1130d688beasomers		err(1, "ifconfig_inet6_get_addrinfo");
1140d688beasomers	}
1150d688beasomers	if (0 != getnameinfo((struct sockaddr *)addr.sin6, addr.sin6->sin6_len,
1160d688beasomers	    addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST)) {
1170d688beasomers		inet_ntop(AF_INET6, &addr.sin6->sin6_addr, addr_buf,
1180d688beasomers		    sizeof(addr_buf));
1190d688beasomers	}
1200d688beasomers	printf("\tinet6 %s", addr_buf);
1210d688beasomers
1220d688beasomers	if (addr.dstin6) {
1230d688beasomers		inet_ntop(AF_INET6, addr.dstin6, addr_buf, sizeof(addr_buf));
1240d688beasomers		printf(" --> %s", addr_buf);
1250d688beasomers	}
1260d688beasomers
1270d688beasomers	/* Print the netmask */
1280d688beasomers	printf(" prefixlen %d ", addr.prefixlen);
1290d688beasomers
1300d688beasomers	/* Print the scopeid*/
1310d688beasomers	if (addr.sin6->sin6_scope_id) {
1320d688beasomers		printf("scopeid 0x%x ", addr.sin6->sin6_scope_id);
1330d688beasomers	}
1340d688beasomers
1350d688beasomers	/* Print the flags */
1360d688beasomers	if ((addr.flags & IN6_IFF_ANYCAST) != 0) {
1370d688beasomers		printf("anycast ");
1380d688beasomers	}
1390d688beasomers	if ((addr.flags & IN6_IFF_TENTATIVE) != 0) {
1400d688beasomers		printf("tentative ");
1410d688beasomers	}
1420d688beasomers	if ((addr.flags & IN6_IFF_DUPLICATED) != 0) {
1430d688beasomers		printf("duplicated ");
1440d688beasomers	}
1450d688beasomers	if ((addr.flags & IN6_IFF_DETACHED) != 0) {
1460d688beasomers		printf("detached ");
1470d688beasomers	}
1480d688beasomers	if ((addr.flags & IN6_IFF_DEPRECATED) != 0) {
1490d688beasomers		printf("deprecated ");
1500d688beasomers	}
1510d688beasomers	if ((addr.flags & IN6_IFF_AUTOCONF) != 0) {
1520d688beasomers		printf("autoconf ");
1530d688beasomers	}
1540d688beasomers	if ((addr.flags & IN6_IFF_TEMPORARY) != 0) {
1550d688beasomers		printf("temporary ");
1560d688beasomers	}
1570d688beasomers	if ((addr.flags & IN6_IFF_PREFER_SOURCE) != 0) {
1580d688beasomers		printf("prefer_source ");
1590d688beasomers	}
1600d688beasomers
1610d688beasomers	/* Print the lifetimes */
1620d688beasomers	clock_gettime(CLOCK_MONOTONIC_FAST, &now);
1630d688beasomers	if (addr.lifetime.ia6t_preferred || addr.lifetime.ia6t_expire) {
1640d688beasomers		printf("pltime ");
1650d688beasomers		if (addr.lifetime.ia6t_preferred) {
1660d688beasomers			printf("%ld ", MAX(0l,
1670d688beasomers			    addr.lifetime.ia6t_preferred - now.tv_sec));
1680d688beasomers		} else {
1690d688beasomers			printf("infty ");
1700d688beasomers		}
1710d688beasomers
1720d688beasomers		printf("vltime ");
1730d688beasomers		if (addr.lifetime.ia6t_expire) {
1740d688beasomers			printf("%ld ", MAX(0l,
1750d688beasomers			    addr.lifetime.ia6t_expire - now.tv_sec));
1760d688beasomers		} else {
1770d688beasomers			printf("infty ");
1780d688beasomers		}
1790d688beasomers	}
1800d688beasomers
1810d688beasomers	/* Print the vhid */
1820d688beasomers	if (addr.vhid != 0) {
1830d688beasomers		printf("vhid %d ", addr.vhid);
1840d688beasomers	}
1850d688beasomers	printf("\n");
1860d688beasomers}
1870d688beasomers
1880d688beasomersstatic void
1890d688beasomersprint_link_addr(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
1900d688beasomers{
1910d688beasomers	char addr_buf[NI_MAXHOST];
1920d688beasomers	struct sockaddr_dl *sdl;
1930d688beasomers	int n;
1940d688beasomers
1950d688beasomers	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
1960d688beasomers	if ((sdl != NULL) && (sdl->sdl_alen > 0)) {
1970d688beasomers		if (((sdl->sdl_type == IFT_ETHER) ||
1980d688beasomers		    (sdl->sdl_type == IFT_L2VLAN) ||
1990d688beasomers		    (sdl->sdl_type == IFT_BRIDGE)) &&
2000d688beasomers		    (sdl->sdl_alen == ETHER_ADDR_LEN)) {
2010d688beasomers			ether_ntoa_r((struct ether_addr *)LLADDR(sdl),
2020d688beasomers			    addr_buf);
2030d688beasomers			printf("\tether %s\n", addr_buf);
2040d688beasomers		} else {
2050d688beasomers			n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
2060d688beasomers
2070d688beasomers			printf("\tlladdr %s\n", link_ntoa(sdl) + n);
2080d688beasomers		}
2090d688beasomers	}
2100d688beasomers}
2110d688beasomers
2120d688beasomersstatic void
2130d688beasomersprint_ifaddr(ifconfig_handle_t *lifh, struct ifaddrs *ifa, void *udata __unused)
2140d688beasomers{
2150d688beasomers	switch (ifa->ifa_addr->sa_family) {
2160d688beasomers	case AF_INET:
2170d688beasomers		print_inet4_addr(lifh, ifa);
2180d688beasomers		break;
2190d688beasomers	case AF_INET6:
2200d688beasomers
2210d688beasomers		/*
2220d688beasomers		 * printing AF_INET6 status requires calling SIOCGIFAFLAG_IN6
2230d688beasomers		 * and SIOCGIFALIFETIME_IN6.  TODO: figure out the best way to
2240d688beasomers		 * do that from within libifconfig
2250d688beasomers		 */
2260d688beasomers		print_inet6_addr(lifh, ifa);
2270d688beasomers		break;
2280d688beasomers	case AF_LINK:
2290d688beasomers		print_link_addr(lifh, ifa);
2300d688beasomers		break;
2310d688beasomers	case AF_LOCAL:
2320d688beasomers	case AF_UNSPEC:
2330d688beasomers	default:
2340d688beasomers		/* TODO */
2350d688beasomers		break;
2360d688beasomers	}
2370d688beasomers}
2380d688beasomers
2390d688beasomersstatic void
2400d688beasomersprint_nd6(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
2410d688beasomers{
2420d688beasomers	struct in6_ndireq nd;
2430d688beasomers
2440d688beasomers	if (ifconfig_get_nd6(lifh, ifa->ifa_name, &nd) == 0) {
2450d688beasomers		printf("\tnd6 options=%x\n", nd.ndi.flags);
2460d688beasomers	} else {
2470d688beasomers		err(1, "Failed to get nd6 options");
2480d688beasomers	}
2490d688beasomers}
2500d688beasomers
2510d688beasomersstatic void
2520d688beasomersprint_fib(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
2530d688beasomers{
2540d688beasomers	int fib;
2550d688beasomers
2560d688beasomers	if (ifconfig_get_fib(lifh, ifa->ifa_name, &fib) == 0) {
2570d688beasomers		printf("\tfib: %d\n", fib);
2580d688beasomers	} else {
2590d688beasomers		err(1, "Failed to get interface FIB");
2600d688beasomers	}
2610d688beasomers}
2620d688beasomers
2630d688beasomersstatic void
2640d688beasomersprint_lagg(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
2650d688beasomers{
2660d688beasomers	struct lagg_protos lpr[] = LAGG_PROTOS;
2670d688beasomers	struct ifconfig_lagg_status *ls;
2680d688beasomers	struct lacp_opreq *lp;
2690d688beasomers	const char *proto = "<unknown>";
2700d688beasomers	int i;
2710d688beasomers
2720d688beasomers	if (ifconfig_lagg_get_lagg_status(lifh, ifa->ifa_name, &ls) < 0) {
2730d688beasomers		if (ifconfig_err_errno(lifh) == EINVAL) {
2740d688beasomers			return;
2750d688beasomers		}
2760d688beasomers		err(1, "Failed to get interface lagg status");
2770d688beasomers	}
2780d688beasomers
2790d688beasomers	/* First print the proto */
2800d688beasomers	for (i = 0; i < nitems(lpr); i++) {
2810d688beasomers		if (ls->ra->ra_proto == lpr[i].lpr_proto) {
2820d688beasomers			proto = lpr[i].lpr_name;
2830d688beasomers			break;
2840d688beasomers		}
2850d688beasomers	}
2860d688beasomers	printf("\tlaggproto %s", proto);
2870d688beasomers
2880d688beasomers	/* Now print the lagg hash */
2890d688beasomers	if (ls->rf->rf_flags & LAGG_F_HASHMASK) {
2900d688beasomers		const char *sep = "";
2910d688beasomers
2920d688beasomers		printf(" lagghash ");
2930d688beasomers		if (ls->rf->rf_flags & LAGG_F_HASHL2) {
2940d688beasomers			printf("%sl2", sep);
2950d688beasomers			sep = ",";
2960d688beasomers		}
2970d688beasomers		if (ls->rf->rf_flags & LAGG_F_HASHL3) {
2980d688beasomers			printf("%sl3", sep);
2990d688beasomers			sep = ",";
3000d688beasomers		}
3010d688beasomers		if (ls->rf->rf_flags & LAGG_F_HASHL4) {
3020d688beasomers			printf("%sl4", sep);
3030d688beasomers			sep = ",";
3040d688beasomers		}
3050d688beasomers	}
3060d688beasomers	putchar('\n');
3070d688beasomers	printf("\tlagg options:\n");
3080d688beasomers	printf("\t\tflags=%x", ls->ro->ro_opts);
3090d688beasomers	putchar('\n');
3100d688beasomers	printf("\t\tflowid_shift: %d\n", ls->ro->ro_flowid_shift);
3110d688beasomers	if (ls->ra->ra_proto == LAGG_PROTO_ROUNDROBIN) {
3120d688beasomers		printf("\t\trr_limit: %d\n", ls->ro->ro_bkt);
3130d688beasomers	}
3140d688beasomers	printf("\tlagg statistics:\n");
3150d688beasomers	printf("\t\tactive ports: %d\n", ls->ro->ro_active);
3160d688beasomers	printf("\t\tflapping: %u\n", ls->ro->ro_flapping);
3170d688beasomers	for (i = 0; i < ls->ra->ra_ports; i++) {
3180d688beasomers		lp = (struct lacp_opreq *)&ls->ra->ra_port[i].rp_lacpreq;
3190d688beasomers		printf("\tlaggport: %s ", ls->ra->ra_port[i].rp_portname);
3200d688beasomers		printf("flags=%x", ls->ra->ra_port[i].rp_flags);
3210d688beasomers		if (ls->ra->ra_proto == LAGG_PROTO_LACP) {
3220d688beasomers			printf(" state=%x", lp->actor_state);
3230d688beasomers		}
3240d688beasomers		putchar('\n');
3250d688beasomers	}
3260d688beasomers
3270d688beasomers	printf("\n");
3280d688beasomers	ifconfig_lagg_free_lagg_status(ls);
3290d688beasomers}
3300d688beasomers
3310d688beasomersstatic void
3320d688beasomersprint_laggport(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
3330d688beasomers{
3340d688beasomers	struct lagg_reqport rp;
3350d688beasomers
3360d688beasomers	if (ifconfig_lagg_get_laggport_status(lifh, ifa->ifa_name, &rp) < 0) {
3370d688beasomers		if ((ifconfig_err_errno(lifh) == EINVAL) ||
3380d688beasomers		    (ifconfig_err_errno(lifh) == ENOENT)) {
3390d688beasomers			return;
3400d688beasomers		} else {
3410d688beasomers			err(1, "Failed to get lagg port status");
3420d688beasomers		}
3430d688beasomers	}
3440d688beasomers
3450d688beasomers	printf("\tlaggdev: %s\n", rp.rp_ifname);
3460d688beasomers}
3470d688beasomers
3480d688beasomersstatic void
3490d688beasomersprint_groups(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
3500d688beasomers{
3510d688beasomers	struct ifgroupreq ifgr;
3520d688beasomers	struct ifg_req *ifg;
3530d688beasomers	int len;
3540d688beasomers	int cnt = 0;
3550d688beasomers
3560d688beasomers	if (ifconfig_get_groups(lifh, ifa->ifa_name, &ifgr) != 0) {
3570d688beasomers		err(1, "Failed to get groups");
3580d688beasomers	}
3590d688beasomers
3600d688beasomers	ifg = ifgr.ifgr_groups;
3610d688beasomers	len = ifgr.ifgr_len;
3620d688beasomers	for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
3630d688beasomers		len -= sizeof(struct ifg_req);
3640d688beasomers		if (strcmp(ifg->ifgrq_group, "all")) {
3650d688beasomers			if (cnt == 0) {
3660d688beasomers				printf("\tgroups: ");
3670d688beasomers			}
3680d688beasomers			cnt++;
3690d688beasomers			printf("%s ", ifg->ifgrq_group);
3700d688beasomers		}
3710d688beasomers	}
3720d688beasomers	if (cnt) {
3730d688beasomers		printf("\n");
3740d688beasomers	}
3750d688beasomers
3760d688beasomers	free(ifgr.ifgr_groups);
3770d688beasomers}
3780d688beasomers
3790d688beasomersstatic void
3800d688beasomersprint_media(ifconfig_handle_t *lifh, struct ifaddrs *ifa)
3810d688beasomers{
3820d688beasomers	int i;
3830d688beasomers
3840d688beasomers	/* Outline:
3850d688beasomers	 * 1) Determine whether the iface supports SIOGIFMEDIA or SIOGIFXMEDIA
3860d688beasomers	 * 2) Get the full media list
3870d688beasomers	 * 3) Print the current media word
3880d688beasomers	 * 4) Print the active media word, if different
3890d688beasomers	 * 5) Print the status
3900d688beasomers	 * 6) Print the supported media list
3910d688beasomers	 *
3920d688beasomers	 * How to print the media word:
3930d688beasomers	 * 1) Get the top-level interface type and description
3940d688beasomers	 * 2) Print the subtype
3950d688beasomers	 * 3) For current word only, print the top type, if it exists
3960d688beasomers	 * 4) Print options list
3970d688beasomers	 * 5) Print the instance, if there is one
3980d688beasomers	 *
3990d688beasomers	 * How to get the top-level interface type
4000d688beasomers	 * 1) Shift ifmw right by 0x20 and index into IFM_TYPE_DESCRIPTIONS
4010d688beasomers	 *
4020d688beasomers	 * How to get the top-level interface subtype
4030d688beasomers	 * 1) Shift ifmw right by 0x20, index into ifmedia_types_to_subtypes
4040d688beasomers	 * 2) Iterate through the resulting table's subtypes table, ignoring
4050d688beasomers	 *    aliases.  Iterate through the resulting ifmedia_description
4060d688beasomers	 *    tables,  finding an entry with the right media subtype
4070d688beasomers	 */
4080d688beasomers	struct ifmediareq *ifmr;
4090d688beasomers	char opts[80];
4100d688beasomers
4110d688beasomers	if (ifconfig_media_get_mediareq(lifh, ifa->ifa_name, &ifmr) != 0) {
4120d688beasomers		if (ifconfig_err_errtype(lifh) != OK) {
4130d688beasomers			err(1, "Failed to get media info");
4140d688beasomers		} else {
4150d688beasomers			return; /* Interface doesn't support media info */
4160d688beasomers		}
4170d688beasomers	}
4180d688beasomers
4190d688beasomers	printf("\tmedia: %s %s", ifconfig_media_get_type(ifmr->ifm_current),
4200d688beasomers	    ifconfig_media_get_subtype(ifmr->ifm_current));
4210d688beasomers	if (ifmr->ifm_active != ifmr->ifm_current) {
4220d688beasomers		printf(" (%s", ifconfig_media_get_subtype(ifmr->ifm_active));
4230d688beasomers		ifconfig_media_get_options_string(ifmr->ifm_active, opts,
4240d688beasomers		    sizeof(opts));
425