NetBSD/usr.bin/netstat/show.c
gdt c9bfbf1142 Move RTF_ANNOUNCE flag so that it no longer conflicts with RTF_PROTO2.
RTF_ANNOUNCE was defined as RTF_PROTO2.  The flag is used to indicated
that host should act as a proxy for a link level arp or ndp request.
(If RTF_PROTO2 is used as an experimental flag (as advertised),
various problems can occur.)

This commit provides a first-class definition with its own bit for
RTF_ANNOUNCE, removes the old aliasing definitions, and adds support
for the new RTF_ANNOUNCE flag to netstat(8) and route(8).,

Also, remove unused RTF_ flags that collide with RTF_PROTO1:
  netinet/icmp6.h defined RTF_PROBEMTU as RTF_PROTO1
  netinet/if_inarp.h defined RTF_USETRAILERS as RTF_PROTO1
  (Neither of these flags are used anywhere.  Both have been removed
  to reduce chances of collision with RTF_PROTO1.)

Figuring this out and the diff are the work of Beverly Schwartz of
BBN.

(Passed release build, boot in VM, with no apparently related atf
failures.)

Approved for Public Release, Distribution Unlimited
This material is based upon work supported by the Defense Advanced
Research Projects Agency and Space and Naval Warfare Systems Center,
Pacific, under Contract No. N66001-09-C-2073.
2011-11-11 15:09:32 +00:00

740 lines
16 KiB
C

