17407056obrien/*-
2872b698pfg * SPDX-License-Identifier: BSD-3-Clause
3872b698pfg *
4a79cb7bglebius * Copyright (c) 2013 Gleb Smirnoff <glebius@FreeBSD.org>
5f9ab90drgrimes * Copyright (c) 1983, 1988, 1993
6f9ab90drgrimes *	The Regents of the University of California.  All rights reserved.
7f9ab90drgrimes *
8f9ab90drgrimes * Redistribution and use in source and binary forms, with or without
9f9ab90drgrimes * modification, are permitted provided that the following conditions
10f9ab90drgrimes * are met:
11f9ab90drgrimes * 1. Redistributions of source code must retain the above copyright
12f9ab90drgrimes *    notice, this list of conditions and the following disclaimer.
13f9ab90drgrimes * 2. Redistributions in binary form must reproduce the above copyright
14f9ab90drgrimes *    notice, this list of conditions and the following disclaimer in the
15f9ab90drgrimes *    documentation and/or other materials provided with the distribution.
167e6cabdimp * 3. Neither the name of the University nor the names of its contributors
17f9ab90drgrimes *    may be used to endorse or promote products derived from this software
18f9ab90drgrimes *    without specific prior written permission.
19f9ab90drgrimes *
20f9ab90drgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21f9ab90drgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22f9ab90drgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23f9ab90drgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24f9ab90drgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25f9ab90drgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26f9ab90drgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27f9ab90drgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28f9ab90drgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29f9ab90drgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30f9ab90drgrimes * SUCH DAMAGE.
31f9ab90drgrimes */
32f9ab90drgrimes
33cd48a1dcharnier#if 0
34f9ab90drgrimes#ifndef lint
35e0dc992peterstatic char sccsid[] = "@(#)if.c	8.3 (Berkeley) 4/28/95";
36f9ab90drgrimes#endif /* not lint */
37cd48a1dcharnier#endif
38cd48a1dcharnier
39cd48a1dcharnier#include <sys/cdefs.h>
40cd48a1dcharnier__FBSDID("$FreeBSD$");
41f9ab90drgrimes
42f256f35ngie#include <sys/param.h>
43f9ab90drgrimes#include <sys/protosw.h>
44f9ab90drgrimes#include <sys/socket.h>
4527187e7jhb#include <sys/socketvar.h>
46d3ecee0wollman#include <sys/time.h>
47f9ab90drgrimes
48f9ab90drgrimes#include <net/if.h>
49f9ab90drgrimes#include <net/if_dl.h>
50a9efebefenner#include <net/if_types.h>
51ae33ce6wollman#include <net/ethernet.h>
52f9ab90drgrimes#include <netinet/in.h>
53f9ab90drgrimes#include <netinet/in_var.h>
54f9ab90drgrimes#include <arpa/inet.h>
55b0bc7b1glebius#ifdef PF
56b0bc7b1glebius#include <net/pfvar.h>
57b0bc7b1glebius#include <net/if_pfsync.h>
58b0bc7b1glebius#endif
59f9ab90drgrimes
60c224d59mlaier#include <err.h>
61c224d59mlaier#include <errno.h>
624abf8ebglebius#include <ifaddrs.h>
63cf4f3e3glebius#include <libutil.h>
6495417d6ume#ifdef INET6
6595417d6ume#include <netdb.h>
6695417d6ume#endif
67f9ab90drgrimes#include <signal.h>
684abf8ebglebius#include <stdbool.h>
69e1db503yar#include <stdint.h>
70f9ab90drgrimes#include <stdio.h>
714c85934guido#include <stdlib.h>
72f9ab90drgrimes#include <string.h>
734abf8ebglebius#include <sysexits.h>
740842b7ddelphij#include <unistd.h>
750ea1b83marcel#include <libxo/xo.h>
76f9ab90drgrimes
77f9ab90drgrimes#include "netstat.h"
78f9ab90drgrimes
79df2a89dhrsstatic void sidewaysintpr(void);
8070f0bdfshin
81b0bc7b1glebius#ifdef PF
825190d38glebiusstatic const char* pfsyncacts[] = {
835190d38glebius	/* PFSYNC_ACT_CLR */		"clear all request",
845190d38glebius	/* PFSYNC_ACT_INS */		"state insert",
855190d38glebius	/* PFSYNC_ACT_INS_ACK */	"state inserted ack",
865190d38glebius	/* PFSYNC_ACT_UPD */		"state update",
875190d38glebius	/* PFSYNC_ACT_UPD_C */		"compressed state update",
885190d38glebius	/* PFSYNC_ACT_UPD_REQ */	"uncompressed state request",
895190d38glebius	/* PFSYNC_ACT_DEL */		"state delete",
905190d38glebius	/* PFSYNC_ACT_DEL_C */		"compressed state delete",
915190d38glebius	/* PFSYNC_ACT_INS_F */		"fragment insert",
925190d38glebius	/* PFSYNC_ACT_DEL_F */		"fragment delete",
935190d38glebius	/* PFSYNC_ACT_BUS */		"bulk update mark",
945190d38glebius	/* PFSYNC_ACT_TDB */		"TDB replay counter update",
955190d38glebius	/* PFSYNC_ACT_EOF */		"end of frame mark",
965190d38glebius};
975190d38glebius
980ea1b83marcelstatic const char* pfsyncacts_name[] = {
990ea1b83marcel	/* PFSYNC_ACT_CLR */		"clear-all-request",
1000ea1b83marcel	/* PFSYNC_ACT_INS */		"state-insert",
1010ea1b83marcel	/* PFSYNC_ACT_INS_ACK */	"state-inserted-ack",
1020ea1b83marcel	/* PFSYNC_ACT_UPD */		"state-update",
1030ea1b83marcel	/* PFSYNC_ACT_UPD_C */		"compressed-state-update",
1040ea1b83marcel	/* PFSYNC_ACT_UPD_REQ */	"uncompressed-state-request",
1050ea1b83marcel	/* PFSYNC_ACT_DEL */		"state-delete",
1060ea1b83marcel	/* PFSYNC_ACT_DEL_C */		"compressed-state-delete",
1070ea1b83marcel	/* PFSYNC_ACT_INS_F */		"fragment-insert",
1080ea1b83marcel	/* PFSYNC_ACT_DEL_F */		"fragment-delete",
1090ea1b83marcel	/* PFSYNC_ACT_BUS */		"bulk-update-mark",
1100ea1b83marcel	/* PFSYNC_ACT_TDB */		"TDB-replay-counter-update",
1110ea1b83marcel	/* PFSYNC_ACT_EOF */		"end-of-frame-mark",
1120ea1b83marcel};
1130ea1b83marcel
1145190d38glebiusstatic void
1150ea1b83marcelpfsync_acts_stats(const char *list, const char *desc, uint64_t *a)
1165190d38glebius{
1175190d38glebius	int i;
1185190d38glebius
1190ea1b83marcel	xo_open_list(list);
1200ea1b83marcel	for (i = 0; i < PFSYNC_ACT_MAX; i++, a++) {
1210ea1b83marcel		if (*a || sflag <= 1) {
1220ea1b83marcel			xo_open_instance(list);
1230ea1b83marcel			xo_emit("\t\t{e:name}{:count/%ju} {N:/%s%s %s}\n",
1240ea1b83marcel			    pfsyncacts_name[i], (uintmax_t)(*a),
1250ea1b83marcel			    pfsyncacts[i], plural(*a), desc);
1260ea1b83marcel			xo_close_instance(list);
1270ea1b83marcel		}
1280ea1b83marcel	}
1290ea1b83marcel	xo_close_list(list);
1305190d38glebius}
1315190d38glebius
1327407056obrien/*
133c224d59mlaier * Dump pfsync statistics structure.
134c224d59mlaier */
135c224d59mlaiervoid
13627187e7jhbpfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
137c224d59mlaier{
13865d9a55markj	struct pfsyncstats pfsyncstat;
13965d9a55markj
14065d9a55markj	if (fetch_stats("net.pfsync.stats", off, &pfsyncstat,
14165d9a55markj	    sizeof(pfsyncstat), kread) != 0)
14265d9a55markj		return;
143c224d59mlaier
1440ea1b83marcel	xo_emit("{T:/%s}:\n", name);
1450ea1b83marcel	xo_open_container(name);
146c224d59mlaier
1477407056obrien#define	p(f, m) if (pfsyncstat.f || sflag <= 1) \
1480ea1b83marcel	xo_emit(m, (uintmax_t)pfsyncstat.f, plural(pfsyncstat.f))
149e1db503yar
1500ea1b83marcel	p(pfsyncs_ipackets, "\t{:received-inet-packets/%ju} "
1510ea1b83marcel	    "{N:/packet%s received (IPv4)}\n");
1520ea1b83marcel	p(pfsyncs_ipackets6, "\t{:received-inet6-packets/%ju} "
1530ea1b83marcel	    "{N:/packet%s received (IPv6)}\n");
1540ea1b83marcel	pfsync_acts_stats("input-histogram", "received",
1555190d38glebius	    &pfsyncstat.pfsyncs_iacts[0]);
1560ea1b83marcel	p(pfsyncs_badif, "\t\t/{:dropped-bad-interface/%ju} "
1570ea1b83marcel	    "{N:/packet%s discarded for bad interface}\n");
1580ea1b83marcel	p(pfsyncs_badttl, "\t\t{:dropped-bad-ttl/%ju} "
1590ea1b83marcel	    "{N:/packet%s discarded for bad ttl}\n");
1600ea1b83marcel	p(pfsyncs_hdrops, "\t\t{:dropped-short-header/%ju} "
1610ea1b83marcel	    "{N:/packet%s shorter than header}\n");
1620ea1b83marcel	p(pfsyncs_badver, "\t\t{:dropped-bad-version/%ju} "
1630ea1b83marcel	    "{N:/packet%s discarded for bad version}\n");
1640ea1b83marcel	p(pfsyncs_badauth, "\t\t{:dropped-bad-auth/%ju} "
1650ea1b83marcel	    "{N:/packet%s discarded for bad HMAC}\n");
1660ea1b83marcel	p(pfsyncs_badact,"\t\t{:dropped-bad-action/%ju} "
1670ea1b83marcel	    "{N:/packet%s discarded for bad action}\n");
1680ea1b83marcel	p(pfsyncs_badlen, "\t\t{:dropped-short/%ju} "
1690ea1b83marcel	    "{N:/packet%s discarded for short packet}\n");
1700ea1b83marcel	p(pfsyncs_badval, "\t\t{:dropped-bad-values/%ju} "
1710ea1b83marcel	    "{N:/state%s discarded for bad values}\n");
1720ea1b83marcel	p(pfsyncs_stale, "\t\t{:dropped-stale-state/%ju} "
1730ea1b83marcel	    "{N:/stale state%s}\n");
1740ea1b83marcel	p(pfsyncs_badstate, "\t\t{:dropped-failed-lookup/%ju} "
1750ea1b83marcel	    "{N:/failed state lookup\\/insert%s}\n");
1760ea1b83marcel	p(pfsyncs_opackets, "\t{:sent-inet-packets/%ju} "
1770ea1b83marcel	    "{N:/packet%s sent (IPv4})\n");
1780ea1b83marcel	p(pfsyncs_opackets6, "\t{:send-inet6-packets/%ju} "
1790ea1b83marcel	    "{N:/packet%s sent (IPv6})\n");
1800ea1b83marcel	pfsync_acts_stats("output-histogram", "sent",
1815190d38glebius	    &pfsyncstat.pfsyncs_oacts[0]);
1820ea1b83marcel	p(pfsyncs_onomem, "\t\t{:discarded-no-memory/%ju} "
1830ea1b83marcel	    "{N:/failure%s due to mbuf memory error}\n");
1840ea1b83marcel	p(pfsyncs_oerrors, "\t\t{:send-errors/%ju} "
1850ea1b83marcel	    "{N:/send error%s}\n");
186c224d59mlaier#undef p
1870ea1b83marcel	xo_close_container(name);
188c224d59mlaier}
189b0bc7b1glebius#endif /* PF */
190076296ejoe
191076296ejoe/*
192076296ejoe * Display a formatted value, or a '-' in the same space.
193076296ejoe */
194ee746c9assarstatic void
1950ea1b83marcelshow_stat(const char *fmt, int width, const char *name,
1962d45fb7hrs    u_long value, short showvalue, int div1000)
197076296ejoe{
1984df7030bde	const char *lsep, *rsep;
1990ea1b83marcel	char newfmt[64];
200076296ejoe
2014df7030bde	lsep = "";
2024df7030bde	if (strncmp(fmt, "LS", 2) == 0) {
2034df7030bde		lsep = " ";
2044df7030bde		fmt += 2;
2054df7030bde	}
2064df7030bde	rsep = " ";
2074df7030bde	if (strncmp(fmt, "NRS", 3) == 0) {
2084df7030bde		rsep = "";
2094df7030bde		fmt += 3;
2104df7030bde	}
211cf4f3e3glebius	if (showvalue == 0) {
212cf4f3e3glebius		/* Print just dash. */
2130ea1b83marcel		xo_emit("{P:/%s}{D:/%*s}{P:/%s}", lsep, width, "-", rsep);
214cf4f3e3glebius		return;
215076296ejoe	}
216076296ejoe
2170ea1b83marcel	/*
2180ea1b83marcel	 * XXX: workaround {P:} modifier can't be empty and doesn't seem to
2190ea1b83marcel	 * take args... so we need to conditionally include it in the format.
2200ea1b83marcel	 */
2210ea1b83marcel#define maybe_pad(pad)	do {						    \
2220ea1b83marcel	if (strlen(pad)) {						    \
2230ea1b83marcel		snprintf(newfmt, sizeof(newfmt), "{P:%s}", pad);	    \
2240ea1b83marcel		xo_emit(newfmt);					    \
2250ea1b83marcel	}								    \
2260ea1b83marcel} while (0)
2270ea1b83marcel
228cf4f3e3glebius	if (hflag) {
229cf4f3e3glebius		char buf[5];
230076296ejoe
231cf4f3e3glebius		/* Format in human readable form. */
232cf4f3e3glebius		humanize_number(buf, sizeof(buf), (int64_t)value, "",
2332d45fb7hrs		    HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL | \
2342d45fb7hrs		    ((div1000) ? HN_DIVISOR_1000 : 0));
2350ea1b83marcel		maybe_pad(lsep);
2360ea1b83marcel		snprintf(newfmt, sizeof(newfmt), "{:%s/%%%ds}", name, width);
2370ea1b83marcel		xo_emit(newfmt, buf);
2380ea1b83marcel		maybe_pad(rsep);
239cf4f3e3glebius	} else {
240cf4f3e3glebius		/* Construct the format string. */
2410ea1b83marcel		maybe_pad(lsep);
2420ea1b83marcel		snprintf(newfmt, sizeof(newfmt), "{:%s/%%%d%s}",
2430ea1b83marcel		    name, width, fmt);
2440ea1b83marcel		xo_emit(newfmt, value);
2450ea1b83marcel		maybe_pad(rsep);
246cf4f3e3glebius	}
247cf4f3e3glebius}
248076296ejoe
249f9ab90drgrimes/*
2504abf8ebglebius * Find next multiaddr for a given interface name.
2514abf8ebglebius */
2524abf8ebglebiusstatic struct ifmaddrs *
2534abf8ebglebiusnext_ifma(struct ifmaddrs *ifma, const char *name, const sa_family_t family)
2544abf8ebglebius{
2554abf8ebglebius
2564abf8ebglebius	for(; ifma != NULL; ifma = ifma->ifma_next) {
2574abf8ebglebius		struct sockaddr_dl *sdl;
2584abf8ebglebius
2594abf8ebglebius		sdl = (struct sockaddr_dl *)ifma->ifma_name;
2604abf8ebglebius		if (ifma->ifma_addr->sa_family == family &&
2614abf8ebglebius		    strcmp(sdl->sdl_data, name) == 0)
2624abf8ebglebius			break;
2634abf8ebglebius	}
2644abf8ebglebius
2654abf8ebglebius	return (ifma);
2664abf8ebglebius}
2674abf8ebglebius
2684abf8ebglebius/*
269f9ab90drgrimes * Print a description of the network interfaces.
270f9ab90drgrimes */
271f9ab90drgrimesvoid
272df2a89dhrsintpr(void (*pfunc)(char *), int af)
273f9ab90drgrimes{
2744abf8ebglebius	struct ifaddrs *ifap, *ifa;
2754abf8ebglebius	struct ifmaddrs *ifmap, *ifma;
276363ca1bume	u_int ifn_len_max = 5, ifn_len;
2779614edbume	u_int has_ipv6 = 0, net_len = 13, addr_len = 17;
278363ca1bume
2794abf8ebglebius	if (interval)
280df2a89dhrs		return sidewaysintpr();
2814abf8ebglebius
2824abf8ebglebius	if (getifaddrs(&ifap) != 0)
2834abf8ebglebius		err(EX_OSERR, "getifaddrs");
2844abf8ebglebius	if (aflag && getifmaddrs(&ifmap) != 0)
2854abf8ebglebius		err(EX_OSERR, "getifmaddrs");
2862bf6d90wollman
287363ca1bume	if (Wflag) {
288363ca1bume		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
289363ca1bume			if (interface != NULL &&
290363ca1bume			    strcmp(ifa->ifa_name, interface) != 0)
291363ca1bume				continue;
292363ca1bume			if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
293363ca1bume				continue;
294363ca1bume			ifn_len = strlen(ifa->ifa_name);
295363ca1bume			if ((ifa->ifa_flags & IFF_UP) == 0)
296363ca1bume				++ifn_len;
297363ca1bume			ifn_len_max = MAX(ifn_len_max, ifn_len);
2989614edbume			if (ifa->ifa_addr->sa_family == AF_INET6)
2999614edbume				has_ipv6 = 1;
300363ca1bume		}
3019614edbume		if (has_ipv6) {
3029614edbume			net_len = 24;
3039614edbume			addr_len = 39;
3049614edbume		} else
3059614edbume			net_len = 18;
306363ca1bume	}
307363ca1bume
3080ea1b83marcel	xo_open_list("interface");
3096765386ru	if (!pfunc) {
310bad3c35ume		xo_emit("{T:/%-*.*s}", ifn_len_max, ifn_len_max, "Name");
3119614edbume		xo_emit(" {T:/%5.5s} {T:/%-*.*s} {T:/%-*.*s} {T:/%8.8s} "
3120ea1b83marcel		    "{T:/%5.5s} {T:/%5.5s}",
3139614edbume		    "Mtu", net_len, net_len, "Network", addr_len, addr_len,
3149614edbume		    "Address", "Ipkts", "Ierrs", "Idrop");
31570f0bdfshin		if (bflag)
3160ea1b83marcel			xo_emit(" {T:/%10.10s}","Ibytes");
3170ea1b83marcel		xo_emit(" {T:/%8.8s} {T:/%5.5s}", "Opkts", "Oerrs");
31870f0bdfshin		if (bflag)
3190ea1b83marcel			xo_emit(" {T:/%10.10s}","Obytes");
3200ea1b83marcel		xo_emit(" {T:/%5s}", "Coll");
32170f0bdfshin		if (dflag)
322e165e91ume			xo_emit(" {T:/%5.5s}", "Drop");
3230ea1b83marcel		xo_emit("\n");
32470f0bdfshin	}
32570f0bdfshin
3264abf8ebglebius	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
3274abf8ebglebius		bool network = false, link = false;
328f9e194cglebius		char *name, *xname, buf[IFNAMSIZ+1];
32969eae64ume		const char *nn, *rn;
33070f0bdfshin
3314abf8ebglebius		if (interface != NULL && strcmp(ifa->ifa_name, interface) != 0)
3324abf8ebglebius			continue;
333c6d8349joe
3340ea1b83marcel		name = ifa->ifa_name;
3350ea1b83marcel
3364abf8ebglebius		if (pfunc) {
3374abf8ebglebius
3384abf8ebglebius			(*pfunc)(name);
3394abf8ebglebius
3404abf8ebglebius			/*
3414abf8ebglebius			 * Skip all ifaddrs belonging to same interface.
3424abf8ebglebius			 */
3434abf8ebglebius			while(ifa->ifa_next != NULL &&
3444abf8ebglebius			    (strcmp(ifa->ifa_next->ifa_name, name) == 0)) {
3454abf8ebglebius				ifa = ifa->ifa_next;
346a955db2ru			}
3474abf8ebglebius			continue;
3484abf8ebglebius		}
349076296ejoe
3504abf8ebglebius		if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
3514abf8ebglebius			continue;
3524abf8ebglebius
3530ea1b83marcel		xo_open_instance("interface");
3540ea1b83marcel
355f9e194cglebius		if ((ifa->ifa_flags & IFF_UP) == 0) {
356f9e194cglebius			xname = stpcpy(buf, name);
357f9e194cglebius			*xname++ = '*';
358f9e194cglebius			*xname = '\0';
359f9e194cglebius			xname = buf;
360f9e194cglebius		} else
361f9e194cglebius			xname = name;
362f9e194cglebius
3634abd020ume		xo_emit("{d:/%-*.*s}{etk:name}{eq:flags/0x%x}",
3644abd020ume		    ifn_len_max, ifn_len_max, xname, name, ifa->ifa_flags);
3654abf8ebglebius
3664abf8ebglebius#define IFA_MTU(ifa)	(((struct if_data *)(ifa)->ifa_data)->ifi_mtu)
3672d45fb7hrs		show_stat("lu", 6, "mtu", IFA_MTU(ifa), IFA_MTU(ifa), 0);
3684abf8ebglebius#undef IFA_MTU
3694abf8ebglebius
3704abf8ebglebius		switch (ifa->ifa_addr->sa_family) {
3714abf8ebglebius		case AF_UNSPEC:
3729614edbume			xo_emit("{:network/%-*.*s} ", net_len, net_len,
3739614edbume			    "none");
3749614edbume			xo_emit("{:address/%-*.*s} ", addr_len, addr_len,
3759614edbume			    "none");
3764abf8ebglebius			break;
3774abf8ebglebius		case AF_INET:
37870f0bdfshin#ifdef INET6
3794abf8ebglebius		case AF_INET6:
38014dab03ume#endif /* INET6 */
38169eae64ume			nn = netname(ifa->ifa_addr, ifa->ifa_netmask);
38269eae64ume			rn = routename(ifa->ifa_addr, numeric_addr);
383df2a89dhrs			if (Wflag) {
3844abd020ume				xo_emit("{t:network/%-*s} ", net_len, nn);
3854abd020ume				xo_emit("{t:address/%-*s} ", addr_len, rn);
386df2a89dhrs			} else {
3874abd020ume				xo_emit("{d:network/%-*.*s}{et:network} ",
3884abd020ume				    net_len, net_len, nn, nn);
3894abd020ume				xo_emit("{d:address/%-*.*s}{et:address} ",
3904abd020ume				    addr_len, addr_len, rn, rn);
391df2a89dhrs			}
3920c208dfjulian
393df2a89dhrs			network = true;
3944abf8ebglebius			break;
3954abf8ebglebius		case AF_LINK:
3964abf8ebglebius		    {
3974abf8ebglebius			struct sockaddr_dl *sdl;
3981b12c4fdelphij			char linknum[sizeof("<Link#32767>")];
3994abf8ebglebius
4004abf8ebglebius			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
4011b12c4fdelphij			snprintf(linknum, sizeof(linknum), "<Link#%d>", sdl->sdl_index);
4029614edbume			xo_emit("{t:network/%-*.*s} ", net_len, net_len,
4039614edbume			    linknum);
404df2a89dhrs			if (sdl->sdl_nlen == 0 &&
405df2a89dhrs			    sdl->sdl_alen == 0 &&
406df2a89dhrs			    sdl->sdl_slen == 0)
407da474a1ume				xo_emit("{P:/%*s} ", addr_len, "");
408df2a89dhrs			else
409da474a1ume				xo_emit("{t:address/%-*.*s} ", addr_len,
4109614edbume				    addr_len, routename(ifa->ifa_addr, 1));
411df2a89dhrs			link = true;
4124abf8ebglebius			break;
4134abf8ebglebius		    }
414f9ab90drgrimes		}
415c6d8349joe
4164abf8ebglebius#define	IFA_STAT(s)	(((struct if_data *)ifa->ifa_data)->ifi_ ## s)
4170ea1b83marcel		show_stat("lu", 8, "received-packets", IFA_STAT(ipackets),
4182d45fb7hrs		    link|network, 1);
4192d45fb7hrs		show_stat("lu", 5, "received-errors", IFA_STAT(ierrors),
4202d45fb7hrs		    link, 1);
4212d45fb7hrs		show_stat("lu", 5, "dropped-packets", IFA_STAT(iqdrops),
4222d45fb7hrs		    link, 1);
4230069285glebius		if (bflag)
4240ea1b83marcel			show_stat("lu", 10, "received-bytes", IFA_STAT(ibytes),
4252d45fb7hrs			    link|network, 0);
4260ea1b83marcel		show_stat("lu", 8, "sent-packets", IFA_STAT(opackets),
4272d45fb7hrs		    link|network, 1);
4282d45fb7hrs		show_stat("lu", 5, "send-errors", IFA_STAT(oerrors), link, 1);
4290069285glebius		if (bflag)
4300ea1b83marcel			show_stat("lu", 10, "sent-bytes", IFA_STAT(obytes),
4312d45fb7hrs			    link|network, 0);
4322d45fb7hrs		show_stat("NRSlu", 5, "collisions", IFA_STAT(collisions),
4332d45fb7hrs		    link, 1);
4341bb1461glebius		if (dflag)
4350ea1b83marcel			show_stat("LSlu", 5, "dropped-packets",
4362d45fb7hrs			    IFA_STAT(oqdrops), link, 1);
4370ea1b83marcel		xo_emit("\n");
4384df7030bde
4390ea1b83marcel		if (!aflag) {
4400ea1b83marcel			xo_close_instance("interface");
4414abf8ebglebius			continue;
4420ea1b83marcel		}
443a9efebefenner
4444abf8ebglebius		/*
4454abf8ebglebius		 * Print family's multicast addresses.
4464abf8ebglebius		 */
4470ea1b83marcel		xo_open_list("multicast-address");
4484abf8ebglebius		for (ifma = next_ifma(ifmap, ifa->ifa_name,
4490ea1b83marcel		    ifa->ifa_addr->sa_family);
4500ea1b83marcel		    ifma != NULL;
4510ea1b83marcel		    ifma = next_ifma(ifma, ifa->ifa_name,
4520ea1b83marcel		    ifa->ifa_addr->sa_family)) {
4534abf8ebglebius			const char *fmt = NULL;
4544abf8ebglebius
4550ea1b83marcel			xo_open_instance("multicast-address");
4564abf8ebglebius			switch (ifma->ifma_addr->sa_family) {
4574abf8ebglebius			case AF_LINK:
4584abf8ebglebius			    {
4594abf8ebglebius				struct sockaddr_dl *sdl;
4604abf8ebglebius
4614abf8ebglebius				sdl = (struct sockaddr_dl *)ifma->ifma_addr;
462df2a89dhrs				if (sdl->sdl_type != IFT_ETHER &&
463df2a89dhrs				    sdl->sdl_type != IFT_FDDI)
464ae33ce6wollman					break;
4654abf8ebglebius			    }
466df2a89dhrs				/* FALLTHROUGH */
467df2a89dhrs			case AF_INET:
468df2a89dhrs#ifdef INET6
469df2a89dhrs			case AF_INET6:
470df2a89dhrs#endif /* INET6 */
471df2a89dhrs				fmt = routename(ifma->ifma_addr, numeric_addr);
472df2a89dhrs				break;
4734abf8ebglebius			}
4744abf8ebglebius			if (fmt) {
475df2a89dhrs				if (Wflag)
476df2a89dhrs					xo_emit("{P:/%27s }"
477df2a89dhrs					    "{t:address/%-17s/}", "", fmt);
478df2a89dhrs				else
479df2a89dhrs					xo_emit("{P:/%25s }"
480df2a89dhrs					    "{t:address/%-17.17s/}", "", fmt);
4814abf8ebglebius				if (ifma->ifma_addr->sa_family == AF_LINK) {
4820ea1b83marcel					xo_emit(" {:received-packets/%8lu}",
4830ea1b83marcel					    IFA_STAT(imcasts));
4840ea1b83marcel					xo_emit("{P:/%*s}", bflag? 17 : 6, "");
4850ea1b83marcel					xo_emit(" {:sent-packets/%8lu}",
4860ea1b83marcel					    IFA_STAT(omcasts));
4870ea1b83marcel 				}
4880ea1b83marcel				xo_emit("\n");
489ae33ce6wollman			}
4900ea1b83marcel			xo_close_instance("multicast-address");
4914abf8ebglebius			ifma = ifma->ifma_next;
492a9efebefenner		}
4930ea1b83marcel		xo_close_list("multicast-address");
4940ea1b83marcel		xo_close_instance("interface");
495f9ab90drgrimes	}
4960ea1b83marcel	xo_close_list("interface");
4974abf8ebglebius
4984abf8ebglebius	freeifaddrs(ifap);
4994abf8ebglebius	if (aflag)
5004abf8ebglebius		freeifmaddrs(ifmap);
501f9ab90drgrimes}
502f9ab90drgrimes
5034abf8ebglebiusstruct iftot {
5046ab5649dfr	u_long	ift_ip;			/* input packets */
5056ab5649dfr	u_long	ift_ie;			/* input errors */
506c984dc5attilio	u_long	ift_id;			/* input drops */
5076ab5649dfr	u_long	ift_op;			/* output packets */
5086ab5649dfr	u_long	ift_oe;			/* output errors */
5091bb1461glebius	u_long	ift_od;			/* output drops */
5106ab5649dfr	u_long	ift_co;			/* collisions */
5116ab5649dfr	u_long	ift_ib;			/* input bytes */
5126ab5649dfr	u_long	ift_ob;			/* output bytes */
5134c85934guido};
514f9ab90drgrimes
515f9ab90drgrimes/*
5164abf8ebglebius * Obtain stats for interface(s).
517f9ab90drgrimes */
518f9ab90drgrimesstatic void
5194abf8ebglebiusfill_iftot(struct iftot *st)
520f9ab90drgrimes{
5214abf8ebglebius	struct ifaddrs *ifap, *ifa;
5224abf8ebglebius	bool found = false;
523f9ab90drgrimes
5244abf8ebglebius	if (getifaddrs(&ifap) != 0)
5250ea1b83marcel		xo_err(EX_OSERR, "getifaddrs");
5262bf6d90wollman
5274abf8ebglebius	bzero(st, sizeof(*st));
5284c85934guido
5294abf8ebglebius	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
5304abf8ebglebius		if (ifa->ifa_addr->sa_family != AF_LINK)
5314abf8ebglebius			continue;
5324abf8ebglebius		if (interface) {
5334abf8ebglebius			if (strcmp(ifa->ifa_name, interface) == 0)
5344abf8ebglebius				found = true;
5354abf8ebglebius			else
5364abf8ebglebius				continue;
5374c85934guido		}
5384abf8ebglebius
5394abf8ebglebius		st->ift_ip += IFA_STAT(ipackets);
5404abf8ebglebius		st->ift_ie += IFA_STAT(ierrors);
5414abf8ebglebius		st->ift_id += IFA_STAT(iqdrops);
5424abf8ebglebius		st->ift_ib += IFA_STAT(ibytes);
5434abf8ebglebius		st->ift_op += IFA_STAT(opackets);
5444abf8ebglebius		st->ift_oe += IFA_STAT(oerrors);
5451bb1461glebius		st->ift_od += IFA_STAT(oqdrops);
5464abf8ebglebius		st->ift_ob += IFA_STAT(obytes);
5474abf8ebglebius 		st->ift_co += IFA_STAT(collisions);
5484c85934guido	}
5494abf8ebglebius
5504abf8ebglebius	if (interface && found == false)
5510ea1b83marcel		xo_err(