From: David van Moolenbroek Date: Wed, 15 Feb 2017 12:08:12 +0000 (+0000) Subject: Import NetBSD route(8) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/doc/mdoc.7.txt?a=commitdiff_plain;h=483e5955576fddc25239f5c789016a05fd002f93;p=minix.git Import NetBSD route(8) Change-Id: I724a2a56157ea72afdd3f6a82239687894c8e3e8 --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index 6739c0f9f..d50db4cfd 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -225,6 +225,7 @@ ./sbin/printconfig minix-base ./sbin/rcorder minix-base ./sbin/reboot minix-base +./sbin/route minix-base ./sbin/shutdown minix-base ./sbin/sysctl minix-base ./service minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index cb436cccd..823cc4047 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -175,6 +175,7 @@ ./usr/libdata/debug/sbin/printconfig.debug minix-debug debug ./usr/libdata/debug/sbin/rcorder.debug minix-debug debug ./usr/libdata/debug/sbin/reboot.debug minix-debug debug +./usr/libdata/debug/sbin/route.debug minix-debug debug ./usr/libdata/debug/sbin/shutdown.debug minix-debug debug ./usr/libdata/debug/sbin/sysctl.debug minix-debug debug ./usr/libdata/debug/service minix-debug diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index e00b6f1bb..58e2aaba0 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3455,6 +3455,7 @@ ./usr/man/man8/renice.8 minix-man ./usr/man/man8/repartition.8 minix-man ./usr/man/man8/rotate.8 minix-man +./usr/man/man8/route.8 minix-man ./usr/man/man8/rshd.8 minix-man ./usr/man/man8/screendump.8 minix-man ./usr/man/man8/serial-ip.8 minix-man obsolete diff --git a/sbin/Makefile b/sbin/Makefile index 0f38ce01f..0e9409926 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -11,7 +11,7 @@ SUBDIR= \ fsck ifconfig init \ mknod nologin \ ping \ - reboot rcorder \ + reboot rcorder route \ shutdown sysctl \ # support for various file systems diff --git a/sbin/route/Makefile b/sbin/route/Makefile new file mode 100644 index 000000000..a9113e319 --- /dev/null +++ b/sbin/route/Makefile @@ -0,0 +1,30 @@ +# $NetBSD: Makefile,v 1.29 2015/09/14 05:12:52 ozaki-r Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +.include + +RUMPPRG=route +MAN= route.8 +SRCS= route.c show.c keywords.c rtutil.c + +.PATH: ${.CURDIR}/../../lib/libc/net +RUMPSRCS= getaddrinfo.c getifaddrs.c getnameinfo.c +RUMPSRCS+= if_indextoname.c if_nametoindex.c +.if (${MKRUMP} != "no") +CPPFLAGS+= -DRUMP_ACTION +.endif + +.if (${USE_INET6} != "no") +CPPFLAGS+=-DINET6 +.endif + +# The Makefile over in ../../distrib/utils/x_route +# would like keywords.[ch] to always exist here, so +# they are now checked in as sources. +# +# CPPFLAGS+=-I. +# CLEANFILES+= keywords.c keywords.h +# keywords.c keywords.h : keywords.sh +# ${HOST_SH} keywords.sh + +.include diff --git a/sbin/route/extern.h b/sbin/route/extern.h new file mode 100644 index 000000000..9742b583d --- /dev/null +++ b/sbin/route/extern.h @@ -0,0 +1,40 @@ +/* $NetBSD: extern.h,v 1.15 2014/11/06 21:29:32 christos Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +struct sockaddr; +struct sockaddr_x25; +struct sockaddr_ns; + +void parse_show_opts(int, char * const *, int *, int *, const char **, bool); +/* show.c */ +void show(int, char * const *, int); + +/* route.c */ +extern int nflag, Sflag; +#define NOTDEFSTRING "0.0.0.0/xxx.xxx.xxx.xxx\0" +int keyword(const char *); +const char *ns_print(struct sockaddr_ns *); +void usage(const char *)__attribute__((__noreturn__)); diff --git a/sbin/route/keywords.c b/sbin/route/keywords.c new file mode 100644 index 000000000..5acc8875e --- /dev/null +++ b/sbin/route/keywords.c @@ -0,0 +1,65 @@ +/* $NetBSD: keywords.c,v 1.10 2013/03/01 18:25:17 joerg Exp $ */ + +/* WARNING! This file was generated by keywords.sh */ + +#include "keywords.h" + +struct keytab keywords[] = { + + {"add", K_ADD}, + {"atalk", K_ATALK}, + {"blackhole", K_BLACKHOLE}, + {"change", K_CHANGE}, + {"cloned", K_CLONED}, + {"cloning", K_CLONING}, + {"delete", K_DELETE}, + {"dst", K_DST}, + {"expire", K_EXPIRE}, + {"flush", K_FLUSH}, + {"gateway", K_GATEWAY}, + {"genmask", K_GENMASK}, + {"get", K_GET}, + {"host", K_HOST}, + {"hopcount", K_HOPCOUNT}, + {"iface", K_IFACE}, + {"interface", K_INTERFACE}, + {"ifa", K_IFA}, + {"ifp", K_IFP}, + {"inet", K_INET}, + {"inet6", K_INET6}, + {"link", K_LINK}, + {"llinfo", K_LLINFO}, + {"lock", K_LOCK}, + {"lockrest", K_LOCKREST}, + {"mask", K_MASK}, + {"monitor", K_MONITOR}, + {"mtu", K_MTU}, + {"net", K_NET}, + {"netmask", K_NETMASK}, + {"nostatic", K_NOSTATIC}, + {"prefixlen", K_PREFIXLEN}, + {"proto1", K_PROTO1}, + {"proto2", K_PROTO2}, + {"recvpipe", K_RECVPIPE}, + {"reject", K_REJECT}, + {"rtt", K_RTT}, + {"rttvar", K_RTTVAR}, + {"sa", K_SA}, + {"sendpipe", K_SENDPIPE}, + {"show", K_SHOW}, + {"ssthresh", K_SSTHRESH}, + {"static", K_STATIC}, + {"x25", K_X25}, + {"xns", K_XNS}, + {"xresolve", K_XRESOLVE}, + {"flushall", K_FLUSHALL}, + {"nocloned", K_NOCLONED}, + {"nocloning", K_NOCLONING}, + {"noblackhole", K_NOBLACKHOLE}, + {"noreject", K_NOREJECT}, + {"mpls", K_MPLS}, + {"tag", K_TAG}, + {"proxy", K_PROXY}, + {0, 0} +}; + diff --git a/sbin/route/keywords.h b/sbin/route/keywords.h new file mode 100644 index 000000000..33700aa11 --- /dev/null +++ b/sbin/route/keywords.h @@ -0,0 +1,64 @@ +/* $NetBSD: keywords.h,v 1.13 2013/03/01 18:25:17 joerg Exp $ */ + +/* WARNING! This file was generated by keywords.sh */ + +extern struct keytab { + const char *kt_cp; + int kt_i; +} keywords[]; + + +#define K_ADD 1 +#define K_ATALK 2 +#define K_BLACKHOLE 3 +#define K_CHANGE 4 +#define K_CLONED 5 +#define K_CLONING 6 +#define K_DELETE 7 +#define K_DST 8 +#define K_EXPIRE 9 +#define K_FLUSH 10 +#define K_GATEWAY 11 +#define K_GENMASK 12 +#define K_GET 13 +#define K_HOST 14 +#define K_HOPCOUNT 15 +#define K_IFACE 16 +#define K_INTERFACE 17 +#define K_IFA 18 +#define K_IFP 19 +#define K_INET 20 +#define K_INET6 21 +#define K_LINK 22 +#define K_LLINFO 23 +#define K_LOCK 24 +#define K_LOCKREST 25 +#define K_MASK 26 +#define K_MONITOR 27 +#define K_MTU 28 +#define K_NET 29 +#define K_NETMASK 30 +#define K_NOSTATIC 31 +#define K_PREFIXLEN 32 +#define K_PROTO1 33 +#define K_PROTO2 34 +#define K_RECVPIPE 35 +#define K_REJECT 36 +#define K_RTT 37 +#define K_RTTVAR 38 +#define K_SA 39 +#define K_SENDPIPE 40 +#define K_SHOW 41 +#define K_SSTHRESH 42 +#define K_STATIC 43 +#define K_X25 44 +#define K_XNS 45 +#define K_XRESOLVE 46 +#define K_FLUSHALL 47 +#define K_NOCLONED 48 +#define K_NOCLONING 49 +#define K_NOBLACKHOLE 50 +#define K_NOREJECT 51 +#define K_MPLS 52 +#define K_TAG 53 +#define K_PROXY 54 diff --git a/sbin/route/keywords.sh b/sbin/route/keywords.sh new file mode 100755 index 000000000..ae1476d72 --- /dev/null +++ b/sbin/route/keywords.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# $NetBSD: keywords.sh,v 1.11 2013/03/01 18:25:17 joerg Exp $ +# @(#)keywords 8.2 (Berkeley) 3/19/94 +# +# WARNING! If you change this file, re-run it! + +# This program requires "new" awk (or GNU awk). +awk=${AWK:-awk} + +cat << _EOF_ > _keywords.t1 +add +atalk +blackhole +change +cloned +cloning +delete +dst +expire +flush +gateway +genmask +get +host +hopcount +iface +interface +ifa +ifp +inet +inet6 +link +llinfo +lock +lockrest +mask +monitor +mtu +net +netmask +nostatic +prefixlen +proto1 +proto2 +recvpipe +reject +rtt +rttvar +sa +sendpipe +show +ssthresh +static +x25 +xns +xresolve +flushall +nocloned +nocloning +noblackhole +noreject +mpls +tag +proxy +_EOF_ + + +################################################################ +# Setup +################################################################ + +# This creates a stream of: +# keyword KEYWORD +# (lower case, upper case). +tr a-z A-Z < _keywords.t1 | +paste _keywords.t1 - > _keywords.t2 + + +################################################################ +# Generate the h file +################################################################ +exec > keywords.h + +echo '/* $'NetBSD'$ */ + +/* WARNING! This file was generated by keywords.sh */ + +extern struct keytab { + const char *kt_cp; + int kt_i; +} keywords[]; + +' # defines follow + +$awk '{ + printf("#define\tK_%s\t%d\n", $2, NR); +}' < _keywords.t2 + + +################################################################ +# Generate the c file +################################################################ +exec > keywords.c + +echo '/* $'NetBSD'$ */ + +/* WARNING! This file was generated by keywords.sh */ + +#include "keywords.h" + +struct keytab keywords[] = { +' # initializers follow + +$awk '{ + printf("\t{\"%s\", K_%s},\n", $1, $2); +}' < _keywords.t2 + +echo ' {0, 0} +}; +' # tail + + +################################################################ +# Cleanup +################################################################ + +rm -f _keywords.t1 _keywords.t2 +exit 0 diff --git a/sbin/route/prog_ops.h b/sbin/route/prog_ops.h new file mode 100644 index 000000000..c37dc2649 --- /dev/null +++ b/sbin/route/prog_ops.h @@ -0,0 +1,73 @@ +/* $NetBSD: prog_ops.h,v 1.3 2014/11/06 21:29:32 christos Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 _PROG_OPS_H_ +#define _PROG_OPS_H_ + +#include + +#ifndef CRUNCHOPS +/* XXX: Keep same order with netstat! */ +struct prog_ops { + int (*op_init)(void); + + int (*op_sysctl)(const int *, u_int, void *, size_t *, + const void *, size_t); + + int (*op_socket)(int, int, int); + int (*op_open)(const char *, int, ...); + pid_t (*op_getpid)(void); + + ssize_t (*op_read)(int, void *, size_t); + ssize_t (*op_write)(int, const void *, size_t); + + + int (*op_shutdown)(int, int); +}; +extern const struct prog_ops prog_ops; + +#define prog_init prog_ops.op_init +#define prog_socket prog_ops.op_socket +#define prog_open prog_ops.op_open +#define prog_getpid prog_ops.op_getpid +#define prog_read prog_ops.op_read +#define prog_write prog_ops.op_write +#define prog_sysctl prog_ops.op_sysctl +#define prog_shutdown prog_ops.op_shutdown +#else +#define prog_init ((int (*)(void))NULL) +#define prog_socket socket +#define prog_open open +#define prog_getpid getpid +#define prog_read read +#define prog_write write +#define prog_sysctl sysctl +#define prog_shutdown shutdown +#endif + +#endif /* _PROG_OPS_H_ */ diff --git a/sbin/route/route.8 b/sbin/route/route.8 new file mode 100644 index 000000000..aa90f9e2c --- /dev/null +++ b/sbin/route/route.8 @@ -0,0 +1,449 @@ +.\" $NetBSD: route.8,v 1.55 2015/03/23 18:33:17 roy Exp $ +.\" +.\" 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. +.\" 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. +.\" +.\" @(#)route.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd March 19, 2015 +.Dt ROUTE 8 +.Os +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables +.Sh SYNOPSIS +.Nm +.Op Fl dfLnqSsTtv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +.Nm +is a utility used to manually manipulate the network +routing tables. +Except for setting up the default route, it is normally not needed, +as a system routing table management daemon such as +.Xr routed 8 , +should tend to this task. +.Pp +.Nm +can be used to modify nearly any aspect of the routing policy, +except packet forwarding, which can be manipulated through the +.Xr sysctl 8 +command. +.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 +.Bl -tag -width Ds +.It Fl d +Turn on debugging +.It Fl f +Remove all routes (as per +.Cm flush ) . +If used in conjunction with the +.Cm add , +.Cm change , +.Cm delete +or +.Cm get +commands, +.Nm +removes the routes before performing the command. +.It Fl L +Don't show link layer entries in routing table. +.It Fl n +Bypasses 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 forgo this, especially when attempting to repair networking operations). +.It Fl q +Suppress all output from commands that manipulate the routing table. +.It Fl S +Print a space when a flag is missing so that flags are vertically aligned +instead of printing the flags that are set as a contiguous string. +.It Fl s +(short) Suppresses all output from a +.Cm get +command except for the actual gateway that will be used. +How the gateway is printed depends on the type of route being looked up. +.It Fl T +Show tags in the route display. +.It Fl t +Test only, don't perform any actions. +.It Fl v +(verbose) Print additional details. +.El +.Pp +The +.Nm +utility provides several commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm flushall +Remove all routes including the default gateway. +.It Cm delete +Delete a specific route. +.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 show +Print out the route table similar to "netstat \-r" (see +.Xr netstat 1 ) . +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.El +.Pp +The monitor command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm +.Op Fl n +.Cm monitor +.Ed +.Pp +The flush command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm +.Op Fl n +.Cm flush +.Op Ar family +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm +will ``flush'' the routing tables of all gateway entries. +When the address family is specified by any of the +.Fl xns , +.Fl atalk , +.Fl inet , +.Fl inet6 , +or +.Fl mpls +modifiers, only routes having destinations with addresses in the +delineated family will be manipulated. +.Pp +The other commands have the following syntax: +.Pp +.Bd -filled -offset indent -compact +.Nm +.Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Ar destination gateway +.Ed +.Pp +where +.Ar destination +is the destination host or network, and +.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 ``local address part'' of +.Dv INADDR_ANY , +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 +can 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 ; +and +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0 . +.Pp +The keyword +.Cm default +can be used as the +.Ar destination +to set up a default route to a smart +.Ar gateway . +If no other routes match, this default route will be used as a last resort. +.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. +.Pp +The optional modifiers +.Fl xns , +.Fl atalk , +and +.Fl link +specify that all subsequent addresses are in the +.Tn XNS , +or +.Tn AppleTalk +address families, +or are specified as link-level addresses in the form described in +.Xr link_addr 3 , +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional modifier +.Fl tag +specifies an address associated with the route. +How the address is used is specific to the address family of +the destination and the interface used to forward the packet. +Currently route tags are consumed only by the +.Xr mpls 4 +stack; therefore +.Nm +assumes that the subsequent addresses are in the +.Tn MPLS +address family. +See +.Xr mpls 4 +for examples of setting routes involving MPLS. +.Pp +The optional +.Fl netmask +qualifier is intended +to achieve the effect of an +.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 +.Dv AF_INET +case +can be overridden by making sure this option follows the destination parameter. +.Fl prefixlen +is also available for similar purpose, in IPv4 and IPv6 case. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags are displayed using the following ID characters in the routing +display and may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bl -column "ID" "xnoblackhole" "xRTF_BLACKHOLE" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +.It Sy "ID" Ta Sy "Modifier" Ta Sy " Flag Bit" Ta Sy "Description" +.It Li " " Ta -iface Ta ~RTF_GATEWAY Ta destination is directly reachable +.It Li 1 Ta -proto1 Ta " RTF_PROTO1" Ta set protocol specific flag #1 +.It Li 2 Ta -proto2 Ta " RTF_PROTO2" Ta set protocol specific flag #2 +.It Li B Ta -blackhole Ta " RTF_BLACKHOLE" Ta discard pkts (during updates) +.It Li b Ta "" Ta " RTF_BROADCAST" Ta Route represents a broadcast address +.It Li " " Ta -noblackhole Ta ~RTF_BLACKHOLE Ta clear blackhole flag +.It Li C Ta -cloning Ta " RTF_CLONING" Ta generates a new route on use +.It Li " " Ta -nocloning Ta ~RTF_CLONING Ta stop generating new routes on use +.It Li c Ta -cloned Ta " RTF_CLONED" Ta route generated by RTF_CLONING +.It Li " " Ta -nocloned Ta ~RTF_CLONED Ta deny removal with RTF_CLONING +.It Li D Ta "" Ta " RTF_DYNAMIC" Ta created dynamically (redirect) +.It Li G Ta "" Ta " RTF_GATEWAY" Ta forwarded to dest by intermediary +.It Li H Ta "" Ta " RTF_HOST" Ta host entry (net otherwise) +.It Li L Ta -llinfo Ta " RTF_LLINFO" Ta translate proto to link addr +.It Li l Ta "" Ta " RTF_LOCAL" Ta Route represents a local address +.It Li M Ta "" Ta " RTF_MODIFIED" Ta modified dynamically (redirect) +.It Li p Ta -proxy Ta " RTF_ANNOUNCE" Ta make entry a link level proxy +.It Li R Ta -reject Ta " RTF_REJECT" Ta send ICMP unreachable on match +.It Li " " Ta -noreject Ta ~RTF_REJECT Ta clear reject flag +.It Li S Ta -static Ta " RTF_STATIC" Ta manually added route +.It Li " " Ta -nostatic Ta ~RTF_STATIC Ta pretend route added automatically +.It Li U Ta "" Ta " RTF_UP" Ta route usable +.It Li X Ta -xresolve Ta " RTF_XRESOLVE" Ta emit mesg on use (for ext lookup) +.El +.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 +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route 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 +.Nm +uses a routing socket and the new message types +.Dv RTM_ADD , +.Dv RTM_DELETE , +.Dv RTM_GET , +and +.Dv RTM_CHANGE . +As such, only the super-user may modify +the routing tables. +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and \*[Gt]0 if an error occurs. +This includes the use of the +.Cm get +command to look up a route that is incomplete. +.Sh EXAMPLES +This sets the default route to 192.168.0.1: +.Dl route add default 192.168.0.1 +This shows all routes, without DNS resolution (this is useful if the +DNS is not available): +.Dl route -n show +To install a static route through 10.200.0.1 to reach the network +192.168.1.0/28, use this: +.Dl route add -net 192.168.1.0 -netmask 255.255.255.240 10.200.0.1 +.Sh DIAGNOSTICS +.Bl -tag -width Ds +.It Sy "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 Sy "delete [ host \&| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It Sy "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It Sy "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 Sy "not in table" +A delete operation was attempted for an entry which +wasn't present in the tables. +.It Sy "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 Sy "Permission denied" +The attempted operation is privileged. +Only root may modify the routing tables. +These privileges are enforced by the kernel. +.El +.Sh SEE ALSO +.Xr mpls 4 , +.Xr netintro 4 , +.Xr route 4 , +.Xr routed 8 , +.Xr sysctl 8 +.\" .Xr XNSrouted 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +IPv6 support was added by WIDE/KAME project. +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed 8 Ns 's +abilities. +.Pp +Some uses of the +.Fl ifa +or +.Fl ifp +modifiers with the add command will incorrectly fail with a +.Dq Network is unreachable +message if there is no default route. +See case +.Dv RTM_ADD +in +.Pa sys/net/rtsock.c:route_output +for details. diff --git a/sbin/route/route.c b/sbin/route/route.c new file mode 100644 index 000000000..71addfa4f --- /dev/null +++ b/sbin/route/route.c @@ -0,0 +1,1809 @@ +/* $NetBSD: route.c,v 1.151 2015/03/23 18:33:17 roy Exp $ */ + +/* + * 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. + * 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 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1989, 1991, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: route.c,v 1.151 2015/03/23 18:33:17 roy Exp $"); +#endif +#endif /* not lint */ + +#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 "keywords.h" +#include "extern.h" +#include "prog_ops.h" +#include "rtutil.h" + +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_at sat; + struct sockaddr_dl sdl; +#ifndef SMALL + struct sockaddr_mpls smpls; +#endif /* SMALL */ + struct sockaddr_storage sstorage; +}; + +typedef union sockunion *sup; + +struct sou { + union sockunion *so_dst, *so_gate, *so_mask, *so_genmask, *so_ifa, + *so_ifp, *so_mpls; +}; + +static const char *route_strerror(int); +static void set_metric(const char *, int); +static int newroute(int, char *const *); +static void inet_makenetandmask(u_int32_t, struct sockaddr_in *, struct sou *); +#ifdef INET6 +static int inet6_makenetandmask(const struct sockaddr_in6 *, struct sou *); +#endif +static int getaddr(int, const char *, struct hostent **, struct sou *); +static int flushroutes(int, char *const [], int); +static char *netmask_string(const struct sockaddr *, int, int); +static int prefixlen(const char *, struct sou *); +#ifndef SMALL +static void interfaces(void); +__dead static void monitor(void); +static int print_getmsg(struct rt_msghdr *, int, struct sou *); +static const char *linkstate(struct if_msghdr *); +static sup readtag(sup, const char *); +static void addtag(sup, const char *, int); +#endif /* SMALL */ +static int rtmsg(int, int, struct sou *); +static void mask_addr(struct sou *); +static void print_rtmsg(struct rt_msghdr *, int); +static void pmsg_common(struct rt_msghdr *); +static void pmsg_addrs(const char *, int); +static void bprintf(FILE *, int, const char *); +static void sodump(sup, const char *); +static void sockaddr(const char *, struct sockaddr *); + +int pid, rtm_addrs; +int sock; +int forcehost, forcenet, doflush, af; +int iflag, Lflag, nflag, qflag, tflag, Sflag, Tflag; +int verbose, aflen = sizeof(struct sockaddr_in), rtag; +int locking, lockrest, debugonly, shortoutput; +struct rt_metrics rt_metrics; +int rtm_inits; +short ns_nullh[] = {0,0,0}; +short ns_bh[] = {-1,-1,-1}; + +static const char opts[] = "dfLnqSsTtv"; + +void +usage(const char *cp) +{ + + if (cp) + warnx("botched keyword: %s", cp); + (void)fprintf(stderr, + "Usage: %s [-%s] cmd [[-] args]\n", getprogname(), opts); + exit(1); + /* NOTREACHED */ +} + +#define PRIETHER "02x:%02x:%02x:%02x:%02x:%02x" +#define PRIETHER_ARGS(__enaddr) (__enaddr)[0], (__enaddr)[1], (__enaddr)[2], \ + (__enaddr)[3], (__enaddr)[4], (__enaddr)[5] + +int +main(int argc, char * const *argv) +{ + int ch; + + if (argc < 2) + usage(NULL); + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case 'd': + debugonly = 1; + break; + case 'f': + doflush = 1; + break; + case 'L': + Lflag = RT_LFLAG; + break; + case 'n': + nflag = RT_NFLAG; + break; + case 'q': + qflag = 1; + break; + case 'S': + Sflag = 1; + break; + case 's': + shortoutput = 1; + break; + case 'T': + Tflag = RT_TFLAG; + break; + case 't': + tflag = 1; + break; + case 'v': + verbose = RT_VFLAG; + break; + case '?': + default: + usage(NULL); + /*NOTREACHED*/ + } + argc -= optind; + argv += optind; + + if (prog_init && prog_init() == -1) + err(1, "init failed"); + + pid = prog_getpid(); + if (tflag) + sock = prog_open("/dev/null", O_WRONLY, 0); + else + sock = prog_socket(PF_ROUTE, SOCK_RAW, 0); + if (sock < 0) + err(EXIT_FAILURE, "socket"); + + if (*argv == NULL) { + if (doflush) + ch = K_FLUSH; + else + goto no_cmd; + } else + ch = keyword(*argv); + + switch (ch) { +#ifndef SMALL + case K_GET: +#endif /* SMALL */ + case K_CHANGE: + case K_ADD: + case K_DELETE: + if (doflush) + (void)flushroutes(1, argv, 0); + return newroute(argc, argv); + + case K_SHOW: + show(argc, argv, Lflag|nflag|Tflag|verbose); + return 0; + +#ifndef SMALL + case K_MONITOR: + monitor(); + return 0; + +#endif /* SMALL */ + case K_FLUSH: + return flushroutes(argc, argv, 0); + + case K_FLUSHALL: + return flushroutes(argc, argv, 1); + no_cmd: + default: + usage(*argv); + /*NOTREACHED*/ + } +} + +static char * +netmask_string(const struct sockaddr *mask, int len, int family) +{ + static char smask[INET6_ADDRSTRLEN]; + struct sockaddr_in nsin; + struct sockaddr_in6 nsin6; + + if (len >= 0) + snprintf(smask, sizeof(smask), "%d", len); + else { + switch (family) { + case AF_INET: + memset(&nsin, 0, sizeof(nsin)); + memcpy(&nsin, mask, mask->sa_len); + snprintf(smask, sizeof(smask), "%s", + inet_ntoa(nsin.sin_addr)); + break; + case AF_INET6: + memset(&nsin6, 0, sizeof(nsin6)); + memcpy(&nsin6, mask, mask->sa_len); + inet_ntop(family, &nsin6.sin6_addr, smask, + sizeof(smask)); + break; + default: + snprintf(smask, sizeof(smask), "%s", any_ntoa(mask)); + } + } + + return smask; +} +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +static int +flushroutes(int argc, char * const argv[], int doall) +{ + struct sockaddr *sa; + size_t needed; + int flags, mib[6], rlen, seqno; + char *buf, *next, *lim; + const char *afname; + struct rt_msghdr *rtm; + + flags = 0; + af = AF_UNSPEC; + /* Don't want to read back our messages */ + prog_shutdown(sock, SHUT_RD); + parse_show_opts(argc, argv, &af, &flags, &afname, false); + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(EXIT_FAILURE, "route-sysctl-estimate"); + buf = lim = NULL; + if (needed) { + if ((buf = malloc(needed)) == NULL) + err(EXIT_FAILURE, "malloc"); + if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + err(EXIT_FAILURE, "actual retrieval of routing table"); + lim = buf + needed; + } + if (verbose) { + (void)printf("Examining routing table from sysctl\n"); + if (af != AF_UNSPEC) + printf("(address family %s)\n", afname); + } + if (needed == 0) + return 0; + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & flags) != flags) + continue; + if (!(rtm->rtm_flags & (RTF_GATEWAY | RTF_STATIC | + RTF_LLINFO)) && !doall) + continue; +#if defined(__minix) + /* + * MINIX3 only: routes with the RTF_LOCAL flag are immutable, + * so do not try to delete them. + */ + if (rtm->rtm_flags & RTF_LOCAL) + continue; +#endif /* defined(__minix) */ + if (af != AF_UNSPEC && sa->sa_family != af) + continue; + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + if ((rlen = prog_write(sock, next, + rtm->rtm_msglen)) < 0) { + warnx("writing to routing socket: %s", + route_strerror(errno)); + return 1; + } + if (rlen < (int)rtm->rtm_msglen) { + warnx("write to routing socket, got %d for rlen", rlen); + return 1; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + (void)printf("%-20.20s ", netname(sa, NULL, nflag)); + sa = (struct sockaddr *)(RT_ROUNDUP(sa->sa_len) + + (char *)sa); + (void)printf("%-20.20s ", routename(sa, nflag)); + (void)printf("done\n"); + } + } + free(buf); + return 0; +} + +static const char * +route_strerror(int error) +{ + + switch (error) { + case ESRCH: + return "not in table"; + case EBUSY: + return "entry in use"; + case ENOBUFS: + return "routing table overflow"; + default: + return strerror(error); + } +} + +static void +set_metric(const char *value, int key) +{ + int flag = 0; + uint64_t noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) \ + case x: valp = (uint64_t *)&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); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = strtoul(value, NULL, 0); +} + +static int +newroute(int argc, char *const *argv) +{ + const char *cmd, *dest = "", *gateway = ""; + int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + struct sou sou, *soup = &sou; + + sou.so_dst = calloc(1, sizeof(union sockunion)); + sou.so_gate = calloc(1, sizeof(union sockunion)); + sou.so_mask = calloc(1, sizeof(union sockunion)); + sou.so_genmask = calloc(1, sizeof(union sockunion)); + sou.so_ifa = calloc(1, sizeof(union sockunion)); + sou.so_ifp = calloc(1, sizeof(union sockunion)); + sou.so_mpls = calloc(1, sizeof(union sockunion)); + + if (sou.so_dst == NULL || sou.so_gate == NULL || sou.so_mask == NULL || + sou.so_genmask == NULL || sou.so_ifa == NULL || sou.so_ifp == NULL || + sou.so_mpls == NULL) + errx(EXIT_FAILURE, "Cannot allocate memory"); + + cmd = argv[0]; + af = AF_UNSPEC; + if (*cmd != 'g') { + /* Don't want to read back our messages */ + prog_shutdown(sock, SHUT_RD); + } + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + +#ifndef SMALL + case K_ATALK: + af = AF_APPLETALK; + aflen = sizeof(struct sockaddr_at); + break; +#endif + + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; + +#ifdef INET6 + case K_INET6: + af = AF_INET6; + aflen = sizeof(struct sockaddr_in6); + break; +#endif + + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + +#ifndef SMALL + case K_MPLS: + af = AF_MPLS; + aflen = sizeof(struct sockaddr_mpls); + break; + case K_TAG: + if (!--argc) + usage(1+*argv); + af = AF_MPLS; + aflen = sizeof(struct sockaddr_mpls); + (void)getaddr(RTA_TAG, *++argv, 0, soup); + break; +#endif /* SMALL */ + + case K_IFACE: + case K_INTERFACE: + iflag++; + break; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LLINFO: + flags |= RTF_LLINFO; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_NOREJECT: + flags &= ~RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_NOBLACKHOLE: + flags &= ~RTF_BLACKHOLE; + break; + case K_CLONED: + flags |= RTF_CLONED; + break; + case K_NOCLONED: + flags &= ~RTF_CLONED; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_PROXY: + flags |= RTF_ANNOUNCE; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_NOCLONING: + flags &= ~RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + if (!--argc) + usage(1+*argv); + (void)getaddr(RTA_IFA, *++argv, 0, soup); + break; + case K_IFP: + if (!--argc) + usage(1+*argv); + (void)getaddr(RTA_IFP, *++argv, 0, soup); + break; + case K_GENMASK: + if (!--argc) + usage(1+*argv); + (void)getaddr(RTA_GENMASK, *++argv, 0, soup); + break; + case K_GATEWAY: + if (!--argc) + usage(1+*argv); + (void)getaddr(RTA_GATEWAY, *++argv, 0, soup); + break; + case K_DST: + if (!--argc) + usage(1+*argv); + ishost = getaddr(RTA_DST, *++argv, &hp, soup); + dest = *argv; + break; + case K_NETMASK: + if (!--argc) + usage(1+*argv); + (void)getaddr(RTA_NETMASK, *++argv, 0, soup); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; + case K_PREFIXLEN: + if (!--argc) + usage(1+*argv); + ishost = prefixlen(*++argv, soup); + 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: + if (!--argc) + usage(1+*argv); + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp, soup); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void)getaddr(RTA_GATEWAY, *argv, &hp, soup); + } else { + ret = atoi(*argv); + + if (ret == 0) { + if (strcmp(*argv, "0") == 0) { + if (!qflag) { + warnx("%s, %s", + "old usage of trailing 0", + "assuming route to if"); + } + } else + usage(NULL); + iflag = 1; + continue; + } else if (ret > 0 && ret < 10) { + if (!qflag) { + warnx("%s, %s", + "old usage of trailing digit", + "assuming route via gateway"); + } + iflag = 0; + continue; + } + (void)getaddr(RTA_NETMASK, *argv, 0, soup); + } + } + } + if ((rtm_addrs & RTA_DST) == 0) + errx(EXIT_FAILURE, "missing destination specification"); + if (*cmd == 'a' && (rtm_addrs & RTA_GATEWAY) == 0) + errx(EXIT_FAILURE, "missing gateway specification"); + if (forcehost && forcenet) + errx(EXIT_FAILURE, "-host and -net conflict"); + else if (forcehost) + ishost = 1; + else if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags, soup)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + memmove(&soup->so_gate->sin.sin_addr, hp->h_addr_list[0], + hp->h_length); + } else + break; + } + if (*cmd == 'g') + return ret != 0; + if (!qflag) { + oerrno = errno; + (void)printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void)printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void)printf(" (%s)", + inet_ntoa(soup->so_gate->sin.sin_addr)); + } + if (ret == 0) + (void)printf("\n"); + else + (void)printf(": %s\n", route_strerror(oerrno)); + } + free(sou.so_dst); + free(sou.so_gate); + free(sou.so_mask); + free(sou.so_genmask); + free(sou.so_ifa); + free(sou.so_ifp); + free(sou.so_mpls); + + return ret != 0; +} + +static void +inet_makenetandmask(const u_int32_t net, struct sockaddr_in * const isin, + struct sou *soup) +{ + struct sockaddr_in *sin; + u_int32_t addr, mask = 0; + char *cp; + + rtm_addrs |= RTA_NETMASK; + if (net == 0) + mask = addr = 0; + else if (net < 128) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSA_NET; + } else if (net < 192) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 224) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSC_NET; + } else if (net < 256) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSD_NET; + } else if (net < 49152) { /* 192 * 256 */ + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 57344) { /* 224 * 256 */ + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSC_NET; + } else if (net < 65536) { + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 14680064L) { /* 224 * 65536 */ + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSC_NET; + } else if (net < 16777216L) { + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSD_NET; + } else { + addr = net; + if ((addr & IN_CLASSA_HOST) == 0) + mask = IN_CLASSA_NET; + else if ((addr & IN_CLASSB_HOST) == 0) + mask = IN_CLASSB_NET; + else if ((addr & IN_CLASSC_HOST) == 0) + mask = IN_CLASSC_NET; + else + mask = -1; + } + isin->sin_addr.s_addr = htonl(addr); + sin = &soup->so_mask->sin; + sin->sin_addr.s_addr = htonl(mask); + sin->sin_len = 0; + sin->sin_family = 0; + cp = (char *)(&sin->sin_addr + 1); + while (*--cp == 0 && cp > (char *)sin) + ; + sin->sin_len = 1 + cp - (char *)sin; + sin->sin_family = AF_INET; +} + +#ifdef INET6 +/* + * XXX the function may need more improvement... + */ +static int +inet6_makenetandmask(const struct sockaddr_in6 * const sin6, struct sou *soup) +{ + const char *plen; + struct in6_addr in6; + + 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 || strcmp(plen, "128") == 0) + return 1; + else { + rtm_addrs |= RTA_NETMASK; + (void)prefixlen(plen, soup); + 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 which, const char *s, struct hostent **hpp, struct sou *soup) +{ + sup su; + struct hostent *hp; + struct netent *np; + u_int32_t val; + char *t; + int afamily; /* local copy of af so we can change it */ + + if (af == AF_UNSPEC) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + afamily = af; + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = soup->so_dst; + break; + case RTA_GATEWAY: + su = soup->so_gate; + break; + case RTA_NETMASK: + su = soup->so_mask; + break; + case RTA_GENMASK: + su = soup->so_genmask; + break; + case RTA_IFP: + su = soup->so_ifp; + afamily = AF_LINK; + break; + case RTA_IFA: + su = soup->so_ifa; + su->sa.sa_family = af; + break; +#ifndef SMALL + case RTA_TAG: + su = soup->so_mpls; + afamily = AF_MPLS; + break; +#endif + default: + su = NULL; + usage("Internal Error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + su->sa.sa_family = afamily; /* cases that don't want it have left already */ + if (strcmp(s, "default") == 0) { + switch (which) { + case RTA_DST: + forcenet++; + (void)getaddr(RTA_NETMASK, s, 0, soup); + break; + case RTA_NETMASK: + case RTA_GENMASK: + su->sa.sa_len = 0; + } + return 0; + } + switch (afamily) { +#ifdef INET6 + case AF_INET6: + { + struct addrinfo hints, *res; + char *slash = 0; + + if (which == RTA_DST && (slash = (strrchr(s, '/'))) != 0) + *slash = '\0'; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = afamily; /*AF_INET6*/ + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + if (getaddrinfo(s, "0", &hints, &res) != 0) { + hints.ai_flags = 0; + if (slash) { + *slash = '/'; + slash = 0; + } + if (getaddrinfo(s, "0", &hints, &res) != 0) + errx(EXIT_FAILURE, "%s: bad value", s); + } + if (slash) + *slash = '/'; + if (sizeof(su->sin6) != res->ai_addrlen) + errx(EXIT_FAILURE, "%s: bad value", s); + if (res->ai_next) { + errx(EXIT_FAILURE, + "%s: address resolved to multiple values", s); + } + memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6)); + freeaddrinfo(res); + inet6_putscopeid(&su->sin6, INET6_IS_ADDR_LINKLOCAL| + INET6_IS_ADDR_MC_LINKLOCAL); + if (hints.ai_flags == AI_NUMERICHOST) { + if (slash) + return prefixlen(slash + 1, soup); + if (which == RTA_DST) + return inet6_makenetandmask(&su->sin6, soup); + return 0; + } else + return 1; + } +#endif + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return 1; + +#ifndef SMALL + case AF_APPLETALK: + t = strchr (s, '.'); + if (!t) { +badataddr: + errx(EXIT_FAILURE, "bad address: %s", s); + } + val = atoi (s); + if (val > 65535) + goto badataddr; + su->sat.sat_addr.s_net = val; + val = atoi (t); + if (val > 256) + goto badataddr; + su->sat.sat_addr.s_node = val; + rtm_addrs |= RTA_NETMASK; + return(forcehost || su->sat.sat_addr.s_node != 0); + case AF_MPLS: + if (which == RTA_DST) + soup->so_dst = readtag(su, s); + else if (which == RTA_TAG) + soup->so_mpls = readtag(su, s); + else + errx(EXIT_FAILURE, "MPLS can be used only as " + "DST or TAG"); + return 1; +#endif + + case AF_LINK: + link_addr(s, &su->sdl); + return 1; + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + + if ((t = strchr(s, '/')) != NULL && which == RTA_DST) { + *t = '\0'; + if (forcenet == 0) { + if ((val = inet_addr(s)) != INADDR_NONE) { + inet_makenetandmask(htonl(val), &su->sin, soup); + return prefixlen(&t[1], soup); + } + } else { + if ((val = inet_network(s)) != INADDR_NONE) { + inet_makenetandmask(val, &su->sin, soup); + return prefixlen(&t[1], soup); + } + } + *t = '/'; + } + if (inet_aton(s, &su->sin.sin_addr) && + (which != RTA_DST || forcenet == 0)) { + val = su->sin.sin_addr.s_addr; + if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return 1; + else { + val = ntohl(val); + goto netdone; + } + } + if ((val = inet_network(s)) != INADDR_NONE || + ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0)) { +netdone: + if (which == RTA_DST) + inet_makenetandmask(val, &su->sin, soup); + return 0; + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + memmove(&su->sin.sin_addr, hp->h_addr, hp->h_length); + return 1; + } + errx(EXIT_FAILURE, "%s: bad value", s); + /*NOTREACHED*/ +} + +#ifndef SMALL +static sup +readtag(sup su, const char *s) +{ + char *p, *n, *norig; + int mplssize = 0; + sup retsu = su; + + n = strdup(s); + if (n == NULL) + errx(EXIT_FAILURE, "%s: Cannot allocate memory", s); + norig = n; + for (uint i = 0; i < strlen(n); i++) + if(n[i] == ',') + mplssize++; + +#define MPLS_NEW_SIZE (sizeof(struct sockaddr_mpls) + \ + mplssize * sizeof(union mpls_shim)) + + if (mplssize != 0 && sizeof(union sockunion) < MPLS_NEW_SIZE) { + free(su); + retsu = malloc(MPLS_NEW_SIZE); + retsu->smpls.smpls_family = AF_MPLS; + } + retsu->smpls.smpls_len = MPLS_NEW_SIZE; + mplssize = 0; + while ((p = strchr(n, ',')) != NULL) { + p[0] = '\0'; + addtag(retsu, n, mplssize); + n = p + 1; + mplssize++; + } + addtag(retsu, n, mplssize); + + free(norig); + return retsu; +} + +static void +addtag(sup su, const char *s, int where) +{ + union mpls_shim *ms = &su->smpls.smpls_addr; + + if (atoi(s) < 0 || atoi(s) >= (1 << 20)) + errx(EXIT_FAILURE, "%s: Bad tag", s); + ms[where].s_addr = 0; + ms[where].shim.label = atoi(s); + ms[where].s_addr = htonl(ms[where].s_addr); +} +#endif /* SMALL */ + +int +prefixlen(const char *s, struct sou *soup) +{ + int max, len = atoi(s); +#ifdef INET6 + int q, r; +#endif + + switch (af) { + case AF_INET: + max = sizeof(struct in_addr) * 8; + break; +#ifdef INET6 + case AF_INET6: + max = sizeof(struct in6_addr) * 8; + break; +#endif + default: + errx(EXIT_FAILURE, "prefixlen is not supported with af %d", af); + /*NOTREACHED*/ + } + + rtm_addrs |= RTA_NETMASK; + if (len < -1 || len > max) + errx(EXIT_FAILURE, "%s: bad value", s); + +#ifdef INET6 + q = len >> 3; + r = len & 7; +#endif + switch (af) { + case AF_INET: + memset(soup->so_mask, 0, sizeof(*soup->so_mask)); + soup->so_mask->sin.sin_family = AF_INET; + soup->so_mask->sin.sin_len = sizeof(struct sockaddr_in); + soup->so_mask->sin.sin_addr.s_addr = (len == 0 ? 0 + : htonl(0xffffffff << (32 - len))); + break; +#ifdef INET6 + case AF_INET6: + soup->so_mask->sin6.sin6_family = AF_INET6; + soup->so_mask->sin6.sin6_len = sizeof(struct sockaddr_in6); + memset(&soup->so_mask->sin6.sin6_addr, 0, + sizeof(soup->so_mask->sin6.sin6_addr)); + if (q > 0) + memset(&soup->so_mask->sin6.sin6_addr, 0xff, q); + if (r > 0) + *((u_char *)&soup->so_mask->sin6.sin6_addr + q) = + (0xff00 >> r) & 0xff; + break; +#endif + } + return len == max; +} + +#ifndef SMALL +static void +interfaces(void) +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + struct rt_msghdr *rtm; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + err(EXIT_FAILURE, "route-sysctl-estimate"); + if (needed) { + if ((buf = malloc(needed)) == NULL) + err(EXIT_FAILURE, "malloc"); + if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + err(EXIT_FAILURE, + "actual retrieval of interface table"); + } + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } + free(buf); + } +} + +static void +monitor(void) +{ + int n; + union { + char msg[2048]; + struct rt_msghdr hdr; + } u; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + time_t now; + n = prog_read(sock, &u, sizeof(u)); + now = time(NULL); + (void)printf("got message of size %d on %s", n, ctime(&now)); + print_rtmsg(&u.hdr, n); + } +} + +#endif /* SMALL */ + + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +static int +rtmsg(int cmd, int flags, struct sou *soup) +{ + static int seq; + int rlen; + char *cp = m_rtmsg.m_space; + int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = RT_ROUNDUP(u->sa.sa_len); memmove(cp, u, l); cp += l;\ + if (verbose && ! shortoutput) sodump(u,#u);\ + } + + 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') { +#ifdef SMALL + return -1; +#else /* SMALL */ + cmd = RTM_GET; + if (soup->so_ifp->sa.sa_family == AF_UNSPEC) { + soup->so_ifp->sa.sa_family = AF_LINK; + soup->so_ifp->sa.sa_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } +#endif /* SMALL */ + } 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 = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(soup); + NEXTADDR(RTA_DST, soup->so_dst); + NEXTADDR(RTA_GATEWAY, soup->so_gate); + NEXTADDR(RTA_NETMASK, soup->so_mask); + NEXTADDR(RTA_GENMASK, soup->so_genmask); + NEXTADDR(RTA_IFP, soup->so_ifp); + NEXTADDR(RTA_IFA, soup->so_ifa); +#ifndef SMALL + NEXTADDR(RTA_TAG, soup->so_mpls); +#endif + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose && ! shortoutput) { + if (rtm_addrs) + putchar('\n'); + print_rtmsg(&rtm, l); + } + if (debugonly) + return 0; + if ((rlen = prog_write(sock, (char *)&m_rtmsg, l)) < 0) { + warnx("writing to routing socket: %s", route_strerror(errno)); + return -1; + } + if (rlen < l) { + warnx("write to routing socket, got %d for rlen", rlen); + return 1; + } +#ifndef SMALL + if (cmd == RTM_GET) { + do { + l = prog_read(sock, + (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + if (l < 0) + err(EXIT_FAILURE, "read from routing socket"); + else + return print_getmsg(&rtm, l, soup); + } +#endif /* SMALL */ +#undef rtm + return 0; +} + +static void +mask_addr(struct sou *soup) +{ + int olen = soup->so_mask->sa.sa_len; + char *cp1 = olen + (char *)soup->so_mask, *cp2; + + for (soup->so_mask->sa.sa_len = 0; cp1 > (char *)soup->so_mask; ) + if (*--cp1 != 0) { + soup->so_mask->sa.sa_len = 1 + cp1 - (char *)soup->so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (soup->so_dst->sa.sa_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif +#ifndef SMALL + case AF_APPLETALK: +#endif /* SMALL */ + case 0: + return; + } + cp1 = soup->so_mask->sa.sa_len + 1 + (char *)soup->so_dst; + cp2 = soup->so_dst->sa.sa_len + 1 + (char *)soup->so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = soup->so_mask->sa.sa_len + 1 + (char *)soup->so_mask; + while (cp1 > soup->so_dst->sa.sa_data) + *--cp1 &= *--cp2; +} + +const char * const msgtypes[] = { + [RTM_ADD] = "RTM_ADD: Add Route", + [RTM_DELETE] = "RTM_DELETE: Delete Route", + [RTM_CHANGE] = "RTM_CHANGE: Change Metrics, Flags or Gateway", + [RTM_GET] = "RTM_GET: Report Metrics", + [RTM_LOSING] = "RTM_LOSING: Kernel Suspects Partitioning", + [RTM_REDIRECT] = "RTM_REDIRECT: Told to use different route", + [RTM_MISS] = "RTM_MISS: Lookup failed on this address", + [RTM_LOCK] = "RTM_LOCK: fix specified metrics", + [RTM_OLDADD] = "RTM_OLDADD: caused by SIOCADDRT", + [RTM_OLDDEL] = "RTM_OLDDEL: caused by SIOCDELRT", + [RTM_RESOLVE] = "RTM_RESOLVE: Route created by cloning", + [RTM_NEWADDR] = "RTM_NEWADDR: address being added to iface", + [RTM_DELADDR] = "RTM_DELADDR: address being removed from iface", + [RTM_OOIFINFO] = "RTM_OOIFINFO: iface status change (pre-1.5)", + [RTM_OIFINFO] = "RTM_OIFINFO: iface status change (pre-64bit time)", + [RTM_IFANNOUNCE] = "RTM_IFANNOUNCE: iface arrival/departure", + [RTM_IEEE80211] = "RTM_IEEE80211: IEEE80211 wireless event", + [RTM_IFINFO] = "RTM_IFINFO: iface status change", + [RTM_CHGADDR] = "RTM_CHGADDR: address being changed on iface", +}; + +const char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; +const char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016CLONED\017PROTO2\020PROTO1\023LOCAL\024BROADCAST"; +const char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST"; +const char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011TAG"; + + +#ifndef SMALL +static const char * +linkstate(struct if_msghdr *ifm) +{ + static char buf[64]; + + switch (ifm->ifm_data.ifi_link_state) { + case LINK_STATE_UNKNOWN: + return "carrier: unknown"; + case LINK_STATE_DOWN: + return "carrier: no carrier"; + case LINK_STATE_UP: + return "carrier: active"; + default: + (void)snprintf(buf, sizeof(buf), "carrier: 0x%x", + ifm->ifm_data.ifi_link_state); + return buf; + } +} +#endif /* SMALL */ + +static void +print_rtmsg(struct rt_msghdr *rtm, int msglen) +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + struct if_announcemsghdr *ifan; + union { + struct ieee80211_join_event join; + struct ieee80211_leave_event leave; + struct ieee80211_replay_event replay; + struct ieee80211_michael_event michael; + } ev; + size_t evlen = 0; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void)printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (msgtypes[rtm->rtm_type]) + (void)printf("%s: ", msgtypes[rtm->rtm_type]); + else + (void)printf("#%d: ", rtm->rtm_type); + (void)printf("len %d, ", rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void)printf("if# %d, %s, flags: ", ifm->ifm_index, +#ifdef SMALL + "" +#else + linkstate(ifm) +#endif /* SMALL */ + ); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_CHGADDR: + ifam = (struct ifa_msghdr *)rtm; + (void)printf("metric %d, flags: ", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *)rtm; + (void)printf("if# %d, what: ", ifan->ifan_index); + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + printf("associate"); + break; + case RTM_IEEE80211_REASSOC: + printf("re-associate"); + break; + case RTM_IEEE80211_DISASSOC: + printf("disassociate"); + break; + case RTM_IEEE80211_SCAN: + printf("scan complete"); + break; + case RTM_IEEE80211_JOIN: + evlen = sizeof(ev.join); + printf("join"); + break; + case RTM_IEEE80211_LEAVE: + evlen = sizeof(ev.leave); + printf("leave"); + break; + case RTM_IEEE80211_MICHAEL: + evlen = sizeof(ev.michael); + printf("michael"); + break; + case RTM_IEEE80211_REPLAY: + evlen = sizeof(ev.replay); + printf("replay"); + break; + default: + evlen = 0; + printf("#%d", ifan->ifan_what); + break; + } + if (sizeof(*ifan) + evlen > ifan->ifan_msglen) { + printf(" (truncated)\n"); + break; + } + (void)memcpy(&ev, (ifan + 1), evlen); + switch (ifan->ifan_what) { + case RTM_IEEE80211_JOIN: + case RTM_IEEE80211_LEAVE: + printf(" mac %" PRIETHER, + PRIETHER_ARGS(ev.join.iev_addr)); + break; + case RTM_IEEE80211_REPLAY: + case RTM_IEEE80211_MICHAEL: + printf(" src %" PRIETHER " dst %" PRIETHER + " cipher %" PRIu8 " keyix %" PRIu8, + PRIETHER_ARGS(ev.replay.iev_src), + PRIETHER_ARGS(ev.replay.iev_dst), + ev.replay.iev_cipher, + ev.replay.iev_keyix); + if (ifan->ifan_what == RTM_IEEE80211_REPLAY) { + printf(" key rsc %#" PRIx64 + " frame rsc %#" PRIx64, + ev.replay.iev_keyrsc, ev.replay.iev_rsc); + } + break; + default: + break; + } + printf("\n"); + break; + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)rtm; + (void)printf("if# %d, what: ", ifan->ifan_index); + switch (ifan->ifan_what) { + case IFAN_ARRIVAL: + printf("arrival"); + break; + case IFAN_DEPARTURE: + printf("departure"); + break; + default: + printf("#%d", ifan->ifan_what); + break; + } + printf("\n"); + break; + default: + (void)printf("pid %d, seq %d, errno %d, flags: ", + rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +#ifndef SMALL +static int +print_getmsg(struct rt_msghdr *rtm, int msglen, struct sou *soup) +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *ifa = NULL, *mpls = NULL; + struct sockaddr_dl *ifp = NULL; + struct sockaddr *sa; + char *cp; + int i; + + if (! shortoutput) { + (void)printf(" route to: %s\n", + routename(&soup->so_dst->sa, nflag)); + } + if (rtm->rtm_version != RTM_VERSION) { + warnx("routing message version %d not understood", + rtm->rtm_version); + return 1; + } + if (rtm->rtm_msglen > msglen) { + warnx("message length mismatch, in packet %d, returned %d", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + warnx("RTM_GET: %s (errno %d)", + strerror(rtm->rtm_errno), rtm->rtm_errno); + return 1; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + case RTA_IFA: + ifa = sa; + break; + case RTA_TAG: + mpls = sa; + break; + } + RT_ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst && ! shortoutput) + (void)printf("destination: %s\n", + routename(dst, nflag)); + if (mask && ! shortoutput) { + int savenflag = nflag; + + nflag = RT_NFLAG; + (void)printf(" mask: %s\n", + routename(mask, nflag)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) { + const char *name; + + name = routename(gate, nflag); + if (shortoutput) { + if (*name == '\0') + return 1; + (void)printf("%s\n", name); + } else + (void)printf(" gateway: %s\n", name); + } + if (mpls) { + const char *name; + name = routename(mpls, nflag); + if(shortoutput) { + if (*name == '\0') + return 1; + printf("%s\n", name); + } else + printf(" Tag: %s\n", name); + } + + if (ifa && ! shortoutput) + (void)printf(" local addr: %s\n", + routename(ifa, nflag)); + if (ifp && ! shortoutput) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + if (! shortoutput) { + (void)printf(" flags: "); + bprintf(stdout, 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 */ + + if (! shortoutput) { + (void)printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8"PRId64"%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8"PRId64"%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8"PRId64"%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8"PRId64"%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8"PRId64"%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8"PRId64"%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8"PRId64"%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8"PRId64"%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); + } +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + + if (shortoutput) + return (rtm->rtm_addrs & RTF_GATEWAY) == 0; + else if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void)printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } + return 0; +#undef RTA_IGN +} +#endif /* SMALL */ + +void +pmsg_common(struct rt_msghdr *rtm) +{ + (void)printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void)printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs((char *)(rtm + 1), rtm->rtm_addrs); +} + +static void +extract_addrs(const char *cp, int addrs, const struct sockaddr *sa[], int *nmfp) +{ + int i, nmf = -1; + + for (i = 0; i < RTAX_MAX; i++) { + if ((1 << i) & addrs) { + sa[i] = (const struct sockaddr *)cp; + if ((i == RTAX_DST || i == RTAX_IFA) && + nmf == -1) + nmf = sa[i]->sa_family; + RT_ADVANCE(cp, sa[i]); + } else + sa[i] = NULL; + } + + if (nmfp != NULL) + *nmfp = nmf; +} + +static void +pmsg_addrs(const char *cp, int addrs) +{ + const struct sockaddr *sa[RTAX_MAX]; + int i, nmf; + + if (addrs != 0) { + (void)printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void)putchar('\n'); + extract_addrs(cp, addrs, sa, &nmf); + for (i = 0; i < RTAX_MAX; i++) { + if (sa[i] == NULL) + continue; + + if (i == RTAX_NETMASK && sa[i]->sa_len) + (void)printf(" %s", + netmask_string(sa[i], -1, nmf)); + else + (void)printf(" %s", + routename(sa[i], nflag)); + } + } + (void)putchar('\n'); + (void)fflush(stdout); +} + +static void +bprintf(FILE *fp, int b, const char *f) +{ + int i; + int gotsome = 0; + const uint8_t *s = (const uint8_t *)f; + + if (b == 0) { + fputs("none", fp); + return; + } + while ((i = *s++) != 0) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void)putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void)putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void)putc('>', fp); +} + +int +keyword(const char *cp) +{ + struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return kt->kt_i; +} + +static void +sodump(sup su, const char *which) +{ +#ifdef INET6 + char ntop_buf[NI_MAXHOST]; +#endif + + switch (su->sa.sa_family) { + case AF_INET: + (void)printf("%s: inet %s; ", + which, inet_ntoa(su->sin.sin_addr)); + break; +#ifndef SMALL + case AF_APPLETALK: + (void)printf("%s: atalk %d.%d; ", + which, su->sat.sat_addr.s_net, su->sat.sat_addr.s_node); + break; +#endif + case AF_LINK: + (void)printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; +#ifdef INET6 + case AF_INET6: + (void)printf("%s: inet6 %s; ", + which, inet_ntop(AF_INET6, &su->sin6.sin6_addr, + ntop_buf, sizeof(ntop_buf))); + break; +#endif +#ifndef SMALL + case AF_MPLS: + { + union mpls_shim ms; + const union mpls_shim *pms; + int psize = sizeof(struct sockaddr_mpls); + + ms.s_addr = ntohl(su->smpls.smpls_addr.s_addr); + printf("%s: mpls %u; ", + which, ms.shim.label); + + pms = &su->smpls.smpls_addr; + while(psize < su->smpls.smpls_len) { + pms++; + ms.s_addr = ntohl(pms->s_addr); + printf("%u; ", ms.shim.label); + psize += sizeof(ms); + } + break; + } +#endif /* SMALL */ + default: + (void)printf("%s: (%d) %s; ", + which, su->sa.sa_family, any_ntoa(&su->sa)); + } + (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(const char *addr, struct sockaddr *sa) +{ + char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + int byte = 0, state = VIRGIN, new = 0; + + (void)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; +} diff --git a/sbin/route/route_hostops.c b/sbin/route/route_hostops.c new file mode 100644 index 000000000..4597fa3f7 --- /dev/null +++ b/sbin/route/route_hostops.c @@ -0,0 +1,54 @@ +/* $NetBSD: route_hostops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#ifndef lint +__RCSID("$NetBSD: route_hostops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_socket = socket, + .op_open = open, + .op_getpid = getpid, + + .op_read = read, + .op_write = write, + + .op_sysctl = sysctl, + + .op_shutdown = shutdown, +}; diff --git a/sbin/route/route_rumpops.c b/sbin/route/route_rumpops.c new file mode 100644 index 000000000..76e7e5b4d --- /dev/null +++ b/sbin/route/route_rumpops.c @@ -0,0 +1,58 @@ +/* $NetBSD: route_rumpops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#ifndef lint +__RCSID("$NetBSD: route_rumpops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include + +#include +#include +#include + +#include "prog_ops.h" + +const struct prog_ops prog_ops = { + .op_init = rumpclient_init, + + .op_socket = rump_sys_socket, + .op_open = rump_sys_open, + .op_getpid = rump_sys_getpid, + + .op_read = rump_sys_read, + .op_write = rump_sys_write, + + .op_sysctl = rump_sys___sysctl, + + .op_shutdown = rump_sys_shutdown, +}; diff --git a/sbin/route/rtutil.c b/sbin/route/rtutil.c new file mode 100644 index 000000000..f7cfbacfb --- /dev/null +++ b/sbin/route/rtutil.c @@ -0,0 +1,806 @@ +/* $NetBSD: rtutil.c,v 1.6 2015/03/23 18:33:17 roy 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prog_ops.h" +#include "rtutil.h" + + +#define PLEN (LONG_BIT / 4 + 2) +#define PFKEYV2_CHUNK sizeof(u_int64_t) +static char *link_print(const struct sockaddr *); + +/* + * 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' }, + { RTF_LOCAL, 'l'}, + { RTF_BROADCAST, 'b'}, + { 0, 0 } +}; + +#ifndef SMALL +static void p_tag(const struct sockaddr *sa); +#endif +static void p_rtentry(struct rt_msghdr *, int, int); + +/* + * Print routing tables. + */ +void +p_rttables(int paf, int flags, int pflags, int interesting) +{ + 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 ((rtm->rtm_flags & pflags) != pflags) + continue; + if (paf != AF_UNSPEC && sa->sa_family != paf) + continue; + p_rtentry(rtm, flags, interesting); + } + 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 = 34 + * strlen("aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh") == 39 + */ +#ifndef INET6 +#define WID_DST(af) 18 /* width of destination column */ +#define WID_GW(af) 18 /* width of gateway column */ +#else +#define WID_DST(af) ((af) == AF_INET6 ? ((flags & RT_NFLAG) ? 39 : 18) : 18) +#define WID_GW(af) ((af) == AF_INET6 ? ((flags & RT_NFLAG) ? 30 : 18) : 18) +#endif + +/* + * Print header for routing table columns. + */ +void +p_rthdr(int paf, int flags) +{ +#ifndef SMALL + if (flags & RT_AFLAG) + printf("%-*.*s ", PLEN, PLEN, "Address"); + if (paf == PF_KEY) { + printf("%-18s %-5s %-18s %-5s %-5s %-22s\n", + "Source", "Port", "Destination", + "Port", "Proto", "SA(Address/Proto/Type/Direction)"); + return; + } + if (flags & RT_TFLAG) { + 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"); + return; + } +#endif +#ifndef SMALL + 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("%-*.*s %-*.*s %-6.6s\n", + WID_DST(paf), WID_DST(paf), "Destination", + WID_GW(paf), WID_GW(paf), "Gateway", + "Flags"); +#endif +} + +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. + */ +static void +p_rtentry(struct rt_msghdr *rtm, int flags, int interesting) +{ + static int old_af = -1; + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + struct sockaddr *mask, *rti_info[RTAX_MAX]; +#ifndef SMALL + char ifbuf[IF_NAMESIZE]; +#endif + + if ((flags & RT_LFLAG) && (rtm->rtm_flags & RTF_LLINFO)) + return; + + if (old_af != sa->sa_family) { + old_af = sa->sa_family; + p_family(sa->sa_family); + p_rthdr(sa->sa_family, flags); + } + 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), flags); + p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST, + WID_GW(sa->sa_family), flags); + p_flags(rtm->rtm_flags & interesting); +#if 0 /* XXX-elad */ + printf("%6d %8"PRId64" ", (int)rtm->rtm_rmx.rmx_refcnt, + rtm->rtm_rmx.rmx_pksent); +#else + printf("%6s %8s ", "-", "-"); +#endif +#ifndef SMALL + 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 (flags & RT_TFLAG) + p_tag(rti_info[RTAX_TAG]); + printf(" %.16s", if_indextoname(rtm->rtm_index, ifbuf)); + putchar('\n'); + if (flags & RT_VFLAG) + p_rtrmx(&rtm->rtm_rmx); +#endif +} + +/* + * Print address family header before a section of the routing table. + */ +void +p_family(int paf) +{ + const char *afname; + + switch (paf) { + case AF_INET: + afname = "Internet"; + break; +#ifdef INET6 + case AF_INET6: + afname = "Internet6"; + break; +#endif + case PF_KEY: + afname = "Encap"; + break; + case AF_APPLETALK: + afname = "AppleTalk"; + break; +#ifndef SMALL + case AF_MPLS: + afname = "MPLS"; + break; +#endif + default: + afname = NULL; + break; + } + if (afname) + printf("\n%s:\n", afname); + else + printf("\nProtocol Family %d:\n", paf); +} + +void +p_sockaddr(const struct sockaddr *sa, const struct sockaddr *mask, int rflags, + int width, int flags) +{ + char *cp; + + switch (sa->sa_family) { +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 sa6 = *(const struct sockaddr_in6 *)sa; + + inet6_getscopeid(&sa6, INET6_IS_ADDR_LINKLOCAL| + INET6_IS_ADDR_MC_LINKLOCAL); + if (rflags & RTF_HOST) + cp = routename((const struct sockaddr *)&sa6, flags); + else + cp = netname((const struct sockaddr *)&sa6, mask, flags); + break; + } +#endif + default: + if ((rflags & RTF_HOST) || mask == NULL) + cp = routename(sa, flags); + else + cp = netname(sa, mask, flags); + break; + } + if (width < 0) + printf("%s", cp); + else { + if (flags & RT_NFLAG) + printf("%-*s ", width, cp); + else + printf("%-*.*s ", width, width, cp); + } +} + +void +p_flags(int f) +{ + 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("%-6.6s ", name); +} + +#ifndef SMALL +void +p_rtrmx(const struct rt_metrics *rmx) +{ + printf("\texpire %10"PRId64"%c recvpipe %10"PRIu64"%c " + "sendpipe %10"PRIu64"%c\n", + (int64_t)rmx->rmx_expire, + (rmx->rmx_locks & RTV_EXPIRE) ? 'L' : ' ', rmx->rmx_recvpipe, + (rmx->rmx_locks & RTV_RPIPE) ? 'L' : ' ', rmx->rmx_sendpipe, + (rmx->rmx_locks & RTV_SPIPE) ? 'L' : ' '); + printf("\tssthresh %10"PRIu64"%c rtt %10"PRIu64"%c " + "rttvar %10"PRIu64"%c\n", rmx->rmx_ssthresh, + (rmx->rmx_locks & RTV_SSTHRESH) ? 'L' : ' ', + rmx->rmx_rtt, (rmx->rmx_locks & RTV_RTT) ? 'L' : ' ', + rmx->rmx_rttvar, (rmx->rmx_locks & RTV_RTTVAR) ? 'L' : ' '); + printf("\thopcount %10"PRIu64"%c\n", + rmx->rmx_hopcount, (rmx->rmx_locks & RTV_HOPCOUNT) ? 'L' : ' '); +} + +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); +} +#endif + +static char line[MAXHOSTNAMELEN]; +static char domain[MAXHOSTNAMELEN]; + +char * +routename(const struct sockaddr *sa, int flags) +{ + 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( + ((const struct sockaddr_in *)sa)->sin_addr.s_addr, + flags); +#ifdef INET6 + 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)) +#if defined(__minix) + /* + * Local MINIX3 fix for a NetBSD issue: inet6_getscopeid() + * is sometimes called twice in a row when printing routing + * tables, discarding the scope ID the second time.. + */ + if (sin6.sin6_scope_id == 0) +#endif /* defined(__minix) */ + inet6_getscopeid(&sin6, INET6_IS_ADDR_LINKLOCAL| + INET6_IS_ADDR_MC_LINKLOCAL); + return routename6(&sin6, flags); + } +#endif + case AF_LINK: + return link_print(sa); + +#ifndef SMALL + case AF_MPLS: + return mpls_ntoa(sa); + + case AF_APPLETALK: + (void)snprintf(line, sizeof(line), "atalk %d.%d", + ((const struct sockaddr_at *)sa)->sat_addr.s_net, + ((const struct sockaddr_at *)sa)->sat_addr.s_node); + break; +#endif + +#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, int flags) +{ + const char *cp = NULL; + struct in_addr ina; + struct hostent *hp; + + if (in == INADDR_ANY) + cp = "default"; + if (!cp && (flags & RT_NFLAG) == 0) { + 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); +} + +#ifdef INET6 +char * +routename6(const struct sockaddr_in6 *sin6, int flags) +{ + int niflags = 0; + + if ((flags & RT_NFLAG)) + niflags |= NI_NUMERICHOST; + else + niflags |= NI_NOFQDN; + + if (getnameinfo((const struct sockaddr *)sin6, sin6->sin6_len, + line, sizeof(line), NULL, 0, niflags) != 0) + strncpy(line, "invalid", sizeof(line)); + + 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 * +netname4(const struct sockaddr_in* sa4, const struct sockaddr_in *mask, int flags) +{ + const char *cp = NULL; + struct netent *np = NULL; + int mbits; + in_addr_t in = sa4->sin_addr.s_addr; + + if (mask) { + in_addr_t m = mask->sin_addr.s_addr ; + m = ntohl(m); + mbits = m ? 33 - ffs(m) : 0; + } else + mbits = 0; + + in = ntohl(in); + if (in == INADDR_ANY && !mbits) + cp = "default"; + else if (!(flags & RT_NFLAG) && in != INADDR_ANY) { + if ((np = getnetbyaddr(in, AF_INET)) != NULL) + cp = np->n_name; + } + 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(const struct sockaddr_in6 *sa6, const struct sockaddr_in6 *mask, int flags) +{ + struct sockaddr_in6 sin6; + const 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 = (const 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 (flags & RT_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(const struct sockaddr *sa, const struct sockaddr *mask, int flags) +{ + switch (sa->sa_family) { + + case AF_INET: + return netname4((const struct sockaddr_in *)sa, + (const struct sockaddr_in *)mask, flags); +#ifdef INET6 + case AF_INET6: + return netname6((const struct sockaddr_in6 *)sa, + (const struct sockaddr_in6 *)mask, flags); +#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); +} + +static char * +link_print(const struct sockaddr *sa) +{ + const struct sockaddr_dl *sdl = (const struct sockaddr_dl *)sa; + const u_char *lla = (const 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((const struct ether_addr *)lla); + default: + return link_ntoa(sdl); + } +} + +#ifndef SMALL +char * +mpls_ntoa(const struct sockaddr *sa) +{ + static char obuf[16]; + size_t olen; + 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); + olen = strlen(obuf); + snprintf(obuf + olen, sizeof(obuf) - olen, ",%u", + ms.shim.label); + psize+=sizeof(ms); + } + return obuf; +} +#endif + +void +p_addr(const struct sockaddr *sa, const struct sockaddr *mask, int rflags, int flags) +{ + p_sockaddr(sa, mask, rflags, WID_DST(sa->sa_family), flags); +} + +void +p_gwaddr(const struct sockaddr *sa, int gwaf, int flags) +{ + p_sockaddr(sa, 0, RTF_HOST, WID_GW(gwaf), flags); +} diff --git a/sbin/route/rtutil.h b/sbin/route/rtutil.h new file mode 100644 index 000000000..3a23cf5cb --- /dev/null +++ b/sbin/route/rtutil.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#define RT_AFLAG __BIT(0) /* show address field */ +#define RT_TFLAG __BIT(1) /* show tag field */ +#define RT_VFLAG __BIT(2) /* show verbose statistics */ +#define RT_NFLAG __BIT(3) /* numeric output */ +#define RT_LFLAG __BIT(4) /* don't show LLINFO entries */ + +void p_rttables(int, int, int, int); +void p_rthdr(int, int); +void p_family(int); +void p_sockaddr(const struct sockaddr *, const struct sockaddr *, int, int, int); +void p_flags(int); +struct rt_metrics; +void p_rtrmx(const struct rt_metrics *); +void p_addr(const struct sockaddr *sa, const struct sockaddr *mask, int, int); +void p_gwaddr(const struct sockaddr *sa, int, int); + +char *routename(const struct sockaddr *sa, int); +char *routename4(in_addr_t, int); +#ifdef INET6 +char *routename6(const struct sockaddr_in6 *, int); +char *netname6(const struct sockaddr_in6 *, const struct sockaddr_in6 *, int); +#endif +char *netname(const struct sockaddr *, const struct sockaddr *, int); +char *netname4(const struct sockaddr_in *, const struct sockaddr_in *, int); + +char *mpls_ntoa(const struct sockaddr *); +char *any_ntoa(const struct sockaddr *); diff --git a/sbin/route/show.c b/sbin/route/show.c new file mode 100644 index 000000000..8dbe6669d --- /dev/null +++ b/sbin/route/show.c @@ -0,0 +1,144 @@ +/* $NetBSD: show.c,v 1.48 2015/03/23 18:33:17 roy 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 +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)route.c 8.3 (Berkeley) 3/9/94"; +#else +__RCSID("$NetBSD: show.c,v 1.48 2015/03/23 18:33:17 roy Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "keywords.h" +#include "rtutil.h" +#include "extern.h" +#include "prog_ops.h" + +void +parse_show_opts(int argc, char * const *argv, int *afp, int *flagsp, + const char **afnamep, bool nolink) +{ + const char *afname = "unspec"; + int af, flags; + + flags = 0; + af = AF_UNSPEC; + for (; argc >= 2; argc--) { + if (*argv[argc - 1] != '-') + goto bad; + switch (keyword(argv[argc - 1] + 1)) { + case K_HOST: + flags |= RTF_HOST; + break; + case K_LLINFO: + flags |= RTF_LLINFO; + break; + case K_INET: + af = AF_INET; + afname = argv[argc - 1] + 1; + break; +#ifdef INET6 + case K_INET6: + af = AF_INET6; + afname = argv[argc - 1] + 1; + break; +#endif +#ifndef SMALL + case K_ATALK: + af = AF_APPLETALK; + afname = argv[argc - 1] + 1; + break; + case K_MPLS: + af = AF_MPLS; + afname = argv[argc - 1] + 1; + break; +#endif /* SMALL */ + case K_LINK: + if (nolink) + goto bad; + af = AF_LINK; + afname = argv[argc - 1] + 1; + break; + default: + goto bad; + } + } + switch (argc) { + case 1: + case 0: + break; + default: + bad: + usage(argv[argc - 1]); + } + if (afnamep != NULL) + *afnamep = afname; + *afp = af; + *flagsp = flags; +} + +/* + * Print routing tables. + */ +void +show(int argc, char *const *argv, int flags) +{ + int af, rflags; + static int interesting = RTF_UP | RTF_GATEWAY | RTF_HOST | + RTF_REJECT | RTF_LLINFO | RTF_LOCAL | RTF_BROADCAST; + + parse_show_opts(argc, argv, &af, &rflags, NULL, true); + p_rttables(af, flags, rflags, interesting); +}