#include <stdint.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/radix.h>
#include <netinet/in.h>
#include <sys/sysctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <time.h>
#include "netstat.h"
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
struct bits {
uint32_t b_mask;
char b_val;
} bits[] = {
{ RTF_UP, 'U' },
{ RTF_GATEWAY, 'G' },
{ RTF_HOST, 'H' },
{ RTF_REJECT, 'R' },
{ RTF_DYNAMIC, 'D' },
{ RTF_MODIFIED, 'M' },
{ RTF_MULTICAST,'m' },
{ RTF_DONE, 'd' },
{ RTF_CLONING, 'C' },
{ RTF_XRESOLVE, 'X' },
{ RTF_LLINFO, 'L' },
{ RTF_STATIC, 'S' },
{ RTF_PROTO1, '1' },
{ RTF_PROTO2, '2' },
{ RTF_WASCLONED,'W' },
{ RTF_PRCLONING,'c' },
{ RTF_PROTO3, '3' },
{ RTF_BLACKHOLE,'B' },
{ RTF_BROADCAST,'b' },
{ RTF_IFSCOPE, 'I' },
#ifdef RTF_IFREF
{ RTF_IFREF, 'i' },
#endif
{ 0 }
};
typedef union {
uint32_t dummy;
struct sockaddr u_sa;
u_short u_data[128];
} sa_u;
static void ntreestuff __P((void));
static void np_rtentry __P((struct rt_msghdr2 *));
static void p_sockaddr __P((struct sockaddr *, struct sockaddr *, int, int));
static void p_flags __P((int, char *));
static uint32_t forgemask __P((uint32_t));
static void domask __P((char *, uint32_t, uint32_t));
void
routepr(uint32_t rtree)
{
printf("Routing tables\n");
if (dflag == 0) {
ntreestuff();
} else {
if (rtree == 0) {
printf("rt_tables: symbol not in namelist\n");
return;
}
}
}
void
pr_family(int af)
{
char *afname;
switch (af) {
case AF_INET:
afname = "Internet";
break;
#ifdef INET6
case AF_INET6:
afname = "Internet6";
break;
#endif
case AF_IPX:
afname = "IPX";
break;
default:
afname = NULL;
break;
}
if (afname)
printf("\n%s:\n", afname);
else
printf("\nProtocol Family %d:\n", af);
}
#ifndef INET6
#define WID_DST(af) 18
#define WID_GW(af) 18
#define WID_IF(af) 7
#else
#define WID_DST(af) \
((af) == AF_INET6 ? (lflag ? 39 : (nflag ? 39: 18)) : 18)
#define WID_GW(af) \
((af) == AF_INET6 ? (lflag ? 31 : (nflag ? 31 : 18)) : 18)
#define WID_IF(af) ((af) == AF_INET6 ? 8 : 7)
#endif
void
pr_rthdr(int af)
{
if (Aflag)
printf("%-8.8s ","Address");
if (af == AF_INET || lflag)
if (lflag)
printf("%-*.*s %-*.*s %-10.10s %6.6s %8.8s %6.6s %*.*s %6s\n",
WID_DST(af), WID_DST(af), "Destination",
WID_GW(af), WID_GW(af), "Gateway",
"Flags", "Refs", "Use", "Mtu",
WID_IF(af), WID_IF(af), "Netif", "Expire");
else
printf("%-*.*s %-*.*s %-10.10s %6.6s %8.8s %*.*s %6s\n",
WID_DST(af), WID_DST(af), "Destination",
WID_GW(af), WID_GW(af), "Gateway",
"Flags", "Refs", "Use",
WID_IF(af), WID_IF(af), "Netif", "Expire");
else
printf("%-*.*s %-*.*s %-10.10s %8.8s %6s\n",
WID_DST(af), WID_DST(af), "Destination",
WID_GW(af), WID_GW(af), "Gateway",
"Flags", "Netif", "Expire");
}
static void
ntreestuff(void)
{
size_t needed;
int mib[6];
char *buf, *next, *lim;
struct rt_msghdr2 *rtm;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_DUMP2;
mib[5] = 0;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
err(1, "sysctl: net.route.0.0.dump estimate");
}
if ((buf = malloc(needed)) == 0) {
err(2, "malloc(%lu)", (unsigned long)needed);
}
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
err(1, "sysctl: net.route.0.0.dump");
}
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr2 *)next;
np_rtentry(rtm);
}
}
static void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (addrs & (1 << i)) {
rti_info[i] = sa;
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
} else {
rti_info[i] = NULL;
}
}
}
static void
np_rtentry(struct rt_msghdr2 *rtm)
{
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
struct sockaddr *rti_info[RTAX_MAX];
static int old_fam;
int fam = 0;
u_short lastindex = 0xffff;
static char ifname[IFNAMSIZ + 1];
sa_u addr, mask;
if ((rtm->rtm_flags & RTF_WASCLONED) &&
(rtm->rtm_parentflags & RTF_PRCLONING) &&
!aflag) {
return;
}
fam = sa->sa_family;
if (af != AF_UNSPEC && af != fam)
return;
if (fam != old_fam) {
pr_family(fam);
pr_rthdr(fam);
old_fam = fam;
}
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
bzero(&addr, sizeof(addr));
if ((rtm->rtm_addrs & RTA_DST))
bcopy(rti_info[RTAX_DST], &addr, rti_info[RTAX_DST]->sa_len);
bzero(&mask, sizeof(mask));
if ((rtm->rtm_addrs & RTA_NETMASK))
bcopy(rti_info[RTAX_NETMASK], &mask, rti_info[RTAX_NETMASK]->sa_len);
p_sockaddr(&addr.u_sa, &mask.u_sa, rtm->rtm_flags,
WID_DST(addr.u_sa.sa_family));
p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST,
WID_GW(addr.u_sa.sa_family));
p_flags(rtm->rtm_flags, "%-10.10s ");
if (addr.u_sa.sa_family == AF_INET || lflag) {
printf("%6u %8u ", rtm->rtm_refcnt, (unsigned int)rtm->rtm_use);
if (lflag) {
if (rtm->rtm_rmx.rmx_mtu != 0)
printf("%6u ", rtm->rtm_rmx.rmx_mtu);
else
printf("%6s ", "");
}
}
if (rtm->rtm_index != lastindex) {
if_indextoname(rtm->rtm_index, ifname);
lastindex = rtm->rtm_index;
}
printf("%*.*s", WID_IF(addr.u_sa.sa_family),
WID_IF(addr.u_sa.sa_family), ifname);
if (rtm->rtm_rmx.rmx_expire) {
time_t expire_time;
if ((expire_time =
rtm->rtm_rmx.rmx_expire - time((time_t *)0)) > 0)
printf(" %6d", (int)expire_time);
}
putchar('\n');
}
static void
p_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags, int width)
{
char workbuf[128], *cplim;
char *cp = workbuf;
switch(sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if ((sin->sin_addr.s_addr == INADDR_ANY) &&
mask &&
(ntohl(((struct sockaddr_in *)mask)->sin_addr.s_addr) == 0L || mask->sa_len == 0))
cp = "default" ;
else if (flags & RTF_HOST)
cp = routename(sin->sin_addr.s_addr);
else if (mask)
cp = netname(sin->sin_addr.s_addr,
ntohl(((struct sockaddr_in *)mask)->
sin_addr.s_addr));
else
cp = netname(sin->sin_addr.s_addr, 0L);
break;
}
#ifdef INET6
case AF_INET6: {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
struct in6_addr *in6 = &sa6->sin6_addr;
if (IN6_IS_ADDR_LINKLOCAL(in6) ||
IN6_IS_ADDR_MC_LINKLOCAL(in6)) {
sa6->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)&in6->s6_addr[2]);
*(u_short *)&in6->s6_addr[2] = 0;
}
if (flags & RTF_HOST)
cp = routename6(sa6);
else if (mask)
cp = netname6(sa6, mask);
else
cp = netname6(sa6, NULL);
break;
}
#endif
case AF_LINK: {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
sdl->sdl_slen == 0) {
(void) sprintf(workbuf, "link#%d", sdl->sdl_index);
} else {
switch (sdl->sdl_type) {
case IFT_ETHER: {
int i;
u_char *lla = (u_char *)sdl->sdl_data +
sdl->sdl_nlen;
cplim = "";
for (i = 0; i < sdl->sdl_alen; i++, lla++) {
cp += sprintf(cp, "%s%x", cplim, *lla);
cplim = ":";
}
cp = workbuf;
break;
}
default:
cp = link_ntoa(sdl);
break;
}
}
break;
}
default: {
u_char *s = (u_char *)sa->sa_data, *slim;
slim = sa->sa_len + (u_char *) sa;
cplim = cp + sizeof(workbuf) - 6;
cp += sprintf(cp, "(%d)", sa->sa_family);
while (s < slim && cp < cplim) {
cp += sprintf(cp, " %02x", *s++);
if (s < slim)
cp += sprintf(cp, "%02x", *s++);
}
cp = workbuf;
}
}
if (width < 0 ) {
printf("%s ", cp);
} else {
if (nflag)
printf("%-*s ", width, cp);
else
printf("%-*.*s ", width, width, cp);
}
}
static void
p_flags(int f, char *format)
{
char name[33], *flags;
struct bits *p = bits;
for (flags = name; p->b_mask; p++)
if (p->b_mask & f)
*flags++ = p->b_val;
*flags = '\0';
printf(format, name);
}
char *
routename(uint32_t in)
{
char *cp;
static char line[MAXHOSTNAMELEN];
struct hostent *hp;
cp = 0;
if (!nflag) {
hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
AF_INET);
if (hp) {
cp = hp->h_name;
}
}
if (cp) {
strncpy(line, cp, sizeof(line) - 1);
line[sizeof(line) - 1] = '\0';
} else {
#define C(x) ((x) & 0xff)
in = ntohl(in);
sprintf(line, "%u.%u.%u.%u",
C(in >> 24), C(in >> 16), C(in >> 8), C(in));
}
return (line);
}
static uint32_t
forgemask(uint32_t a)
{
uint32_t m;
if (IN_CLASSA(a))
m = IN_CLASSA_NET;
else if (IN_CLASSB(a))
m = IN_CLASSB_NET;
else
m = IN_CLASSC_NET;
return (m);
}
static void
domask(char *dst, uint32_t addr, uint32_t mask)
{
int b, i;
if (!mask || (forgemask(addr) == mask)) {
*dst = '\0';
return;
}
i = 0;
for (b = 0; b < 32; b++)
if (mask & (1 << b)) {
int bb;
i = b;
for (bb = b+1; bb < 32; bb++)
if (!(mask & (1 << bb))) {
i = -1;
break;
}
break;
}
if (i == -1)
sprintf(dst, "&0x%x", mask);
else
sprintf(dst, "/%d", 32-i);
}
char *
netname(uint32_t in, uint32_t mask)
{
char *cp = 0;
static char line[MAXHOSTNAMELEN];
struct netent *np = 0;
uint32_t net, omask, dmask;
uint32_t i;
i = ntohl(in);
dmask = forgemask(i);
omask = mask;
if (!nflag && i) {
net = i & dmask;
if (!(np = getnetbyaddr(i, AF_INET)) && net != i)
np = getnetbyaddr(net, AF_INET);
if (np) {
cp = np->n_name;
}
}
if (cp)
strncpy(line, cp, sizeof(line) - 1);
else {
switch (dmask) {
case IN_CLASSA_NET:
if ((i & IN_CLASSA_HOST) == 0) {
sprintf(line, "%u", C(i >> 24));
break;
}
case IN_CLASSB_NET:
if ((i & IN_CLASSB_HOST) == 0) {
sprintf(line, "%u.%u",
C(i >> 24), C(i >> 16));
break;
}
case IN_CLASSC_NET:
if ((i & IN_CLASSC_HOST) == 0) {
sprintf(line, "%u.%u.%u",
C(i >> 24), C(i >> 16), C(i >> 8));
break;
}
default:
sprintf(line, "%u.%u.%u.%u",
C(i >> 24), C(i >> 16), C(i >> 8), C(i));
break;
}
}
domask(line+strlen(line), i, omask);
return (line);
}
#ifdef INET6
char *
netname6(struct sockaddr_in6 *sa6, struct sockaddr *sam)
{
static char line[MAXHOSTNAMELEN];
u_char *lim;
int masklen, illegal = 0, flag = NI_WITHSCOPEID;
struct in6_addr *mask = sam ? &((struct sockaddr_in6 *)sam)->sin6_addr : 0;
if (sam && sam->sa_len == 0) {
masklen = 0;
} else if (mask) {
u_char *p = (u_char *)mask;
for (masklen = 0, lim = p + 16; p < lim; p++) {
switch (*p) {
case 0xff:
masklen += 8;
break;
case 0xfe:
masklen += 7;
break;
case 0xfc:
masklen += 6;
break;
case 0xf8:
masklen += 5;
break;
case 0xf0:
masklen += 4;
break;
case 0xe0:
masklen += 3;
break;
case 0xc0:
masklen += 2;
break;
case 0x80:
masklen += 1;
break;
case 0x00:
break;
default:
illegal ++;
break;
}
}
if (illegal)
fprintf(stderr, "illegal prefixlen\n");
} else {
masklen = 128;
}
if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr))
return("default");
if (nflag)
flag |= NI_NUMERICHOST;
getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line),
NULL, 0, flag);
if (nflag)
sprintf(&line[strlen(line)], "/%d", masklen);
return line;
}
char *
routename6(struct sockaddr_in6 *sa6)
{
static char line[MAXHOSTNAMELEN];
int flag = NI_WITHSCOPEID;
struct sockaddr_in6 sa6_local = {sizeof(sa6_local), AF_INET6, };
sa6_local.sin6_addr = sa6->sin6_addr;
sa6_local.sin6_scope_id = sa6->sin6_scope_id;
if (nflag)
flag |= NI_NUMERICHOST;
getnameinfo((struct sockaddr *)&sa6_local, sa6_local.sin6_len,
line, sizeof(line), NULL, 0, flag);
return line;
}
#endif
void
rt_stats(void)
{
struct rtstat rtstat;
int rttrash;
int mib[6];
size_t len;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_STAT;
mib[5] = 0;
len = sizeof(struct rtstat);
if (sysctl(mib, 6, &rtstat, &len, 0, 0) == -1)
return;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = 0;
mib[4] = NET_RT_TRASH;
mib[5] = 0;
len = sizeof(rttrash);
if (sysctl(mib, 6, &rttrash, &len, 0, 0) == -1)
return;
printf("routing:\n");
#define p(f, m) if (rtstat.f || sflag <= 1) \
printf(m, rtstat.f, plural(rtstat.f))
p(rts_badredirect, "\t%u bad routing redirect%s\n");
p(rts_dynamic, "\t%u dynamically created route%s\n");
p(rts_newgateway, "\t%u new gateway%s due to redirects\n");
p(rts_unreach, "\t%u destination%s found unreachable\n");
p(rts_wildcard, "\t%u use%s of a wildcard route\n");
#undef p
if (rttrash || sflag <= 1)
printf("\t%u route%s not in table but not freed\n",
rttrash, plural(rttrash));
}
void
upHex(char *p0)
{
char *p = p0;
for (; *p; p++)
switch (*p) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
*p += ('A' - 'a');
break;
}
}