/* $NetBSD: show.c,v 1.15 2011/11/11 15:09:33 gdt Exp $ */
/* $OpenBSD: show.c,v 1.1 2006/05/27 19:16:37 claudio Exp $ */
/*
* Copyright (c) 1983, 1988, 1993
* The Regents of the University of California. 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.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
#include <sys/param.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/pfvar.h>
#include <net/pfkeyv2.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netmpls/mpls.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "netstat.h"
#include "prog_ops.h"
char *any_ntoa(const struct sockaddr *);
char *link_print(struct sockaddr *);
#define PFKEYV2_CHUNK sizeof(u_int64_t)
/*
* Definitions for showing gateway flags.
*/
struct bits {
int b_mask;
char b_val;
};
static const struct bits bits[] = {
{ RTF_UP, 'U' },
{ RTF_GATEWAY, 'G' },
{ RTF_HOST, 'H' },
{ RTF_REJECT, 'R' },
{ RTF_BLACKHOLE, 'B' },
{ RTF_DYNAMIC, 'D' },
{ RTF_MODIFIED, 'M' },
{ RTF_DONE, 'd' }, /* Completed -- for routing messages only */
{ RTF_MASK, 'm' }, /* Mask Present -- for routing messages only */
{ RTF_CLONING, 'C' },
{ RTF_XRESOLVE, 'X' },
{ RTF_LLINFO, 'L' },
{ RTF_STATIC, 'S' },
{ RTF_PROTO1, '1' },
{ RTF_PROTO2, '2' },
/* { RTF_PROTO3, '3' }, */
{ RTF_CLONED, 'c' },
/* { RTF_JUMBO, 'J' }, */
{ RTF_ANNOUNCE, 'p' },
{ 0, 0 }
};
void pr_rthdr(int, int);
void p_rtentry(struct rt_msghdr *);
void pr_family(int);
void p_sockaddr(struct sockaddr *, struct sockaddr *, int, int);
char *routename4(in_addr_t);
char *routename6(struct sockaddr_in6 *);
static void p_tag(const struct sockaddr *sa);
/*
* Print routing tables.
*/
void
p_rttables(int paf)
{
struct rt_msghdr *rtm;
char *buf = NULL, *next, *lim = NULL;
size_t needed;
int mib[6];
struct sockaddr *sa;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = paf;
mib[4] = NET_RT_DUMP;
mib[5] = 0;
if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
err(1, "route-sysctl-estimate");
if (needed > 0) {
if ((buf = malloc(needed)) == 0)
err(1, NULL);
if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
err(1, "sysctl of routing table");
lim = buf + needed;
}
printf("Routing tables\n");
if (buf) {
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
sa = (struct sockaddr *)(rtm + 1);
if (paf != AF_UNSPEC && sa->sa_family != paf)
continue;
p_rtentry(rtm);
}
free(buf);
buf = NULL;
}
if (paf != 0 && paf != PF_KEY)
return;
#if 0 /* XXX-elad */
mib[0] = CTL_NET;
mib[1] = PF_KEY;
mib[2] = PF_KEY_V2;
mib[3] = NET_KEY_SPD_DUMP;
mib[4] = mib[5] = 0;
if (prog_sysctl(mib, 4, NULL, &needed, NULL, 0) == -1) {
if (errno == ENOPROTOOPT)
return;
err(1, "spd-sysctl-estimate");
}
if (needed > 0) {
if ((buf = malloc(needed)) == 0)
err(1, NULL);
if (prog_sysctl(mib, 4, buf, &needed, NULL, 0) == -1)
err(1,"sysctl of spd");
lim = buf + needed;
}
if (buf) {
printf("\nEncap:\n");
for (next = buf; next < lim; next += msg->sadb_msg_len *
PFKEYV2_CHUNK) {
msg = (struct sadb_msg *)next;
if (msg->sadb_msg_len == 0)
break;
p_pfkentry(msg);
}
free(buf);
buf = NULL;
}
#endif /* 0 */
}
/*
* column widths; each followed by one space
* width of destination/gateway column
* strlen("fe80::aaaa:bbbb:cccc:dddd@gif0") == 30, strlen("/128") == 4
*/
#define WID_DST(af) ((af) == AF_INET6 ? (nflag ? 34 : 18) : 18)
#define WID_GW(af) ((af) == AF_INET6 ? (nflag ? 30 : 18) : 18)
/*
* Print header for routing table columns.
*/
void
pr_rthdr(int paf, int pAflag)
{
if (pAflag)
printf("%-*.*s ", PLEN, PLEN, "Address");
if (paf != PF_KEY) {
if (tagflag == 1)
printf("%-*.*s %-*.*s %-6.6s %6.6s %8.8s %6.6s %7.7s"
" %s\n", WID_DST(paf), WID_DST(paf), "Destination",
WID_GW(paf), WID_GW(paf), "Gateway",
"Flags", "Refs", "Use", "Mtu", "Tag", "Interface");
else
printf("%-*.*s %-*.*s %-6.6s %6.6s %8.8s %6.6s %s\n",
WID_DST(paf), WID_DST(paf), "Destination",
WID_GW(paf), WID_GW(paf), "Gateway",
"Flags", "Refs", "Use", "Mtu", "Interface");
} else
printf("%-18s %-5s %-18s %-5s %-5s %-22s\n",
"Source", "Port", "Destination",
"Port", "Proto", "SA(Address/Proto/Type/Direction)");
}
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 *)((char *)(sa) +
RT_ROUNDUP(sa->sa_len));
} else
rti_info[i] = NULL;
}
}
/*
* Print a routing table entry.
*/
void
p_rtentry(struct rt_msghdr *rtm)
{
static int old_af = -1;
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
struct sockaddr *mask, *rti_info[RTAX_MAX];
char ifbuf[IF_NAMESIZE];
if (old_af != sa->sa_family) {
old_af = sa->sa_family;
pr_family(sa->sa_family);
pr_rthdr(sa->sa_family, 0);
}
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
mask = rti_info[RTAX_NETMASK];
if ((sa = rti_info[RTAX_DST]) == NULL)
return;
p_sockaddr(sa, mask, rtm->rtm_flags, WID_DST(sa->sa_family));
p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST,
WID_GW(sa->sa_family));
p_flags(rtm->rtm_flags, "%-6.6s ");
#if 0 /* XXX-elad */
printf("%6d %8"PRId64" ", (int)rtm->rtm_rmx.rmx_refcnt,
rtm->rtm_rmx.rmx_pksent);
#else
printf("%6s %8s ", "-", "-");
#endif
if (rtm->rtm_rmx.rmx_mtu)
printf("%6"PRId64, rtm->rtm_rmx.rmx_mtu);
else
printf("%6s", "-");
putchar((rtm->rtm_rmx.rmx_locks & RTV_MTU) ? 'L' : ' ');
if (tagflag == 1)
p_tag(rti_info[RTAX_TAG]);
printf(" %.16s", if_indextoname(rtm->rtm_index, ifbuf));
putchar('\n');
}
/*
* Print address family header before a section of the routing table.
*/
void
pr_family(int paf)
{
const char *afname;
switch (paf) {
case AF_INET:
afname = "Internet";
break;
case AF_INET6:
afname = "Internet6";
break;
case PF_KEY:
afname = "Encap";
break;
case AF_APPLETALK:
afname = "AppleTalk";
break;
case AF_MPLS:
afname = "MPLS";
break;
default:
afname = NULL;
break;
}
if (afname)
printf("\n%s:\n", afname);
else
printf("\nProtocol Family %d:\n", paf);
}
void
p_addr(struct sockaddr *sa, struct sockaddr *mask, int flags)
{
p_sockaddr(sa, mask, flags, WID_DST(sa->sa_family));
}
void
p_gwaddr(struct sockaddr *sa, int gwaf)
{
p_sockaddr(sa, 0, RTF_HOST, WID_GW(gwaf));
}
void
p_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags, int width)
{
char *cp;
switch (sa->sa_family) {
case AF_INET6:
{
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
struct in6_addr *in6 = &sa6->sin6_addr;
/*
* XXX: This is a special workaround for KAME kernels.
* sin6_scope_id field of SA should be set in the future.
*/
if (IN6_IS_ADDR_LINKLOCAL(in6) ||
IN6_IS_ADDR_MC_LINKLOCAL(in6)) {
/* XXX: override is ok? */
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 = routename((struct sockaddr *)sa6);
else
cp = netname((struct sockaddr *)sa6, mask);
break;
}
default:
if ((flags & RTF_HOST) || mask == NULL)
cp = routename(sa);
else
cp = netname(sa, mask);
break;
}
if (width < 0)
printf("%s", cp);
else {
if (nflag)
printf("%-*s ", width, cp);
else
printf("%-*.*s ", width, width, cp);
}
}
void
p_flags(int f, const char *format)
{
char name[33], *flags;
const struct bits *p = bits;
for (flags = name; p->b_mask && flags < &name[sizeof(name) - 2]; p++)
if (p->b_mask & f)
*flags++ = p->b_val;
*flags = '\0';
printf(format, name);
}
static void
p_tag(const struct sockaddr *sa)
{
char *line;
if (sa == NULL || sa->sa_family != AF_MPLS) {
printf("%7s", "-");
return;
}
line = mpls_ntoa(sa);
if (strlen(line) < 7)
printf("%7s", line);
else
printf("%s", line);
}
static char line[MAXHOSTNAMELEN];
static char domain[MAXHOSTNAMELEN];
char *
routename(struct sockaddr *sa)
{
char *cp = NULL;
static int first = 1;
if (first) {
first = 0;
if (gethostname(domain, sizeof(domain)) == 0 &&
(cp = strchr(domain, '.')))
(void)strlcpy(domain, cp + 1, sizeof(domain));
else
domain[0] = '\0';
cp = NULL;
}
if (sa->sa_len == 0) {
(void)strlcpy(line, "default", sizeof(line));
return (line);
}
switch (sa->sa_family) {
case AF_INET:
return
(routename4(((struct sockaddr_in *)sa)->sin_addr.s_addr));
case AF_INET6:
{
struct sockaddr_in6 sin6;
memset(&sin6, 0, sizeof(sin6));
memcpy(&sin6, sa, sa->sa_len);
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
if (sa->sa_len == sizeof(struct sockaddr_in6) &&
(IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
sin6.sin6_scope_id == 0) {
sin6.sin6_scope_id =
ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
sin6.sin6_addr.s6_addr[2] = 0;
sin6.sin6_addr.s6_addr[3] = 0;
}
return (routename6(&sin6));
}
case AF_LINK:
return (link_print(sa));
case AF_MPLS:
return mpls_ntoa(sa);
#if 0 /* XXX-elad */
case AF_UNSPEC:
if (sa->sa_len == sizeof(struct sockaddr_rtlabel)) {
static char name[RTLABEL_LEN];
struct sockaddr_rtlabel *sr;
sr = (struct sockaddr_rtlabel *)sa;
strlcpy(name, sr->sr_label, sizeof(name));
return (name);
}
/* FALLTHROUGH */
#endif
default:
(void)snprintf(line, sizeof(line), "(%d) %s",
sa->sa_family, any_ntoa(sa));
break;
}
return (line);
}
char *
routename4(in_addr_t in)
{
const char *cp = NULL;
struct in_addr ina;
struct hostent *hp;
if (in == INADDR_ANY)
cp = "default";
if (!cp && !nflag) {
if ((hp = gethostbyaddr((char *)&in,
sizeof(in), AF_INET)) != NULL) {
char *p;
if ((p = strchr(hp->h_name, '.')) &&
!strcmp(p + 1, domain))
*p = '\0';
cp = hp->h_name;
}
}
ina.s_addr = in;
strlcpy(line, cp ? cp : inet_ntoa(ina), sizeof(line));
return (line);
}
char *
routename6(struct sockaddr_in6 *sin6)
{
int niflags = 0;
if (nflag)
niflags |= NI_NUMERICHOST;
else
niflags |= NI_NOFQDN;
if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
line, sizeof(line), NULL, 0, niflags) != 0)
strncpy(line, "invalid", sizeof(line));
return (line);
}
/*
* Return the name of the network whose address is given.
* The address is assumed to be that of a net or subnet, not a host.
*/
char *
netname4(in_addr_t in, in_addr_t mask)
{
const char *cp = NULL;
struct netent *np = NULL;
int mbits;
in = ntohl(in);
mask = ntohl(mask);
if (!nflag && in != INADDR_ANY) {
if ((np = getnetbyaddr(in, AF_INET)) != NULL)
cp = np->n_name;
}
mbits = mask ? 33 - ffs(mask) : 0;
if (in == INADDR_ANY && !mbits)
cp = "default";
if (cp)
strlcpy(line, cp, sizeof(line));
#define C(x) ((x) & 0xff)
else if (mbits < 9)
snprintf(line, sizeof(line), "%u/%d", C(in >> 24), mbits);
else if (mbits < 17)
snprintf(line, sizeof(line), "%u.%u/%d",
C(in >> 24) , C(in >> 16), mbits);
else if (mbits < 25)
snprintf(line, sizeof(line), "%u.%u.%u/%d",
C(in >> 24), C(in >> 16), C(in >> 8), mbits);
else
snprintf(line, sizeof(line), "%u.%u.%u.%u/%d", C(in >> 24),
C(in >> 16), C(in >> 8), C(in), mbits);
#undef C
return (line);
}
#ifdef INET6
char *
netname6(struct sockaddr_in6 *sa6, struct sockaddr_in6 *mask)
{
struct sockaddr_in6 sin6;
u_char *p;
int masklen, final = 0, illegal = 0;
int i, lim, flag, error;
char hbuf[NI_MAXHOST];
sin6 = *sa6;
flag = 0;
masklen = 0;
if (mask) {
lim = mask->sin6_len - offsetof(struct sockaddr_in6, sin6_addr);
if (lim < 0)
lim = 0;
else if (lim > (int)sizeof(struct in6_addr))
lim = sizeof(struct in6_addr);
for (p = (u_char *)&mask->sin6_addr, i = 0; i < lim; p++) {
if (final && *p) {
illegal++;
sin6.sin6_addr.s6_addr[i++] = 0x00;
continue;
}
switch (*p & 0xff) {
case 0xff:
masklen += 8;
break;
case 0xfe:
masklen += 7;
final++;
break;
case 0xfc:
masklen += 6;
final++;
break;
case 0xf8:
masklen += 5;
final++;
break;
case 0xf0:
masklen += 4;
final++;
break;
case 0xe0:
masklen += 3;
final++;
break;
case 0xc0:
masklen += 2;
final++;
break;
case 0x80:
masklen += 1;
final++;
break;
case 0x00:
final++;
break;
default:
final++;
illegal++;
break;
}
if (!illegal)
sin6.sin6_addr.s6_addr[i++] &= *p;
else
sin6.sin6_addr.s6_addr[i++] = 0x00;
}
while (i < (int)sizeof(struct in6_addr))
sin6.sin6_addr.s6_addr[i++] = 0x00;
} else
masklen = 128;
if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr)) {
snprintf(line, sizeof(line), "default");
return (line);
}
if (illegal)
warnx("illegal prefixlen");
if (nflag)
flag |= NI_NUMERICHOST;
error = getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
hbuf, sizeof(hbuf), NULL, 0, flag);
if (error)
snprintf(hbuf, sizeof(hbuf), "invalid");
snprintf(line, sizeof(line), "%s/%d", hbuf, masklen);
return (line);
}
#endif
/*
* Return the name of the network whose address is given.
* The address is assumed to be that of a net or subnet, not a host.
*/
char *
netname(struct sockaddr *sa, struct sockaddr *mask)
{
switch (sa->sa_family) {
case AF_INET:
return netname4(((struct sockaddr_in *)sa)->sin_addr.s_addr,
((struct sockaddr_in *)mask)->sin_addr.s_addr);
#ifdef INET6
case AF_INET6:
return netname6((struct sockaddr_in6 *)sa,
(struct sockaddr_in6 *)mask);
#endif
case AF_LINK:
return (link_print(sa));
default:
snprintf(line, sizeof(line), "af %d: %s",
sa->sa_family, any_ntoa(sa));
break;
}
return (line);
}
static const char hexlist[] = "0123456789abcdef";
char *
any_ntoa(const struct sockaddr *sa)
{
static char obuf[240];
const char *in = sa->sa_data;
char *out = obuf;
int len = sa->sa_len - offsetof(struct sockaddr, sa_data);
*out++ = 'Q';
do {
*out++ = hexlist[(*in >> 4) & 15];
*out++ = hexlist[(*in++) & 15];
*out++ = '.';
} while (--len > 0 && (out + 3) < &obuf[sizeof(obuf) - 1]);
out[-1] = '\0';
return (obuf);
}
char *
link_print(struct sockaddr *sa)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
u_char *lla = (u_char *)sdl->sdl_data + sdl->sdl_nlen;
if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
sdl->sdl_slen == 0) {
(void)snprintf(line, sizeof(line), "link#%d", sdl->sdl_index);
return (line);
}
switch (sdl->sdl_type) {
case IFT_ETHER:
case IFT_CARP:
return (ether_ntoa((struct ether_addr *)lla));
default:
return (link_ntoa(sdl));
}
}
char *
mpls_ntoa(const struct sockaddr *sa)
{
static char obuf[16];
const union mpls_shim *pms;
union mpls_shim ms;
int psize = sizeof(struct sockaddr_mpls);
pms = &((const struct sockaddr_mpls*)sa)->smpls_addr;
ms.s_addr = ntohl(pms->s_addr);
snprintf(obuf, sizeof(obuf), "%u", ms.shim.label);
while(psize < sa->sa_len) {
pms++;
ms.s_addr = ntohl(pms->s_addr);
snprintf(obuf, sizeof(obuf), "%s,%u", obuf,
ms.shim.label);
psize+=sizeof(ms);
}
return obuf;
}