#include <sys/param.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <arpa/inet.h>
#ifdef PF
#include <net/pfvar.h>
#include <net/pflow.h>
#include <net/if_pfsync.h>
#endif
#include <errno.h>
#include <ifaddrs.h>
#include <libutil.h>
#ifdef INET6
#include <netdb.h>
#endif
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <libxo/xo.h>
#include "netstat.h"
static void sidewaysintpr(void);
#ifdef PF
static const char* pfsyncacts[] = {
"clear all request",
"13.1 state insert",
"state inserted ack",
"13.1 state update",
"compressed state update",
"uncompressed state request",
"state delete",
"compressed state delete",
"fragment insert",
"fragment delete",
"bulk update mark",
"TDB replay counter update",
"end of frame mark",
"14.0 state insert",
"14.0 state update",
"state insert",
"state update",
};
static const char* pfsyncacts_name[] = {
"clear-all-request",
"state-insert-1301",
"state-inserted-ack",
"state-update-1301",
"compressed-state-update",
"uncompressed-state-request",
"state-delete",
"compressed-state-delete",
"fragment-insert",
"fragment-delete",
"bulk-update-mark",
"TDB-replay-counter-update",
"end-of-frame-mark",
"state-insert-1400",
"state-update-1400",
"state-insert",
"state-update",
};
static void
pfsync_acts_stats(const char *list, const char *desc, uint64_t *a)
{
int i;
xo_open_list(list);
for (i = 0; i < PFSYNC_ACT_MAX; i++, a++) {
if (*a || sflag <= 1) {
xo_open_instance(list);
xo_emit("\t\t{e:name}{:count/%ju} {N:/%s%s %s}\n",
pfsyncacts_name[i], (uintmax_t)(*a),
pfsyncacts[i], plural(*a), desc);
xo_close_instance(list);
}
}
xo_close_list(list);
}
void
pfsync_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
{
struct pfsyncstats pfsyncstat;
if (fetch_stats("net.pfsync.stats", off, &pfsyncstat,
sizeof(pfsyncstat), kread) != 0)
return;
xo_emit("{T:/%s}:\n", name);
xo_open_container(name);
#define p(f, m) if (pfsyncstat.f || sflag <= 1) \
xo_emit(m, (uintmax_t)pfsyncstat.f, plural(pfsyncstat.f))
p(pfsyncs_ipackets, "\t{:received-inet-packets/%ju} "
"{N:/packet%s received (IPv4)}\n");
p(pfsyncs_ipackets6, "\t{:received-inet6-packets/%ju} "
"{N:/packet%s received (IPv6)}\n");
pfsync_acts_stats("input-histogram", "received",
&pfsyncstat.pfsyncs_iacts[0]);
p(pfsyncs_badif, "\t\t{:dropped-bad-interface/%ju} "
"{N:/packet%s discarded for bad interface}\n");
p(pfsyncs_badttl, "\t\t{:dropped-bad-ttl/%ju} "
"{N:/packet%s discarded for bad ttl}\n");
p(pfsyncs_hdrops, "\t\t{:dropped-short-header/%ju} "
"{N:/packet%s shorter than header}\n");
p(pfsyncs_badver, "\t\t{:dropped-bad-version/%ju} "
"{N:/packet%s discarded for bad version}\n");
p(pfsyncs_badauth, "\t\t{:dropped-bad-auth/%ju} "
"{N:/packet%s discarded for bad HMAC}\n");
p(pfsyncs_badact,"\t\t{:dropped-bad-action/%ju} "
"{N:/packet%s discarded for bad action}\n");
p(pfsyncs_badlen, "\t\t{:dropped-short/%ju} "
"{N:/packet%s discarded for short packet}\n");
p(pfsyncs_badval, "\t\t{:dropped-bad-values/%ju} "
"{N:/state%s discarded for bad values}\n");
p(pfsyncs_stale, "\t\t{:dropped-stale-state/%ju} "
"{N:/stale state%s}\n");
p(pfsyncs_badstate, "\t\t{:dropped-failed-lookup/%ju} "
"{N:/failed state lookup\\/insert%s}\n");
p(pfsyncs_opackets, "\t{:sent-inet-packets/%ju} "
"{N:/packet%s sent (IPv4})\n");
p(pfsyncs_opackets6, "\t{:send-inet6-packets/%ju} "
"{N:/packet%s sent (IPv6})\n");
pfsync_acts_stats("output-histogram", "sent",
&pfsyncstat.pfsyncs_oacts[0]);
p(pfsyncs_onomem, "\t\t{:discarded-no-memory/%ju} "
"{N:/failure%s due to mbuf memory error}\n");
p(pfsyncs_oerrors, "\t\t{:send-errors/%ju} "
"{N:/send error%s}\n");
#undef p
xo_close_container(name);
}
void
pflow_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
{
struct pflowstats pflowstat;
if (fetch_stats("net.pflow.stats", off, &pflowstat,
sizeof(pflowstat), kread) != 0)
return;
xo_emit("{T:/%s}:\n", name);
xo_open_container(name);
#define p(f, m) if (pflowstat.f || sflag <= 1) \
xo_emit(m, (uintmax_t)pflowstat.f, plural(pflowstat.f))
p(pflow_flows, "\t{:flows/%ju} {N:/flow%s sent}\n");
p(pflow_packets, "\t{:packets/%ju} {N:/packet%s sent}\n");
p(pflow_onomem, "\t{:nomem/%ju} "
"{N:/send failed due to mbuf memory error}\n");
p(pflow_oerrors, "\t{:send-error/%ju} {N:/send error}\n");
#undef p
xo_close_container(name);
}
#endif
static void
show_stat(const char *fmt, int width, const char *name,
u_long value, short showvalue, int div1000)
{
const char *lsep, *rsep;
char newfmt[64];
lsep = "";
if (strncmp(fmt, "LS", 2) == 0) {
lsep = " ";
fmt += 2;
}
rsep = " ";
if (strncmp(fmt, "NRS", 3) == 0) {
rsep = "";
fmt += 3;
}
if (showvalue == 0) {
xo_emit("{P:/%s}{D:/%*s}{P:/%s}", lsep, width, "-", rsep);
return;
}
#define maybe_pad(pad) do { \
if (strlen(pad)) { \
snprintf(newfmt, sizeof(newfmt), "{P:%s}", pad); \
xo_emit(newfmt); \
} \
} while (0)
if (hflag) {
char buf[5];
humanize_number(buf, sizeof(buf), (int64_t)value, "",
HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL | \
((div1000) ? HN_DIVISOR_1000 : 0));
maybe_pad(lsep);
snprintf(newfmt, sizeof(newfmt), "{:%s/%%%ds}", name, width);
xo_emit(newfmt, buf);
maybe_pad(rsep);
} else {
maybe_pad(lsep);
snprintf(newfmt, sizeof(newfmt), "{:%s/%%%d%s}",
name, width, fmt);
xo_emit(newfmt, value);
maybe_pad(rsep);
}
}
static struct ifmaddrs *
next_ifma(struct ifmaddrs *ifma, const char *name, const sa_family_t family)
{
for(; ifma != NULL; ifma = ifma->ifma_next) {
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)ifma->ifma_name;
if (ifma->ifma_addr->sa_family == family &&
sdl->sdl_nlen == strlen(name) &&
strncmp(sdl->sdl_data, name, sdl->sdl_nlen) == 0)
break;
}
return (ifma);
}
enum process_op { MEASURE, EMIT };
static void
process_ifa_addr(enum process_op op, struct ifaddrs *ifa, int *max_net_len,
int *max_addr_len, bool *network, bool *link)
{
int net_len, addr_len;
const char *nn, *rn;
if (op == EMIT) {
net_len = *max_net_len;
addr_len = *max_addr_len;
}
switch (ifa->ifa_addr->sa_family) {
case AF_UNSPEC:
if (op == MEASURE) {
net_len = strlen("none");
addr_len = strlen("none");
} else {
xo_emit("{:network/%-*.*s} ", net_len, net_len,
"none");
xo_emit("{:address/%-*.*s} ", addr_len, addr_len,
"none");
}
break;
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
nn = netname(ifa->ifa_addr, ifa->ifa_netmask);
rn = routename(ifa->ifa_addr, numeric_addr);
if (op == MEASURE) {
net_len = strlen(nn);
addr_len = strlen(rn);
} else {
xo_emit("{t:network/%-*s} ", net_len, nn);
xo_emit("{t:address/%-*s} ", addr_len, rn);
}
if (network != NULL)
*network = true;
break;
case AF_LINK:
{
struct sockaddr_dl *sdl;
char linknum[sizeof("<Link#32767>")];
sdl = (struct sockaddr_dl *)ifa->ifa_addr;
snprintf(linknum, sizeof(linknum), "<Link#%d>", sdl->sdl_index);
if (op == MEASURE) {
net_len = strlen(linknum);
if (sdl->sdl_nlen == 0 &&
sdl->sdl_alen == 0 &&
sdl->sdl_slen == 0)
addr_len = 1;
else
addr_len = strlen(routename(ifa->ifa_addr, 1));
} else {
xo_emit("{t:network/%-*.*s} ", net_len, net_len,
linknum);
if (sdl->sdl_nlen == 0 &&
sdl->sdl_alen == 0 &&
sdl->sdl_slen == 0)
xo_emit("{P:/%*s} ", addr_len, "");
else
xo_emit("{t:address/%-*.*s} ", addr_len,
addr_len, routename(ifa->ifa_addr, 1));
}
if (link != NULL)
*link = true;
break;
}
}
if (op == MEASURE) {
if (net_len > *max_net_len)
*max_net_len = net_len;
if (addr_len > *max_addr_len)
*max_addr_len = addr_len;
}
}
static int
max_num_len(int max_len, u_long num)
{
int len = 2;
for (; num > 10; len++)
num /= 10;
return (MAX(max_len, len));
}
void
intpr(void (*pfunc)(char *), int af)
{
struct ifaddrs *ifap, *ifa;
struct ifmaddrs *ifmap, *ifma;
u_int ifn_len_max = 5, ifn_len;
u_int net_len = strlen("Network "), addr_len = strlen("Address ");
u_int npkt_len = 8, nbyte_len = 10, nerr_len = 5;
if (interval)
return (sidewaysintpr());
if (getifaddrs(&ifap) != 0)
xo_err(EX_OSERR, "getifaddrs");
if (aflag && getifmaddrs(&ifmap) != 0)
xo_err(EX_OSERR, "getifmaddrs");
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (interface != NULL &&
strcmp(ifa->ifa_name, interface) != 0)
continue;
if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
continue;
ifn_len = strlen(ifa->ifa_name);
if ((ifa->ifa_flags & IFF_UP) == 0)
++ifn_len;
ifn_len_max = MAX(ifn_len_max, ifn_len);
process_ifa_addr(MEASURE, ifa, &net_len, &addr_len,
NULL, NULL);
#define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
if (!hflag) {
npkt_len = max_num_len(npkt_len, IFA_STAT(ipackets));
npkt_len = max_num_len(npkt_len, IFA_STAT(opackets));
nerr_len = max_num_len(nerr_len, IFA_STAT(ierrors));
nerr_len = max_num_len(nerr_len, IFA_STAT(iqdrops));
nerr_len = max_num_len(nerr_len, IFA_STAT(collisions));
if (dflag)
nerr_len = max_num_len(nerr_len,
IFA_STAT(oqdrops));
if (bflag) {
nbyte_len = max_num_len(nbyte_len,
IFA_STAT(ibytes));
nbyte_len = max_num_len(nbyte_len,
IFA_STAT(obytes));
}
}
}
xo_open_list("interface");
if (!pfunc) {
xo_emit("{T:/%-*.*s}", ifn_len_max, ifn_len_max, "Name");
xo_emit(" {T:/%5.5s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
"{T:/%*.*s} {T:/%*.*s}",
"Mtu", net_len, net_len, "Network", addr_len, addr_len,
"Address", npkt_len, npkt_len, "Ipkts",
nerr_len, nerr_len, "Ierrs", nerr_len, nerr_len, "Idrop");
if (bflag)
xo_emit(" {T:/%*.*s}", nbyte_len, nbyte_len, "Ibytes");
xo_emit(" {T:/%*.*s} {T:/%*.*s}", npkt_len, npkt_len, "Opkts",
nerr_len, nerr_len, "Oerrs");
if (bflag)
xo_emit(" {T:/%*.*s}", nbyte_len, nbyte_len, "Obytes");
xo_emit(" {T:/%*s}", nerr_len, "Coll");
if (dflag)
xo_emit(" {T:/%*.*s}", nerr_len, nerr_len, "Drop");
xo_emit("\n");
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
bool network = false, link = false;
char *name, *xname, buf[IFNAMSIZ+1];
if (interface != NULL && strcmp(ifa->ifa_name, interface) != 0)
continue;
name = ifa->ifa_name;
if (pfunc) {
(*pfunc)(name);
while(ifa->ifa_next != NULL &&
(strcmp(ifa->ifa_next->ifa_name, name) == 0)) {
ifa = ifa->ifa_next;
}
continue;
}
if (af != AF_UNSPEC && ifa->ifa_addr->sa_family != af)
continue;
xo_open_instance("interface");
if ((ifa->ifa_flags & IFF_UP) == 0) {
xname = stpcpy(buf, name);
*xname++ = '*';
*xname = '\0';
xname = buf;
} else
xname = name;
xo_emit("{d:/%-*.*s}{etk:name}{eq:flags/0x%x}",
ifn_len_max, ifn_len_max, xname, name, ifa->ifa_flags);
#define IFA_MTU(ifa) (((struct if_data *)(ifa)->ifa_data)->ifi_mtu)
show_stat("lu", 6, "mtu", IFA_MTU(ifa), IFA_MTU(ifa), 0);
#undef IFA_MTU
process_ifa_addr(EMIT, ifa, &net_len, &addr_len,
&network, &link);
show_stat("lu", npkt_len, "received-packets",
IFA_STAT(ipackets), link|network, 1);
show_stat("lu", nerr_len, "received-errors", IFA_STAT(ierrors),
link, 1);
show_stat("lu", nerr_len, "dropped-packets", IFA_STAT(iqdrops),
link, 1);
if (bflag)
show_stat("lu", nbyte_len, "received-bytes",
IFA_STAT(ibytes), link|network, 0);
show_stat("lu", npkt_len, "sent-packets", IFA_STAT(opackets),
link|network, 1);
show_stat("lu", nerr_len, "send-errors", IFA_STAT(oerrors),
link, 1);
if (bflag)
show_stat("lu", nbyte_len, "sent-bytes",
IFA_STAT(obytes), link|network, 0);
show_stat("NRSlu", nerr_len, "collisions", IFA_STAT(collisions),
link, 1);
if (dflag)
show_stat("LSlu", nerr_len, "dropped-packets",
IFA_STAT(oqdrops), link, 1);
xo_emit("\n");
if (!aflag) {
xo_close_instance("interface");
continue;
}
xo_open_list("multicast-address");
for (ifma = next_ifma(ifmap, ifa->ifa_name,
ifa->ifa_addr->sa_family);
ifma != NULL;
ifma = next_ifma(ifma, ifa->ifa_name,
ifa->ifa_addr->sa_family)) {
const char *fmt = NULL;
xo_open_instance("multicast-address");
switch (ifma->ifma_addr->sa_family) {
case AF_LINK:
{
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)ifma->ifma_addr;
if (sdl->sdl_type != IFT_ETHER &&
sdl->sdl_type != IFT_FDDI)
break;
}
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
fmt = routename(ifma->ifma_addr, numeric_addr);
break;
}
if (fmt) {
if (Wflag)
xo_emit("{P:/%27s }"
"{t:address/%-17s/}", "", fmt);
else
xo_emit("{P:/%25s }"
"{t:address/%-17.17s/}", "", fmt);
if (ifma->ifma_addr->sa_family == AF_LINK) {
xo_emit(" {:received-packets/%8lu}",
IFA_STAT(imcasts));
xo_emit("{P:/%*s}", bflag? 17 : 6, "");
xo_emit(" {:sent-packets/%8lu}",
IFA_STAT(omcasts));
}
xo_emit("\n");
}
xo_close_instance("multicast-address");
ifma = ifma->ifma_next;
}
xo_close_list("multicast-address");
xo_close_instance("interface");
}
xo_close_list("interface");
freeifaddrs(ifap);
if (aflag)
freeifmaddrs(ifmap);
}
struct iftot {
u_long ift_ip;
u_long ift_ie;
u_long ift_id;
u_long ift_op;
u_long ift_oe;
u_long ift_od;
u_long ift_co;
u_long ift_ib;
u_long ift_ob;
};
static void
fill_iftot(struct iftot *st)
{
struct ifaddrs *ifap, *ifa;
bool found = false;
if (getifaddrs(&ifap) != 0)
xo_err(EX_OSERR, "getifaddrs");
bzero(st, sizeof(*st));
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_LINK)
continue;
if (interface) {
if (strcmp(ifa->ifa_name, interface) == 0)
found = true;
else
continue;
}
st->ift_ip += IFA_STAT(ipackets);
st->ift_ie += IFA_STAT(ierrors);
st->ift_id += IFA_STAT(iqdrops);
st->ift_ib += IFA_STAT(ibytes);
st->ift_op += IFA_STAT(opackets);
st->ift_oe += IFA_STAT(oerrors);
st->ift_od += IFA_STAT(oqdrops);
st->ift_ob += IFA_STAT(obytes);
st->ift_co += IFA_STAT(collisions);
}
if (interface && found == false)
xo_err(EX_DATAERR, "interface %s not found", interface);
freeifaddrs(ifap);
}
static sig_atomic_t signalled;
static void
catchalarm(int signo __unused)
{
signalled = true;
}
static void
running_stats_banner(void)
{
xo_emit("{T:/%17s} {T:/%14s} {T:/%16s}\n", "input",
interface != NULL ? interface : "(Total)", "output");
xo_emit("{T:/%10s} {T:/%5s} {T:/%5s} {T:/%10s} {T:/%10s} {T:/%5s} "
"{T:/%10s} {T:/%5s}",
"packets", "errs", "idrops", "bytes", "packets", "errs", "bytes",
"colls");
if (dflag)
xo_emit(" {T:/%5.5s}", "drops");
xo_emit("\n");
xo_flush();
}
static void
sidewaysintpr(void)
{
struct iftot ift[2], *new, *old;
struct itimerval interval_it;
int oldmask, line;
new = &ift[0];
old = &ift[1];
fill_iftot(old);
(void)signal(SIGALRM, catchalarm);
signalled = false;
interval_it.it_interval.tv_sec = interval;
interval_it.it_interval.tv_usec = 0;
interval_it.it_value = interval_it.it_interval;
setitimer(ITIMER_REAL, &interval_it, NULL);
xo_open_list("interface-statistics");
running_stats_banner();
line = 0;
for (;;) {
oldmask = sigblock(sigmask(SIGALRM));
while (!signalled)
sigpause(0);
signalled = false;
sigsetmask(oldmask);
fill_iftot(new);
if (line == 21) {
running_stats_banner();
line = 0;
}
++line;
xo_open_instance("stats");
show_stat("lu", 10, "received-packets",
new->ift_ip - old->ift_ip, 1, 1);
show_stat("lu", 5, "received-errors",
new->ift_ie - old->ift_ie, 1, 1);
show_stat("lu", 5, "dropped-packets",
new->ift_id - old->ift_id, 1, 1);
show_stat("lu", 10, "received-bytes",
new->ift_ib - old->ift_ib, 1, 0);
show_stat("lu", 10, "sent-packets",
new->ift_op - old->ift_op, 1, 1);
show_stat("lu", 5, "send-errors",
new->ift_oe - old->ift_oe, 1, 1);
show_stat("lu", 10, "sent-bytes",
new->ift_ob - old->ift_ob, 1, 0);
show_stat("NRSlu", 5, "collisions",
new->ift_co - old->ift_co, 1, 1);
if (dflag)
show_stat("LSlu", 5, "dropped-packets",
new->ift_od - old->ift_od, 1, 1);
xo_close_instance("stats");
xo_emit("\n");
xo_flush();
if ((noutputs != 0) && (--noutputs == 0)) {
xo_close_list("interface-statistics");
return;
}
if (new == &ift[0]) {
new = &ift[1];
old = &ift[0];
} else {
new = &ift[0];
old = &ift[1];
}
}
}