]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD route(8) 57/3457/1
authorDavid van Moolenbroek <david@minix3.org>
Wed, 15 Feb 2017 12:08:12 +0000 (12:08 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:40:13 +0000 (23:40 +0000)
Change-Id: I724a2a56157ea72afdd3f6a82239687894c8e3e8

17 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
sbin/Makefile
sbin/route/Makefile [new file with mode: 0644]
sbin/route/extern.h [new file with mode: 0644]
sbin/route/keywords.c [new file with mode: 0644]
sbin/route/keywords.h [new file with mode: 0644]
sbin/route/keywords.sh [new file with mode: 0755]
sbin/route/prog_ops.h [new file with mode: 0644]
sbin/route/route.8 [new file with mode: 0644]
sbin/route/route.c [new file with mode: 0644]
sbin/route/route_hostops.c [new file with mode: 0644]
sbin/route/route_rumpops.c [new file with mode: 0644]
sbin/route/rtutil.c [new file with mode: 0644]
sbin/route/rtutil.h [new file with mode: 0644]
sbin/route/show.c [new file with mode: 0644]

index 6739c0f9f103bc58cd7d9c8a77548ffd8fca5af9..d50db4cfd6d1cdd0c79b9418f8db7f876d46d645 100644 (file)
 ./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
index cb436cccd514eb77369fa2ab3939a8bee2d6ab95..823cc40475c786995cc06caae7f4ea68ec3a16a6 100644 (file)
 ./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
index e00b6f1bb3a05a0661b6c76869a4a5c299fa5d25..58e2aaba00a21481cb55d761cea10e7393328d79 100644 (file)
 ./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
index 0f38ce01f5d211871098c406127982579d310a64..0e9409926010213b7adae4149ff1886d21c06491 100644 (file)
@@ -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 (file)
index 0000000..a9113e3
--- /dev/null
@@ -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 <bsd.own.mk>
+
+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 <bsd.prog.mk>
diff --git a/sbin/route/extern.h b/sbin/route/extern.h
new file mode 100644 (file)
index 0000000..9742b58
--- /dev/null
@@ -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 (file)
index 0000000..5acc887
--- /dev/null
@@ -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 (file)
index 0000000..33700aa
--- /dev/null
@@ -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 (executable)
index 0000000..ae1476d
--- /dev/null
@@ -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 (file)
index 0000000..c37dc26
--- /dev/null
@@ -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 <sys/types.h>
+
+#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 (file)
index 0000000..aa90f9e
--- /dev/null
@@ -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 (file)
index 0000000..71addfa
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net80211/ieee80211_netbsd.h>
+#include <netinet/in.h>
+#include <netatalk/at.h>
+#include <netmpls/mpls.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <paths.h>
+#include <err.h>
+
+#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 [[-<qualifers>] 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 (file)
index 0000000..4597fa3
--- /dev/null
@@ -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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: route_hostops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..76e7e5b
--- /dev/null
@@ -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 <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: route_rumpops.c,v 1.1 2010/12/13 17:39:47 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+#include <rump/rumpclient.h>
+
+#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 (file)
index 0000000..f7cfbac
--- /dev/null
@@ -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 <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/pfvar.h>
+#include <net/pfkeyv2.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netatalk/at.h>
+#include <netmpls/mpls.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "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 (file)
index 0000000..3a23cf5
--- /dev/null
@@ -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 (file)
index 0000000..8dbe666
--- /dev/null
@@ -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 <sys/cdefs.h>
+#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 <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netmpls/mpls.h>
+
+#include <sys/sysctl.h>
+
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#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);
+}