Index: head/sbin/route/keywords =================================================================== --- head/sbin/route/keywords (revision 260523) +++ head/sbin/route/keywords (revision 260524) @@ -1,57 +1,59 @@ # @(#)keywords 8.2 (Berkeley) 3/19/94 # $FreeBSD$ +4 +6 add atalk blackhole change cloning del delete dst expire fib flush gateway genmask get host hopcount iface interface ifa ifp inet inet6 iso link llinfo lock lockrest mask monitor mtu net netmask nostatic nostick osi prefixlen proto1 proto2 proto3 proxy recvpipe reject rtt rttvar sa sendpipe show ssthresh static sticky weight x25 xns xresolve Index: head/sbin/route/route.8 =================================================================== --- head/sbin/route/route.8 (revision 260523) +++ head/sbin/route/route.8 (revision 260524) @@ -1,494 +1,512 @@ .\" Copyright (c) 1983, 1991, 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. .\" 4. 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. .\" .\" @(#)route.8 8.3 (Berkeley) 3/19/94 .\" $FreeBSD$ .\" .Dd October 17, 2013 .Dt ROUTE 8 .Os .Sh NAME .Nm route .Nd manually manipulate the routing tables .Sh SYNOPSIS .Nm .Op Fl dnqtv .Ar command .Oo .Op Ar modifiers .Ar args .Oc .Sh DESCRIPTION The .Nm utility is used to manually manipulate the network routing tables. It normally is not needed, as a system routing table management daemon, such as .Xr routed 8 , should tend to this task. .Pp The .Nm utility supports a limited number of general options, but a rich command language, enabling the user to specify any arbitrary request that could be delivered via the programmatic interface discussed in .Xr route 4 . .Pp The following options are available: .Bl -tag -width indent +.It Fl 4 +Specify +.Cm inet +address family as family hint for subcommands. +.It Fl 6 +Specify +.Cm inet +address family as family hint for subcommands. .It Fl d Run in debug-only mode, i.e., do not actually modify the routing table. .It Fl n Bypass attempts to print host and network names symbolically when reporting actions. (The process of translating between symbolic names and numerical equivalents can be quite time consuming, and may require correct operation of the network; thus it may be expedient to forget this, especially when attempting to repair networking operations). .It Fl t Run in test-only mode. .Pa /dev/null is used instead of a socket. .It Fl v (verbose) Print additional details. .It Fl q Suppress all output from the .Cm add , change , delete , and .Cm flush commands. .El .Pp The .Nm utility provides the following commands: .Pp .Bl -tag -width Fl -compact .It Cm add Add a route. .It Cm flush Remove all routes. .It Cm delete Delete a specific route. .It Cm del Another name for the .Cm delete command. .It Cm change Change aspects of a route (such as its gateway). .It Cm get Lookup and display the route for a destination. .It Cm monitor Continuously report any changes to the routing information base, routing lookup misses, or suspected network partitionings. .It Cm show Another name for the .Cm get command. .El .Pp The monitor command has the syntax: .Pp .Bd -ragged -offset indent -compact .Nm .Op Fl n .Cm monitor Op Fl fib Ar number .Ed .Pp The flush command has the syntax: .Pp .Bd -ragged -offset indent -compact .Nm .Oo Fl n Cm flush Oc Oo Ar family Oc Op Fl fib Ar number .Ed .Pp If the .Cm flush command is specified, .Nm will ``flush'' the routing tables of all gateway entries. When the address family may is specified by any of the .Fl osi , .Fl xns , .Fl atalk , .Fl inet6 , +.Fl 6, +.Fl inet, or -.Fl inet +.Fl 4 modifiers, only routes having destinations with addresses in the -delineated family will be deleted. +delineated family will be deleted. Additionally, +.Fl 4 +or +.Fl 6 +can be used as aliases for +.Fl inet +and +.Fl inet6 +modifiers. When a .Fl fib option is specified, the operation will be applied to the specified FIB .Pq routing table . .Pp The other commands have the following syntax: .Pp .Bd -ragged -offset indent -compact .Nm .Op Fl n .Ar command .Op Fl net No \&| Fl host .Ar destination gateway .Op Ar netmask .Op Fl fib Ar number .Ed .Pp where .Ar destination is the destination host or network, .Ar gateway is the next-hop intermediary via which packets should be routed. Routes to a particular host may be distinguished from those to a network by interpreting the Internet address specified as the .Ar destination argument. The optional modifiers .Fl net and .Fl host force the destination to be interpreted as a network or a host, respectively. Otherwise, if the .Ar destination has a .Dq local address part of INADDR_ANY .Pq Li 0.0.0.0 , or if the .Ar destination is the symbolic name of a network, then the route is assumed to be to a network; otherwise, it is presumed to be a route to a host. Optionally, the .Ar destination could also be specified in the .Ar net Ns / Ns Ar bits format. .Pp For example, .Li 128.32 is interpreted as .Fl host Li 128.0.0.32 ; .Li 128.32.130 is interpreted as .Fl host Li 128.32.0.130 ; .Fl net Li 128.32 is interpreted as .Li 128.32.0.0; .Fl net Li 128.32.130 is interpreted as .Li 128.32.130.0; and .Li 192.168.64/20 is interpreted as .Fl net Li 192.168.64 Fl netmask Li 255.255.240.0 . .Pp A .Ar destination of .Ar default is a synonym for the default route. For .Li IPv4 it is .Fl net Fl inet Li 0.0.0.0 , and for .Li IPv6 it is .Fl net Fl inet6 Li :: . .Pp If the destination is directly reachable via an interface requiring no intermediary system to act as a gateway, the .Fl interface modifier should be specified; the gateway given is the address of this host on the common network, indicating the interface to be used for transmission. Alternately, if the interface is point to point the name of the interface itself may be given, in which case the route remains valid even if the local or remote addresses change. .Pp The optional modifiers .Fl xns , .Fl osi , .Fl atalk , and .Fl link specify that all subsequent addresses are in the .Tn XNS , .Tn OSI , or .Tn AppleTalk address families, or are specified as link-level addresses, and the names must be numeric specifications rather than symbolic names. .Pp The optional .Fl netmask modifier is intended to achieve the effect of an .Tn OSI .Tn ESIS redirect with the netmask option, or to manually add subnet routes with netmasks different from that of the implied network interface (as would otherwise be communicated using the OSPF or ISIS routing protocols). One specifies an additional ensuing address parameter (to be interpreted as a network mask). The implicit network mask generated in the AF_INET case can be overridden by making sure this option follows the destination parameter. .Pp For .Dv AF_INET6 , the .Fl prefixlen qualifier is available instead of the .Fl mask qualifier because non-continuous masks are not allowed in IPv6. For example, .Fl prefixlen Li 32 specifies network mask of .Li ffff:ffff:0000:0000:0000:0000:0000:0000 to be used. The default value of prefixlen is 64 to get along with the aggregatable address. But 0 is assumed if .Cm default is specified. Note that the qualifier works only for .Dv AF_INET6 address family. .Pp Routes have associated flags which influence operation of the protocols when sending to destinations matched by the routes. These flags may be set (or sometimes cleared) by indicating the following corresponding modifiers: .Bd -literal -xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) -iface ~RTF_GATEWAY - destination is directly reachable -static RTF_STATIC - manually added route -nostatic ~RTF_STATIC - pretend route added by kernel or daemon -reject RTF_REJECT - emit an ICMP unreachable when matched -blackhole RTF_BLACKHOLE - silently discard pkts (during updates) -proto1 RTF_PROTO1 - set protocol specific routing flag #1 -proto2 RTF_PROTO2 - set protocol specific routing flag #2 -proto3 RTF_PROTO3 - set protocol specific routing flag #3 .Ed .Pp The optional modifiers .Fl rtt , .Fl rttvar , .Fl sendpipe , .Fl recvpipe , .Fl mtu , .Fl hopcount , .Fl expire , and .Fl ssthresh provide initial values to quantities maintained in the routing entry by transport level protocols, such as TCP or TP4. These may be individually locked by preceding each such modifier to be locked by the .Fl lock meta-modifier, or one can specify that all ensuing metrics may be locked by the .Fl lockrest meta-modifier. .Pp Note that .Fl expire accepts expiration time of the route as the number of seconds since the Epoch .Pq see Xr time 3 . When the first character of the number is .Dq + or .Dq - , it is interpreted as a value relative to the current time. .Pp The optional modifier .Fl fib Ar number specifies that the command will be applied to a non-default FIB. The .Ar number must be smaller than the .Va net.fibs .Xr sysctl 8 MIB. When this modifier is not specified, or a negative number is specified, the default FIB shown in the .Va net.my_fibnum .Xr sysctl 8 MIB will be used. .Pp The .Ar number allows multiple FIBs by a comma-separeted list and/or range specification. The .Qq Fl fib Li 2,4,6 means the FIB number 2, 4, and 6. The .Qq Fl fib Li 1,3-5,6 means the 1, 3, 4, 5, and 6. .Pp In a .Cm change or .Cm add command where the destination and gateway are not sufficient to specify the route (as in the .Tn ISO case where several interfaces may have the same address), the .Fl ifp or .Fl ifa modifiers may be used to determine the interface or interface address. .Pp All symbolic names specified for a .Ar destination or .Ar gateway are looked up first as a host name using .Xr gethostbyname 3 . If this lookup fails, .Xr getnetbyname 3 is then used to interpret the name as that of a network. .Pp The .Nm utility uses a routing socket and the new message types .Dv RTM_ADD , RTM_DELETE , RTM_GET , and .Dv RTM_CHANGE . As such, only the super-user may modify the routing tables. .Sh EXIT STATUS .Ex -std .Sh EXAMPLES Add a default route to the network routing table. This will send all packets for destinations not available in the routing table to the default gateway at 192.168.1.1: .Pp .Dl route add -net 0.0.0.0/0 192.168.1.1 .Pp A shorter version of adding a default route can also be written as: .Pp .Dl route add default 192.168.1.1 .Pp Add a static route to the 172.16.10.0/24 network via the 172.16.1.1 gateway: .Pp .Dl route add -net 172.16.10.0/24 172.16.1.1 .Pp Change the gateway of an already established static route in the routing table: .Pp .Dl route change -net 172.16.10.0/24 172.16.1.2 .Pp Display the route for a destination network: .Pp .Dl route show 172.16.10.0 .Pp Delete a static route from the routing table: .Pp .Dl route delete -net 172.16.10.0/24 172.16.1.2 .Pp Remove all routes from the routing table: .Pp .Dl route flush .Sh DIAGNOSTICS .Bl -diag .It "add [host \&| network ] %s: gateway %s flags %x" The specified route is being added to the tables. The values printed are from the routing table entry supplied in the .Xr ioctl 2 call. If the gateway address used was not the primary address of the gateway (the first one returned by .Xr gethostbyname 3 ) , the gateway address is printed numerically as well as symbolically. .It "delete [ host \&| network ] %s: gateway %s flags %x" As above, but when deleting an entry. .It "%s %s done" When the .Cm flush command is specified, each routing table entry deleted is indicated with a message of this form. .It "Network is unreachable" An attempt to add a route failed because the gateway listed was not on a directly-connected network. The next-hop gateway must be given. .It "not in table" A delete operation was attempted for an entry which was not present in the tables. .It "routing table overflow" An add operation was attempted, but the system was low on resources and was unable to allocate memory to create the new entry. .It "gateway uses the same route" A .Cm change operation resulted in a route whose gateway uses the same route as the one being changed. The next-hop gateway should be reachable through a different route. .El .Sh SEE ALSO .\".Xr esis 4 , .Xr netintro 4 , .Xr route 4 , .Xr arp 8 , .Xr IPXrouted 8 , .Xr routed 8 .\".Xr XNSrouted 8 .Sh HISTORY The .Nm utility appeared in .Bx 4.2 . .Sh BUGS The first paragraph may have slightly exaggerated .Xr routed 8 Ns 's abilities. .Pp Currently, routes with the .Dv RTF_BLACKHOLE flag set need to have the gateway set to an instance of the .Xr lo 4 driver, using the .Fl iface option, for the flag to have any effect; unless IP fast forwarding is enabled, in which case the meaning of the flag will always be honored. Index: head/sbin/route/route.c =================================================================== --- head/sbin/route/route.c (revision 260523) +++ head/sbin/route/route.c (revision 260524) @@ -1,1976 +1,1996 @@ /* * Copyright (c) 1983, 1989, 1991, 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. * 4. 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ATALK_BUF_SIZE 20 struct fibl { TAILQ_ENTRY(fibl) fl_next; int fl_num; int fl_error; int fl_errno; }; static struct keytab { const char *kt_cp; int kt_i; } const keywords[] = { #include "keywords.h" {0, 0} }; static struct sockaddr_storage so[RTAX_MAX]; static int pid, rtm_addrs; static int s; static int forcehost, forcenet, nflag, af, qflag, tflag; static int verbose, aflen; static int locking, lockrest, debugonly; static struct rt_metrics rt_metrics; static u_long rtm_inits; static uid_t uid; static int defaultfib; static int numfibs; static char domain[MAXHOSTNAMELEN + 1]; static bool domain_initialized; static int rtm_seq; static char rt_line[NI_MAXHOST]; static char net_line[MAXHOSTNAMELEN + 1]; static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; static TAILQ_HEAD(fibl_head_t, fibl) fibl_head; static int atalk_aton(const char *, struct at_addr *); static char *atalk_ntoa(struct at_addr, char [ATALK_BUF_SIZE]); static void printb(int, const char *); static void flushroutes(int argc, char *argv[]); static int flushroutes_fib(int); static int getaddr(int, char *, struct hostent **, int); static int keyword(const char *); #ifdef INET static void inet_makenetandmask(u_long, struct sockaddr_in *, struct sockaddr_in *, u_long); #endif #ifdef INET6 static int inet6_makenetandmask(struct sockaddr_in6 *, const char *); #endif static void interfaces(void); static void monitor(int, char*[]); static const char *netname(struct sockaddr *); static void newroute(int, char **); static int newroute_fib(int, char *, int); static void pmsg_addrs(char *, int, size_t); static void pmsg_common(struct rt_msghdr *, size_t); static int prefixlen(const char *); static void print_getmsg(struct rt_msghdr *, int, int); static void print_rtmsg(struct rt_msghdr *, size_t); static const char *routename(struct sockaddr *); static int rtmsg(int, int, int); static void set_metric(char *, int); static int set_sofib(int); static void sockaddr(char *, struct sockaddr *, size_t); static void sodump(struct sockaddr *, const char *); static int fiboptlist_csv(const char *, struct fibl_head_t *); static int fiboptlist_range(const char *, struct fibl_head_t *); static void usage(const char *) __dead2; static void usage(const char *cp) { if (cp != NULL) warnx("bad keyword: %s", cp); - errx(EX_USAGE, "usage: route [-dnqtv] command [[modifiers] args]"); + errx(EX_USAGE, "usage: route [-46dnqtv] command [[modifiers] args]"); /* NOTREACHED */ } int main(int argc, char **argv) { int ch; size_t len; if (argc < 2) usage(NULL); - while ((ch = getopt(argc, argv, "nqdtv")) != -1) + while ((ch = getopt(argc, argv, "46nqdtv")) != -1) switch(ch) { + case '4': +#ifdef INET + af = AF_INET; + aflen = sizeof(struct sockaddr_in); +#else + errx(1, "IPv4 support is not compiled in"); +#endif + break; + case '6': +#ifdef INET6 + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); +#else + errx(1, "IPv6 support is not compiled in"); +#endif + break; case 'n': nflag = 1; break; case 'q': qflag = 1; break; case 'v': verbose = 1; break; case 't': tflag = 1; break; case 'd': debugonly = 1; break; case '?': default: usage(NULL); } argc -= optind; argv += optind; pid = getpid(); uid = geteuid(); if (tflag) s = open(_PATH_DEVNULL, O_WRONLY, 0); else s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) err(EX_OSERR, "socket"); len = sizeof(numfibs); if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) == -1) numfibs = -1; len = sizeof(defaultfib); if (numfibs != -1 && sysctlbyname("net.my_fibnum", (void *)&defaultfib, &len, NULL, 0) == -1) defaultfib = -1; if (*argv != NULL) switch (keyword(*argv)) { case K_GET: case K_SHOW: uid = 0; /* FALLTHROUGH */ case K_CHANGE: case K_ADD: case K_DEL: case K_DELETE: newroute(argc, argv); /* NOTREACHED */ case K_MONITOR: monitor(argc, argv); /* NOTREACHED */ case K_FLUSH: flushroutes(argc, argv); exit(0); /* NOTREACHED */ } usage(*argv); /* NOTREACHED */ } static int set_sofib(int fib) { if (fib < 0) return (0); return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib, sizeof(fib))); } static int fiboptlist_range(const char *arg, struct fibl_head_t *flh) { struct fibl *fl; char *str0, *str, *token, *endptr; int fib[2], i, error; str0 = str = strdup(arg); error = 0; i = 0; while ((token = strsep(&str, "-")) != NULL) { switch (i) { case 0: case 1: errno = 0; fib[i] = strtol(token, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib[i] < 0 || (numfibs != -1 && fib[i] > numfibs - 1)) errno = EINVAL; } if (errno) error = 1; break; default: error = 1; } if (error) goto fiboptlist_range_ret; i++; } if (fib[0] >= fib[1]) { error = 1; goto fiboptlist_range_ret; } for (i = fib[0]; i <= fib[1]; i++) { fl = calloc(1, sizeof(*fl)); if (fl == NULL) { error = 1; goto fiboptlist_range_ret; } fl->fl_num = i; TAILQ_INSERT_TAIL(flh, fl, fl_next); } fiboptlist_range_ret: free(str0); return (error); } #define ALLSTRLEN 64 static int fiboptlist_csv(const char *arg, struct fibl_head_t *flh) { struct fibl *fl; char *str0, *str, *token, *endptr; int fib, error; str0 = str = NULL; if (strcmp("all", arg) == 0) { str = calloc(1, ALLSTRLEN); if (str == NULL) { error = 1; goto fiboptlist_csv_ret; } if (numfibs > 1) snprintf(str, ALLSTRLEN - 1, "%d-%d", 0, numfibs - 1); else snprintf(str, ALLSTRLEN - 1, "%d", 0); } else if (strcmp("default", arg) == 0) { str0 = str = calloc(1, ALLSTRLEN); if (str == NULL) { error = 1; goto fiboptlist_csv_ret; } snprintf(str, ALLSTRLEN - 1, "%d", defaultfib); } else str0 = str = strdup(arg); error = 0; while ((token = strsep(&str, ",")) != NULL) { if (*token != '-' && strchr(token, '-') != NULL) { error = fiboptlist_range(token, flh); if (error) goto fiboptlist_csv_ret; } else { errno = 0; fib = strtol(token, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib < 0 || (numfibs != -1 && fib > numfibs - 1)) errno = EINVAL; } if (errno) { error = 1; goto fiboptlist_csv_ret; } fl = calloc(1, sizeof(*fl)); if (fl == NULL) { error = 1; goto fiboptlist_csv_ret; } fl->fl_num = fib; TAILQ_INSERT_TAIL(flh, fl, fl_next); } } fiboptlist_csv_ret: if (str0 != NULL) free(str0); return (error); } /* * Purge all entries in the routing tables not * associated with network interfaces. */ static void flushroutes(int argc, char *argv[]) { struct fibl *fl; int error; if (uid != 0 && !debugonly && !tflag) errx(EX_NOPERM, "must be root to alter routing table"); shutdown(s, SHUT_RD); /* Don't want to read back our messages */ TAILQ_INIT(&fibl_head); while (argc > 1) { argc--; argv++; if (**argv != '-') usage(*argv); switch (keyword(*argv + 1)) { #ifdef INET + case K_4: case K_INET: af = AF_INET; break; #endif #ifdef INET6 + case K_6: case K_INET6: af = AF_INET6; break; #endif case K_ATALK: af = AF_APPLETALK; break; case K_LINK: af = AF_LINK; break; case K_FIB: if (!--argc) usage(*argv); error = fiboptlist_csv(*++argv, &fibl_head); if (error) errx(EX_USAGE, "invalid fib number: %s", *argv); break; default: usage(*argv); } } if (TAILQ_EMPTY(&fibl_head)) { error = fiboptlist_csv("default", &fibl_head); if (error) errx(EX_OSERR, "fiboptlist_csv failed."); } TAILQ_FOREACH(fl, &fibl_head, fl_next) flushroutes_fib(fl->fl_num); } static int flushroutes_fib(int fib) { struct rt_msghdr *rtm; size_t needed; char *buf, *next, *lim; int mib[7], rlen, seqno, count = 0; int error; error = set_sofib(fib); if (error) { warn("fib number %d is ignored", fib); return (error); } retry: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = AF_UNSPEC; mib[4] = NET_RT_DUMP; mib[5] = 0; /* no flags */ mib[6] = fib; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "route-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(EX_OSERR, "malloc failed"); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { if (errno == ENOMEM && count++ < 10) { warnx("Routing table grew, retrying"); sleep(1); free(buf); goto retry; } err(EX_OSERR, "route-sysctl-get"); } lim = buf + needed; if (verbose) (void)printf("Examining routing table from sysctl\n"); seqno = 0; /* ??? */ for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; if (verbose) print_rtmsg(rtm, rtm->rtm_msglen); if ((rtm->rtm_flags & RTF_GATEWAY) == 0) continue; if (af != 0) { struct sockaddr *sa = (struct sockaddr *)(rtm + 1); if (sa->sa_family != af) continue; } if (debugonly) continue; rtm->rtm_type = RTM_DELETE; rtm->rtm_seq = seqno; rlen = write(s, next, rtm->rtm_msglen); if (rlen < 0 && errno == EPERM) err(1, "write to routing socket"); if (rlen < (int)rtm->rtm_msglen) { warn("write to routing socket"); (void)printf("got only %d for rlen\n", rlen); free(buf); goto retry; break; } seqno++; if (qflag) continue; if (verbose) print_rtmsg(rtm, rlen); else { struct sockaddr *sa = (struct sockaddr *)(rtm + 1); printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? routename(sa) : netname(sa)); sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa); printf("%-20.20s ", routename(sa)); if (fib >= 0) printf("-fib %-3d ", fib); printf("done\n"); } } return (error); } static const char * routename(struct sockaddr *sa) { struct sockaddr_dl *sdl; const char *cp; char atalk_buf[ATALK_BUF_SIZE]; int n; if (!domain_initialized) { domain_initialized = true; if (gethostname(domain, MAXHOSTNAMELEN) == 0 && (cp = strchr(domain, '.'))) { domain[MAXHOSTNAMELEN] = '\0'; (void)strcpy(domain, cp + 1); } else domain[0] = '\0'; } /* If the address is zero-filled, use "default". */ if (sa->sa_len == 0 && nflag == 0) return ("default"); #if defined(INET) || defined(INET6) switch (sa->sa_family) { #ifdef INET case AF_INET: /* If the address is zero-filled, use "default". */ if (nflag == 0 && ((struct sockaddr_in *)(void *)sa)->sin_addr.s_addr == INADDR_ANY) return("default"); break; #endif #ifdef INET6 case AF_INET6: /* If the address is zero-filled, use "default". */ if (nflag == 0 && IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)sa)->sin6_addr)) return("default"); break; #endif } #endif switch (sa->sa_family) { #if defined(INET) || defined(INET6) #ifdef INET case AF_INET: #endif #ifdef INET6 case AF_INET6: #endif { struct sockaddr_storage ss; int error; char *p; memset(&ss, 0, sizeof(ss)); if (sa->sa_len == 0) ss.ss_family = sa->sa_family; else memcpy(&ss, sa, sa->sa_len); /* Expand sa->sa_len because it could be shortened. */ if (sa->sa_family == AF_INET) ss.ss_len = sizeof(struct sockaddr_in); else if (sa->sa_family == AF_INET6) ss.ss_len = sizeof(struct sockaddr_in6); error = getnameinfo((struct sockaddr *)&ss, ss.ss_len, rt_line, sizeof(rt_line), NULL, 0, (nflag == 0) ? 0 : NI_NUMERICHOST); if (error) { warnx("getnameinfo(): %s", gai_strerror(error)); strncpy(rt_line, "invalid", sizeof(rt_line)); } /* Remove the domain part if any. */ p = strchr(rt_line, '.'); if (p != NULL && strcmp(p + 1, domain) == 0) *p = '\0'; return (rt_line); break; } #endif case AF_APPLETALK: (void)snprintf(rt_line, sizeof(rt_line), "atalk %s", atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr, atalk_buf)); break; case AF_LINK: sdl = (struct sockaddr_dl *)(void *)sa; if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { n = snprintf(rt_line, sizeof(rt_line), "link#%d", sdl->sdl_index); if (n > (int)sizeof(rt_line)) rt_line[0] = '\0'; return (rt_line); } else return (link_ntoa(sdl)); break; default: { u_short *sp = (u_short *)(void *)sa; u_short *splim = sp + ((sa->sa_len + 1) >> 1); char *cps = rt_line + sprintf(rt_line, "(%d)", sa->sa_family); char *cpe = rt_line + sizeof(rt_line); while (++sp < splim && cps < cpe) /* start with sa->sa_data */ if ((n = snprintf(cps, cpe - cps, " %x", *sp)) > 0) cps += n; else *cps = '\0'; break; } } return (rt_line); } /* * Return the name of the network whose address is given. * The address is assumed to be that of a net, not a host. */ static const char * netname(struct sockaddr *sa) { struct sockaddr_dl *sdl; char atalk_buf[ATALK_BUF_SIZE]; int n; #ifdef INET struct netent *np = NULL; const char *cp = NULL; u_long i; #endif switch (sa->sa_family) { #ifdef INET case AF_INET: { struct in_addr in; in = ((struct sockaddr_in *)(void *)sa)->sin_addr; i = in.s_addr = ntohl(in.s_addr); if (in.s_addr == 0) cp = "default"; else if (!nflag) { np = getnetbyaddr(i, AF_INET); if (np != NULL) cp = np->n_name; } #define C(x) (unsigned)((x) & 0xff) if (cp != NULL) strncpy(net_line, cp, sizeof(net_line)); else if ((in.s_addr & 0xffffff) == 0) (void)sprintf(net_line, "%u", C(in.s_addr >> 24)); else if ((in.s_addr & 0xffff) == 0) (void)sprintf(net_line, "%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16)); else if ((in.s_addr & 0xff) == 0) (void)sprintf(net_line, "%u.%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16), C(in.s_addr >> 8)); else (void)sprintf(net_line, "%u.%u.%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); #undef C break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 sin6; int niflags = 0; memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6, sa, sa->sa_len); sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; if (nflag) niflags |= NI_NUMERICHOST; if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, net_line, sizeof(net_line), NULL, 0, niflags) != 0) strncpy(net_line, "invalid", sizeof(net_line)); return(net_line); } #endif case AF_APPLETALK: (void)snprintf(net_line, sizeof(net_line), "atalk %s", atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr, atalk_buf)); break; case AF_LINK: sdl = (struct sockaddr_dl *)(void *)sa; if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { n = snprintf(net_line, sizeof(net_line), "link#%d", sdl->sdl_index); if (n > (int)sizeof(net_line)) net_line[0] = '\0'; return (net_line); } else return (link_ntoa(sdl)); break; default: { u_short *sp = (u_short *)(void *)sa->sa_data; u_short *splim = sp + ((sa->sa_len + 1)>>1); char *cps = net_line + sprintf(net_line, "af %d:", sa->sa_family); char *cpe = net_line + sizeof(net_line); while (sp < splim && cps < cpe) if ((n = snprintf(cps, cpe - cps, " %x", *sp++)) > 0) cps += n; else *cps = '\0'; break; } } return (net_line); } static void set_metric(char *value, int key) { int flag = 0; char *endptr; u_long noval, *valp = &noval; switch (key) { #define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break caseof(K_MTU, RTV_MTU, rmx_mtu); caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); caseof(K_RTT, RTV_RTT, rmx_rtt); caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); caseof(K_WEIGHT, RTV_WEIGHT, rmx_weight); } rtm_inits |= flag; if (lockrest || locking) rt_metrics.rmx_locks |= flag; if (locking) locking = 0; errno = 0; *valp = strtol(value, &endptr, 0); if (errno == 0 && *endptr != '\0') errno = EINVAL; if (errno) err(EX_USAGE, "%s", value); if (flag & RTV_EXPIRE && (value[0] == '+' || value[0] == '-')) { struct timespec ts; clock_gettime(CLOCK_REALTIME_FAST, &ts); *valp += ts.tv_sec; } } #define F_ISHOST 0x01 #define F_FORCENET 0x02 #define F_FORCEHOST 0x04 #define F_PROXY 0x08 #define F_INTERFACE 0x10 static void newroute(int argc, char **argv) { struct hostent *hp; struct fibl *fl; char *cmd; const char *dest, *gateway, *errmsg; int key, error, flags, nrflags, fibnum; if (uid != 0 && !debugonly && !tflag) errx(EX_NOPERM, "must be root to alter routing table"); dest = NULL; gateway = NULL; flags = RTF_STATIC; nrflags = 0; hp = NULL; TAILQ_INIT(&fibl_head); cmd = argv[0]; if (*cmd != 'g' && *cmd != 's') shutdown(s, SHUT_RD); /* Don't want to read back our messages */ while (--argc > 0) { if (**(++argv)== '-') { switch (key = keyword(1 + *argv)) { case K_LINK: af = AF_LINK; aflen = sizeof(struct sockaddr_dl); break; #ifdef INET + case K_4: case K_INET: af = AF_INET; aflen = sizeof(struct sockaddr_in); break; #endif #ifdef INET6 + case K_6: case K_INET6: af = AF_INET6; aflen = sizeof(struct sockaddr_in6); break; #endif case K_ATALK: af = AF_APPLETALK; aflen = sizeof(struct sockaddr_at); break; case K_SA: af = PF_ROUTE; aflen = sizeof(struct sockaddr_storage); break; case K_IFACE: case K_INTERFACE: nrflags |= F_INTERFACE; break; case K_NOSTATIC: flags &= ~RTF_STATIC; break; case K_LOCK: locking = 1; break; case K_LOCKREST: lockrest = 1; break; case K_HOST: nrflags |= F_FORCEHOST; break; case K_REJECT: flags |= RTF_REJECT; break; case K_BLACKHOLE: flags |= RTF_BLACKHOLE; break; case K_PROTO1: flags |= RTF_PROTO1; break; case K_PROTO2: flags |= RTF_PROTO2; break; case K_PROTO3: flags |= RTF_PROTO3; break; case K_PROXY: nrflags |= F_PROXY; break; case K_XRESOLVE: flags |= RTF_XRESOLVE; break; case K_STATIC: flags |= RTF_STATIC; break; case K_STICKY: flags |= RTF_STICKY; break; case K_NOSTICK: flags &= ~RTF_STICKY; break; case K_FIB: if (!--argc) usage(NULL); error = fiboptlist_csv(*++argv, &fibl_head); if (error) errx(EX_USAGE, "invalid fib number: %s", *argv); break; case K_IFA: if (!--argc) usage(NULL); getaddr(RTAX_IFA, *++argv, 0, nrflags); break; case K_IFP: if (!--argc) usage(NULL); getaddr(RTAX_IFP, *++argv, 0, nrflags); break; case K_GENMASK: if (!--argc) usage(NULL); getaddr(RTAX_GENMASK, *++argv, 0, nrflags); break; case K_GATEWAY: if (!--argc) usage(NULL); getaddr(RTAX_GATEWAY, *++argv, 0, nrflags); gateway = *argv; break; case K_DST: if (!--argc) usage(NULL); if (getaddr(RTAX_DST, *++argv, &hp, nrflags)) nrflags |= F_ISHOST; dest = *argv; break; case K_NETMASK: if (!--argc) usage(NULL); getaddr(RTAX_NETMASK, *++argv, 0, nrflags); /* FALLTHROUGH */ case K_NET: nrflags |= F_FORCENET; break; case K_PREFIXLEN: if (!--argc) usage(NULL); if (prefixlen(*++argv) == -1) { nrflags &= ~F_FORCENET; nrflags |= F_ISHOST; } else { nrflags |= F_FORCENET; nrflags &= ~F_ISHOST; } break; case K_MTU: case K_HOPCOUNT: case K_EXPIRE: case K_RECVPIPE: case K_SENDPIPE: case K_SSTHRESH: case K_RTT: case K_RTTVAR: case K_WEIGHT: if (!--argc) usage(NULL); set_metric(*++argv, key); break; default: usage(1+*argv); } } else { if ((rtm_addrs & RTA_DST) == 0) { dest = *argv; if (getaddr(RTAX_DST, *argv, &hp, nrflags)) nrflags |= F_ISHOST; } else if ((rtm_addrs & RTA_GATEWAY) == 0) { gateway = *argv; getaddr(RTAX_GATEWAY, *argv, &hp, nrflags); } else { getaddr(RTAX_NETMASK, *argv, 0, nrflags); nrflags |= F_FORCENET; } } } /* Do some sanity checks on resulting request */ if (so[RTAX_DST].ss_len == 0) { warnx("destination parameter required"); usage(NULL); } if (so[RTAX_NETMASK].ss_len != 0 && so[RTAX_DST].ss_family != so[RTAX_NETMASK].ss_family) { warnx("destination and netmask family need to be the same"); usage(NULL); } if (nrflags & F_FORCEHOST) { nrflags |= F_ISHOST; #ifdef INET6 if (af == AF_INET6) { rtm_addrs &= ~RTA_NETMASK; memset(&so[RTAX_NETMASK], 0, sizeof(so[RTAX_NETMASK])); } #endif } if (nrflags & F_FORCENET) nrflags &= ~F_ISHOST; flags |= RTF_UP; if (nrflags & F_ISHOST) flags |= RTF_HOST; if ((nrflags & F_INTERFACE) == 0) flags |= RTF_GATEWAY; if (nrflags & F_PROXY) flags |= RTF_ANNOUNCE; if (dest == NULL) dest = ""; if (gateway == NULL) gateway = ""; if (TAILQ_EMPTY(&fibl_head)) { error = fiboptlist_csv("default", &fibl_head); if (error) errx(EX_OSERR, "fiboptlist_csv failed."); } error = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { fl->fl_error = newroute_fib(fl->fl_num, cmd, flags); if (fl->fl_error) fl->fl_errno = errno; error += fl->fl_error; } if (*cmd == 'g' || *cmd == 's') exit(error); error = 0; if (!qflag) { fibnum = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error == 0) fibnum++; } if (fibnum > 0) { int firstfib = 1; printf("%s %s %s", cmd, (nrflags & F_ISHOST) ? "host" : "net", dest); if (*gateway) printf(": gateway %s", gateway); if (numfibs > 1) { TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error == 0 && fl->fl_num >= 0) { if (firstfib) { printf(" fib "); firstfib = 0; } printf("%d", fl->fl_num); if (fibnum-- > 1) printf(","); } } } printf("\n"); } fibnum = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error != 0) { printf("%s %s %s", cmd, (nrflags & F_ISHOST) ? "host" : "net", dest); if (*gateway) printf(": gateway %s", gateway); if (fl->fl_num >= 0) printf(" fib %d", fl->fl_num); switch (fl->fl_errno) { case ESRCH: errmsg = "not in table"; break; case EBUSY: errmsg = "entry in use"; break; case ENOBUFS: errmsg = "not enough memory"; break; case EADDRINUSE: /* * handle recursion avoidance * in rt_setgate() */ errmsg = "gateway uses the same route"; break; case EEXIST: errmsg = "route already in table"; break; default: errmsg = strerror(fl->fl_errno); break; } printf(": %s\n", errmsg); error = 1; } } } exit(error); } static int newroute_fib(int fib, char *cmd, int flags) { int error; error = set_sofib(fib); if (error) { warn("fib number %d is ignored", fib); return (error); } error = rtmsg(*cmd, flags, fib); return (error); } #ifdef INET static void inet_makenetandmask(u_long net, struct sockaddr_in *sin, struct sockaddr_in *sin_mask, u_long bits) { u_long mask = 0; rtm_addrs |= RTA_NETMASK; /* * MSB of net should be meaningful. 0/0 is exception. */ if (net > 0) while ((net & 0xff000000) == 0) net <<= 8; /* * If no /xx was specified we must calculate the * CIDR address. */ if ((bits == 0) && (net != 0)) { u_long i, j; for(i = 0, j = 0xff; i < 4; i++) { if (net & j) { break; } j <<= 8; } /* i holds the first non zero bit */ bits = 32 - (i*8); } if (bits != 0) mask = 0xffffffff << (32 - bits); sin->sin_addr.s_addr = htonl(net); sin_mask->sin_addr.s_addr = htonl(mask); sin_mask->sin_len = sizeof(struct sockaddr_in); sin_mask->sin_family = AF_INET; } #endif #ifdef INET6 /* * XXX the function may need more improvement... */ static int inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen) { struct in6_addr in6; if (plen == NULL) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && sin6->sin6_scope_id == 0) { plen = "0"; } else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) { /* aggregatable global unicast - RFC2374 */ memset(&in6, 0, sizeof(in6)); if (!memcmp(&sin6->sin6_addr.s6_addr[8], &in6.s6_addr[8], 8)) plen = "64"; } } if (plen == NULL || strcmp(plen, "128") == 0) return (1); rtm_addrs |= RTA_NETMASK; prefixlen(plen); return (0); } #endif /* * Interpret an argument as a network address of some kind, * returning 1 if a host address, 0 if a network address. */ static int getaddr(int idx, char *str, struct hostent **hpp, int nrflags) { struct sockaddr *sa; #if defined(INET) struct sockaddr_in *sin; struct hostent *hp; struct netent *np; u_long val; char *q; #elif defined(INET6) char *q; #endif if (idx < 0 || idx >= RTAX_MAX) usage("internal error"); if (af == 0) { #if defined(INET) af = AF_INET; aflen = sizeof(struct sockaddr_in); #elif defined(INET6) af = AF_INET6; aflen = sizeof(struct sockaddr_in6); #else af = AF_LINK; aflen = sizeof(struct sockaddr_dl); #endif } #ifndef INET hpp = NULL; #endif rtm_addrs |= (1 << idx); sa = (struct sockaddr *)&so[idx]; sa->sa_family = af; sa->sa_len = aflen; switch (idx) { case RTAX_GATEWAY: if (nrflags & F_INTERFACE) { struct ifaddrs *ifap, *ifa; struct sockaddr_dl *sdl0 = (struct sockaddr_dl *)(void *)sa; struct sockaddr_dl *sdl = NULL; if (getifaddrs(&ifap)) err(EX_OSERR, "getifaddrs"); for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; if (strcmp(str, ifa->ifa_name) != 0) continue; sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr; } /* If we found it, then use it */ if (sdl != NULL) { /* * Note that we need to copy before calling * freeifaddrs(). */ memcpy(sdl0, sdl, sdl->sdl_len); } freeifaddrs(ifap); if (sdl != NULL) return(1); } break; case RTAX_IFP: sa->sa_family = AF_LINK; break; } if (strcmp(str, "default") == 0) { /* * Default is net 0.0.0.0/0 */ switch (idx) { case RTAX_DST: forcenet++; getaddr(RTAX_NETMASK, str, 0, nrflags); break; } return (0); } switch (sa->sa_family) { #ifdef INET6 case AF_INET6: { struct addrinfo hints, *res; int ecode; q = NULL; if (idx == RTAX_DST && (q = strchr(str, '/')) != NULL) *q = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_family = sa->sa_family; hints.ai_socktype = SOCK_DGRAM; ecode = getaddrinfo(str, NULL, &hints, &res); if (ecode != 0 || res->ai_family != AF_INET6 || res->ai_addrlen != sizeof(struct sockaddr_in6)) errx(EX_OSERR, "%s: %s", str, gai_strerror(ecode)); memcpy(sa, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (q != NULL) *q++ = '/'; if (idx == RTAX_DST) return (inet6_makenetandmask((struct sockaddr_in6 *)(void *)sa, q)); return (0); } #endif /* INET6 */ case AF_APPLETALK: { struct sockaddr_at *sat = (struct sockaddr_at *)(void *)sa; if (!atalk_aton(str, &sat->sat_addr)) errx(EX_NOHOST, "bad address: %s", str); rtm_addrs |= RTA_NETMASK; return(forcehost || sat->sat_addr.s_node != 0); } case AF_LINK: link_addr(str, (struct sockaddr_dl *)(void *)sa); return (1); case PF_ROUTE: sockaddr(str, sa, sizeof(struct sockaddr_storage)); return (1); #ifdef INET case AF_INET: #endif default: break; } #ifdef INET sin = (struct sockaddr_in *)(void *)sa; if (hpp == NULL) hpp = &hp; *hpp = NULL; q = strchr(str,'/'); if (q != NULL && idx == RTAX_DST) { *q = '\0'; if ((val = inet_network(str)) != INADDR_NONE) { inet_makenetandmask(val, sin, (struct sockaddr_in *)&so[RTAX_NETMASK], strtoul(q+1, 0, 0)); return (0); } *q = '/'; } if ((idx != RTAX_DST || forcenet == 0) && inet_aton(str, &sin->sin_addr)) { val = sin->sin_addr.s_addr; if (idx != RTAX_DST || forcehost || inet_lnaof(sin->sin_addr) != INADDR_ANY) return (1); else { val = ntohl(val); goto netdone; } } if (idx == RTAX_DST && forcehost == 0 && ((val = inet_network(str)) != INADDR_NONE || ((np = getnetbyname(str)) != NULL && (val = np->n_net) != 0))) { netdone: inet_makenetandmask(val, sin, (struct sockaddr_in *)&so[RTAX_NETMASK], 0); return (0); } hp = gethostbyname(str); if (hp != NULL) { *hpp = hp; sin->sin_family = hp->h_addrtype; memmove((char *)&sin->sin_addr, hp->h_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); return (1); } #endif errx(EX_NOHOST, "bad address: %s", str); } static int prefixlen(const char *str) { int len = atoi(str), q, r; int max; char *p; rtm_addrs |= RTA_NETMASK; switch (af) { #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&so[RTAX_NETMASK]; max = 128; p = (char *)&sin6->sin6_addr; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); break; } #endif #ifdef INET case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)&so[RTAX_NETMASK]; max = 32; p = (char *)&sin->sin_addr; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); break; } #endif default: errx(EX_OSERR, "prefixlen not supported in this af"); } if (len < 0 || max < len) errx(EX_USAGE, "%s: invalid prefixlen", str); q = len >> 3; r = len & 7; memset((void *)p, 0, max / 8); if (q > 0) memset((void *)p, 0xff, q); if (r > 0) *((u_char *)p + q) = (0xff00 >> r) & 0xff; if (len == max) return (-1); else return (len); } static void interfaces(void) { size_t needed; int mib[6]; char *buf, *lim, *next, count = 0; struct rt_msghdr *rtm; retry2: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = AF_UNSPEC; mib[4] = NET_RT_IFLIST; mib[5] = 0; /* no flags */ if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "route-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(EX_OSERR, "malloc failed"); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { if (errno == ENOMEM && count++ < 10) { warnx("Routing table grew, retrying"); sleep(1); free(buf); goto retry2; } err(EX_OSERR, "actual retrieval of interface table"); } lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; print_rtmsg(rtm, rtm->rtm_msglen); } } static void monitor(int argc, char *argv[]) { int n, fib, error; char msg[2048], *endptr; fib = defaultfib; while (argc > 1) { argc--; argv++; if (**argv != '-') usage(*argv); switch (keyword(*argv + 1)) { case K_FIB: if (!--argc) usage(*argv); errno = 0; fib = strtol(*++argv, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib < 0 || (numfibs != -1 && fib > numfibs - 1)) errno = EINVAL; } if (errno) errx(EX_USAGE, "invalid fib number: %s", *argv); break; default: usage(*argv); } } error = set_sofib(fib); if (error) errx(EX_USAGE, "invalid fib number: %d", fib); verbose = 1; if (debugonly) { interfaces(); exit(0); } for (;;) { time_t now; n = read(s, msg, 2048); now = time(NULL); (void)printf("\ngot message of size %d on %s", n, ctime(&now)); print_rtmsg((struct rt_msghdr *)(void *)msg, n); } } static int rtmsg(int cmd, int flags, int fib) { int rlen; char *cp = m_rtmsg.m_space; int l; #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ l = (((struct sockaddr *)&(u))->sa_len == 0) ? \ sizeof(long) : \ 1 + ((((struct sockaddr *)&(u))->sa_len - 1) \ | (sizeof(long) - 1)); \ memmove(cp, (char *)&(u), l); \ cp += l; \ if (verbose) \ sodump((struct sockaddr *)&(u), #w); \ } errno = 0; memset(&m_rtmsg, 0, sizeof(m_rtmsg)); if (cmd == 'a') cmd = RTM_ADD; else if (cmd == 'c') cmd = RTM_CHANGE; else if (cmd == 'g' || cmd == 's') { cmd = RTM_GET; if (so[RTAX_IFP].ss_family == 0) { so[RTAX_IFP].ss_family = AF_LINK; so[RTAX_IFP].ss_len = sizeof(struct sockaddr_dl); rtm_addrs |= RTA_IFP; } } else cmd = RTM_DELETE; #define rtm m_rtmsg.m_rtm rtm.rtm_type = cmd; rtm.rtm_flags = flags; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = ++rtm_seq; rtm.rtm_addrs = rtm_addrs; rtm.rtm_rmx = rt_metrics; rtm.rtm_inits = rtm_inits; NEXTADDR(RTA_DST, so[RTAX_DST]); NEXTADDR(RTA_GATEWAY, so[RTAX_GATEWAY]); NEXTADDR(RTA_NETMASK, so[RTAX_NETMASK]); NEXTADDR(RTA_GENMASK, so[RTAX_GENMASK]); NEXTADDR(RTA_IFP, so[RTAX_IFP]); NEXTADDR(RTA_IFA, so[RTAX_IFA]); rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; if (verbose) print_rtmsg(&rtm, l); if (debugonly) return (0); if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { if (errno == EPERM) err(1, "writing to routing socket"); warn("writing to routing socket"); return (-1); } if (cmd == RTM_GET) { do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); } while (l > 0 && (rtm.rtm_seq != rtm_seq || rtm.rtm_pid != pid)); if (l < 0) warn("read from routing socket"); else print_getmsg(&rtm, l, fib); } #undef rtm return (0); } static const char *const msgtypes[] = { "", "RTM_ADD: Add Route", "RTM_DELETE: Delete Route", "RTM_CHANGE: Change Metrics or flags", "RTM_GET: Report Metrics", "RTM_LOSING: Kernel Suspects Partitioning", "RTM_REDIRECT: Told to use different route", "RTM_MISS: Lookup failed on this address", "RTM_LOCK: fix specified metrics", "RTM_OLDADD: caused by SIOCADDRT", "RTM_OLDDEL: caused by SIOCDELRT", "RTM_RESOLVE: Route created by cloning", "RTM_NEWADDR: address being added to iface", "RTM_DELADDR: address being removed from iface", "RTM_IFINFO: iface status change", "RTM_NEWMADDR: new multicast group membership on iface", "RTM_DELMADDR: multicast group membership removed from iface", "RTM_IFANNOUNCE: interface arrival/departure", "RTM_IEEE80211: IEEE 802.11 wireless event", }; static const char metricnames[] = "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" "\1mtu"; static const char routeflags[] = "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" "\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY"; static const char ifnetflags[] = "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" "\017LINK2\020MULTICAST"; static const char addrnames[] = "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; static const char errfmt[] = "\n%s: truncated route message, only %zu bytes left\n"; static void print_rtmsg(struct rt_msghdr *rtm, size_t msglen) { struct if_msghdr *ifm; struct ifa_msghdr *ifam; #ifdef RTM_NEWMADDR struct ifma_msghdr *ifmam; #endif struct if_announcemsghdr *ifan; const char *state; if (verbose == 0) return; if (rtm->rtm_version != RTM_VERSION) { (void)printf("routing message version %d not understood\n", rtm->rtm_version); return; } if (rtm->rtm_type < nitems(msgtypes)) (void)printf("%s: ", msgtypes[rtm->rtm_type]); else (void)printf("unknown type %d: ", rtm->rtm_type); (void)printf("len %d, ", rtm->rtm_msglen); #define REQUIRE(x) do { \ if (msglen < sizeof(x)) \ goto badlen; \ else \ msglen -= sizeof(x); \ } while (0) switch (rtm->rtm_type) { case RTM_IFINFO: REQUIRE(struct if_msghdr); ifm = (struct if_msghdr *)rtm; (void)printf("if# %d, ", ifm->ifm_index); switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_DOWN: state = "down"; break; case LINK_STATE_UP: state = "up"; break; default: state = "unknown"; break; } (void)printf("link: %s, flags:", state); printb(ifm->ifm_flags, ifnetflags); pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs, msglen); break; case RTM_NEWADDR: case RTM_DELADDR: REQUIRE(struct ifa_msghdr); ifam = (struct ifa_msghdr *)rtm; (void)printf("metric %d, flags:", ifam->ifam_metric); printb(ifam->ifam_flags, routeflags); pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs, msglen); break; #ifdef RTM_NEWMADDR case RTM_NEWMADDR: case RTM_DELMADDR: REQUIRE(struct ifma_msghdr); ifmam = (struct ifma_msghdr *)rtm; pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs, msglen); break; #endif case RTM_IFANNOUNCE: REQUIRE(struct if_announcemsghdr); ifan = (struct if_announcemsghdr *)rtm; (void)printf("if# %d, what: ", ifan->ifan_index); switch (ifan->ifan_what) { case IFAN_ARRIVAL: (void)printf("arrival"); break; case IFAN_DEPARTURE: printf("departure"); break; default: printf("#%d", ifan->ifan_what); break; } printf("\n"); fflush(stdout); break; default: printf("pid: %ld, seq %d, errno %d, flags:", (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); printb(rtm->rtm_flags, routeflags); pmsg_common(rtm, msglen); } return; badlen: (void)printf(errfmt, __func__, msglen); #undef REQUIRE } static void print_getmsg(struct rt_msghdr *rtm, int msglen, int fib) { struct sockaddr *sp[RTAX_MAX]; struct timespec ts; char *cp; int i; memset(sp, 0, sizeof(sp)); (void)printf(" route to: %s\n", routename((struct sockaddr *)&so[RTAX_DST])); if (rtm->rtm_version != RTM_VERSION) { warnx("routing message version %d not understood", rtm->rtm_version); return; } if (rtm->rtm_msglen > msglen) { warnx("message length mismatch, in packet %d, returned %d", rtm->rtm_msglen, msglen); return; } if (rtm->rtm_errno) { errno = rtm->rtm_errno; warn("message indicates error %d", errno); return; } cp = ((char *)(rtm + 1)); for (i = 0; i < RTAX_MAX; i++) if (rtm->rtm_addrs & (1 << i)) { sp[i] = (struct sockaddr *)cp; cp += SA_SIZE((struct sockaddr *)cp); } if ((rtm->rtm_addrs & RTA_IFP) && (sp[RTAX_IFP]->sa_family != AF_LINK || ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen == 0)) sp[RTAX_IFP] = NULL; if (sp[RTAX_DST] && sp[RTAX_NETMASK]) sp[RTAX_NETMASK]->sa_family = sp[RTAX_DST]->sa_family; /* XXX */ if (sp[RTAX_DST]) (void)printf("destination: %s\n", routename(sp[RTAX_DST])); if (sp[RTAX_NETMASK]) (void)printf(" mask: %s\n", routename(sp[RTAX_NETMASK])); if (sp[RTAX_GATEWAY] && (rtm->rtm_flags & RTF_GATEWAY)) (void)printf(" gateway: %s\n", routename(sp[RTAX_GATEWAY])); if (fib >= 0) (void)printf(" fib: %u\n", (unsigned int)fib); if (sp[RTAX_IFP]) (void)printf(" interface: %.*s\n", ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen, ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_data); (void)printf(" flags: "); printb(rtm->rtm_flags, routeflags); #define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') #define msec(u) (((u) + 500) / 1000) /* usec to msec */ printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe", "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire"); printf("%8lu%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); printf("%8lu%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); printf("%8lu%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); printf("%8lu%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); printf("%8lu%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); printf("%8lu%c ", rtm->rtm_rmx.rmx_weight, lock(WEIGHT)); if (rtm->rtm_rmx.rmx_expire > 0) clock_gettime(CLOCK_REALTIME_FAST, &ts); else ts.tv_sec = 0; printf("%8ld%c\n", (long)(rtm->rtm_rmx.rmx_expire - ts.tv_sec), lock(EXPIRE)); #undef lock #undef msec #define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) if (verbose) pmsg_common(rtm, msglen); else if (rtm->rtm_addrs &~ RTA_IGN) { (void)printf("sockaddrs: "); printb(rtm->rtm_addrs, addrnames); putchar('\n'); } #undef RTA_IGN } static void pmsg_common(struct rt_msghdr *rtm, size_t msglen) { (void)printf("\nlocks: "); printb(rtm->rtm_rmx.rmx_locks, metricnames); (void)printf(" inits: "); printb(rtm->rtm_inits, metricnames); if (msglen > sizeof(struct rt_msghdr)) pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs, msglen - sizeof(struct rt_msghdr)); else (void)fflush(stdout); } static void pmsg_addrs(char *cp, int addrs, size_t len) { struct sockaddr *sa; int i; if (addrs == 0) { (void)putchar('\n'); return; } (void)printf("\nsockaddrs: "); printb(addrs, addrnames); putchar('\n'); for (i = 0; i < RTAX_MAX; i++) if (addrs & (1 << i)) { sa = (struct sockaddr *)cp; if (len == 0 || len < SA_SIZE(sa)) { (void)printf(errfmt, __func__, len); break; } (void)printf(" %s", routename(sa)); len -= SA_SIZE(sa); cp += SA_SIZE(sa); } (void)putchar('\n'); (void)fflush(stdout); } static void printb(int b, const char *str) { int i; int gotsome = 0; if (b == 0) return; while ((i = *str++) != 0) { if (b & (1 << (i-1))) { if (gotsome == 0) i = '<'; else i = ','; putchar(i); gotsome = 1; for (; (i = *str) > 32; str++) putchar(i); } else while (*str > 32) str++; } if (gotsome) putchar('>'); } int keyword(const char *cp) { const struct keytab *kt = keywords; while (kt->kt_cp != NULL && strcmp(kt->kt_cp, cp) != 0) kt++; return (kt->kt_i); } static void sodump(struct sockaddr *sa, const char *which) { char atalk_buf[ATALK_BUF_SIZE]; #ifdef INET6 char nbuf[INET6_ADDRSTRLEN]; #endif switch (sa->sa_family) { case AF_LINK: (void)printf("%s: link %s; ", which, link_ntoa((struct sockaddr_dl *)(void *)sa)); break; #ifdef INET case AF_INET: (void)printf("%s: inet %s; ", which, inet_ntoa(((struct sockaddr_in *)(void *)sa)->sin_addr)); break; #endif #ifdef INET6 case AF_INET6: (void)printf("%s: inet6 %s; ", which, inet_ntop(sa->sa_family, &((struct sockaddr_in6 *)(void *)sa)->sin6_addr, nbuf, sizeof(nbuf))); break; #endif case AF_APPLETALK: (void)printf("%s: atalk %s; ", which, atalk_ntoa(((struct sockaddr_at *)(void *)sa)->sat_addr, atalk_buf)); break; } (void)fflush(stdout); } /* States*/ #define VIRGIN 0 #define GOTONE 1 #define GOTTWO 2 /* Inputs */ #define DIGIT (4*0) #define END (4*1) #define DELIM (4*2) static void sockaddr(char *addr, struct sockaddr *sa, size_t size) { char *cp = (char *)sa; char *cplim = cp + size; int byte = 0, state = VIRGIN, new = 0 /* foil gcc */; memset(cp, 0, size); cp++; do { if ((*addr >= '0') && (*addr <= '9')) { new = *addr - '0'; } else if ((*addr >= 'a') && (*addr <= 'f')) { new = *addr - 'a' + 10; } else if ((*addr >= 'A') && (*addr <= 'F')) { new = *addr - 'A' + 10; } else if (*addr == '\0') state |= END; else state |= DELIM; addr++; switch (state /* | INPUT */) { case GOTTWO | DIGIT: *cp++ = byte; /*FALLTHROUGH*/ case VIRGIN | DIGIT: state = GOTONE; byte = new; continue; case GOTONE | DIGIT: state = GOTTWO; byte = new + (byte << 4); continue; default: /* | DELIM */ state = VIRGIN; *cp++ = byte; byte = 0; continue; case GOTONE | END: case GOTTWO | END: *cp++ = byte; /* FALLTHROUGH */ case VIRGIN | END: break; } break; } while (cp < cplim); sa->sa_len = cp - (char *)sa; } static int atalk_aton(const char *text, struct at_addr *addr) { u_int net, node; if (sscanf(text, "%u.%u", &net, &node) != 2 || net > 0xffff || node > 0xff) return(0); addr->s_net = htons(net); addr->s_node = node; return(1); } static char * atalk_ntoa(struct at_addr at, char buf[ATALK_BUF_SIZE]) { (void)snprintf(buf, ATALK_BUF_SIZE, "%u.%u", ntohs(at.s_net), at.s_node); buf[ATALK_BUF_SIZE - 1] = '\0'; return(buf); } Index: head/usr.bin/netstat/main.c =================================================================== --- head/usr.bin/netstat/main.c (revision 260523) +++ head/usr.bin/netstat/main.c (revision 260524) @@ -1,856 +1,870 @@ /*- * Copyright (c) 1983, 1988, 1993 * 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. * 4. 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. */ #ifndef lint char const copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n\ Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 3/1/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #ifdef NETGRAPH #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" static struct nlist nl[] = { #define N_IFNET 0 { .n_name = "_ifnet" }, /* XXXGL: can be deleted */ #define N_RTSTAT 1 { .n_name = "_rtstat" }, #define N_RTREE 2 { .n_name = "_rt_tables"}, #define N_MRTSTAT 3 { .n_name = "_mrtstat" }, #define N_MFCHASHTBL 4 { .n_name = "_mfchashtbl" }, #define N_VIFTABLE 5 { .n_name = "_viftable" }, #define N_IPX 6 { .n_name = "_ipxpcb_list"}, #define N_IPXSTAT 7 { .n_name = "_ipxstat"}, #define N_SPXSTAT 8 { .n_name = "_spx_istat"}, #define N_DDPSTAT 9 { .n_name = "_ddpstat"}, #define N_DDPCB 10 { .n_name = "_ddpcb"}, #define N_NGSOCKS 11 { .n_name = "_ngsocklist"}, #define N_IP6STAT 12 { .n_name = "_ip6stat" }, #define N_ICMP6STAT 13 { .n_name = "_icmp6stat" }, #define N_IPSECSTAT 14 { .n_name = "_ipsec4stat" }, #define N_IPSEC6STAT 15 { .n_name = "_ipsec6stat" }, #define N_PIM6STAT 16 { .n_name = "_pim6stat" }, #define N_MRT6STAT 17 { .n_name = "_mrt6stat" }, #define N_MF6CTABLE 18 { .n_name = "_mf6ctable" }, #define N_MIF6TABLE 19 { .n_name = "_mif6table" }, #define N_PFKEYSTAT 20 { .n_name = "_pfkeystat" }, #define N_RTTRASH 21 { .n_name = "_rttrash" }, #define N_CARPSTAT 22 { .n_name = "_carpstats" }, #define N_PFSYNCSTAT 23 { .n_name = "_pfsyncstats" }, #define N_AHSTAT 24 { .n_name = "_ahstat" }, #define N_ESPSTAT 25 { .n_name = "_espstat" }, #define N_IPCOMPSTAT 26 { .n_name = "_ipcompstat" }, #define N_TCPSTAT 27 { .n_name = "_tcpstat" }, #define N_UDPSTAT 28 { .n_name = "_udpstat" }, #define N_IPSTAT 29 { .n_name = "_ipstat" }, #define N_ICMPSTAT 30 { .n_name = "_icmpstat" }, #define N_IGMPSTAT 31 { .n_name = "_igmpstat" }, #define N_PIMSTAT 32 { .n_name = "_pimstat" }, #define N_TCBINFO 33 { .n_name = "_tcbinfo" }, #define N_UDBINFO 34 { .n_name = "_udbinfo" }, #define N_DIVCBINFO 35 { .n_name = "_divcbinfo" }, #define N_RIPCBINFO 36 { .n_name = "_ripcbinfo" }, #define N_UNP_COUNT 37 { .n_name = "_unp_count" }, #define N_UNP_GENCNT 38 { .n_name = "_unp_gencnt" }, #define N_UNP_DHEAD 39 { .n_name = "_unp_dhead" }, #define N_UNP_SHEAD 40 { .n_name = "_unp_shead" }, #define N_RIP6STAT 41 { .n_name = "_rip6stat" }, #define N_SCTPSTAT 42 { .n_name = "_sctpstat" }, #define N_MFCTABLESIZE 43 { .n_name = "_mfctablesize" }, #define N_ARPSTAT 44 { .n_name = "_arpstat" }, #define N_UNP_SPHEAD 45 { .n_name = "unp_sphead" }, #define N_SFSTAT 46 { .n_name = "_sfstat"}, { .n_name = NULL }, }; struct protox { int pr_index; /* index into nlist of cb head */ int pr_sindex; /* index into nlist of stat block */ u_char pr_wanted; /* 1 if wanted, 0 otherwise */ void (*pr_cblocks)(u_long, const char *, int, int); /* control blocks printing routine */ void (*pr_stats)(u_long, const char *, int, int); /* statistics printing routine */ void (*pr_istats)(char *); /* per/if statistics printing routine */ const char *pr_name; /* well-known name */ int pr_usesysctl; /* non-zero if we use sysctl, not kvm */ int pr_protocol; } protox[] = { { N_TCBINFO, N_TCPSTAT, 1, protopr, tcp_stats, NULL, "tcp", 1, IPPROTO_TCP }, { N_UDBINFO, N_UDPSTAT, 1, protopr, udp_stats, NULL, "udp", 1, IPPROTO_UDP }, #ifdef SCTP { -1, N_SCTPSTAT, 1, sctp_protopr, sctp_stats, NULL, "sctp", 1, IPPROTO_SCTP }, #endif #ifdef SDP { -1, -1, 1, protopr, NULL, NULL, "sdp", 1, IPPROTO_TCP }, #endif { N_DIVCBINFO, -1, 1, protopr, NULL, NULL, "divert", 1, IPPROTO_DIVERT }, { N_RIPCBINFO, N_IPSTAT, 1, protopr, ip_stats, NULL, "ip", 1, IPPROTO_RAW }, { N_RIPCBINFO, N_ICMPSTAT, 1, protopr, icmp_stats, NULL, "icmp", 1, IPPROTO_ICMP }, { N_RIPCBINFO, N_IGMPSTAT, 1, protopr, igmp_stats, NULL, "igmp", 1, IPPROTO_IGMP }, #ifdef IPSEC { -1, N_IPSECSTAT, 1, NULL, /* keep as compat */ ipsec_stats, NULL, "ipsec", 0, 0}, { -1, N_AHSTAT, 1, NULL, ah_stats, NULL, "ah", 0, 0}, { -1, N_ESPSTAT, 1, NULL, esp_stats, NULL, "esp", 0, 0}, { -1, N_IPCOMPSTAT, 1, NULL, ipcomp_stats, NULL, "ipcomp", 0, 0}, #endif { N_RIPCBINFO, N_PIMSTAT, 1, protopr, pim_stats, NULL, "pim", 1, IPPROTO_PIM }, { -1, N_CARPSTAT, 1, NULL, carp_stats, NULL, "carp", 1, 0 }, #ifdef PF { -1, N_PFSYNCSTAT, 1, NULL, pfsync_stats, NULL, "pfsync", 1, 0 }, #endif { -1, N_ARPSTAT, 1, NULL, arp_stats, NULL, "arp", 1, 0 }, { -1, -1, 0, NULL, NULL, NULL, NULL, 0, 0 } }; #ifdef INET6 struct protox ip6protox[] = { { N_TCBINFO, N_TCPSTAT, 1, protopr, tcp_stats, NULL, "tcp", 1, IPPROTO_TCP }, { N_UDBINFO, N_UDPSTAT, 1, protopr, udp_stats, NULL, "udp", 1, IPPROTO_UDP }, { N_RIPCBINFO, N_IP6STAT, 1, protopr, ip6_stats, ip6_ifstats, "ip6", 1, IPPROTO_RAW }, { N_RIPCBINFO, N_ICMP6STAT, 1, protopr, icmp6_stats, icmp6_ifstats, "icmp6", 1, IPPROTO_ICMPV6 }, #ifdef SDP { -1, -1, 1, protopr, NULL, NULL, "sdp", 1, IPPROTO_TCP }, #endif #ifdef IPSEC { -1, N_IPSEC6STAT, 1, NULL, ipsec_stats, NULL, "ipsec6", 0, 0 }, #endif #ifdef notyet { -1, N_PIM6STAT, 1, NULL, pim6_stats, NULL, "pim6", 1, 0 }, #endif { -1, N_RIP6STAT, 1, NULL, rip6_stats, NULL, "rip6", 1, 0 }, { -1, -1, 0, NULL, NULL, NULL, NULL, 0, 0 } }; #endif /*INET6*/ #ifdef IPSEC struct protox pfkeyprotox[] = { { -1, N_PFKEYSTAT, 1, NULL, pfkey_stats, NULL, "pfkey", 0, 0 }, { -1, -1, 0, NULL, NULL, NULL, NULL, 0, 0 } }; #endif struct protox atalkprotox[] = { { N_DDPCB, N_DDPSTAT, 1, atalkprotopr, ddp_stats, NULL, "ddp", 0, 0 }, { -1, -1, 0, NULL, NULL, NULL, NULL, 0, 0 } }; #ifdef NETGRAPH struct protox netgraphprotox[] = { { N_NGSOCKS, -1, 1, netgraphprotopr, NULL, NULL, "ctrl", 0, 0 }, { N_NGSOCKS, -1, 1, netgraphprotopr, NULL, NULL, "data", 0, 0 }, { -1, -1, 0, NULL, NULL, NULL, NULL, 0, 0 } }; #endif #ifdef IPX struct protox ipxprotox[] = { { N_IPX, N_IPXSTAT, 1, ipxprotopr, ipx_stats, NULL, "ipx", 0, 0 }, { N_IPX, N_SPXSTAT, 1, ipxprotopr, spx_stats, NULL, "spx", 0, 0 }, { -1, -1, 0, NULL, NULL, NULL, 0, 0, 0 } }; #endif struct protox *protoprotox[] = { protox, #ifdef INET6 ip6protox, #endif #ifdef IPSEC pfkeyprotox, #endif #ifdef IPX ipxprotox, #endif atalkprotox, NULL }; static void printproto(struct protox *, const char *); static void usage(void); static struct protox *name2protox(const char *); static struct protox *knownname(const char *); static kvm_t *kvmd; static char *nlistf = NULL, *memf = NULL; int Aflag; /* show addresses of protocol control block */ int aflag; /* show all sockets (including servers) */ int Bflag; /* show information about bpf consumers */ int bflag; /* show i/f total bytes in/out */ int dflag; /* show i/f dropped packets */ int gflag; /* show group (multicast) routing or stats */ int hflag; /* show counters in human readable format */ int iflag; /* show interfaces */ int Lflag; /* show size of listen queues */ int mflag; /* show memory stats */ int noutputs = 0; /* how much outputs before we exit */ int numeric_addr; /* show addresses numerically */ int numeric_port; /* show ports numerically */ static int pflag; /* show given protocol */ int Qflag; /* show netisr information */ int rflag; /* show routing tables (or routing stats) */ int sflag; /* show protocol statistics */ int Wflag; /* wide display */ int Tflag; /* TCP Information */ int xflag; /* extra information, includes all socket buffer info */ int zflag; /* zero stats */ int interval; /* repeat interval for i/f stats */ char *interface; /* desired i/f for stats, or NULL for all i/fs */ int unit; /* unit number for above */ int af; /* address family */ int live; /* true if we are examining a live system */ int main(int argc, char *argv[]) { struct protox *tp = NULL; /* for printing cblocks & stats */ int ch; int fib = -1; char *endptr; af = AF_UNSPEC; - while ((ch = getopt(argc, argv, "AaBbdF:f:ghI:iLlM:mN:np:Qq:rSTsuWw:xz")) + while ((ch = getopt(argc, argv, "46AaBbdF:f:ghI:iLlM:mN:np:Qq:rSTsuWw:xz")) != -1) switch(ch) { + case '4': +#ifdef INET + af = AF_INET; +#else + errx(1, "IPv4 support is not compiled in"); +#endif + break; + case '6': +#ifdef INET6 + af = AF_INET6; +#else + errx(1, "IPv6 support is not compiled in"); +#endif + break; case 'A': Aflag = 1; break; case 'a': aflag = 1; break; case 'B': Bflag = 1; break; case 'b': bflag = 1; break; case 'd': dflag = 1; break; case 'F': fib = strtol(optarg, &endptr, 0); if (*endptr != '\0' || (fib == 0 && (errno == EINVAL || errno == ERANGE))) errx(1, "%s: invalid fib", optarg); break; case 'f': if (strcmp(optarg, "ipx") == 0) af = AF_IPX; else if (strcmp(optarg, "inet") == 0) af = AF_INET; #ifdef INET6 else if (strcmp(optarg, "inet6") == 0) af = AF_INET6; #endif #ifdef IPSEC else if (strcmp(optarg, "pfkey") == 0) af = PF_KEY; #endif else if (strcmp(optarg, "unix") == 0) af = AF_UNIX; else if (strcmp(optarg, "atalk") == 0) af = AF_APPLETALK; #ifdef NETGRAPH else if (strcmp(optarg, "ng") == 0 || strcmp(optarg, "netgraph") == 0) af = AF_NETGRAPH; #endif else if (strcmp(optarg, "link") == 0) af = AF_LINK; else { errx(1, "%s: unknown address family", optarg); } break; case 'g': gflag = 1; break; case 'h': hflag = 1; break; case 'I': { char *cp; iflag = 1; for (cp = interface = optarg; isalpha(*cp); cp++) continue; unit = atoi(cp); break; } case 'i': iflag = 1; break; case 'L': Lflag = 1; break; case 'M': memf = optarg; break; case 'm': mflag = 1; break; case 'N': nlistf = optarg; break; case 'n': numeric_addr = numeric_port = 1; break; case 'p': if ((tp = name2protox(optarg)) == NULL) { errx(1, "%s: unknown or uninstrumented protocol", optarg); } pflag = 1; break; case 'Q': Qflag = 1; break; case 'q': noutputs = atoi(optarg); if (noutputs != 0) noutputs++; break; case 'r': rflag = 1; break; case 's': ++sflag; break; case 'S': numeric_addr = 1; break; case 'u': af = AF_UNIX; break; case 'W': case 'l': Wflag = 1; break; case 'w': interval = atoi(optarg); iflag = 1; break; case 'T': Tflag = 1; break; case 'x': xflag = 1; break; case 'z': zflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; #define BACKWARD_COMPATIBILITY #ifdef BACKWARD_COMPATIBILITY if (*argv) { if (isdigit(**argv)) { interval = atoi(*argv); if (interval <= 0) usage(); ++argv; iflag = 1; } if (*argv) { nlistf = *argv; if (*++argv) memf = *argv; } } #endif /* * Discard setgid privileges if not the running kernel so that bad * guys can't print interesting stuff from kernel memory. */ live = (nlistf == NULL && memf == NULL); if (!live) setgid(getgid()); if (xflag && Tflag) errx(1, "-x and -T are incompatible, pick one."); if (Bflag) { if (!live) usage(); bpf_stats(interface); exit(0); } if (mflag) { if (!live) { if (kread(0, NULL, 0) == 0) mbpr(kvmd, nl[N_SFSTAT].n_value); } else mbpr(NULL, 0); exit(0); } if (Qflag) { if (!live) { if (kread(0, NULL, 0) == 0) netisr_stats(kvmd); } else netisr_stats(NULL); exit(0); } #if 0 /* * Keep file descriptors open to avoid overhead * of open/close on each call to get* routines. */ sethostent(1); setnetent(1); #else /* * This does not make sense any more with DNS being default over * the files. Doing a setXXXXent(1) causes a tcp connection to be * used for the queries, which is slower. */ #endif if (iflag && !sflag) { intpr(interval, NULL, af); exit(0); } if (rflag) { if (sflag) rt_stats(); else routepr(fib, af); exit(0); } if (gflag) { if (sflag) { if (af == AF_INET || af == AF_UNSPEC) mrt_stats(); #ifdef INET6 if (af == AF_INET6 || af == AF_UNSPEC) mrt6_stats(); #endif } else { if (af == AF_INET || af == AF_UNSPEC) mroutepr(); #ifdef INET6 if (af == AF_INET6 || af == AF_UNSPEC) mroute6pr(); #endif } exit(0); } /* Load all necessary kvm symbols */ kresolve_list(nl); if (tp) { printproto(tp, tp->pr_name); exit(0); } if (af == AF_INET || af == AF_UNSPEC) for (tp = protox; tp->pr_name; tp++) printproto(tp, tp->pr_name); #ifdef INET6 if (af == AF_INET6 || af == AF_UNSPEC) for (tp = ip6protox; tp->pr_name; tp++) printproto(tp, tp->pr_name); #endif /*INET6*/ #ifdef IPSEC if (af == PF_KEY || af == AF_UNSPEC) for (tp = pfkeyprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); #endif /*IPSEC*/ #ifdef IPX if (af == AF_IPX || af == AF_UNSPEC) { for (tp = ipxprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); } #endif /* IPX */ if (af == AF_APPLETALK || af == AF_UNSPEC) for (tp = atalkprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); #ifdef NETGRAPH if (af == AF_NETGRAPH || af == AF_UNSPEC) for (tp = netgraphprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); #endif /* NETGRAPH */ if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) unixpr(nl[N_UNP_COUNT].n_value, nl[N_UNP_GENCNT].n_value, nl[N_UNP_DHEAD].n_value, nl[N_UNP_SHEAD].n_value, nl[N_UNP_SPHEAD].n_value); exit(0); } /* * Print out protocol statistics or control blocks (per sflag). * If the interface was not specifically requested, and the symbol * is not in the namelist, ignore this one. */ static void printproto(struct protox *tp, const char *name) { void (*pr)(u_long, const char *, int, int); u_long off; if (sflag) { if (iflag) { if (tp->pr_istats) intpr(interval, tp->pr_istats, af); else if (pflag) printf("%s: no per-interface stats routine\n", tp->pr_name); return; } else { pr = tp->pr_stats; if (!pr) { if (pflag) printf("%s: no stats routine\n", tp->pr_name); return; } if (tp->pr_usesysctl && live) off = 0; else if (tp->pr_sindex < 0) { if (pflag) printf( "%s: stats routine doesn't work on cores\n", tp->pr_name); return; } else off = nl[tp->pr_sindex].n_value; } } else { pr = tp->pr_cblocks; if (!pr) { if (pflag) printf("%s: no PCB routine\n", tp->pr_name); return; } if (tp->pr_usesysctl && live) off = 0; else if (tp->pr_index < 0) { if (pflag) printf( "%s: PCB routine doesn't work on cores\n", tp->pr_name); return; } else off = nl[tp->pr_index].n_value; } if (pr != NULL && (off || (live && tp->pr_usesysctl) || af != AF_UNSPEC)) (*pr)(off, name, af, tp->pr_protocol); } static int kvmd_init(void) { char errbuf[_POSIX2_LINE_MAX]; if (kvmd != NULL) return (0); kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); setgid(getgid()); if (kvmd == NULL) { warnx("kvm not available: %s", errbuf); return (-1); } return (0); } /* * Resolve symbol list, return 0 on success. */ int kresolve_list(struct nlist *_nl) { if ((kvmd == NULL) && (kvmd_init() != 0)) return (-1); if (_nl[0].n_type != 0) return (0); if (kvm_nlist(kvmd, _nl) < 0) { if (nlistf) errx(1, "%s: kvm_nlist: %s", nlistf, kvm_geterr(kvmd)); else errx(1, "kvm_nlist: %s", kvm_geterr(kvmd)); } return (0); } /* * Read kernel memory, return 0 on success. */ int kread(u_long addr, void *buf, size_t size) { if (kvmd_init() < 0) return (-1); if (!buf) return (0); if (kvm_read(kvmd, addr, buf, size) != (ssize_t)size) { warnx("%s", kvm_geterr(kvmd)); return (-1); } return (0); } /* * Read an array of N counters in kernel memory into array of N uint64_t's. */ int kread_counters(u_long addr, void *buf, size_t size) { uint64_t *c = buf; if (kvmd_init() < 0) return (-1); if (kread(addr, buf, size) < 0) return (-1); while (size != 0) { *c = kvm_counter_u64_fetch(kvmd, *c); size -= sizeof(*c); c++; } return (0); } const char * plural(uintmax_t n) { return (n != 1 ? "s" : ""); } const char * plurales(uintmax_t n) { return (n != 1 ? "es" : ""); } const char * pluralies(uintmax_t n) { return (n != 1 ? "ies" : "y"); } /* * Find the protox for the given "well-known" name. */ static struct protox * knownname(const char *name) { struct protox **tpp, *tp; for (tpp = protoprotox; *tpp; tpp++) for (tp = *tpp; tp->pr_name; tp++) if (strcmp(tp->pr_name, name) == 0) return (tp); return (NULL); } /* * Find the protox corresponding to name. */ static struct protox * name2protox(const char *name) { struct protox *tp; char **alias; /* alias from p->aliases */ struct protoent *p; /* * Try to find the name in the list of "well-known" names. If that * fails, check if name is an alias for an Internet protocol. */ if ((tp = knownname(name)) != NULL) return (tp); setprotoent(1); /* make protocol lookup cheaper */ while ((p = getprotoent()) != NULL) { /* assert: name not same as p->name */ for (alias = p->p_aliases; *alias; alias++) if (strcmp(name, *alias) == 0) { endprotoent(); return (knownname(p->p_name)); } } endprotoent(); return (NULL); } static void usage(void) { (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", -"usage: netstat [-AaLnSTWx] [-f protocol_family | -p protocol]\n" +"usage: netstat [-46AaLnSTWx] [-f protocol_family | -p protocol]\n" " [-M core] [-N system]", -" netstat -i | -I interface [-abdhnW] [-f address_family]\n" +" netstat -i | -I interface [-46abdhnW] [-f address_family]\n" " [-M core] [-N system]", -" netstat -w wait [-I interface] [-d] [-M core] [-N system] [-q howmany]", -" netstat -s [-s] [-z] [-f protocol_family | -p protocol]\n" +" netstat -w wait [-I interface] [-46d] [-M core] [-N system] [-q howmany]", +" netstat -s [-s] [-46z] [-f protocol_family | -p protocol]\n" " [-M core] [-N system]", -" netstat -i | -I interface -s [-f protocol_family | -p protocol]\n" +" netstat -i | -I interface [-46s] [-f protocol_family | -p protocol]\n" " [-M core] [-N system]", " netstat -m [-M core] [-N system]", " netstat -B [-I interface]", -" netstat -r [-AanW] [-f address_family] [-M core] [-N system]", +" netstat -r [-46AanW] [-f address_family] [-M core] [-N system]", " netstat -rs [-s] [-M core] [-N system]", -" netstat -g [-W] [-f address_family] [-M core] [-N system]", -" netstat -gs [-s] [-f address_family] [-M core] [-N system]", +" netstat -g [-46W] [-f address_family] [-M core] [-N system]", +" netstat -gs [-46s] [-f address_family] [-M core] [-N system]", " netstat -Q"); exit(1); } Index: head/usr.bin/netstat/netstat.1 =================================================================== --- head/usr.bin/netstat/netstat.1 (revision 260523) +++ head/usr.bin/netstat/netstat.1 (revision 260524) @@ -1,548 +1,557 @@ .\" Copyright (c) 1983, 1990, 1992, 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. .\" 4. 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. .\" .\" @(#)netstat.1 8.8 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" .Dd October 15, 2013 .Dt NETSTAT 1 .Os .Sh NAME .Nm netstat .Nd show network status .Sh DESCRIPTION The .Nm command symbolically displays the contents of various network-related data structures. There are a number of output formats, depending on the options for the information presented. .Bl -tag -width indent .It Xo .Bk -words .Nm -.Op Fl AaLnSTWx +.Op Fl 46AaLnSTWx .Op Fl f Ar protocol_family | Fl p Ar protocol .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display a list of active sockets (protocol control blocks) for each network protocol, for a particular .Ar protocol_family , or for a single .Ar protocol . If .Fl A is also present, show the address of a protocol control block (PCB) associated with a socket; used for debugging. If .Fl a is also present, show the state of all sockets; normally sockets used by server processes are not shown. If .Fl L is also present, show the size of the various listen queues. The first count shows the number of unaccepted connections, the second count shows the amount of unaccepted incomplete connections, and the third count is the maximum number of queued connections. If .Fl S is also present, show network addresses as numbers (as with .Fl n ) but show ports symbolically. If .Fl x is present, display socket buffer and tcp timer statistics for each internet socket. When .Fl T is present, display information from the TCP control block, including retransmits, out-of-order packets received, and zero-sized windows advertised. .It Xo .Bk -words .Nm .Fl i | I Ar interface -.Op Fl abdhnW +.Op Fl 46abdhnW .Op Fl f Ar address_family .Ek .Xc Show the state of all network interfaces or a single .Ar interface which have been auto-configured (interfaces statically configured into a system, but not located at boot time are not shown). An asterisk .Pq Dq Li * after an interface name indicates that the interface is .Dq down . If .Fl a is also present, multicast addresses currently in use are shown for each Ethernet interface and for each IP interface address. Multicast addresses are shown on separate lines following the interface address with which they are associated. If .Fl b is also present, show the number of bytes in and out. If .Fl d is also present, show the number of dropped packets. If .Fl h is also present, print all counters in human readable form. If .Fl W is also present, print interface names using a wider field size. .It Xo .Bk -words .Nm .Fl w Ar wait .Op Fl I Ar interface .Op Fl d .Op Fl M Ar core .Op Fl N Ar system .Op Fl q Ar howmany .Ek .Xc At intervals of .Ar wait seconds, display the information regarding packet traffic on all configured network interfaces or a single .Ar interface . If .Fl q is also present, exit after .Ar howmany outputs. If .Fl d is also present, show the number of dropped packets. .It Xo .Bk -words .Nm .Fl s Op Fl s -.Op Fl z +.Op Fl 46z .Op Fl f Ar protocol_family | Fl p Ar protocol .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display system-wide statistics for each network protocol, for a particular .Ar protocol_family , or for a single .Ar protocol . If .Fl s is repeated, counters with a value of zero are suppressed. If .Fl z is also present, reset statistic counters after displaying them. .It Xo .Bk -words .Nm .Fl i | I Ar interface Fl s +.Op Fl 46 .Op Fl f Ar protocol_family | Fl p Ar protocol .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display per-interface statistics for each network protocol, for a particular .Ar protocol_family , or for a single .Ar protocol . .It Xo .Bk -words .Nm .Fl m .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Show statistics recorded by the memory management routines .Pq Xr mbuf 9 . The network manages a private pool of memory buffers. .It Xo .Bk -words .Nm .Fl B .Op Fl z .Op Fl I Ar interface .Ek .Xc Show statistics about .Xr bpf 4 peers. This includes information like how many packets have been matched, dropped and received by the bpf device, also information about current buffer sizes and device states. .It Xo .Bk -words .Nm .Fl r -.Op Fl AanW +.Op Fl 46AanW .Op Fl F Ar fibnum .Op Fl f Ar address_family .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display the contents of routing tables. When .Fl f is specified, a routing table for a particular .Ar address_family is displayed. When .Fl F is specified, a routing table with the number .Ar fibnum is displayed. If the specified .Ar fibnum is -1 or .Fl F is not specified, the default routing table is displayed. If .Fl A is also present, show the contents of the internal Patricia tree structures; used for debugging. If .Fl a is also present, show protocol-cloned routes (routes generated by an .Dv RTF_PRCLONING parent route); normally these routes are not shown. When .Fl W is also present, show the path MTU for each route, and print interface names with a wider field size. .It Xo .Bk -words .Nm .Fl rs .Op Fl s .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display routing statistics. If .Fl s is repeated, counters with a value of zero are suppressed. .It Xo .Bk -words .Nm .Fl g -.Op Fl W +.Op Fl 46W .Op Fl f Ar address_family .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Display the contents of the multicast virtual interface tables, and multicast forwarding caches. Entries in these tables will appear only when the kernel is actively forwarding multicast sessions. This option is applicable only to the .Cm inet and .Cm inet6 address families. .It Xo .Bk -words .Nm .Fl gs -.Op Fl s +.Op Fl 46s .Op Fl f Ar address_family .Op Fl M Ar core .Op Fl N Ar system .Ek .Xc Show multicast routing statistics. If .Fl s is repeated, counters with a value of zero are suppressed. .It Xo .Bk -words .Nm .Fl Q .Ek .Xc Show .Xr netisr 9 statistics. The flags field shows available ISR handlers: .Bl -column ".Li W" ".Dv NETISR_SNP_FLAGS_DRAINEDCPU" .It Li C Ta Dv NETISR_SNP_FLAGS_M2CPUID Ta "Able to map mbuf to cpu id" .It Li D Ta Dv NETISR_SNP_FLAGS_DRAINEDCPU Ta "Has queue drain handler" .It Li F Ta Dv NETISR_SNP_FLAGS_M2FLOW Ta "Able to map mbuf to flow id" .El .El .Pp Some options have the general meaning: .Bl -tag -width flag +.It Fl 4 +Is shorthand for +.Fl f +.Ar inet +.It Fl 6 +Is shorthand for +.Fl f +.Ar inet6 .It Fl f Ar address_family , Fl p Ar protocol Limit display to those records of the specified .Ar address_family or a single .Ar protocol . The following address families and protocols are recognized: .Pp .Bl -tag -width ".Cm netgraph , ng Pq Dv AF_NETGRAPH" -compact .It Em Family .Em Protocols .It Cm inet Pq Dv AF_INET .Cm divert , icmp , igmp , ip , ipsec , pim, sctp , tcp , udp .It Cm inet6 Pq Dv AF_INET6 .Cm icmp6 , ip6 , ipsec6 , rip6 , tcp , udp .It Cm pfkey Pq Dv PF_KEY .Cm pfkey .It Cm atalk Pq Dv AF_APPLETALK .Cm ddp .It Cm netgraph , ng Pq Dv AF_NETGRAPH .Cm ctrl , data .It Cm ipx Pq Dv AF_IPX .Cm ipx , spx .\".It Cm ns Pq Dv AF_NS .\".Cm idp , ns_err , spp .\".It Cm iso Pq Dv AF_ISO .\".Cm clnp , cltp , esis , tp .It Cm unix Pq Dv AF_UNIX .It Cm link Pq Dv AF_LINK .El .Pp The program will complain if .Ar protocol is unknown or if there is no statistics routine for it. .It Fl M Extract values associated with the name list from the specified core instead of the default .Pa /dev/kmem . .It Fl N Extract the name list from the specified system instead of the default, which is the kernel image the system has booted from. .It Fl n Show network addresses and ports as numbers. Normally .Nm attempts to resolve addresses and ports, and display them symbolically. .It Fl W In certain displays, avoid truncating addresses even if this causes some fields to overflow. .El .Pp The default display, for active sockets, shows the local and remote addresses, send and receive queue sizes (in bytes), protocol, and the internal state of the protocol. Address formats are of the form .Dq host.port or .Dq network.port if a socket's address specifies a network but no specific host address. When known, the host and network addresses are displayed symbolically according to the databases .Xr hosts 5 and .Xr networks 5 , respectively. If a symbolic name for an address is unknown, or if the .Fl n option is specified, the address is printed numerically, according to the address family. For more information regarding the Internet IPv4 .Dq dot format , refer to .Xr inet 3 . Unspecified, or .Dq wildcard , addresses and ports appear as .Dq Li * . .Pp The interface display provides a table of cumulative statistics regarding packets transferred, errors, and collisions. The network addresses of the interface and the maximum transmission unit .Pq Dq mtu are also displayed. .Pp The routing table display indicates the available routes and their status. Each route consists of a destination host or network, and a gateway to use in forwarding packets. The flags field shows a collection of information about the route stored as binary choices. The individual flags are discussed in more detail in the .Xr route 8 and .Xr route 4 manual pages. The mapping between letters and flags is: .Bl -column ".Li W" ".Dv RTF_WASCLONED" .It Li 1 Ta Dv RTF_PROTO1 Ta "Protocol specific routing flag #1" .It Li 2 Ta Dv RTF_PROTO2 Ta "Protocol specific routing flag #2" .It Li 3 Ta Dv RTF_PROTO3 Ta "Protocol specific routing flag #3" .It Li B Ta Dv RTF_BLACKHOLE Ta "Just discard pkts (during updates)" .It Li b Ta Dv RTF_BROADCAST Ta "The route represents a broadcast address" .It Li C Ta Dv RTF_CLONING Ta "Generate new routes on use" .It Li c Ta Dv RTF_PRCLONING Ta "Protocol-specified generate new routes on use" .It Li D Ta Dv RTF_DYNAMIC Ta "Created dynamically (by redirect)" .It Li G Ta Dv RTF_GATEWAY Ta "Destination requires forwarding by intermediary" .It Li H Ta Dv RTF_HOST Ta "Host entry (net otherwise)" .It Li L Ta Dv RTF_LLINFO Ta "Valid protocol to link address translation" .It Li M Ta Dv RTF_MODIFIED Ta "Modified dynamically (by redirect)" .It Li R Ta Dv RTF_REJECT Ta "Host or net unreachable" .It Li S Ta Dv RTF_STATIC Ta "Manually added" .It Li U Ta Dv RTF_UP Ta "Route usable" .It Li W Ta Dv RTF_WASCLONED Ta "Route was generated as a result of cloning" .It Li X Ta Dv RTF_XRESOLVE Ta "External daemon translates proto to link address" .El .Pp Direct routes are created for each interface attached to the local host; the gateway field for such entries shows the address of the outgoing interface. The refcnt field gives the current number of active uses of the route. Connection oriented protocols normally hold on to a single route for the duration of a connection while connectionless protocols obtain a route while sending to the same destination. The use field provides a count of the number of packets sent using that route. The interface entry indicates the network interface utilized for the route. .Pp When .Nm is invoked with the .Fl w option and a .Ar wait interval argument, it displays a running count of statistics related to network interfaces. An obsolescent version of this option used a numeric parameter with no option, and is currently supported for backward compatibility. By default, this display summarizes information for all interfaces. Information for a specific interface may be displayed with the .Fl I option. .Pp The .Xr bpf 4 flags displayed when .Nm is invoked with the .Fl B option represent the underlying parameters of the bpf peer. Each flag is represented as a single lower case letter. The mapping between the letters and flags in order of appearance are: .Bl -column ".Li i" .It Li p Ta Set if listening promiscuously .It Li i Ta Dv BIOCIMMEDIATE No has been set on the device .It Li f Ta Dv BIOCGHDRCMPLT No status: source link addresses are being filled automatically .It Li s Ta Dv BIOCGSEESENT No status: see packets originating locally and remotely on the interface. .It Li a Ta Packet reception generates a signal .It Li l Ta Dv BIOCLOCK No status: descriptor has been locked .El .Pp For more information about these flags, please refer to .Xr bpf 4 . .Pp The .Fl x flag causes .Nm to output all the information recorded about data stored in the socket buffers. The fields are: .Bl -column ".Li R-MBUF" .It Li R-MBUF Ta Number of mbufs in the receive queue. .It Li S-MBUF Ta Number of mbufs in the send queue. .It Li R-CLUS Ta Number of clusters, of any type, in the receive queue. .It Li S-CLUS Ta Number of clusters, of any type, in the send queue. .It Li R-HIWA Ta Receive buffer high water mark, in bytes. .It Li S-HIWA Ta Send buffer high water mark, in bytes. .It Li R-LOWA Ta Receive buffer low water mark, in bytes. .It Li S-LOWA Ta Send buffer low water mark, in bytes. .It Li R-BCNT Ta Receive buffer byte count. .It Li S-BCNT Ta Send buffer byte count. .It Li R-BMAX Ta Maximum bytes that can be used in the receive buffer. .It Li S-BMAX Ta Maximum bytes that can be used in the send buffer. .El .Sh SEE ALSO .Xr fstat 1 , .Xr nfsstat 1 , .Xr procstat 1 , .Xr ps 1 , .Xr sockstat 1 , .Xr bpf 4 , .Xr inet 4 , .Xr route 4 , .Xr unix 4 , .Xr hosts 5 , .Xr networks 5 , .Xr protocols 5 , .Xr services 5 , .Xr iostat 8 , .Xr route 8 , .Xr trpt 8 , .Xr vmstat 8 , .Xr mbuf 9 .Sh HISTORY The .Nm command appeared in .Bx 4.2 . .Pp IPv6 support was added by WIDE/KAME project. .Sh BUGS The notion of errors is ill-defined.