xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/route.c (revision 12748:40c62600acd3)
10Sstevel@tonic-gate /*
2*12748SSowmini.Varadhan@oracle.COM  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
30Sstevel@tonic-gate  */
40Sstevel@tonic-gate 
50Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
61291Sjl138328 /* All Rights Reserved	*/
70Sstevel@tonic-gate 
80Sstevel@tonic-gate /* Copyright (c) 1990  Mentat Inc. */
90Sstevel@tonic-gate 
100Sstevel@tonic-gate /*
110Sstevel@tonic-gate  *
120Sstevel@tonic-gate  * Copyright (c) 1983, 1989, 1991, 1993
130Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
140Sstevel@tonic-gate  *
150Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
160Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
170Sstevel@tonic-gate  * are met:
180Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
190Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
200Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
210Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
220Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
230Sstevel@tonic-gate  * 3. All advertising materials mentioning features or use of this software
240Sstevel@tonic-gate  *    must display the following acknowledgement:
250Sstevel@tonic-gate  *	This product includes software developed by the University of
260Sstevel@tonic-gate  *	California, Berkeley and its contributors.
270Sstevel@tonic-gate  * 4. Neither the name of the University nor the names of its contributors
280Sstevel@tonic-gate  *    may be used to endorse or promote products derived from this software
290Sstevel@tonic-gate  *    without specific prior written permission.
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
320Sstevel@tonic-gate  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
330Sstevel@tonic-gate  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
340Sstevel@tonic-gate  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
350Sstevel@tonic-gate  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
360Sstevel@tonic-gate  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
370Sstevel@tonic-gate  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
380Sstevel@tonic-gate  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
390Sstevel@tonic-gate  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
400Sstevel@tonic-gate  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
410Sstevel@tonic-gate  * SUCH DAMAGE.
420Sstevel@tonic-gate  *
430Sstevel@tonic-gate  *	@(#)route.c	8.6 (Berkeley) 4/28/95
440Sstevel@tonic-gate  *	@(#)linkaddr.c	8.1 (Berkeley) 6/4/93
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate 
470Sstevel@tonic-gate #include <sys/param.h>
480Sstevel@tonic-gate #include <sys/file.h>
490Sstevel@tonic-gate #include <sys/socket.h>
500Sstevel@tonic-gate #include <sys/ioctl.h>
511291Sjl138328 #include <sys/stat.h>
520Sstevel@tonic-gate #include <sys/stream.h>
531291Sjl138328 #include <sys/sysmacros.h>
54156Sdduvall #include <sys/tihdr.h>
551291Sjl138328 #include <sys/types.h>
56192Scarlsonj #include <sys/ccompile.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate #include <net/if.h>
590Sstevel@tonic-gate #include <net/route.h>
600Sstevel@tonic-gate #include <net/if_dl.h>
610Sstevel@tonic-gate #include <netinet/in.h>
620Sstevel@tonic-gate #include <arpa/inet.h>
630Sstevel@tonic-gate #include <netdb.h>
640Sstevel@tonic-gate #include <inet/mib2.h>
650Sstevel@tonic-gate #include <inet/ip.h>
660Sstevel@tonic-gate 
671291Sjl138328 #include <limits.h>
680Sstevel@tonic-gate #include <locale.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate #include <errno.h>
710Sstevel@tonic-gate #include <unistd.h>
720Sstevel@tonic-gate #include <stdio.h>
730Sstevel@tonic-gate #include <stdlib.h>
741676Sjpk #include <stddef.h>
750Sstevel@tonic-gate #include <string.h>
760Sstevel@tonic-gate #include <stropts.h>
770Sstevel@tonic-gate #include <fcntl.h>
781291Sjl138328 #include <stdarg.h>
790Sstevel@tonic-gate #include <assert.h>
801676Sjpk #include <strings.h>
811676Sjpk 
821676Sjpk #include <libtsnet.h>
831676Sjpk #include <tsol/label.h>
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static struct keytab {
860Sstevel@tonic-gate 	char	*kt_cp;
870Sstevel@tonic-gate 	int	kt_i;
880Sstevel@tonic-gate } keywords[] = {
890Sstevel@tonic-gate #define	K_ADD		1
900Sstevel@tonic-gate 	{"add",		K_ADD},
910Sstevel@tonic-gate #define	K_BLACKHOLE	2
920Sstevel@tonic-gate 	{"blackhole",	K_BLACKHOLE},
930Sstevel@tonic-gate #define	K_CHANGE	3
940Sstevel@tonic-gate 	{"change",	K_CHANGE},
950Sstevel@tonic-gate #define	K_CLONING	4
960Sstevel@tonic-gate 	{"cloning",	K_CLONING},
970Sstevel@tonic-gate #define	K_DELETE	5
980Sstevel@tonic-gate 	{"delete",	K_DELETE},
990Sstevel@tonic-gate #define	K_DST		6
1000Sstevel@tonic-gate 	{"dst",		K_DST},
1010Sstevel@tonic-gate #define	K_EXPIRE	7
1020Sstevel@tonic-gate 	{"expire",	K_EXPIRE},
1030Sstevel@tonic-gate #define	K_FLUSH		8
1040Sstevel@tonic-gate 	{"flush",	K_FLUSH},
1050Sstevel@tonic-gate #define	K_GATEWAY	9
1060Sstevel@tonic-gate 	{"gateway",	K_GATEWAY},
1070Sstevel@tonic-gate #define	K_GET		11
1080Sstevel@tonic-gate 	{"get",		K_GET},
1090Sstevel@tonic-gate #define	K_HOPCOUNT	12
1100Sstevel@tonic-gate 	{"hopcount",	K_HOPCOUNT},
1110Sstevel@tonic-gate #define	K_HOST		13
1120Sstevel@tonic-gate 	{"host",	K_HOST},
1130Sstevel@tonic-gate #define	K_IFA		14
1140Sstevel@tonic-gate 	{"ifa",		K_IFA},
1150Sstevel@tonic-gate #define	K_IFACE		15
1160Sstevel@tonic-gate 	{"iface",	K_IFACE},
1170Sstevel@tonic-gate #define	K_IFP		16
1180Sstevel@tonic-gate 	{"ifp",		K_IFP},
1190Sstevel@tonic-gate #define	K_INET		17
1200Sstevel@tonic-gate 	{"inet",	K_INET},
1210Sstevel@tonic-gate #define	K_INET6		18
1220Sstevel@tonic-gate 	{"inet6",	K_INET6},
1230Sstevel@tonic-gate #define	K_INTERFACE	19
1240Sstevel@tonic-gate 	{"interface",	K_INTERFACE},
1250Sstevel@tonic-gate #define	K_LINK		20
1260Sstevel@tonic-gate 	{"link",	K_LINK},
1270Sstevel@tonic-gate #define	K_LOCK		21
1280Sstevel@tonic-gate 	{"lock",	K_LOCK},
1290Sstevel@tonic-gate #define	K_LOCKREST	22
1300Sstevel@tonic-gate 	{"lockrest",	K_LOCKREST},
1310Sstevel@tonic-gate #define	K_MASK		23
1320Sstevel@tonic-gate 	{"mask",	K_MASK},
1330Sstevel@tonic-gate #define	K_MONITOR	24
1340Sstevel@tonic-gate 	{"monitor",	K_MONITOR},
1350Sstevel@tonic-gate #define	K_MTU		25
1360Sstevel@tonic-gate 	{"mtu",		K_MTU},
1370Sstevel@tonic-gate #define	K_NET		26
1380Sstevel@tonic-gate 	{"net",		K_NET},
1390Sstevel@tonic-gate #define	K_NETMASK	27
1400Sstevel@tonic-gate 	{"netmask",	K_NETMASK},
1410Sstevel@tonic-gate #define	K_NOSTATIC	28
1420Sstevel@tonic-gate 	{"nostatic",	K_NOSTATIC},
1430Sstevel@tonic-gate #define	K_PRIVATE	29
1440Sstevel@tonic-gate 	{"private",	K_PRIVATE},
1450Sstevel@tonic-gate #define	K_PROTO1	30
1460Sstevel@tonic-gate 	{"proto1",	K_PROTO1},
1470Sstevel@tonic-gate #define	K_PROTO2	31
1480Sstevel@tonic-gate 	{"proto2",	K_PROTO2},
1490Sstevel@tonic-gate #define	K_RECVPIPE	32
1500Sstevel@tonic-gate 	{"recvpipe",	K_RECVPIPE},
1510Sstevel@tonic-gate #define	K_REJECT	33
1520Sstevel@tonic-gate 	{"reject",	K_REJECT},
1530Sstevel@tonic-gate #define	K_RTT		34
1540Sstevel@tonic-gate 	{"rtt",		K_RTT},
1550Sstevel@tonic-gate #define	K_RTTVAR	35
1560Sstevel@tonic-gate 	{"rttvar",	K_RTTVAR},
1570Sstevel@tonic-gate #define	K_SA		36
1580Sstevel@tonic-gate 	{"sa",		K_SA},
1590Sstevel@tonic-gate #define	K_SENDPIPE	37
1600Sstevel@tonic-gate 	{"sendpipe",	K_SENDPIPE},
1610Sstevel@tonic-gate #define	K_SSTHRESH	38
1620Sstevel@tonic-gate 	{"ssthresh",	K_SSTHRESH},
1630Sstevel@tonic-gate #define	K_STATIC	39
1640Sstevel@tonic-gate 	{"static",	K_STATIC},
1650Sstevel@tonic-gate #define	K_XRESOLVE	40
1660Sstevel@tonic-gate 	{"xresolve",	K_XRESOLVE},
1670Sstevel@tonic-gate #define	K_MULTIRT	41
1680Sstevel@tonic-gate 	{"multirt",	K_MULTIRT},
1690Sstevel@tonic-gate #define	K_SETSRC	42
1700Sstevel@tonic-gate 	{"setsrc",	K_SETSRC},
1711291Sjl138328 #define	K_SHOW		43
1721291Sjl138328 	{"show",	K_SHOW},
1731676Sjpk #define	K_SECATTR	43
1741676Sjpk 	{"secattr",	K_SECATTR},
17511042SErik.Nordmark@Sun.COM #define	K_INDIRECT	44
17611042SErik.Nordmark@Sun.COM 	{"indirect",	K_INDIRECT},
1770Sstevel@tonic-gate 	{0, 0}
1780Sstevel@tonic-gate };
1790Sstevel@tonic-gate 
1801291Sjl138328 /*
1811291Sjl138328  * Size of buffers used to hold command lines from the saved route file as
1821291Sjl138328  * well as error strings.
1831291Sjl138328  */
1841291Sjl138328 #define	BUF_SIZE 2048
1851291Sjl138328 
1861291Sjl138328 typedef union sockunion {
1870Sstevel@tonic-gate 	struct	sockaddr sa;
1880Sstevel@tonic-gate 	struct	sockaddr_in sin;
1890Sstevel@tonic-gate 	struct	sockaddr_dl sdl;
1900Sstevel@tonic-gate 	struct	sockaddr_in6 sin6;
1911291Sjl138328 } su_t;
1921291Sjl138328 
1931291Sjl138328 /*
1941291Sjl138328  * This structure represents the digested information from parsing arguments
1951291Sjl138328  * to route add, change, delete, and get.
1961291Sjl138328  *
1971291Sjl138328  */
1981291Sjl138328 typedef struct rtcmd_irep {
1991291Sjl138328 	int ri_cmd;
2001291Sjl138328 	int ri_flags;
2011291Sjl138328 	int ri_af;
2021291Sjl138328 	ulong_t	ri_inits;
2031291Sjl138328 	struct rt_metrics ri_metrics;
2041291Sjl138328 	int ri_addrs;
2051291Sjl138328 	su_t ri_dst;
2061291Sjl138328 	char *ri_dest_str;
2071291Sjl138328 	su_t ri_src;
2081291Sjl138328 	su_t ri_gate;
2091291Sjl138328 	struct hostent *ri_gate_hp;
2101291Sjl138328 	char *ri_gate_str;
2111291Sjl138328 	su_t ri_mask;
2121291Sjl138328 	su_t ri_ifa;
2131291Sjl138328 	su_t ri_ifp;
2141291Sjl138328 	char *ri_ifp_str;
2151676Sjpk 	int ri_rtsa_cnt;	/* number of gateway security attributes */
2161676Sjpk 	struct rtsa_s ri_rtsa;	/* enough space for one attribute */
2171291Sjl138328 } rtcmd_irep_t;
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate typedef struct	mib_item_s {
2201676Sjpk 	struct mib_item_s *next_item;
2211676Sjpk 	long group;
2221676Sjpk 	long mib_id;
2231676Sjpk 	long length;
2241676Sjpk 	intmax_t *valp;
2250Sstevel@tonic-gate } mib_item_t;
2260Sstevel@tonic-gate 
2271291Sjl138328 typedef enum {
2281291Sjl138328 	ADDR_TYPE_ANY,
2291291Sjl138328 	ADDR_TYPE_HOST,
2301291Sjl138328 	ADDR_TYPE_NET
2311291Sjl138328 } addr_type_t;
2320Sstevel@tonic-gate 
2331291Sjl138328 typedef enum {
2341291Sjl138328 	SEARCH_MODE_NULL,
2351291Sjl138328 	SEARCH_MODE_PRINT,
2361291Sjl138328 	SEARCH_MODE_DEL
2371291Sjl138328 } search_mode_t;
2381291Sjl138328 
2391291Sjl138328 static boolean_t	args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
2401291Sjl138328     char *cmd_string);
2410Sstevel@tonic-gate static void		bprintf(FILE *fp, int b, char *s);
2421291Sjl138328 static boolean_t	compare_rtcmd(rtcmd_irep_t *srch_rt,
2431291Sjl138328     rtcmd_irep_t *file_rt);
2440Sstevel@tonic-gate static void		delRouteEntry(mib2_ipRouteEntry_t *rp,
2450Sstevel@tonic-gate     mib2_ipv6RouteEntry_t *rp6, int seqno);
2461291Sjl138328 static void		del_rtcmd_irep(rtcmd_irep_t *rcip);
2470Sstevel@tonic-gate static void		flushroutes(int argc, char *argv[]);
2481291Sjl138328 static boolean_t	getaddr(rtcmd_irep_t *rcip, int which, char *s,
2491291Sjl138328     addr_type_t atype);
2500Sstevel@tonic-gate static boolean_t	in6_getaddr(char *s, struct sockaddr_in6 *sin6,
2510Sstevel@tonic-gate     int *plenp, struct hostent **hpp);
2520Sstevel@tonic-gate static boolean_t	in_getaddr(char *s, struct sockaddr_in *sin,
2531291Sjl138328     int *plenp, int which, struct hostent **hpp, addr_type_t atype,
2541291Sjl138328     rtcmd_irep_t *rcip);
2550Sstevel@tonic-gate static int		in_getprefixlen(char *addr, int max_plen);
2560Sstevel@tonic-gate static boolean_t	in_prefixlentomask(int prefixlen, int maxlen,
2570Sstevel@tonic-gate     uchar_t *mask);
2581291Sjl138328 static void		inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
2590Sstevel@tonic-gate     struct sockaddr_in *sin);
2600Sstevel@tonic-gate static in_addr_t	inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
2611291Sjl138328 static int		keyword(const char *cp);
2620Sstevel@tonic-gate static void		link_addr(const char *addr, struct sockaddr_dl *sdl);
2630Sstevel@tonic-gate static char		*link_ntoa(const struct sockaddr_dl *sdl);
2640Sstevel@tonic-gate static mib_item_t	*mibget(int sd);
2650Sstevel@tonic-gate static char		*netname(struct sockaddr *sa);
2661291Sjl138328 static int		newroute(char **argv);
2671291Sjl138328 static rtcmd_irep_t	*new_rtcmd_irep(void);
2681676Sjpk static void		pmsg_addrs(const char *cp, size_t len, uint_t addrs);
2691676Sjpk static void		pmsg_common(const struct rt_msghdr *rtm, size_t len);
2701291Sjl138328 static void		print_getmsg(rtcmd_irep_t *req_rt,
2711291Sjl138328     struct rt_msghdr *rtm, int msglen);
2721291Sjl138328 static void		print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
2731291Sjl138328     boolean_t gw_good, boolean_t to_saved);
2740Sstevel@tonic-gate static void		print_rtmsg(struct rt_msghdr *rtm, int msglen);
275192Scarlsonj static void		quit(char *s, int err) __NORETURN;
2761676Sjpk static char		*routename(const struct sockaddr *sa);
2770Sstevel@tonic-gate static void		rtmonitor(int argc, char *argv[]);
2781291Sjl138328 static int		rtmsg(rtcmd_irep_t *rcip);
2791676Sjpk static int		salen(const struct sockaddr *sa);
2801291Sjl138328 static void		save_route(int argc, char **argv, int do_flush);
2811291Sjl138328 static void		save_string(char **dst, char *src);
2821291Sjl138328 static int		search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
2831291Sjl138328     search_mode_t mode);
2841291Sjl138328 static void		set_metric(rtcmd_irep_t *rcip, char *value, int key,
2851291Sjl138328     boolean_t lock);
2861291Sjl138328 static int		show_saved_routes(int argc);
2870Sstevel@tonic-gate static void		sockaddr(char *addr, struct sockaddr *sa);
2881291Sjl138328 static void		sodump(su_t *su, char *which);
2891291Sjl138328 static void		syntax_arg_missing(char *keyword);
2901291Sjl138328 static void		syntax_bad_keyword(char *keyword);
2911291Sjl138328 static void		syntax_error(char *err, ...);
2920Sstevel@tonic-gate static void		usage(char *cp);
2931291Sjl138328 static void		write_to_rtfile(FILE *fp, int argc, char **argv);
2941676Sjpk static void		pmsg_secattr(const char *, size_t, const char *);
2950Sstevel@tonic-gate 
2961291Sjl138328 static pid_t		pid;
2970Sstevel@tonic-gate static int		s;
2981291Sjl138328 static boolean_t	nflag;
2990Sstevel@tonic-gate static int		af = AF_INET;
3000Sstevel@tonic-gate static boolean_t	qflag, tflag;
3011291Sjl138328 static boolean_t	verbose;
3021291Sjl138328 static boolean_t	debugonly;
3030Sstevel@tonic-gate static boolean_t	fflag;
3041291Sjl138328 static boolean_t	update_table;
3051291Sjl138328 static boolean_t	perm_flag;
3061291Sjl138328 static boolean_t	early_v6_keyword;
3071291Sjl138328 static char		perm_file_sfx[] = "/etc/inet/static_routes";
3081291Sjl138328 static char		*perm_file;
3091291Sjl138328 static char		temp_file_sfx[] = "/etc/inet/static_routes.tmp";
3101291Sjl138328 static char		*temp_file;
3111291Sjl138328 static struct in6_addr	in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3121291Sjl138328     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
3131291Sjl138328 /*
3141291Sjl138328  * WARNING:
3151291Sjl138328  * This next variable indicates whether certain functions exit when an error
3161291Sjl138328  * is detected in the user input.  Currently, exit_on_error is only set false
3171291Sjl138328  * in search_rtfile(), when argument are being parsed.  Only those functions
3181291Sjl138328  * used by search_rtfile() to parse its arguments are designed to work in
3191291Sjl138328  * both modes.  Take particular care in setting this false to ensure that any
3201291Sjl138328  * functions you call that might act on this flag properly return errors when
3211291Sjl138328  * exit_on_error is false.
3221291Sjl138328  */
3231291Sjl138328 static int		exit_on_error = B_TRUE;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate static struct {
3260Sstevel@tonic-gate 	struct	rt_msghdr m_rtm;
3271676Sjpk 	char	m_space[BUF_SIZE];
3280Sstevel@tonic-gate } m_rtmsg;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate  * Sizes of data structures extracted from the base mib.
3320Sstevel@tonic-gate  * This allows the size of the tables entries to grow while preserving
3330Sstevel@tonic-gate  * binary compatibility.
3340Sstevel@tonic-gate  */
3350Sstevel@tonic-gate static int ipRouteEntrySize;
3360Sstevel@tonic-gate static int ipv6RouteEntrySize;
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate #define	ROUNDUP_LONG(a) \
3390Sstevel@tonic-gate 	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
3400Sstevel@tonic-gate #define	ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
3410Sstevel@tonic-gate #define	C(x)	((x) & 0xff)
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate /*
3440Sstevel@tonic-gate  * return values from in_getprefixlen()
3450Sstevel@tonic-gate  */
3460Sstevel@tonic-gate #define	BAD_ADDR	-1	/* prefix is invalid */
3470Sstevel@tonic-gate #define	NO_PREFIX	-2	/* no prefix was found */
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate void
usage(char * cp)3500Sstevel@tonic-gate usage(char *cp)
3510Sstevel@tonic-gate {
3521291Sjl138328 	if (cp != NULL) {
3530Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
3540Sstevel@tonic-gate 		    cp);
3551291Sjl138328 	}
3561291Sjl138328 	(void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
3571291Sjl138328 	    "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
3580Sstevel@tonic-gate 	exit(1);
3590Sstevel@tonic-gate 	/* NOTREACHED */
3600Sstevel@tonic-gate }
3610Sstevel@tonic-gate 
3621291Sjl138328 /*PRINTFLIKE1*/
3631291Sjl138328 void
syntax_error(char * err,...)3641291Sjl138328 syntax_error(char *err, ...)
3651291Sjl138328 {
3661291Sjl138328 	va_list args;
3671291Sjl138328 
3681291Sjl138328 	if (exit_on_error) {
3691291Sjl138328 		va_start(args, err);
3701291Sjl138328 		(void) vfprintf(stderr, err, args);
3711291Sjl138328 		va_end(args);
3721291Sjl138328 		exit(1);
3731291Sjl138328 	}
3741291Sjl138328 	/* NOTREACHED */
3751291Sjl138328 }
3761291Sjl138328 
3771291Sjl138328 void
syntax_bad_keyword(char * keyword)3781291Sjl138328 syntax_bad_keyword(char *keyword)
3791291Sjl138328 {
3801291Sjl138328 	syntax_error(gettext("route: botched keyword: %s\n"), keyword);
3811291Sjl138328 }
3821291Sjl138328 
3831291Sjl138328 void
syntax_arg_missing(char * keyword)3841291Sjl138328 syntax_arg_missing(char *keyword)
3851291Sjl138328 {
3861291Sjl138328 	syntax_error(gettext("route: argument required following keyword %s\n"),
3871291Sjl138328 	    keyword);
3881291Sjl138328 }
3891291Sjl138328 
3900Sstevel@tonic-gate void
quit(char * s,int sverrno)3910Sstevel@tonic-gate quit(char *s, int sverrno)
3920Sstevel@tonic-gate {
3930Sstevel@tonic-gate 	(void) fprintf(stderr, "route: ");
3940Sstevel@tonic-gate 	if (s != NULL)
3950Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", s);
3960Sstevel@tonic-gate 	(void) fprintf(stderr, "%s\n", strerror(sverrno));
3970Sstevel@tonic-gate 	exit(sverrno);
3980Sstevel@tonic-gate 	/* NOTREACHED */
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate int
main(int argc,char ** argv)4020Sstevel@tonic-gate main(int argc, char **argv)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	extern int optind;
4051291Sjl138328 	extern char *optarg;
4060Sstevel@tonic-gate 	int ch;
4071291Sjl138328 	int rval;
4081291Sjl138328 	size_t size;
4091291Sjl138328 	const char *root_dir = NULL;
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4140Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
4150Sstevel@tonic-gate #endif
4160Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	if (argc < 2)
4191676Sjpk 		usage(NULL);
4200Sstevel@tonic-gate 
4211291Sjl138328 	while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
4220Sstevel@tonic-gate 		switch (ch) {
4230Sstevel@tonic-gate 		case 'n':
4240Sstevel@tonic-gate 			nflag = B_TRUE;
4250Sstevel@tonic-gate 			break;
4260Sstevel@tonic-gate 		case 'q':
4270Sstevel@tonic-gate 			qflag = B_TRUE;
4280Sstevel@tonic-gate 			break;
4290Sstevel@tonic-gate 		case 'v':
4300Sstevel@tonic-gate 			verbose = B_TRUE;
4310Sstevel@tonic-gate 			break;
4320Sstevel@tonic-gate 		case 't':
4330Sstevel@tonic-gate 			tflag = B_TRUE;
4340Sstevel@tonic-gate 			break;
4350Sstevel@tonic-gate 		case 'd':
4360Sstevel@tonic-gate 			debugonly = B_TRUE;
4370Sstevel@tonic-gate 			break;
4380Sstevel@tonic-gate 		case 'f':
4390Sstevel@tonic-gate 			fflag = B_TRUE;
4400Sstevel@tonic-gate 			break;
4411291Sjl138328 		case 'p':
4421291Sjl138328 			perm_flag = B_TRUE;
4431291Sjl138328 			break;
4441291Sjl138328 		case 'R':
4451291Sjl138328 			root_dir = optarg;
4461291Sjl138328 			break;
4470Sstevel@tonic-gate 		case '?':
4480Sstevel@tonic-gate 		default:
4491676Sjpk 			usage(NULL);
4500Sstevel@tonic-gate 			/* NOTREACHED */
4510Sstevel@tonic-gate 		}
4520Sstevel@tonic-gate 	}
4530Sstevel@tonic-gate 	argc -= optind;
4540Sstevel@tonic-gate 	argv += optind;
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	pid = getpid();
4570Sstevel@tonic-gate 	if (tflag)
4580Sstevel@tonic-gate 		s = open("/dev/null", O_WRONLY);
4590Sstevel@tonic-gate 	else
4600Sstevel@tonic-gate 		s = socket(PF_ROUTE, SOCK_RAW, 0);
4610Sstevel@tonic-gate 	if (s < 0)
4620Sstevel@tonic-gate 		quit("socket", errno);
4631291Sjl138328 
4641291Sjl138328 	/*
4651291Sjl138328 	 * Handle the -p and -R flags.  The -R flag only applies
4661291Sjl138328 	 * when the -p flag is set.
4671291Sjl138328 	 */
4681291Sjl138328 	if (root_dir == NULL) {
4691291Sjl138328 		perm_file = perm_file_sfx;
4701291Sjl138328 		temp_file = temp_file_sfx;
4711291Sjl138328 	} else {
4721291Sjl138328 		size = strlen(root_dir) + sizeof (perm_file_sfx);
4731291Sjl138328 		perm_file = malloc(size);
4741291Sjl138328 		if (perm_file == NULL)
4751291Sjl138328 			quit("malloc", errno);
4761291Sjl138328 		(void) snprintf(perm_file, size, "%s%s", root_dir,
4771291Sjl138328 		    perm_file_sfx);
4781291Sjl138328 		size = strlen(root_dir) + sizeof (temp_file_sfx);
4791291Sjl138328 		temp_file = malloc(size);
4801291Sjl138328 		if (temp_file == NULL)
4811291Sjl138328 			quit("malloc", errno);
4821291Sjl138328 		(void) snprintf(temp_file, size, "%s%s", root_dir,
4831291Sjl138328 		    temp_file_sfx);
4841291Sjl138328 	}
4851291Sjl138328 	/*
4861291Sjl138328 	 * Whether or not to act on the routing table.  The only time the
4871291Sjl138328 	 * routing table is not modified is when both -p and -R are present.
4881291Sjl138328 	 */
4891291Sjl138328 	update_table = (!perm_flag || root_dir == NULL);
4901291Sjl138328 	if (tflag)
4911291Sjl138328 		perm_flag = 0;
4921291Sjl138328 
4930Sstevel@tonic-gate 	if (fflag) {
4940Sstevel@tonic-gate 		/*
4950Sstevel@tonic-gate 		 * Accept an address family keyword after the -f.  Since the
4960Sstevel@tonic-gate 		 * default address family is AF_INET, reassign af only for the
4970Sstevel@tonic-gate 		 * other valid address families.
4980Sstevel@tonic-gate 		 */
4990Sstevel@tonic-gate 		if (*argv != NULL) {
5001291Sjl138328 			switch (keyword(*argv)) {
5010Sstevel@tonic-gate 			case K_INET6:
5021291Sjl138328 				af = AF_INET6;
5031291Sjl138328 				early_v6_keyword = B_TRUE;
5041291Sjl138328 				/* fallthrough */
5051291Sjl138328 			case K_INET:
5060Sstevel@tonic-gate 				/* Skip over the address family parameter. */
5070Sstevel@tonic-gate 				argc--;
5080Sstevel@tonic-gate 				argv++;
5090Sstevel@tonic-gate 				break;
5100Sstevel@tonic-gate 			}
5110Sstevel@tonic-gate 		}
512156Sdduvall 		flushroutes(0, NULL);
5130Sstevel@tonic-gate 	}
5141291Sjl138328 
5150Sstevel@tonic-gate 	if (*argv != NULL) {
5160Sstevel@tonic-gate 		switch (keyword(*argv)) {
5170Sstevel@tonic-gate 		case K_GET:
5180Sstevel@tonic-gate 		case K_CHANGE:
5190Sstevel@tonic-gate 		case K_ADD:
5200Sstevel@tonic-gate 		case K_DELETE:
5211291Sjl138328 			rval = 0;
5221291Sjl138328 			if (update_table) {
5231291Sjl138328 				rval = newroute(argv);
5241291Sjl138328 			}
5251291Sjl138328 			if (perm_flag && (rval == 0 || rval == EEXIST ||
5261291Sjl138328 			    rval == ESRCH)) {
5271291Sjl138328 				save_route(argc, argv, B_FALSE);
5281291Sjl138328 				return (0);
5291291Sjl138328 			}
5301291Sjl138328 			return (rval);
5311291Sjl138328 		case K_SHOW:
5321291Sjl138328 			if (perm_flag) {
5331291Sjl138328 				return (show_saved_routes(argc));
5341291Sjl138328 			} else {
5351291Sjl138328 				syntax_error(gettext(
5361291Sjl138328 				    "route: show command requires -p\n"));
5371291Sjl138328 			}
5381291Sjl138328 			/* NOTREACHED */
5390Sstevel@tonic-gate 		case K_MONITOR:
5400Sstevel@tonic-gate 			rtmonitor(argc, argv);
5410Sstevel@tonic-gate 			/* NOTREACHED */
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 		case K_FLUSH:
5440Sstevel@tonic-gate 			flushroutes(argc, argv);
5451291Sjl138328 			return (0);
5460Sstevel@tonic-gate 		}
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate 	if (!fflag)
5490Sstevel@tonic-gate 		usage(*argv);
5500Sstevel@tonic-gate 	return (0);
5510Sstevel@tonic-gate }
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate /*
5540Sstevel@tonic-gate  * Purge all entries in the routing tables not
5550Sstevel@tonic-gate  * associated with network interfaces.
5560Sstevel@tonic-gate  */
5570Sstevel@tonic-gate void
flushroutes(int argc,char * argv[])5580Sstevel@tonic-gate flushroutes(int argc, char *argv[])
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	int seqno;
5610Sstevel@tonic-gate 	int sd;	/* mib stream */
5620Sstevel@tonic-gate 	mib_item_t	*item;
5630Sstevel@tonic-gate 	mib2_ipRouteEntry_t *rp;
5640Sstevel@tonic-gate 	mib2_ipv6RouteEntry_t *rp6;
5650Sstevel@tonic-gate 	int oerrno;
5660Sstevel@tonic-gate 	int off = 0;
5670Sstevel@tonic-gate 	int on = 1;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	if (argc > 1) {
5700Sstevel@tonic-gate 		argv++;
5710Sstevel@tonic-gate 		if (argc == 2 && **argv == '-') {
5720Sstevel@tonic-gate 			/*
5730Sstevel@tonic-gate 			 * The address family (preceded by a dash) may be used
5740Sstevel@tonic-gate 			 * to flush the routes of that particular family.
5750Sstevel@tonic-gate 			 */
5760Sstevel@tonic-gate 			switch (keyword(*argv + 1)) {
5770Sstevel@tonic-gate 			case K_INET:
5780Sstevel@tonic-gate 				af = AF_INET;
5790Sstevel@tonic-gate 				break;
5800Sstevel@tonic-gate 			case K_LINK:
5810Sstevel@tonic-gate 				af = AF_LINK;
5820Sstevel@tonic-gate 				break;
5830Sstevel@tonic-gate 			case K_INET6:
5840Sstevel@tonic-gate 				af = AF_INET6;
5850Sstevel@tonic-gate 				break;
5860Sstevel@tonic-gate 			default:
5870Sstevel@tonic-gate 				usage(*argv);
5880Sstevel@tonic-gate 				/* NOTREACHED */
5890Sstevel@tonic-gate 			}
5900Sstevel@tonic-gate 		} else {
5910Sstevel@tonic-gate 			usage(*argv);
5920Sstevel@tonic-gate 		}
5930Sstevel@tonic-gate 	}
5941291Sjl138328 	if (perm_flag) {
5951291Sjl138328 		/* This flushes the persistent route file */
5961291Sjl138328 		save_route(0, NULL, B_TRUE);
5971291Sjl138328 	}
5981291Sjl138328 	if (!update_table) {
5991291Sjl138328 		return;
6001291Sjl138328 	}
6011291Sjl138328 
6021291Sjl138328 	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
6031291Sjl138328 	    sizeof (off)) < 0)
6041291Sjl138328 		quit("setsockopt", errno);
6051291Sjl138328 
6060Sstevel@tonic-gate 	sd = open("/dev/ip", O_RDWR);
6070Sstevel@tonic-gate 	oerrno = errno;
6080Sstevel@tonic-gate 	if (sd < 0) {
6090Sstevel@tonic-gate 		switch (errno) {
6100Sstevel@tonic-gate 		case EACCES:
6110Sstevel@tonic-gate 			(void) fprintf(stderr,
6120Sstevel@tonic-gate 			    gettext("route: flush: insufficient privileges\n"));
6130Sstevel@tonic-gate 			exit(oerrno);
6140Sstevel@tonic-gate 			/* NOTREACHED */
6150Sstevel@tonic-gate 		default:
6160Sstevel@tonic-gate 			quit(gettext("can't open mib stream"), oerrno);
6170Sstevel@tonic-gate 			/* NOTREACHED */
6180Sstevel@tonic-gate 		}
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate 	if ((item = mibget(sd)) == NULL)
6210Sstevel@tonic-gate 		quit("mibget", errno);
6220Sstevel@tonic-gate 	if (verbose) {
6230Sstevel@tonic-gate 		(void) printf("Examining routing table from "
6240Sstevel@tonic-gate 		    "T_SVR4_OPTMGMT_REQ\n");
6250Sstevel@tonic-gate 	}
6260Sstevel@tonic-gate 	seqno = 0;		/* ??? */
6270Sstevel@tonic-gate 	switch (af) {
6280Sstevel@tonic-gate 	case AF_INET:
6290Sstevel@tonic-gate 		/* Extract ipRouteEntrySize */
6300Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6310Sstevel@tonic-gate 			if (item->mib_id != 0)
6320Sstevel@tonic-gate 				continue;
6330Sstevel@tonic-gate 			if (item->group == MIB2_IP) {
6340Sstevel@tonic-gate 				ipRouteEntrySize =
6350Sstevel@tonic-gate 				    ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
6360Sstevel@tonic-gate 				assert(IS_P2ALIGNED(ipRouteEntrySize,
6370Sstevel@tonic-gate 				    sizeof (mib2_ipRouteEntry_t *)));
6380Sstevel@tonic-gate 				break;
6390Sstevel@tonic-gate 			}
6400Sstevel@tonic-gate 		}
6410Sstevel@tonic-gate 		if (ipRouteEntrySize == 0) {
6420Sstevel@tonic-gate 			(void) fprintf(stderr,
6430Sstevel@tonic-gate 			    gettext("ipRouteEntrySize can't be determined.\n"));
6440Sstevel@tonic-gate 			exit(1);
6450Sstevel@tonic-gate 		}
6460Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6470Sstevel@tonic-gate 			/*
6480Sstevel@tonic-gate 			 * skip all the other trash that comes up the mib stream
6490Sstevel@tonic-gate 			 */
6500Sstevel@tonic-gate 			if (item->group != MIB2_IP ||
6510Sstevel@tonic-gate 			    item->mib_id != MIB2_IP_ROUTE)
6520Sstevel@tonic-gate 				continue;
6530Sstevel@tonic-gate 			for (rp = (mib2_ipRouteEntry_t *)item->valp;
6540Sstevel@tonic-gate 			    (char *)rp < (char *)item->valp + item->length;
6550Sstevel@tonic-gate 			    /* LINTED */
6560Sstevel@tonic-gate 			    rp = (mib2_ipRouteEntry_t *)
65711042SErik.Nordmark@Sun.COM 			    ((char *)rp + ipRouteEntrySize)) {
6580Sstevel@tonic-gate 				delRouteEntry(rp, NULL, seqno);
6590Sstevel@tonic-gate 				seqno++;
6600Sstevel@tonic-gate 			}
6610Sstevel@tonic-gate 			break;
6620Sstevel@tonic-gate 		}
6630Sstevel@tonic-gate 		break;
6640Sstevel@tonic-gate 	case AF_INET6:
6650Sstevel@tonic-gate 		/* Extract ipv6RouteEntrySize */
6660Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6670Sstevel@tonic-gate 			if (item->mib_id != 0)
6680Sstevel@tonic-gate 				continue;
6690Sstevel@tonic-gate 			if (item->group == MIB2_IP6) {
6700Sstevel@tonic-gate 				ipv6RouteEntrySize =
6710Sstevel@tonic-gate 				    ((mib2_ipv6IfStatsEntry_t *)item->valp)->
67211042SErik.Nordmark@Sun.COM 				    ipv6RouteEntrySize;
6730Sstevel@tonic-gate 				assert(IS_P2ALIGNED(ipv6RouteEntrySize,
6740Sstevel@tonic-gate 				    sizeof (mib2_ipv6RouteEntry_t *)));
6750Sstevel@tonic-gate 				break;
6760Sstevel@tonic-gate 			}
6770Sstevel@tonic-gate 		}
6780Sstevel@tonic-gate 		if (ipv6RouteEntrySize == 0) {
6790Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6800Sstevel@tonic-gate 			    "ipv6RouteEntrySize cannot be determined.\n"));
6810Sstevel@tonic-gate 			exit(1);
6820Sstevel@tonic-gate 		}
6830Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6840Sstevel@tonic-gate 			/*
6850Sstevel@tonic-gate 			 * skip all the other trash that comes up the mib stream
6860Sstevel@tonic-gate 			 */
6870Sstevel@tonic-gate 			if (item->group != MIB2_IP6 ||
6880Sstevel@tonic-gate 			    item->mib_id != MIB2_IP6_ROUTE)
6890Sstevel@tonic-gate 				continue;
6900Sstevel@tonic-gate 			for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
6910Sstevel@tonic-gate 			    (char *)rp6 < (char *)item->valp + item->length;
6920Sstevel@tonic-gate 			    /* LINTED */
6930Sstevel@tonic-gate 			    rp6 = (mib2_ipv6RouteEntry_t *)
69411042SErik.Nordmark@Sun.COM 			    ((char *)rp6 + ipv6RouteEntrySize)) {
6950Sstevel@tonic-gate 				delRouteEntry(NULL, rp6, seqno);
6960Sstevel@tonic-gate 				seqno++;
6970Sstevel@tonic-gate 			}
6980Sstevel@tonic-gate 			break;
6990Sstevel@tonic-gate 		}
7000Sstevel@tonic-gate 		break;
7010Sstevel@tonic-gate 	}
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
7040Sstevel@tonic-gate 	    sizeof (on)) < 0)
7050Sstevel@tonic-gate 		quit("setsockopt", errno);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate /*
7090Sstevel@tonic-gate  * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
7100Sstevel@tonic-gate  * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
7110Sstevel@tonic-gate  * order to facilitate the flushing of RTF_GATEWAY routes.
7120Sstevel@tonic-gate  */
7130Sstevel@tonic-gate static void
delRouteEntry(mib2_ipRouteEntry_t * rp,mib2_ipv6RouteEntry_t * rp6,int seqno)7140Sstevel@tonic-gate delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
7150Sstevel@tonic-gate {
7160Sstevel@tonic-gate 	char *cp;
7170Sstevel@tonic-gate 	int ire_type;
7180Sstevel@tonic-gate 	int rlen;
7190Sstevel@tonic-gate 	struct rt_msghdr *rtm;
7200Sstevel@tonic-gate 	struct sockaddr_in sin;
7210Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
7220Sstevel@tonic-gate 	int slen;
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	if (rp != NULL)
7250Sstevel@tonic-gate 		ire_type = rp->ipRouteInfo.re_ire_type;
7260Sstevel@tonic-gate 	else
7270Sstevel@tonic-gate 		ire_type = rp6->ipv6RouteInfo.re_ire_type;
7280Sstevel@tonic-gate 	if (ire_type != IRE_DEFAULT &&
7290Sstevel@tonic-gate 	    ire_type != IRE_PREFIX &&
7300Sstevel@tonic-gate 	    ire_type != IRE_HOST &&
7310Sstevel@tonic-gate 	    ire_type != IRE_HOST_REDIRECT)
7320Sstevel@tonic-gate 		return;
7330Sstevel@tonic-gate 
7340Sstevel@tonic-gate 	rtm = &m_rtmsg.m_rtm;
7350Sstevel@tonic-gate 	(void) memset(rtm, 0, sizeof (m_rtmsg));
7360Sstevel@tonic-gate 	rtm->rtm_type = RTM_DELETE;
7370Sstevel@tonic-gate 	rtm->rtm_seq = seqno;
7380Sstevel@tonic-gate 	rtm->rtm_flags |= RTF_GATEWAY;
7390Sstevel@tonic-gate 	rtm->rtm_version = RTM_VERSION;
7400Sstevel@tonic-gate 	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
7410Sstevel@tonic-gate 	cp = m_rtmsg.m_space;
7420Sstevel@tonic-gate 	if (rp != NULL) {
7430Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in);
7440Sstevel@tonic-gate 		if (rp->ipRouteMask == IP_HOST_MASK)
7450Sstevel@tonic-gate 			rtm->rtm_flags |= RTF_HOST;
7460Sstevel@tonic-gate 		(void) memset(&sin, 0, slen);
7470Sstevel@tonic-gate 		sin.sin_family = AF_INET;
7480Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteDest;
7490Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7500Sstevel@tonic-gate 		cp += slen;
7510Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteNextHop;
7520Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7530Sstevel@tonic-gate 		cp += slen;
7540Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteMask;
7550Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7560Sstevel@tonic-gate 		cp += slen;
7570Sstevel@tonic-gate 	} else {
7580Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in6);
7590Sstevel@tonic-gate 		if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
7600Sstevel@tonic-gate 			rtm->rtm_flags |= RTF_HOST;
7610Sstevel@tonic-gate 		(void) memset(&sin6, 0, slen);
7620Sstevel@tonic-gate 		sin6.sin6_family = AF_INET6;
7630Sstevel@tonic-gate 		sin6.sin6_addr = rp6->ipv6RouteDest;
7640Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7650Sstevel@tonic-gate 		cp += slen;
7660Sstevel@tonic-gate 		sin6.sin6_addr = rp6->ipv6RouteNextHop;
7670Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7680Sstevel@tonic-gate 		cp += slen;
7690Sstevel@tonic-gate 		(void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
7700Sstevel@tonic-gate 		(void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
7710Sstevel@tonic-gate 		    (uchar_t *)&sin6.sin6_addr.s6_addr);
7720Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7730Sstevel@tonic-gate 		cp += slen;
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
7760Sstevel@tonic-gate 	if (debugonly) {
7770Sstevel@tonic-gate 		/*
7780Sstevel@tonic-gate 		 * In debugonly mode, the routing socket message to delete the
7790Sstevel@tonic-gate 		 * current entry is not actually sent.  However if verbose is
7800Sstevel@tonic-gate 		 * also set, the routing socket message that would have been
7810Sstevel@tonic-gate 		 * is printed.
7820Sstevel@tonic-gate 		 */
7830Sstevel@tonic-gate 		if (verbose)
7840Sstevel@tonic-gate 			print_rtmsg(rtm, rtm->rtm_msglen);
7850Sstevel@tonic-gate 		return;
7860Sstevel@tonic-gate 	}
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
7890Sstevel@tonic-gate 	if (rlen < (int)rtm->rtm_msglen) {
7900Sstevel@tonic-gate 		if (rlen < 0) {
7910Sstevel@tonic-gate 			(void) fprintf(stderr,
7920Sstevel@tonic-gate 			    gettext("route: write to routing socket: %s\n"),
7930Sstevel@tonic-gate 			    strerror(errno));
7940Sstevel@tonic-gate 		} else {
7950Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("route: write to "
7960Sstevel@tonic-gate 			    "routing socket got only %d for rlen\n"), rlen);
7970Sstevel@tonic-gate 		}
7980Sstevel@tonic-gate 		return;
7990Sstevel@tonic-gate 	}
8000Sstevel@tonic-gate 	if (qflag) {
8010Sstevel@tonic-gate 		/*
8020Sstevel@tonic-gate 		 * In quiet mode, nothing is printed at all (unless the write()
8030Sstevel@tonic-gate 		 * itself failed.
8040Sstevel@tonic-gate 		 */
8050Sstevel@tonic-gate 		return;
8060Sstevel@tonic-gate 	}
8070Sstevel@tonic-gate 	if (verbose) {
8080Sstevel@tonic-gate 		print_rtmsg(rtm, rlen);
8090Sstevel@tonic-gate 	} else {
8100Sstevel@tonic-gate 		struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 		(void) printf("%-20.20s ",
8130Sstevel@tonic-gate 		    rtm->rtm_flags & RTF_HOST ? routename(sa) :
81411042SErik.Nordmark@Sun.COM 		    netname(sa));
8150Sstevel@tonic-gate 		/* LINTED */
8160Sstevel@tonic-gate 		sa = (struct sockaddr *)(salen(sa) + (char *)sa);
8170Sstevel@tonic-gate 		(void) printf("%-20.20s ", routename(sa));
8180Sstevel@tonic-gate 		(void) printf("done\n");
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate /*
8230Sstevel@tonic-gate  * Return the name of the host whose address is given.
8240Sstevel@tonic-gate  */
8250Sstevel@tonic-gate char *
routename(const struct sockaddr * sa)8261676Sjpk routename(const struct sockaddr *sa)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	char *cp;
8290Sstevel@tonic-gate 	static char line[MAXHOSTNAMELEN + 1];
8300Sstevel@tonic-gate 	struct hostent *hp = NULL;
8310Sstevel@tonic-gate 	static char domain[MAXHOSTNAMELEN + 1];
8320Sstevel@tonic-gate 	static boolean_t first = B_TRUE;
8330Sstevel@tonic-gate 	struct in_addr in;
8340Sstevel@tonic-gate 	struct in6_addr in6;
8350Sstevel@tonic-gate 	int error_num;
8360Sstevel@tonic-gate 	ushort_t *s;
8370Sstevel@tonic-gate 	ushort_t *slim;
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	if (first) {
8400Sstevel@tonic-gate 		first = B_FALSE;
8410Sstevel@tonic-gate 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
8420Sstevel@tonic-gate 		    (cp = strchr(domain, '.')))
8430Sstevel@tonic-gate 			(void) strcpy(domain, cp + 1);
8440Sstevel@tonic-gate 		else
8450Sstevel@tonic-gate 			domain[0] = 0;
8460Sstevel@tonic-gate 	}
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	if (salen(sa) == 0) {
8490Sstevel@tonic-gate 		(void) strcpy(line, "default");
8500Sstevel@tonic-gate 		return (line);
8510Sstevel@tonic-gate 	}
8520Sstevel@tonic-gate 	switch (sa->sa_family) {
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	case AF_INET:
8550Sstevel@tonic-gate 		/* LINTED */
8560Sstevel@tonic-gate 		in = ((struct sockaddr_in *)sa)->sin_addr;
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 		cp = NULL;
8590Sstevel@tonic-gate 		if (in.s_addr == INADDR_ANY)
8600Sstevel@tonic-gate 			cp = "default";
8610Sstevel@tonic-gate 		if (cp == NULL && !nflag) {
8620Sstevel@tonic-gate 			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
86311042SErik.Nordmark@Sun.COM 			    AF_INET);
8640Sstevel@tonic-gate 			if (hp != NULL) {
8650Sstevel@tonic-gate 				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
8660Sstevel@tonic-gate 				    (strcmp(cp + 1, domain) == 0))
8670Sstevel@tonic-gate 					*cp = 0;
8680Sstevel@tonic-gate 				cp = hp->h_name;
8690Sstevel@tonic-gate 			}
8700Sstevel@tonic-gate 		}
8710Sstevel@tonic-gate 		if (cp != NULL) {
8720Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
8730Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
8740Sstevel@tonic-gate 		} else {
8750Sstevel@tonic-gate 			in.s_addr = ntohl(in.s_addr);
8760Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
8770Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8),
8780Sstevel@tonic-gate 			    C(in.s_addr));
8790Sstevel@tonic-gate 		}
8800Sstevel@tonic-gate 		break;
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	case AF_LINK:
8830Sstevel@tonic-gate 		return (link_ntoa((struct sockaddr_dl *)sa));
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	case AF_INET6:
8860Sstevel@tonic-gate 		/* LINTED */
8870Sstevel@tonic-gate 		in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 		cp = NULL;
8900Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&in6))
8910Sstevel@tonic-gate 			cp = "default";
8920Sstevel@tonic-gate 		if (cp == NULL && !nflag) {
8930Sstevel@tonic-gate 			hp = getipnodebyaddr((char *)&in6,
89411042SErik.Nordmark@Sun.COM 			    sizeof (struct in6_addr), AF_INET6, &error_num);
8950Sstevel@tonic-gate 			if (hp != NULL) {
8960Sstevel@tonic-gate 				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
8970Sstevel@tonic-gate 				    (strcmp(cp + 1, domain) == 0))
8980Sstevel@tonic-gate 					*cp = 0;
8990Sstevel@tonic-gate 				cp = hp->h_name;
9000Sstevel@tonic-gate 			}
9010Sstevel@tonic-gate 		}
9020Sstevel@tonic-gate 		if (cp != NULL) {
9030Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
9040Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
9050Sstevel@tonic-gate 		} else {
9060Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6, (void *)&in6, line,
9070Sstevel@tonic-gate 			    INET6_ADDRSTRLEN);
9080Sstevel@tonic-gate 		}
9090Sstevel@tonic-gate 		if (hp != NULL)
9100Sstevel@tonic-gate 			freehostent(hp);
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 		break;
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	default:
9150Sstevel@tonic-gate 		s = (ushort_t *)sa;
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 		slim = s + ((salen(sa) + 1) >> 1);
9180Sstevel@tonic-gate 		cp = line + sprintf(line, "(%d)", sa->sa_family);
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 		while (++s < slim) /* start with sa->sa_data */
9210Sstevel@tonic-gate 			cp += sprintf(cp, " %x", *s);
9220Sstevel@tonic-gate 		break;
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 	return (line);
9250Sstevel@tonic-gate }
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate /*
9280Sstevel@tonic-gate  * Return the name of the network whose address is given.
9290Sstevel@tonic-gate  * The address is assumed to be that of a net or subnet, not a host.
9300Sstevel@tonic-gate  */
9310Sstevel@tonic-gate static char *
netname(struct sockaddr * sa)9320Sstevel@tonic-gate netname(struct sockaddr *sa)
9330Sstevel@tonic-gate {
9340Sstevel@tonic-gate 	char *cp = NULL;
9350Sstevel@tonic-gate 	static char line[MAXHOSTNAMELEN + 1];
9360Sstevel@tonic-gate 	struct netent *np;
9370Sstevel@tonic-gate 	in_addr_t net, mask;
9380Sstevel@tonic-gate 	int subnetshift;
9390Sstevel@tonic-gate 	struct in_addr in;
9400Sstevel@tonic-gate 	ushort_t *s;
9410Sstevel@tonic-gate 	ushort_t *slim;
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	switch (sa->sa_family) {
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 	case AF_INET:
9460Sstevel@tonic-gate 		/* LINTED */
9470Sstevel@tonic-gate 		in = ((struct sockaddr_in *)sa)->sin_addr;
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 		in.s_addr = ntohl(in.s_addr);
9500Sstevel@tonic-gate 		if (in.s_addr == INADDR_ANY) {
9510Sstevel@tonic-gate 			cp = "default";
9520Sstevel@tonic-gate 		} else if (!nflag) {
9530Sstevel@tonic-gate 			if (IN_CLASSA(in.s_addr)) {
9540Sstevel@tonic-gate 				mask = IN_CLASSA_NET;
9550Sstevel@tonic-gate 				subnetshift = 8;
9560Sstevel@tonic-gate 			} else if (IN_CLASSB(in.s_addr)) {
9570Sstevel@tonic-gate 				mask = IN_CLASSB_NET;
9580Sstevel@tonic-gate 				subnetshift = 8;
9590Sstevel@tonic-gate 			} else {
9600Sstevel@tonic-gate 				mask = IN_CLASSC_NET;
9610Sstevel@tonic-gate 				subnetshift = 4;
9620Sstevel@tonic-gate 			}
9630Sstevel@tonic-gate 			/*
9640Sstevel@tonic-gate 			 * If there are more bits than the standard mask
9650Sstevel@tonic-gate 			 * would suggest, subnets must be in use.
9660Sstevel@tonic-gate 			 * Guess at the subnet mask, assuming reasonable
9670Sstevel@tonic-gate 			 * width subnet fields.
9680Sstevel@tonic-gate 			 */
9690Sstevel@tonic-gate 			while (in.s_addr &~ mask)
9700Sstevel@tonic-gate 				mask = (long)mask >> subnetshift;
9710Sstevel@tonic-gate 			net = in.s_addr & mask;
9720Sstevel@tonic-gate 			while ((mask & 1) == 0)
9730Sstevel@tonic-gate 				mask >>= 1, net >>= 1;
9740Sstevel@tonic-gate 			np = getnetbyaddr(net, AF_INET);
9750Sstevel@tonic-gate 			if (np != NULL)
9760Sstevel@tonic-gate 				cp = np->n_name;
9770Sstevel@tonic-gate 		}
9780Sstevel@tonic-gate 		if (cp != NULL) {
9790Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
9800Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
9810Sstevel@tonic-gate 		} else if ((in.s_addr & 0xffffff) == 0) {
9820Sstevel@tonic-gate 			(void) sprintf(line, "%u", C(in.s_addr >> 24));
9830Sstevel@tonic-gate 		} else if ((in.s_addr & 0xffff) == 0) {
9840Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
9850Sstevel@tonic-gate 			    C(in.s_addr >> 16));
9860Sstevel@tonic-gate 		} else if ((in.s_addr & 0xff) == 0) {
9870Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
9880Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8));
9890Sstevel@tonic-gate 		} else {
9900Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
9910Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8),
9920Sstevel@tonic-gate 			    C(in.s_addr));
9930Sstevel@tonic-gate 		}
9940Sstevel@tonic-gate 		break;
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	case AF_LINK:
9970Sstevel@tonic-gate 		return (link_ntoa((struct sockaddr_dl *)sa));
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	case AF_INET6:
10000Sstevel@tonic-gate 		return (routename(sa));
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	default:
10030Sstevel@tonic-gate 		/* LINTED */
10040Sstevel@tonic-gate 		s = (ushort_t *)sa->sa_data;
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 		slim = s + ((salen(sa) + 1) >> 1);
10070Sstevel@tonic-gate 		cp = line + sprintf(line, "af %d:", sa->sa_family);
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 		while (s < slim)
10100Sstevel@tonic-gate 			cp += sprintf(cp, " %x", *s++);
10110Sstevel@tonic-gate 		break;
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 	return (line);
10140Sstevel@tonic-gate }
10150Sstevel@tonic-gate 
10161291Sjl138328 /*
10171291Sjl138328  * Initialize a new structure.  Keep in mind that ri_dst_str, ri_gate_str and
10181291Sjl138328  * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
10191291Sjl138328  * or point to dynamically allocated memory.
10201291Sjl138328  */
10211291Sjl138328 rtcmd_irep_t *
new_rtcmd_irep(void)10221291Sjl138328 new_rtcmd_irep(void)
10231291Sjl138328 {
10241291Sjl138328 	rtcmd_irep_t *rcip;
10251291Sjl138328 
10261291Sjl138328 	rcip = calloc(1, sizeof (rtcmd_irep_t));
10271291Sjl138328 	if (rcip == NULL) {
10281291Sjl138328 		quit("calloc", errno);
10291291Sjl138328 	}
10301291Sjl138328 	rcip->ri_af = af;
10311291Sjl138328 	rcip->ri_flags = RTF_STATIC;
10321291Sjl138328 	return (rcip);
10331291Sjl138328 }
10341291Sjl138328 
10350Sstevel@tonic-gate void
del_rtcmd_irep(rtcmd_irep_t * rcip)10361291Sjl138328 del_rtcmd_irep(rtcmd_irep_t *rcip)
10371291Sjl138328 {
10381291Sjl138328 	free(rcip->ri_dest_str);
10391291Sjl138328 	free(rcip->ri_gate_str);
10401291Sjl138328 	free(rcip->ri_ifp_str);
10413846Sdg199075 	/*
10423846Sdg199075 	 * IPv6 host entries come from getipnodebyname, which dynamically
10433846Sdg199075 	 * allocates memory.  IPv4 host entries come from gethostbyname, which
10443846Sdg199075 	 * returns static memory and cannot be freed with freehostent.
10453846Sdg199075 	 */
10463846Sdg199075 	if (rcip->ri_gate_hp != NULL &&
10473846Sdg199075 	    rcip->ri_gate_hp->h_addrtype == AF_INET6)
10481291Sjl138328 		freehostent(rcip->ri_gate_hp);
10491291Sjl138328 	free(rcip);
10501291Sjl138328 }
10511291Sjl138328 
10521291Sjl138328 void
save_string(char ** dst,char * src)10531291Sjl138328 save_string(char **dst, char *src)
10541291Sjl138328 {
10551291Sjl138328 	free(*dst);
10561291Sjl138328 	*dst = strdup(src);
10571291Sjl138328 	if (*dst == NULL) {
10581291Sjl138328 		quit("malloc", errno);
10591291Sjl138328 	}
10601291Sjl138328 }
10611291Sjl138328 
10621291Sjl138328 /*
10631291Sjl138328  * Print the short form summary of a route command.
10641291Sjl138328  * Eg. "add net default: gateway 10.0.0.1"
10651291Sjl138328  * The final newline is not added, allowing the caller to append additional
10661291Sjl138328  * information.
10671291Sjl138328  */
10681291Sjl138328 void
print_rtcmd_short(FILE * to,rtcmd_irep_t * rcip,boolean_t gw_good,boolean_t to_saved)10691291Sjl138328 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
10701291Sjl138328     boolean_t to_saved)
10711291Sjl138328 {
10721291Sjl138328 	char *cmd;
10731291Sjl138328 	char obuf[INET6_ADDRSTRLEN];
10741291Sjl138328 
10751291Sjl138328 	switch (rcip->ri_cmd) {
10761291Sjl138328 	case RTM_ADD:
10771291Sjl138328 		cmd = "add";
10781291Sjl138328 		break;
10791291Sjl138328 	case RTM_CHANGE:
10801291Sjl138328 		cmd = "change";
10811291Sjl138328 		break;
10821291Sjl138328 	case RTM_DELETE:
10831291Sjl138328 		cmd = "delete";
10841291Sjl138328 		break;
10851291Sjl138328 	case RTM_GET:
10861291Sjl138328 		cmd = "get";
10871291Sjl138328 		break;
10881291Sjl138328 	default:
10891291Sjl138328 		assert(0);
10901291Sjl138328 	}
10911291Sjl138328 
10921291Sjl138328 	(void) fprintf(to, "%s%s %s %s", cmd,
10931291Sjl138328 	    (to_saved) ? " persistent" : "",
10941291Sjl138328 	    (rcip->ri_flags & RTF_HOST) ? "host" : "net",
10951291Sjl138328 	    (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str);
10961291Sjl138328 
10971291Sjl138328 	if (rcip->ri_gate_str != NULL) {
10981291Sjl138328 		switch (rcip->ri_af) {
10991291Sjl138328 		case AF_INET:
11001291Sjl138328 			if (nflag) {
11011291Sjl138328 				(void) fprintf(to, ": gateway %s",
11021291Sjl138328 				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
11031291Sjl138328 			} else if (gw_good &&
11041291Sjl138328 			    rcip->ri_gate_hp != NULL &&
11051291Sjl138328 			    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
11061291Sjl138328 				/*
11071291Sjl138328 				 * Print the actual address used in the case
11081291Sjl138328 				 * where there was more than one address
11091291Sjl138328 				 * available for the name, and one was used
11101291Sjl138328 				 * successfully.
11111291Sjl138328 				 */
11121291Sjl138328 				(void) fprintf(to, ": gateway %s (%s)",
11131291Sjl138328 				    rcip->ri_gate_str,
11141291Sjl138328 				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
11151291Sjl138328 			} else {
11161291Sjl138328 				(void) fprintf(to, ": gateway %s",
11171291Sjl138328 				    rcip->ri_gate_str);
11181291Sjl138328 			}
11191291Sjl138328 			break;
11201291Sjl138328 		case AF_INET6:
11211291Sjl138328 			if (inet_ntop(AF_INET6,
112211042SErik.Nordmark@Sun.COM 			    &rcip->ri_gate.sin6.sin6_addr, obuf,
112311042SErik.Nordmark@Sun.COM 			    INET6_ADDRSTRLEN) != NULL) {
11241291Sjl138328 				if (nflag) {
11251291Sjl138328 					(void) fprintf(to, ": gateway %s",
11261291Sjl138328 					    obuf);
11271291Sjl138328 					break;
11281291Sjl138328 				}
11291291Sjl138328 				if (gw_good &&
11301291Sjl138328 				    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
11311291Sjl138328 					(void) fprintf(to, ": gateway %s (%s)",
11321291Sjl138328 					    rcip->ri_gate_str, obuf);
11331291Sjl138328 					break;
11341291Sjl138328 				}
11351291Sjl138328 			}
11361291Sjl138328 			/* FALLTHROUGH */
11371291Sjl138328 		default:
11381291Sjl138328 			(void) fprintf(to, ": gateway %s",
11391291Sjl138328 			    rcip->ri_gate_str);
11401291Sjl138328 			break;
11411291Sjl138328 		}
11421291Sjl138328 	}
11431291Sjl138328 }
11441291Sjl138328 
11451291Sjl138328 void
set_metric(rtcmd_irep_t * rcip,char * value,int key,boolean_t lock)11461291Sjl138328 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
11470Sstevel@tonic-gate {
11480Sstevel@tonic-gate 	int flag = 0;
11490Sstevel@tonic-gate 	uint_t noval, *valp = &noval;
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	switch (key) {
11521291Sjl138328 #define	caseof(x, y, z)	\
11531291Sjl138328 	case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
11541291Sjl138328 
11550Sstevel@tonic-gate 	caseof(K_MTU, RTV_MTU, rmx_mtu);
11560Sstevel@tonic-gate 	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
11570Sstevel@tonic-gate 	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
11580Sstevel@tonic-gate 	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
11590Sstevel@tonic-gate 	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
11600Sstevel@tonic-gate 	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
11610Sstevel@tonic-gate 	caseof(K_RTT, RTV_RTT, rmx_rtt);
11620Sstevel@tonic-gate 	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
11630Sstevel@tonic-gate #undef	caseof
11640Sstevel@tonic-gate 	}
11651291Sjl138328 	rcip->ri_inits |= flag;
11661291Sjl138328 	if (lock)
11671291Sjl138328 		rcip->ri_metrics.rmx_locks |= flag;
11680Sstevel@tonic-gate 	*valp = atoi(value);
11690Sstevel@tonic-gate }
11700Sstevel@tonic-gate 
11711291Sjl138328 /*
11721291Sjl138328  * Parse the options give in argv[], filling in rcip with the results.
11731291Sjl138328  * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
11741291Sjl138328  * tokenized to produce the command line.  Cmd_string is tokenized using
11751291Sjl138328  * strtok, which will overwrite whitespace in the string with nulls.
11761291Sjl138328  *
11771291Sjl138328  * Returns B_TRUE on success and B_FALSE on failure.
11781291Sjl138328  */
11791291Sjl138328 boolean_t
args_to_rtcmd(rtcmd_irep_t * rcip,char ** argv,char * cmd_string)11801291Sjl138328 args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string)
11810Sstevel@tonic-gate {
11821291Sjl138328 	const char *ws = "\f\n\r\t\v ";
11831291Sjl138328 	char *tok = cmd_string;
11841291Sjl138328 	char *keyword_str;
11851291Sjl138328 	addr_type_t atype = ADDR_TYPE_ANY;
11861291Sjl138328 	boolean_t iflag = B_FALSE;
11871291Sjl138328 	boolean_t locknext = B_FALSE;
11881291Sjl138328 	boolean_t lockrest = B_FALSE;
11891291Sjl138328 	boolean_t dash_keyword;
11900Sstevel@tonic-gate 	int key;
11911291Sjl138328 	char *err;
11921291Sjl138328 
11931291Sjl138328 	if (cmd_string == NULL) {
11941291Sjl138328 		tok = argv[0];
11951291Sjl138328 	} else {
11961291Sjl138328 		tok = strtok(cmd_string, ws);
11971291Sjl138328 	}
119894Sjl138328 
11991291Sjl138328 	/*
12001291Sjl138328 	 * The command keywords are already fully checked by main() or
12011291Sjl138328 	 * search_rtfile().
12021291Sjl138328 	 */
12031291Sjl138328 	switch (*tok) {
12041291Sjl138328 	case 'a':
12051291Sjl138328 		rcip->ri_cmd = RTM_ADD;
12061291Sjl138328 		break;
12071291Sjl138328 	case 'c':
12081291Sjl138328 		rcip->ri_cmd = RTM_CHANGE;
12091291Sjl138328 		break;
12101291Sjl138328 	case 'd':
12111291Sjl138328 		rcip->ri_cmd = RTM_DELETE;
12121291Sjl138328 		break;
12131291Sjl138328 	case 'g':
12141291Sjl138328 		rcip->ri_cmd = RTM_GET;
12151291Sjl138328 		break;
12161291Sjl138328 	default:
12171291Sjl138328 		/* NOTREACHED */
12181291Sjl138328 		quit(gettext("Internal Error"), EINVAL);
12191291Sjl138328 		/* NOTREACHED */
1220156Sdduvall 	}
12211291Sjl138328 
12221291Sjl138328 #define	NEXTTOKEN \
12231291Sjl138328 	((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
12241291Sjl138328 
12251291Sjl138328 	while (NEXTTOKEN) {
12261291Sjl138328 		keyword_str = tok;
12271291Sjl138328 		if (*tok == '-') {
12281291Sjl138328 			dash_keyword = B_TRUE;
12291291Sjl138328 			key = keyword(tok + 1);
12301291Sjl138328 		} else {
12311291Sjl138328 			dash_keyword = B_FALSE;
12321291Sjl138328 			key = keyword(tok);
12331291Sjl138328 			if (key != K_HOST && key != K_NET) {
12341291Sjl138328 				/* All others must be preceded by '-' */
12351291Sjl138328 				key = 0;
12361291Sjl138328 			}
12371291Sjl138328 		}
12381291Sjl138328 		switch (key) {
12391291Sjl138328 		case K_HOST:
12401291Sjl138328 			if (atype == ADDR_TYPE_NET) {
12411291Sjl138328 				syntax_error(gettext("route: -host and -net "
12421291Sjl138328 				    "are mutually exclusive\n"));
12431291Sjl138328 				return (B_FALSE);
12441291Sjl138328 			}
12451291Sjl138328 			atype = ADDR_TYPE_HOST;
12461291Sjl138328 			break;
12471291Sjl138328 		case K_NET:
12481291Sjl138328 			if (atype == ADDR_TYPE_HOST) {
12491291Sjl138328 				syntax_error(gettext("route: -host and -net "
12501291Sjl138328 				    "are mutually exclusive\n"));
12511291Sjl138328 				return (B_FALSE);
12521291Sjl138328 			}
12531291Sjl138328 			atype = ADDR_TYPE_NET;
12541291Sjl138328 			break;
12551291Sjl138328 		case K_LINK:
12561291Sjl138328 			rcip->ri_af = AF_LINK;
12571291Sjl138328 			break;
12581291Sjl138328 		case K_INET:
12591291Sjl138328 			rcip->ri_af = AF_INET;
12601291Sjl138328 			break;
12611291Sjl138328 		case K_SA:
12621291Sjl138328 			rcip->ri_af = PF_ROUTE;
12631291Sjl138328 			break;
12641291Sjl138328 		case K_INET6:
12651291Sjl138328 			rcip->ri_af = AF_INET6;
12661291Sjl138328 			break;
12671291Sjl138328 		case K_IFACE:
12681291Sjl138328 		case K_INTERFACE:
12691291Sjl138328 			iflag = B_TRUE;
12701291Sjl138328 			/* fallthrough */
12711291Sjl138328 		case K_NOSTATIC:
12721291Sjl138328 			rcip->ri_flags &= ~RTF_STATIC;
12731291Sjl138328 			break;
12741291Sjl138328 		case K_LOCK:
12751291Sjl138328 			locknext = B_TRUE;
12761291Sjl138328 			break;
12771291Sjl138328 		case K_LOCKREST:
12781291Sjl138328 			lockrest = B_TRUE;
12791291Sjl138328 			break;
12801291Sjl138328 		case K_REJECT:
12811291Sjl138328 			rcip->ri_flags |= RTF_REJECT;
12821291Sjl138328 			break;
12831291Sjl138328 		case K_BLACKHOLE:
12841291Sjl138328 			rcip->ri_flags |= RTF_BLACKHOLE;
12851291Sjl138328 			break;
12861291Sjl138328 		case K_PROTO1:
12871291Sjl138328 			rcip->ri_flags |= RTF_PROTO1;
12881291Sjl138328 			break;
12891291Sjl138328 		case K_PROTO2:
12901291Sjl138328 			rcip->ri_flags |= RTF_PROTO2;
12911291Sjl138328 			break;
12921291Sjl138328 		case K_CLONING:
12931291Sjl138328 			rcip->ri_flags |= RTF_CLONING;
12941291Sjl138328 			break;
12951291Sjl138328 		case K_XRESOLVE:
12961291Sjl138328 			rcip->ri_flags |= RTF_XRESOLVE;
12971291Sjl138328 			break;
12981291Sjl138328 		case K_STATIC:
12991291Sjl138328 			rcip->ri_flags |= RTF_STATIC;
13001291Sjl138328 			break;
13011291Sjl138328 		case K_IFA:
13021291Sjl138328 			if (!NEXTTOKEN) {
13031291Sjl138328 				syntax_arg_missing(keyword_str);
13041291Sjl138328 				return (B_FALSE);
13051291Sjl138328 			}
13061291Sjl138328 			if (!getaddr(rcip, RTA_IFA, tok, atype)) {
13071291Sjl138328 				return (B_FALSE);
13081291Sjl138328 			}
13091291Sjl138328 			break;
13101291Sjl138328 		case K_IFP:
13111291Sjl138328 			if (!NEXTTOKEN) {
13121291Sjl138328 				syntax_arg_missing(keyword_str);
13131291Sjl138328 				return (B_FALSE);
13141291Sjl138328 			}
13151291Sjl138328 			if (!getaddr(rcip, RTA_IFP, tok, atype)) {
13161291Sjl138328 				return (B_FALSE);
13171291Sjl138328 			}
13181291Sjl138328 			break;
13191291Sjl138328 		case K_GATEWAY:
13201291Sjl138328 			if (!NEXTTOKEN) {
13211291Sjl138328 				syntax_arg_missing(keyword_str);
13221291Sjl138328 				return (B_FALSE);
13231291Sjl138328 			}
13241291Sjl138328 			if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
13251291Sjl138328 				return (B_FALSE);
13261291Sjl138328 			}
13271291Sjl138328 			break;
13281291Sjl138328 		case K_DST:
13291291Sjl138328 			if (!NEXTTOKEN) {
13301291Sjl138328 				syntax_arg_missing(keyword_str);
13311291Sjl138328 				return (B_FALSE);
13321291Sjl138328 			}
13331291Sjl138328 			if (!getaddr(rcip, RTA_DST, tok, atype)) {
13341291Sjl138328 				return (B_FALSE);
13351291Sjl138328 			}
13361291Sjl138328 			break;
13371291Sjl138328 		case K_NETMASK:
13381291Sjl138328 			if (!NEXTTOKEN) {
13391291Sjl138328 				syntax_arg_missing(keyword_str);
13401291Sjl138328 				return (B_FALSE);
13411291Sjl138328 			}
13421291Sjl138328 			if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
13431291Sjl138328 				return (B_FALSE);
13441291Sjl138328 			}
13451291Sjl138328 			atype = ADDR_TYPE_NET;
13461291Sjl138328 			break;
13471291Sjl138328 		case K_MTU:
13481291Sjl138328 		case K_HOPCOUNT:
13491291Sjl138328 		case K_EXPIRE:
13501291Sjl138328 		case K_RECVPIPE:
13511291Sjl138328 		case K_SENDPIPE:
13521291Sjl138328 		case K_SSTHRESH:
13531291Sjl138328 		case K_RTT:
13541291Sjl138328 		case K_RTTVAR:
13551291Sjl138328 			if (!NEXTTOKEN) {
13561291Sjl138328 				syntax_arg_missing(keyword_str);
13571291Sjl138328 				return (B_FALSE);
13581291Sjl138328 			}
13591291Sjl138328 			set_metric(rcip, tok, key, locknext || lockrest);
13601291Sjl138328 			locknext = B_FALSE;
13611291Sjl138328 			break;
13621291Sjl138328 		case K_PRIVATE:
13631291Sjl138328 			rcip->ri_flags |= RTF_PRIVATE;
13641291Sjl138328 			break;
13651291Sjl138328 		case K_MULTIRT:
13661291Sjl138328 			rcip->ri_flags |= RTF_MULTIRT;
13671291Sjl138328 			break;
13681291Sjl138328 		case K_SETSRC:
13691291Sjl138328 			if (!NEXTTOKEN) {
13701291Sjl138328 				syntax_arg_missing(keyword_str);
13711291Sjl138328 				return (B_FALSE);
13721291Sjl138328 			}
13731291Sjl138328 			if (!getaddr(rcip, RTA_SRC, tok, atype)) {
13741291Sjl138328 				return (B_FALSE);
13751291Sjl138328 			}
13761291Sjl138328 			rcip->ri_flags |= RTF_SETSRC;
13771291Sjl138328 			break;
13781676Sjpk 		case K_SECATTR:
13791676Sjpk 			if (!NEXTTOKEN) {
13801676Sjpk 				syntax_arg_missing(keyword_str);
13811676Sjpk 				return (B_FALSE);
13821676Sjpk 			}
13832304Swy83408 			if (is_system_labeled()) {
13841676Sjpk 				int err;
13851676Sjpk 
13862304Swy83408 				if (rcip->ri_rtsa_cnt >= 1) {
13872304Swy83408 					syntax_error(gettext("route: can't "
13882304Swy83408 					    "specify more than one security "
13892304Swy83408 					    "attribute\n"));
13902304Swy83408 					return (B_FALSE);
13912304Swy83408 				}
13921676Sjpk 				if (!rtsa_keyword(tok, &rcip->ri_rtsa, &err,
13931676Sjpk 				    NULL)) {
13942304Swy83408 					syntax_error(gettext("route: "
13951676Sjpk 					    "bad security attribute: %s\n"),
13961676Sjpk 					    tsol_strerror(err, errno));
13971676Sjpk 					return (B_FALSE);
13981676Sjpk 				}
13992304Swy83408 				rcip->ri_rtsa_cnt++;
14002304Swy83408 			} else {
14012304Swy83408 				syntax_error(gettext("route: "
14022601Swy83408 				    "system is not labeled; cannot specify "
14032304Swy83408 				    "security attributes.\n"));
14041676Sjpk 				return (B_FALSE);
14051676Sjpk 			}
14061676Sjpk 			break;
140711042SErik.Nordmark@Sun.COM 		case K_INDIRECT:
140811042SErik.Nordmark@Sun.COM 			rcip->ri_flags |= RTF_INDIRECT;
140911042SErik.Nordmark@Sun.COM 			break;
14101291Sjl138328 		default:
14111291Sjl138328 			if (dash_keyword) {
14121291Sjl138328 				syntax_bad_keyword(tok + 1);
14131291Sjl138328 				return (B_FALSE);
14141291Sjl138328 			}
14151291Sjl138328 			if ((rcip->ri_addrs & RTA_DST) == 0) {
14161291Sjl138328 				if (!getaddr(rcip, RTA_DST, tok, atype)) {
14171291Sjl138328 					return (B_FALSE);
14181291Sjl138328 				}
14191291Sjl138328 			} else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
14200Sstevel@tonic-gate 				/*
14210Sstevel@tonic-gate 				 * For the gateway parameter, retrieve the
14220Sstevel@tonic-gate 				 * pointer to the struct hostent so that all
14230Sstevel@tonic-gate 				 * possible addresses can be tried until one
14240Sstevel@tonic-gate 				 * is successful.
14250Sstevel@tonic-gate 				 */
14261291Sjl138328 				if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
14271291Sjl138328 					return (B_FALSE);
14281291Sjl138328 				}
14290Sstevel@tonic-gate 			} else {
14301291Sjl138328 				ulong_t metric;
14310Sstevel@tonic-gate 				/*
14320Sstevel@tonic-gate 				 * Assume that a regular number is a metric.
14330Sstevel@tonic-gate 				 * Needed for compatibility with old route
14340Sstevel@tonic-gate 				 * command syntax.
14350Sstevel@tonic-gate 				 */
14361291Sjl138328 				errno = 0;
14371291Sjl138328 				metric = strtoul(tok, &err, 10);
14381291Sjl138328 				if (errno == 0 && *err == '\0' &&
14390Sstevel@tonic-gate 				    metric < 0x80000000ul) {
14400Sstevel@tonic-gate 					iflag = (metric == 0);
14410Sstevel@tonic-gate 					if (verbose) {
14420Sstevel@tonic-gate 						(void) printf("old usage of "
14430Sstevel@tonic-gate 						    "trailing number, assuming "
14440Sstevel@tonic-gate 						    "route %s\n", iflag ?
14450Sstevel@tonic-gate 						    "to if" : "via gateway");
14460Sstevel@tonic-gate 					}
14470Sstevel@tonic-gate 					continue;
14480Sstevel@tonic-gate 				}
14491291Sjl138328 				if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
14501291Sjl138328 					return (B_FALSE);
14511291Sjl138328 				}
14520Sstevel@tonic-gate 			}
14530Sstevel@tonic-gate 		}
14540Sstevel@tonic-gate 	}
14551291Sjl138328 #undef NEXTTOKEN
14561291Sjl138328 
14571291Sjl138328 	if ((rcip->ri_addrs & RTA_DST) == 0) {
14581291Sjl138328 		syntax_error(gettext("route: destination required\n"));
14591291Sjl138328 		return (B_FALSE);
14601291Sjl138328 	} else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
14611291Sjl138328 	    (rcip->ri_addrs & RTA_GATEWAY) == 0) {
14621291Sjl138328 		syntax_error(gettext(
14631291Sjl138328 		    "route: gateway required for add or delete command\n"));
14641291Sjl138328 		return (B_FALSE);
14651291Sjl138328 	}
14661291Sjl138328 
14671291Sjl138328 	if (!iflag) {
14681291Sjl138328 		rcip->ri_flags |= RTF_GATEWAY;
14691291Sjl138328 	}
14701291Sjl138328 
14711291Sjl138328 	if (atype != ADDR_TYPE_NET) {
14721291Sjl138328 		if (rcip->ri_addrs & RTA_NETMASK) {
14731291Sjl138328 			/*
14741291Sjl138328 			 * We know the netmask, so we can set the host flag
14751291Sjl138328 			 * based on whether the netmask is the host netmask.
14761291Sjl138328 			 */
14771291Sjl138328 			if (rcip->ri_af == AF_INET &&
14781291Sjl138328 			    rcip->ri_mask.sin.sin_addr.s_addr ==
14791291Sjl138328 			    IP_HOST_MASK) {
14801291Sjl138328 				rcip->ri_flags |= RTF_HOST;
14811291Sjl138328 			}
14821291Sjl138328 			if (rcip->ri_af == AF_INET6 &&
14831291Sjl138328 			    memcmp(&rcip->ri_mask.sin6.sin6_addr,
148411042SErik.Nordmark@Sun.COM 			    &in6_host_mask,
148511042SErik.Nordmark@Sun.COM 			    sizeof (struct in6_addr)) == 0) {
14861291Sjl138328 				rcip->ri_flags |= RTF_HOST;
14871291Sjl138328 			}
14881291Sjl138328 		} else {
14891291Sjl138328 			/*
14901291Sjl138328 			 * If no prefix mask has been saved at this point, it
14911291Sjl138328 			 * only makes sense to treat the destination address
14921291Sjl138328 			 * as a host address.
14931291Sjl138328 			 */
14941291Sjl138328 			rcip->ri_flags |= RTF_HOST;
14951291Sjl138328 		}
14961291Sjl138328 	}
14971291Sjl138328 	return (B_TRUE);
14981291Sjl138328 }
14991291Sjl138328 
15001291Sjl138328 /*
15011291Sjl138328  * This command always seeks to the end of the file prior to writing.
15021291Sjl138328  */
15031291Sjl138328 void
write_to_rtfile(FILE * fp,int argc,char ** argv)15041291Sjl138328 write_to_rtfile(FILE *fp, int argc, char **argv)
15051291Sjl138328 {
15061291Sjl138328 	char file_line[BUF_SIZE];
15071291Sjl138328 	int len;
15081291Sjl138328 	int i;
15091291Sjl138328 
15101291Sjl138328 	len = 0;
15111291Sjl138328 	if (early_v6_keyword) {
15121291Sjl138328 		/*
15131291Sjl138328 		 * This flag is set when "inet6" was seen as an
15141291Sjl138328 		 * argument to the -f flag.  Normally, when writing
15151291Sjl138328 		 * routes to the persistent route file, everything on
15161291Sjl138328 		 * the command line after "add" is saved verbatim.
15171291Sjl138328 		 * In this case, the arguments after "add" may not be
15181291Sjl138328 		 * sufficient, as the ipv6 keyword came before "add",
15191291Sjl138328 		 * yet must be present in the persistent route file.
15201291Sjl138328 		 */
15211291Sjl138328 		len += snprintf(file_line, BUF_SIZE, "-inet6 ");
15221291Sjl138328 	}
15231291Sjl138328 	for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
15241291Sjl138328 		len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
15251291Sjl138328 		    argv[i]);
15261291Sjl138328 	}
15271291Sjl138328 	if (len >= BUF_SIZE)
15281291Sjl138328 		quit(gettext("Internal Error"), EINVAL);
15291291Sjl138328 	file_line[len - 1] = '\n';
15301291Sjl138328 	if (fseek(fp, 0, SEEK_END) != 0 ||
15311291Sjl138328 	    fputs(file_line, fp) == EOF) {
15321291Sjl138328 		quit(gettext("failed to write to route file"),
15331291Sjl138328 		    errno);
15341291Sjl138328 	}
15351291Sjl138328 }
15361291Sjl138328 
15371291Sjl138328 boolean_t
compare_rtcmd(rtcmd_irep_t * srch_rt,rtcmd_irep_t * file_rt)15381291Sjl138328 compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
15391291Sjl138328 {
15401291Sjl138328 	if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
15411291Sjl138328 	    memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
15421291Sjl138328 		return (B_FALSE);
15431291Sjl138328 	}
15441291Sjl138328 	return (srch_rt->ri_gate_str == NULL ||
15451291Sjl138328 	    strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
15461291Sjl138328 }
15471291Sjl138328 
15481291Sjl138328 /*
15491291Sjl138328  * Search the route file for routes matching the supplied route.  There are 3
15501291Sjl138328  * modes of operation:
15511291Sjl138328  *    SEARCH_MODE_RET - no side effects.
15521291Sjl138328  *    SEARCH_MODE_PRINT - prints each matching line.
15531291Sjl138328  *    SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
15541291Sjl138328  *
15551291Sjl138328  * In all cases, the number of matches is returned.  If rt is NULL, all routes
15561291Sjl138328  * matching the global af value are considered matching.
15571291Sjl138328  */
15581291Sjl138328 int
search_rtfile(FILE * fp,FILE * temp_fp,rtcmd_irep_t * rt,search_mode_t mode)15591291Sjl138328 search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
15601291Sjl138328 {
15611291Sjl138328 	char *tmp_buf;
15621291Sjl138328 	int match_cnt;
15631291Sjl138328 	boolean_t match;
15641291Sjl138328 	char file_line[BUF_SIZE + 4] = "add ";
15651291Sjl138328 	rtcmd_irep_t *thisrt;
15661291Sjl138328 
15671291Sjl138328 	match_cnt = 0;
15681291Sjl138328 
15691291Sjl138328 	/*
15701291Sjl138328 	 * Leave space at the beginning of file_line for "add ".
15711291Sjl138328 	 */
15721291Sjl138328 	while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
15731291Sjl138328 
15741291Sjl138328 		if (file_line[4] == '#' || file_line[4] == '\n') {
15751291Sjl138328 			/* Handle comments and blank lines */
15761291Sjl138328 			if (mode == SEARCH_MODE_DEL &&
15771291Sjl138328 			    fputs(file_line + 4, temp_fp) == EOF) {
15781291Sjl138328 				quit(gettext(
15791291Sjl138328 				    "route: failed to write to temp file"),
15801291Sjl138328 				    errno);
15811291Sjl138328 			}
15821291Sjl138328 			continue;
15831291Sjl138328 		}
15841291Sjl138328 		thisrt = new_rtcmd_irep();
15851291Sjl138328 		/*
15861291Sjl138328 		 * thisrt->ri_af defaults to whatever address family happens
15871291Sjl138328 		 * to be set in the global af, but routes in the persistent
15881291Sjl138328 		 * route file must be treated as AF_INET by default.
15891291Sjl138328 		 */
15901291Sjl138328 		thisrt->ri_af = AF_INET;
15911291Sjl138328 
15921291Sjl138328 		exit_on_error = B_FALSE;
15931291Sjl138328 		tmp_buf = strdup(file_line);
15941291Sjl138328 		/* args_to_rtcmd() will mangle the string passed. */
15951291Sjl138328 		if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
15961291Sjl138328 			/* There was an error in args_to_rtcmd() or helpers */
15971291Sjl138328 			del_rtcmd_irep(thisrt);
15981291Sjl138328 			free(tmp_buf);
15991291Sjl138328 			continue;
16001291Sjl138328 		}
16011291Sjl138328 		exit_on_error = B_TRUE;
16021291Sjl138328 		free(tmp_buf);
16031291Sjl138328 
16041291Sjl138328 		if (thisrt->ri_gate_str == NULL) {
16051291Sjl138328 			del_rtcmd_irep(thisrt);
16061291Sjl138328 			continue;
16071291Sjl138328 		}
16081291Sjl138328 		match = (rt == NULL) ? (thisrt->ri_af == af) :
16091291Sjl138328 		    compare_rtcmd(rt, thisrt);
16101291Sjl138328 
16111291Sjl138328 		if (match) match_cnt++;
16121291Sjl138328 		if (match && mode == SEARCH_MODE_PRINT) {
16131291Sjl138328 			(void) printf("persistent: route %s", file_line);
16141291Sjl138328 		}
16151291Sjl138328 		if (match && mode == SEARCH_MODE_DEL) {
16161291Sjl138328 			thisrt->ri_cmd = RTM_DELETE;
16171291Sjl138328 			print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
16181291Sjl138328 			(void) printf("\n");
16191291Sjl138328 		}
16201291Sjl138328 		del_rtcmd_irep(thisrt);
16211291Sjl138328 
16221291Sjl138328 		if (!match && mode == SEARCH_MODE_DEL &&
16231291Sjl138328 		    fputs(file_line + 4, temp_fp) == EOF) {
16241291Sjl138328 			quit(gettext("failed to write to temp file"),
16251291Sjl138328 			    errno);
16261291Sjl138328 		}
16271291Sjl138328 	}
16281291Sjl138328 	return (match_cnt);
16291291Sjl138328 }
16301291Sjl138328 
16311291Sjl138328 /*
16321291Sjl138328  * Perform the route operation given in argv on the persistent route file.
16331291Sjl138328  * If do_flush is set, the persistent route file is flushed of all routes
16341291Sjl138328  * matching the global family, and the arguments are ignored.
16351291Sjl138328  */
16361291Sjl138328 void
save_route(int argc,char ** argv,int do_flush)16371291Sjl138328 save_route(int argc, char **argv, int do_flush)
16381291Sjl138328 {
16391291Sjl138328 	rtcmd_irep_t *rt;
16401291Sjl138328 	int perm_fd;
16411291Sjl138328 	FILE *perm_fp;
16421291Sjl138328 	FILE *temp_fp;
16431291Sjl138328 	mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
16441291Sjl138328 	struct flock lock;
16451291Sjl138328 	struct stat st;
16461291Sjl138328 	const char commentstr[] =
16471291Sjl138328 	    "# File generated by route(1M) - do not edit.\n";
16481291Sjl138328 
16491291Sjl138328 	perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
16501291Sjl138328 	if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
16511291Sjl138328 		quit("failed to open route file", errno);
16521291Sjl138328 
16531291Sjl138328 	lock.l_type = F_WRLCK;
16541291Sjl138328 	lock.l_whence = SEEK_SET;
16551291Sjl138328 	lock.l_start = 0;
16561291Sjl138328 	lock.l_len = 0;
16571291Sjl138328 	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
16581291Sjl138328 		quit(gettext("failed to lock route file"), errno);
16591291Sjl138328 		/* NOTREACHED */
16601291Sjl138328 	}
16611291Sjl138328 	if (st.st_size == 0 &&
16621291Sjl138328 	    write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
16631291Sjl138328 	    sizeof (commentstr) - 1)
16641291Sjl138328 		quit(gettext("failed to open route file"), errno);
16651291Sjl138328 
16661291Sjl138328 	if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
16671291Sjl138328 		quit(gettext("failed to open route file"), errno);
16681291Sjl138328 		/* NOTREACHED */
16691291Sjl138328 	}
16701291Sjl138328 
16711291Sjl138328 	if (!do_flush) {
16721291Sjl138328 		rt = new_rtcmd_irep();
16731291Sjl138328 		(void) args_to_rtcmd(rt, argv, NULL);
16741291Sjl138328 	}
16751291Sjl138328 	if (do_flush || rt->ri_cmd == RTM_DELETE) {
16761291Sjl138328 		if ((temp_fp = fopen(temp_file, "w")) == NULL) {
16771291Sjl138328 			quit(gettext("failed to open temp file"), errno);
16781291Sjl138328 			/* NOTREACHED */
16791291Sjl138328 		}
16801291Sjl138328 	}
16811291Sjl138328 	if (do_flush) {
16821291Sjl138328 		(void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
16831291Sjl138328 		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
16841291Sjl138328 			quit(gettext("failed to update route file"), errno);
16851291Sjl138328 			/* NOTREACHED */
16861291Sjl138328 		}
16871291Sjl138328 		(void) fclose(perm_fp);
16881291Sjl138328 		return;
16891291Sjl138328 	}
16901291Sjl138328 
16911291Sjl138328 	switch (rt->ri_cmd) {
16921291Sjl138328 	case RTM_ADD:
16931291Sjl138328 		if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
16941291Sjl138328 			/* Route is already in the file */
16951291Sjl138328 			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
16961291Sjl138328 			(void) fprintf(stderr, ": entry exists\n");
16971291Sjl138328 			exit(1);
16981291Sjl138328 		}
16991291Sjl138328 		write_to_rtfile(perm_fp, argc - 1, argv + 1);
17001291Sjl138328 		print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
17011291Sjl138328 		(void) printf("\n");
17021291Sjl138328 		break;
17031291Sjl138328 
17041291Sjl138328 	case RTM_CHANGE:
17051291Sjl138328 		syntax_error(
17061291Sjl138328 		    gettext("route: change command not supported with -p\n"));
17071291Sjl138328 		/* NOTREACHED */
17081291Sjl138328 
17091291Sjl138328 	case RTM_DELETE:
17101291Sjl138328 		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
17111291Sjl138328 			/* Route not found */
17121291Sjl138328 			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
17131291Sjl138328 			(void) fprintf(stderr, gettext(": not in file\n"));
17141291Sjl138328 			exit(1);
17151291Sjl138328 		}
17161291Sjl138328 		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
17171291Sjl138328 			quit(gettext("failed to update route file"), errno);
17181291Sjl138328 			/* NOTREACHED */
17191291Sjl138328 		}
17201291Sjl138328 		break;
17211291Sjl138328 
17221291Sjl138328 	case RTM_GET:
17231291Sjl138328 		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
17241291Sjl138328 		    0) {
17251291Sjl138328 			print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
17261291Sjl138328 			(void) printf(gettext(": not in file\n"));
17271291Sjl138328 		}
17281291Sjl138328 		break;
17291291Sjl138328 
17301291Sjl138328 	default:
17311291Sjl138328 		quit(gettext("Internal Error"), EINVAL);
17321291Sjl138328 		/* NOTREACHED */
17330Sstevel@tonic-gate 	}
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 	/*
17361291Sjl138328 	 * Closing the file unlocks it.
17370Sstevel@tonic-gate 	 */
17381291Sjl138328 	(void) fclose(perm_fp);
17391291Sjl138328 }
17401291Sjl138328 
17411291Sjl138328 int
show_saved_routes(int argc)17421291Sjl138328 show_saved_routes(int argc)
17431291Sjl138328 {
17441291Sjl138328 	int perm_fd;
17451291Sjl138328 	FILE *perm_fp;
17461291Sjl138328 	struct flock lock;
17471291Sjl138328 	int count = 0;
17481291Sjl138328 
17491291Sjl138328 	if (argc != 1) {
17501291Sjl138328 		syntax_error(gettext("route: invalid arguments for show\n"));
17511291Sjl138328 	}
17521291Sjl138328 
17531291Sjl138328 	perm_fd = open(perm_file, O_RDONLY, 0);
17541291Sjl138328 
17551291Sjl138328 	if (perm_fd == -1) {
17561291Sjl138328 		if (errno == ENOENT) {
17571291Sjl138328 			(void) printf("No persistent routes are defined\n");
17581291Sjl138328 			return (0);
17591291Sjl138328 		} else {
17601291Sjl138328 			quit(gettext("failed to open route file"), errno);
17611291Sjl138328 		}
17621291Sjl138328 	}
17631291Sjl138328 	lock.l_type = F_RDLCK;
17641291Sjl138328 	lock.l_whence = SEEK_SET;
17651291Sjl138328 	lock.l_start = 0;
17661291Sjl138328 	lock.l_len = 0;
17671291Sjl138328 	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
17681291Sjl138328 		quit(gettext("failed to lock route file"),
17691291Sjl138328 		    errno);
17701291Sjl138328 		/* NOTREACHED */
17711291Sjl138328 	}
17721291Sjl138328 	if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
17731291Sjl138328 		quit(gettext("failed to open route file"), errno);
17741291Sjl138328 		/* NOTREACHED */
17750Sstevel@tonic-gate 	}
17761291Sjl138328 	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
17771291Sjl138328 	(void) fseek(perm_fp, 0, SEEK_SET);
17781291Sjl138328 	af = AF_INET6;
17791291Sjl138328 	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
17801291Sjl138328 
17811291Sjl138328 	if (count == 0)
17821291Sjl138328 		(void) printf("No persistent routes are defined\n");
17831291Sjl138328 
17841291Sjl138328 	(void) fclose(perm_fp);
17851291Sjl138328 	return (0);
17861291Sjl138328 }
17871291Sjl138328 
17881291Sjl138328 int
newroute(char ** argv)17891291Sjl138328 newroute(char **argv)
17901291Sjl138328 {
17911291Sjl138328 	rtcmd_irep_t *newrt;
17921291Sjl138328 	int ret, attempts, oerrno;
17931291Sjl138328 	char *err;
17941291Sjl138328 	char obuf[INET6_ADDRSTRLEN];
17951291Sjl138328 #define	hp (newrt->ri_gate_hp)
17961291Sjl138328 
17971291Sjl138328 	newrt = new_rtcmd_irep();
17981291Sjl138328 	(void) args_to_rtcmd(newrt, argv, NULL);
17991291Sjl138328 
18001291Sjl138328 	if (newrt->ri_cmd != RTM_GET && !tflag) {
18011291Sjl138328 		/* Don't want to read back our messages */
18021291Sjl138328 		(void) shutdown(s, 0);
18031291Sjl138328 	}
18041291Sjl138328 	if (newrt->ri_addrs & RTA_IFP) {
18051291Sjl138328 		newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
18061291Sjl138328 		if (newrt->ri_ifp.sdl.sdl_index == 0) {
18071291Sjl138328 			if (errno != ENXIO) {
18081291Sjl138328 				quit("if_nametoindex", errno);
18091291Sjl138328 			} else {
18101291Sjl138328 				(void) fprintf(stderr,
18111291Sjl138328 				    gettext("route: %s: no such interface\n"),
18121291Sjl138328 				    newrt->ri_ifp_str);
18131291Sjl138328 				exit(1);
18141291Sjl138328 			}
18151291Sjl138328 		}
18161291Sjl138328 		newrt->ri_ifp.sdl.sdl_family = AF_LINK;
18171291Sjl138328 	}
18180Sstevel@tonic-gate 	for (attempts = 1; ; attempts++) {
18190Sstevel@tonic-gate 		errno = 0;
18201291Sjl138328 		if ((ret = rtmsg(newrt)) == 0)
18210Sstevel@tonic-gate 			break;
18220Sstevel@tonic-gate 		if (errno != ENETUNREACH && errno != ESRCH)
18230Sstevel@tonic-gate 			break;
18241291Sjl138328 		if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
18250Sstevel@tonic-gate 		    hp->h_addr_list[attempts] != NULL) {
18260Sstevel@tonic-gate 			switch (af) {
18270Sstevel@tonic-gate 			case AF_INET:
18281291Sjl138328 				(void) memmove(&newrt->ri_gate.sin.sin_addr,
18290Sstevel@tonic-gate 				    hp->h_addr_list[attempts], hp->h_length);
18300Sstevel@tonic-gate 				continue;
18310Sstevel@tonic-gate 			case AF_INET6:
18321291Sjl138328 				(void) memmove(&newrt->ri_gate.sin6.sin6_addr,
18330Sstevel@tonic-gate 				    hp->h_addr_list[attempts], hp->h_length);
18340Sstevel@tonic-gate 				continue;
18350Sstevel@tonic-gate 			}
18360Sstevel@tonic-gate 		}
18370Sstevel@tonic-gate 		break;
18380Sstevel@tonic-gate 	}
18390Sstevel@tonic-gate 	oerrno = errno;
18401291Sjl138328 
18411291Sjl138328 	if (newrt->ri_cmd != RTM_GET) {
18421291Sjl138328 		print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
18431291Sjl138328 		if (ret == 0)
18441291Sjl138328 			(void) printf("\n");
18451291Sjl138328 	} else if (ret != 0) {
18461291Sjl138328 		/*
18471291Sjl138328 		 * Note: there is nothing additional to print for get
18481291Sjl138328 		 * if ret == 0.
18491291Sjl138328 		 */
18501291Sjl138328 		if (nflag) {
18511291Sjl138328 			switch (newrt->ri_af) {
18520Sstevel@tonic-gate 			case AF_INET:
18531291Sjl138328 				(void) printf(" %s",
18541291Sjl138328 				    inet_ntoa(newrt->ri_dst.sin.sin_addr));
18550Sstevel@tonic-gate 				break;
18560Sstevel@tonic-gate 			case AF_INET6:
18570Sstevel@tonic-gate 				if (inet_ntop(AF_INET6,
185811042SErik.Nordmark@Sun.COM 				    (void *)&newrt->ri_dst.sin6.sin6_addr,
185911042SErik.Nordmark@Sun.COM 				    obuf, INET6_ADDRSTRLEN) != NULL) {
18601291Sjl138328 					(void) printf(" %s", obuf);
18610Sstevel@tonic-gate 					break;
18620Sstevel@tonic-gate 				}
18630Sstevel@tonic-gate 				/* FALLTHROUGH */
18640Sstevel@tonic-gate 			default:
18651291Sjl138328 				(void) printf("%s", newrt->ri_dest_str);
18660Sstevel@tonic-gate 				break;
18670Sstevel@tonic-gate 			}
18681291Sjl138328 		} else {
18691291Sjl138328 			(void) printf("%s", newrt->ri_dest_str);
18700Sstevel@tonic-gate 		}
18710Sstevel@tonic-gate 	}
18721291Sjl138328 
18730Sstevel@tonic-gate 	if (ret != 0) {
18740Sstevel@tonic-gate 		switch (oerrno) {
18750Sstevel@tonic-gate 		case ESRCH:
18760Sstevel@tonic-gate 			err = "not in table";
18770Sstevel@tonic-gate 			break;
18780Sstevel@tonic-gate 		case EBUSY:
18790Sstevel@tonic-gate 			err = "entry in use";
18800Sstevel@tonic-gate 			break;
18810Sstevel@tonic-gate 		case ENOBUFS:
18820Sstevel@tonic-gate 			err = "routing table overflow";
18830Sstevel@tonic-gate 			break;
18840Sstevel@tonic-gate 		case EEXIST:
18850Sstevel@tonic-gate 			err = "entry exists";
18860Sstevel@tonic-gate 			break;
18870Sstevel@tonic-gate 		case EPERM:
18880Sstevel@tonic-gate 			err = "insufficient privileges";
18890Sstevel@tonic-gate 			break;
18900Sstevel@tonic-gate 		default:
18910Sstevel@tonic-gate 			err = strerror(oerrno);
18920Sstevel@tonic-gate 			break;
18930Sstevel@tonic-gate 		}
18940Sstevel@tonic-gate 		(void) printf(": %s\n", err);
18950Sstevel@tonic-gate 	}
18961291Sjl138328 
18971291Sjl138328 	del_rtcmd_irep(newrt);
18980Sstevel@tonic-gate 
18990Sstevel@tonic-gate 	return (oerrno);
19001291Sjl138328 #undef hp
19010Sstevel@tonic-gate }
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 
19040Sstevel@tonic-gate /*
19050Sstevel@tonic-gate  * Convert a network number to the corresponding IP address.
19060Sstevel@tonic-gate  * If the RTA_NETMASK hasn't been specified yet set it based
19070Sstevel@tonic-gate  * on the class of address.
19080Sstevel@tonic-gate  */
19090Sstevel@tonic-gate static void
inet_makenetandmask(rtcmd_irep_t * rcip,in_addr_t net,struct sockaddr_in * sin)19101291Sjl138328 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
19110Sstevel@tonic-gate {
19120Sstevel@tonic-gate 	in_addr_t addr, mask;
19130Sstevel@tonic-gate 
19140Sstevel@tonic-gate 	if (net == 0) {
19150Sstevel@tonic-gate 		mask = addr = 0;
19160Sstevel@tonic-gate 	} else if (net < 128) {
19170Sstevel@tonic-gate 		addr = net << IN_CLASSA_NSHIFT;
19180Sstevel@tonic-gate 		mask = IN_CLASSA_NET;
19190Sstevel@tonic-gate 	} else if (net < 65536) {
19200Sstevel@tonic-gate 		addr = net << IN_CLASSB_NSHIFT;
19210Sstevel@tonic-gate 		mask = IN_CLASSB_NET;
19220Sstevel@tonic-gate 	} else if (net < 16777216L) {
19230Sstevel@tonic-gate 		addr = net << IN_CLASSC_NSHIFT;
19240Sstevel@tonic-gate 		mask = IN_CLASSC_NET;
19250Sstevel@tonic-gate 	} else {
19260Sstevel@tonic-gate 		addr = net;
19270Sstevel@tonic-gate 		if ((addr & IN_CLASSA_HOST) == 0)
19280Sstevel@tonic-gate 			mask =  IN_CLASSA_NET;
19290Sstevel@tonic-gate 		else if ((addr & IN_CLASSB_HOST) == 0)
19300Sstevel@tonic-gate 			mask =  IN_CLASSB_NET;
19310Sstevel@tonic-gate 		else if ((addr & IN_CLASSC_HOST) == 0)
19320Sstevel@tonic-gate 			mask =  IN_CLASSC_NET;
19330Sstevel@tonic-gate 		else {
19340Sstevel@tonic-gate 			if (IN_CLASSA(addr))
19350Sstevel@tonic-gate 				mask =  IN_CLASSA_NET;
19360Sstevel@tonic-gate 			else if (IN_CLASSB(addr))
19370Sstevel@tonic-gate 				mask =  IN_CLASSB_NET;
19380Sstevel@tonic-gate 			else if (IN_CLASSC(addr))
19390Sstevel@tonic-gate 				mask =  IN_CLASSC_NET;
19400Sstevel@tonic-gate 			else
19410Sstevel@tonic-gate 				mask = IP_HOST_MASK;
19420Sstevel@tonic-gate 			mask = inet_makesubnetmask(addr, mask);
19430Sstevel@tonic-gate 		}
19440Sstevel@tonic-gate 	}
19450Sstevel@tonic-gate 	sin->sin_addr.s_addr = htonl(addr);
19460Sstevel@tonic-gate 
19475577Ssangeeta 	/* Class E default mask is 32 */
19485577Ssangeeta 	if (IN_CLASSE(addr))
19495577Ssangeeta 		mask = IN_CLASSE_NET;
19505577Ssangeeta 
19511291Sjl138328 	if (!(rcip->ri_addrs & RTA_NETMASK)) {
19521291Sjl138328 		rcip->ri_addrs |= RTA_NETMASK;
19531291Sjl138328 		sin = &rcip->ri_mask.sin;
19540Sstevel@tonic-gate 		sin->sin_addr.s_addr = htonl(mask);
19550Sstevel@tonic-gate 		sin->sin_family = AF_INET;
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate }
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate static in_addr_t
inet_makesubnetmask(in_addr_t addr,in_addr_t mask)19600Sstevel@tonic-gate inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
19610Sstevel@tonic-gate {
19620Sstevel@tonic-gate 	int n;
19630Sstevel@tonic-gate 	struct ifconf ifc;
19640Sstevel@tonic-gate 	struct ifreq ifreq;
19650Sstevel@tonic-gate 	struct ifreq *ifr;
19660Sstevel@tonic-gate 	struct sockaddr_in *sin;
19670Sstevel@tonic-gate 	char *buf;
19680Sstevel@tonic-gate 	int numifs;
19690Sstevel@tonic-gate 	size_t bufsize;
19700Sstevel@tonic-gate 	int iosoc;
19710Sstevel@tonic-gate 	in_addr_t if_addr, if_mask;
19720Sstevel@tonic-gate 	in_addr_t if_subnetmask = 0;
19730Sstevel@tonic-gate 	short if_flags;
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 	if (mask == 0)
19760Sstevel@tonic-gate 		return (0);
19770Sstevel@tonic-gate 	if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
19780Sstevel@tonic-gate 		quit("socket", errno);
19790Sstevel@tonic-gate 	if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
19800Sstevel@tonic-gate 		quit("ioctl", errno);
19810Sstevel@tonic-gate 	bufsize = numifs * sizeof (struct ifreq);
19820Sstevel@tonic-gate 	buf = malloc(bufsize);
19830Sstevel@tonic-gate 	if (buf == NULL)
19840Sstevel@tonic-gate 		quit("malloc", errno);
19850Sstevel@tonic-gate 	(void) memset(&ifc, 0, sizeof (ifc));
19860Sstevel@tonic-gate 	ifc.ifc_len = bufsize;
19870Sstevel@tonic-gate 	ifc.ifc_buf = buf;
19880Sstevel@tonic-gate 	if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
19890Sstevel@tonic-gate 		quit("ioctl (get interface configuration)", errno);
19900Sstevel@tonic-gate 	/* Let's check to see if this is maybe a local subnet route. */
19910Sstevel@tonic-gate 	ifr = ifc.ifc_req;
19920Sstevel@tonic-gate 	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
19930Sstevel@tonic-gate 		ifreq = *ifr;
19940Sstevel@tonic-gate 		/* LINTED */
19950Sstevel@tonic-gate 		sin = (struct sockaddr_in *)&ifr->ifr_addr;
19960Sstevel@tonic-gate 		if_addr = ntohl(sin->sin_addr.s_addr);
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate 		if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
19990Sstevel@tonic-gate 			quit("ioctl (get interface flags)", errno);
20000Sstevel@tonic-gate 		if ((ifreq.ifr_flags & IFF_UP) == 0)
20010Sstevel@tonic-gate 			continue;
20020Sstevel@tonic-gate 		if_flags = ifreq.ifr_flags;
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 		if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
20050Sstevel@tonic-gate 			quit("ioctl (get netmask)", errno);
20060Sstevel@tonic-gate 		/* LINTED */
20070Sstevel@tonic-gate 		sin = (struct sockaddr_in *)&ifreq.ifr_addr;
20080Sstevel@tonic-gate 		if_mask = ntohl(sin->sin_addr.s_addr);
20090Sstevel@tonic-gate 		if ((if_addr & mask) == (addr & mask)) {
20100Sstevel@tonic-gate 			/*
20110Sstevel@tonic-gate 			 * Don't trust pt-pt interfaces if there are
20120Sstevel@tonic-gate 			 * other interfaces.
20130Sstevel@tonic-gate 			 */
20140Sstevel@tonic-gate 			if (if_flags & IFF_POINTOPOINT) {
20150Sstevel@tonic-gate 				if_subnetmask = if_mask;
20160Sstevel@tonic-gate 				continue;
20170Sstevel@tonic-gate 			}
20180Sstevel@tonic-gate 			/*
20190Sstevel@tonic-gate 			 * Fine.  Just assume the same net mask as the
20200Sstevel@tonic-gate 			 * directly attached subnet interface is using.
20210Sstevel@tonic-gate 			 */
20220Sstevel@tonic-gate 			return (if_mask);
20230Sstevel@tonic-gate 		}
20240Sstevel@tonic-gate 	}
20250Sstevel@tonic-gate 	if (if_subnetmask != 0)
20260Sstevel@tonic-gate 		return (if_subnetmask);
20270Sstevel@tonic-gate 	return (mask);
20280Sstevel@tonic-gate }
20290Sstevel@tonic-gate 
20300Sstevel@tonic-gate /*
20311291Sjl138328  * Interpret an argument as a network address of some kind.
20320Sstevel@tonic-gate  *
20330Sstevel@tonic-gate  * If the address family is one looked up in getaddr() using one of the
20340Sstevel@tonic-gate  * getipnodebyX() functions (currently only AF_INET6), then callers should
20350Sstevel@tonic-gate  * freehostent() the returned "struct hostent" pointer if one was passed in.
20361291Sjl138328  *
20371291Sjl138328  * If exit_on_error is true, this function will cause route to exit on error by
20381291Sjl138328  * calling syntax_error().  Otherwise, it returns B_TRUE on success or B_FALSE
20391291Sjl138328  * on failure.
20400Sstevel@tonic-gate  */
20410Sstevel@tonic-gate static boolean_t
getaddr(rtcmd_irep_t * rcip,int which,char * s,addr_type_t atype)20421291Sjl138328 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
20430Sstevel@tonic-gate {
20441291Sjl138328 	su_t *su;
20451291Sjl138328 	struct hostent **hpp;
20460Sstevel@tonic-gate 	struct hostent *hp;
20471291Sjl138328 	int masklen;
20480Sstevel@tonic-gate 
20491291Sjl138328 	if (which == RTA_GATEWAY) {
20501291Sjl138328 		hpp = &(rcip->ri_gate_hp);
20511291Sjl138328 	} else {
20521291Sjl138328 		hpp = &hp;
2053156Sdduvall 	}
20540Sstevel@tonic-gate 	*hpp = NULL;
20551291Sjl138328 
20561291Sjl138328 	rcip->ri_addrs |= which;
20570Sstevel@tonic-gate 	switch (which) {
20580Sstevel@tonic-gate 	case RTA_DST:
20591291Sjl138328 		save_string(&rcip->ri_dest_str, s);
20601291Sjl138328 		su = &rcip->ri_dst;
20611291Sjl138328 		su->sa.sa_family = rcip->ri_af;
20620Sstevel@tonic-gate 		break;
20630Sstevel@tonic-gate 	case RTA_GATEWAY:
20641291Sjl138328 		save_string(&rcip->ri_gate_str, s);
20651291Sjl138328 		su = &rcip->ri_gate;
20661291Sjl138328 		su->sa.sa_family = rcip->ri_af;
20670Sstevel@tonic-gate 		break;
20680Sstevel@tonic-gate 	case RTA_NETMASK:
20691291Sjl138328 		su = &rcip->ri_mask;
20701291Sjl138328 		su->sa.sa_family = rcip->ri_af;
20710Sstevel@tonic-gate 		break;
20720Sstevel@tonic-gate 	case RTA_IFP:
20731291Sjl138328 		save_string(&rcip->ri_ifp_str, s);
20741291Sjl138328 		return (B_TRUE);
20750Sstevel@tonic-gate 		/*
20760Sstevel@tonic-gate 		 * RTA_SRC has overloaded meaning. It can represent the
20770Sstevel@tonic-gate 		 * src address of incoming or outgoing packets.
20780Sstevel@tonic-gate 		 */
20790Sstevel@tonic-gate 	case RTA_IFA:
20801291Sjl138328 		su = &rcip->ri_ifa;
20811291Sjl138328 		su->sa.sa_family = rcip->ri_af;
20820Sstevel@tonic-gate 		break;
20830Sstevel@tonic-gate 	case RTA_SRC:
20841291Sjl138328 		su = &rcip->ri_src;
20851291Sjl138328 		su->sa.sa_family = rcip->ri_af;
20860Sstevel@tonic-gate 		break;
20870Sstevel@tonic-gate 	default:
20880Sstevel@tonic-gate 		/* NOTREACHED */
20890Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
20900Sstevel@tonic-gate 		/* NOTREACHED */
20910Sstevel@tonic-gate 	}
20920Sstevel@tonic-gate 	if (strcmp(s, "default") == 0) {
20930Sstevel@tonic-gate 		if (which == RTA_DST) {
20941291Sjl138328 			return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
20950Sstevel@tonic-gate 		}
20960Sstevel@tonic-gate 		if (which == RTA_SRC) {
20970Sstevel@tonic-gate 			return (B_TRUE);
20980Sstevel@tonic-gate 		}
20991291Sjl138328 		return (B_TRUE);
21000Sstevel@tonic-gate 	}
21011291Sjl138328 	switch (rcip->ri_af) {
21020Sstevel@tonic-gate 	case AF_LINK:
21030Sstevel@tonic-gate 		link_addr(s, &su->sdl);
21040Sstevel@tonic-gate 		return (B_TRUE);
21050Sstevel@tonic-gate 	case PF_ROUTE:
21060Sstevel@tonic-gate 		sockaddr(s, &su->sa);
21070Sstevel@tonic-gate 		return (B_TRUE);
21080Sstevel@tonic-gate 	case AF_INET6:
21090Sstevel@tonic-gate 		switch (which) {
21100Sstevel@tonic-gate 		case RTA_DST:
21111291Sjl138328 			if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
21121291Sjl138328 				return (B_FALSE);
21130Sstevel@tonic-gate 			}
21141291Sjl138328 			if (masklen != NO_PREFIX) {
21151291Sjl138328 				(void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
21161291Sjl138328 				    sizeof (rcip->ri_mask.sin6.sin6_addr));
2117156Sdduvall 				if (!in_prefixlentomask(masklen, IPV6_ABITS,
21181291Sjl138328 				    (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
21191291Sjl138328 					syntax_error(gettext(
21201291Sjl138328 					    "route: bad prefix length: %d\n"),
21211291Sjl138328 					    masklen);
21221291Sjl138328 					return (B_FALSE);
21230Sstevel@tonic-gate 				}
21241291Sjl138328 				rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
21251291Sjl138328 				rcip->ri_addrs |= RTA_NETMASK;
21260Sstevel@tonic-gate 			}
21271291Sjl138328 			return (B_TRUE);
21280Sstevel@tonic-gate 		case RTA_GATEWAY:
21290Sstevel@tonic-gate 		case RTA_IFA:
21300Sstevel@tonic-gate 		case RTA_SRC:
21311291Sjl138328 			return (in6_getaddr(s, &su->sin6, NULL, hpp));
21320Sstevel@tonic-gate 		case RTA_NETMASK:
21331291Sjl138328 			syntax_error(
21340Sstevel@tonic-gate 			    gettext("route: -netmask not supported for IPv6: "
21351291Sjl138328 			    "use <prefix>/<prefix-length> instead\n"));
21361291Sjl138328 			return (B_FALSE);
21370Sstevel@tonic-gate 		default:
21380Sstevel@tonic-gate 			quit(gettext("Internal Error"), EINVAL);
21390Sstevel@tonic-gate 			/* NOTREACHED */
21400Sstevel@tonic-gate 		}
21410Sstevel@tonic-gate 	case AF_INET:
21420Sstevel@tonic-gate 		switch (which) {
21430Sstevel@tonic-gate 		case RTA_DST:
21441291Sjl138328 			if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
21451291Sjl138328 			    atype, rcip)) {
21461291Sjl138328 				return (B_FALSE);
21470Sstevel@tonic-gate 			}
21481291Sjl138328 			if (masklen != NO_PREFIX) {
21491291Sjl138328 				(void) memset(&rcip->ri_mask.sin.sin_addr, 0,
21501291Sjl138328 				    sizeof (rcip->ri_mask.sin.sin_addr));
2151156Sdduvall 				if (!in_prefixlentomask(masklen, IP_ABITS,
21521291Sjl138328 				    (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
21531291Sjl138328 					syntax_error(gettext(
21541291Sjl138328 					    "route: bad prefix length: %d\n"),
21551291Sjl138328 					    masklen);
21561291Sjl138328 					return (B_FALSE);
21570Sstevel@tonic-gate 				}
21581291Sjl138328 				rcip->ri_mask.sin.sin_family = rcip->ri_af;
21591291Sjl138328 				rcip->ri_addrs |= RTA_NETMASK;
21600Sstevel@tonic-gate 			}
21611291Sjl138328 			return (B_TRUE);
21620Sstevel@tonic-gate 		case RTA_GATEWAY:
21630Sstevel@tonic-gate 		case RTA_IFA:
21640Sstevel@tonic-gate 		case RTA_NETMASK:
21650Sstevel@tonic-gate 		case RTA_SRC:
21661291Sjl138328 			return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
21671291Sjl138328 			    rcip));
21680Sstevel@tonic-gate 		default:
21690Sstevel@tonic-gate 			quit(gettext("Internal Error"), EINVAL);
21700Sstevel@tonic-gate 			/* NOTREACHED */
21710Sstevel@tonic-gate 		}
21720Sstevel@tonic-gate 	default:
21730Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
21740Sstevel@tonic-gate 		/* NOTREACHED */
21750Sstevel@tonic-gate 	}
21761291Sjl138328 	return (B_TRUE);
21770Sstevel@tonic-gate }
21780Sstevel@tonic-gate 
21790Sstevel@tonic-gate /*
21800Sstevel@tonic-gate  * Interpret an argument as an IPv4 network address of some kind,
21811291Sjl138328  * returning B_TRUE on success or B_FALSE on failure.
21821291Sjl138328  * This function will cause an exit() on failure if exit_on_failure is set.
21831291Sjl138328  *
21841291Sjl138328  * Note that this tries host interpretation before network interpretation,
21851291Sjl138328  * except when -net has been given and the destination address is being parsed.
21860Sstevel@tonic-gate  *
21870Sstevel@tonic-gate  * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
21880Sstevel@tonic-gate  * pass out <n> in *plenp.
21890Sstevel@tonic-gate  * If <n> doesn't parse return BAD_ADDR as *plenp.
21900Sstevel@tonic-gate  * If no /<n> is present return NO_PREFIX as *plenp.
21910Sstevel@tonic-gate  */
21920Sstevel@tonic-gate static boolean_t
in_getaddr(char * s,struct sockaddr_in * sin,int * plenp,int which,struct hostent ** hpp,addr_type_t atype,rtcmd_irep_t * rcip)21930Sstevel@tonic-gate in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
21941291Sjl138328     struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
21950Sstevel@tonic-gate {
21960Sstevel@tonic-gate 	struct hostent *hp;
21970Sstevel@tonic-gate 	struct netent *np;
21980Sstevel@tonic-gate 	in_addr_t val;
21990Sstevel@tonic-gate 	char str[BUFSIZ];
22000Sstevel@tonic-gate 
22011291Sjl138328 	(void) strlcpy(str, s, sizeof (str));
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 	/*
22041291Sjl138328 	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
22050Sstevel@tonic-gate 	 */
22060Sstevel@tonic-gate 	if (plenp != NULL) {
22070Sstevel@tonic-gate 		char *cp;
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate 		*plenp = in_getprefixlen(str, IP_ABITS);
22100Sstevel@tonic-gate 		if (*plenp == BAD_ADDR)
22110Sstevel@tonic-gate 			return (B_FALSE);
22120Sstevel@tonic-gate 		cp = strchr(str, '/');
22130Sstevel@tonic-gate 		if (cp != NULL)
22140Sstevel@tonic-gate 			*cp = '\0';
22150Sstevel@tonic-gate 	} else if (strchr(str, '/') != NULL) {
22161291Sjl138328 		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
22171291Sjl138328 		return (B_FALSE);
22180Sstevel@tonic-gate 	}
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	(void) memset(sin, 0, sizeof (*sin));
22210Sstevel@tonic-gate 	sin->sin_family = AF_INET;
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate 	/*
22241291Sjl138328 	 * Handle 255.255.255.255 as a special case first.
22250Sstevel@tonic-gate 	 */
22261291Sjl138328 	if (strcmp(str, "255.255.255.255") == 0) {
22271291Sjl138328 		sin->sin_addr.s_addr = INADDR_BROADCAST;
22281291Sjl138328 		return (B_TRUE);
22291291Sjl138328 	}
22301291Sjl138328 
22311291Sjl138328 	val = inet_addr(str);
22321291Sjl138328 	if (val != (in_addr_t)-1) {
22331291Sjl138328 		/* Numeric address */
22340Sstevel@tonic-gate 		sin->sin_addr.s_addr = val;
22351291Sjl138328 		if (which == RTA_DST) {
22361291Sjl138328 			if (atype == ADDR_TYPE_NET ||
22371291Sjl138328 			    (atype == ADDR_TYPE_ANY &&
22381291Sjl138328 			    inet_lnaof(sin->sin_addr) == INADDR_ANY)) {
22391291Sjl138328 				/* This looks like a network address. */
22401291Sjl138328 				inet_makenetandmask(rcip, ntohl(val),
224111042SErik.Nordmark@Sun.COM 				    sin);
22421291Sjl138328 			}
22431291Sjl138328 		}
22440Sstevel@tonic-gate 		return (B_TRUE);
22450Sstevel@tonic-gate 	}
22461291Sjl138328 	/* Host or net name */
22471291Sjl138328 	if (which != RTA_DST || atype != ADDR_TYPE_NET) {
22481291Sjl138328 		/* A host name is allowed. */
22491291Sjl138328 		if ((hp = gethostbyname(str)) != NULL) {
22501291Sjl138328 			*hpp = hp;
22511291Sjl138328 			(void) memmove(&sin->sin_addr, hp->h_addr,
22521291Sjl138328 			    hp->h_length);
22531291Sjl138328 			return (B_TRUE);
22541291Sjl138328 		}
22550Sstevel@tonic-gate 	}
22561291Sjl138328 	if (atype != ADDR_TYPE_HOST) {
22571291Sjl138328 		/* A network name is allowed */
22581291Sjl138328 		if ((np = getnetbyname(str)) != NULL &&
22591291Sjl138328 		    (val = np->n_net) != 0) {
22601291Sjl138328 			if (which == RTA_DST) {
22611291Sjl138328 				inet_makenetandmask(rcip, val, sin);
22621291Sjl138328 			}
22631291Sjl138328 			return (B_TRUE);
22641291Sjl138328 		}
22651291Sjl138328 	}
22661291Sjl138328 	syntax_error(gettext("%s: bad value\n"), s);
22671291Sjl138328 	return (B_FALSE);
22680Sstevel@tonic-gate }
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate /*
22710Sstevel@tonic-gate  * Interpret an argument as an IPv6 network address of some kind,
22721291Sjl138328  * returning B_TRUE on success or B_FALSE on failure.
22731291Sjl138328  * This function will cause an exit() on failure if exit_on_failure is set.
22740Sstevel@tonic-gate  *
22750Sstevel@tonic-gate  * If the last argument is non-NULL allow a <addr>/<n> syntax and
22760Sstevel@tonic-gate  * pass out <n> in *plenp.
22770Sstevel@tonic-gate  * If <n> doesn't parse return BAD_ADDR as *plenp.
22780Sstevel@tonic-gate  * If no /<n> is present return NO_PREFIX as *plenp.
22790Sstevel@tonic-gate  */
22800Sstevel@tonic-gate static boolean_t
in6_getaddr(char * s,struct sockaddr_in6 * sin6,int * plenp,struct hostent ** hpp)22810Sstevel@tonic-gate in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
22820Sstevel@tonic-gate     struct hostent **hpp)
22830Sstevel@tonic-gate {
22840Sstevel@tonic-gate 	struct hostent *hp;
22850Sstevel@tonic-gate 	char str[BUFSIZ];
22860Sstevel@tonic-gate 	int error_num;
22870Sstevel@tonic-gate 
22881291Sjl138328 	(void) strlcpy(str, s, sizeof (str));
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate 	/*
22911291Sjl138328 	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
22920Sstevel@tonic-gate 	 */
22930Sstevel@tonic-gate 	if (plenp != NULL) {
22940Sstevel@tonic-gate 		char *cp;
22950Sstevel@tonic-gate 
22960Sstevel@tonic-gate 		*plenp = in_getprefixlen(str, IPV6_ABITS);
22970Sstevel@tonic-gate 		if (*plenp == BAD_ADDR)
22980Sstevel@tonic-gate 			return (B_FALSE);
22990Sstevel@tonic-gate 		cp = strchr(str, '/');
23000Sstevel@tonic-gate 		if (cp != NULL)
23010Sstevel@tonic-gate 			*cp = '\0';
23020Sstevel@tonic-gate 	} else if (strchr(str, '/') != NULL) {
23031291Sjl138328 		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
23041291Sjl138328 		return (B_FALSE);
23050Sstevel@tonic-gate 	}
23060Sstevel@tonic-gate 
23070Sstevel@tonic-gate 	(void) memset(sin6, 0, sizeof (struct sockaddr_in6));
23080Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
23090Sstevel@tonic-gate 
23100Sstevel@tonic-gate 	hp = getipnodebyname(str, AF_INET6, 0, &error_num);
23110Sstevel@tonic-gate 	if (hp != NULL) {
23120Sstevel@tonic-gate 		*hpp = hp;
23130Sstevel@tonic-gate 		(void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
23140Sstevel@tonic-gate 		return (B_TRUE);
23150Sstevel@tonic-gate 	}
23160Sstevel@tonic-gate 	if (error_num == TRY_AGAIN) {
23171291Sjl138328 		/*
23181291Sjl138328 		 * This isn't a problem if we aren't going to use the address
23191291Sjl138328 		 * right away.
23201291Sjl138328 		 */
23211291Sjl138328 		if (!exit_on_error) {
23221291Sjl138328 			return (B_TRUE);
23231291Sjl138328 		}
23241291Sjl138328 		syntax_error(gettext("route: %s: bad address (try "
23250Sstevel@tonic-gate 		    "again later)\n"), s);
23261291Sjl138328 		return (B_FALSE);
23270Sstevel@tonic-gate 	}
23281291Sjl138328 	syntax_error(gettext("route: %s: bad address\n"), s);
23291291Sjl138328 	return (B_FALSE);
23300Sstevel@tonic-gate }
23310Sstevel@tonic-gate 
23320Sstevel@tonic-gate /*
23331291Sjl138328  * Parse <addr>/<n> syntax and return the integer n.
23341291Sjl138328  * If <addr> is missing or <n> is not a valid integer, this function calls
23351291Sjl138328  * syntax_error() and returns BAD_ADDR.
23361291Sjl138328  * if n is not between 0 and max_plen inclusive, this functions calls
23371291Sjl138328  * syntax_error() and returns BAD_ADDR.
23381291Sjl138328  * If /<n> is not present, this function returns NO_PREFIX.
23391291Sjl138328  * The string addr is not modified.
23400Sstevel@tonic-gate  */
23410Sstevel@tonic-gate int
in_getprefixlen(char * addr,int max_plen)23420Sstevel@tonic-gate in_getprefixlen(char *addr, int max_plen)
23430Sstevel@tonic-gate {
23440Sstevel@tonic-gate 	int prefixlen;
23450Sstevel@tonic-gate 	char *str, *end;
23460Sstevel@tonic-gate 
23470Sstevel@tonic-gate 	str = strchr(addr, '/');
23481291Sjl138328 	if (str == addr) {
23491291Sjl138328 		syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
23501291Sjl138328 		return (BAD_ADDR);
23511291Sjl138328 	}
23520Sstevel@tonic-gate 	if (str == NULL)
23530Sstevel@tonic-gate 		return (NO_PREFIX);
23540Sstevel@tonic-gate 	str++;
23550Sstevel@tonic-gate 
23561291Sjl138328 	errno = 0;
23571291Sjl138328 	prefixlen = strtoul(str, &end, 10);
23581291Sjl138328 	if (errno != 0 || str == end) {
23591291Sjl138328 		syntax_error(gettext("route: bad prefix length %s\n"), str);
23600Sstevel@tonic-gate 		return (BAD_ADDR);
23611291Sjl138328 	}
23621291Sjl138328 	if (prefixlen > max_plen) {
23631291Sjl138328 		syntax_error(gettext("route: prefix length %s out of range\n"),
23641291Sjl138328 		    str);
23650Sstevel@tonic-gate 		return (BAD_ADDR);
23661291Sjl138328 	}
23671291Sjl138328 	return (prefixlen);
23680Sstevel@tonic-gate }
23690Sstevel@tonic-gate 
23700Sstevel@tonic-gate /*
23710Sstevel@tonic-gate  * Convert a prefix length to a mask.
23720Sstevel@tonic-gate  * Returns B_TRUE if ok. B_FALSE otherwise.
23730Sstevel@tonic-gate  * Assumes the mask array is zeroed by the caller.
23740Sstevel@tonic-gate  */
23750Sstevel@tonic-gate boolean_t
in_prefixlentomask(int prefixlen,int maxlen,uchar_t * mask)23760Sstevel@tonic-gate in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
23770Sstevel@tonic-gate {
23780Sstevel@tonic-gate 	if (prefixlen < 0 || prefixlen > maxlen)
23790Sstevel@tonic-gate 		return (B_FALSE);
23800Sstevel@tonic-gate 
23810Sstevel@tonic-gate 	while (prefixlen > 0) {
23820Sstevel@tonic-gate 		if (prefixlen >= 8) {
23830Sstevel@tonic-gate 			*mask++ = 0xFF;
23840Sstevel@tonic-gate 			prefixlen -= 8;
23850Sstevel@tonic-gate 			continue;
23860Sstevel@tonic-gate 		}
23870Sstevel@tonic-gate 		*mask |= 1 << (8 - prefixlen);
23880Sstevel@tonic-gate 		prefixlen--;
23890Sstevel@tonic-gate 	}
23900Sstevel@tonic-gate 	return (B_TRUE);
23910Sstevel@tonic-gate }
23920Sstevel@tonic-gate 
23930Sstevel@tonic-gate void
rtmonitor(int argc,char * argv[])23940Sstevel@tonic-gate rtmonitor(int argc, char *argv[])
23950Sstevel@tonic-gate {
23960Sstevel@tonic-gate 	int n;
23970Sstevel@tonic-gate 	intmax_t msg[2048 / sizeof (intmax_t)];
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 	if (tflag)
24000Sstevel@tonic-gate 		exit(0);
24010Sstevel@tonic-gate 	verbose = B_TRUE;
24020Sstevel@tonic-gate 	if (argc > 1) {
24030Sstevel@tonic-gate 		argv++;
24040Sstevel@tonic-gate 		if (argc == 2 && **argv == '-') {
24050Sstevel@tonic-gate 			switch (keyword(*argv + 1)) {
24060Sstevel@tonic-gate 			case K_INET:
24070Sstevel@tonic-gate 				af = AF_INET;
24080Sstevel@tonic-gate 				break;
24090Sstevel@tonic-gate 			case K_LINK:
24100Sstevel@tonic-gate 				af = AF_LINK;
24110Sstevel@tonic-gate 				break;
24120Sstevel@tonic-gate 			case K_INET6:
24130Sstevel@tonic-gate 				af = AF_INET6;
24140Sstevel@tonic-gate 				break;
24150Sstevel@tonic-gate 			default:
24160Sstevel@tonic-gate 				usage(*argv);
24170Sstevel@tonic-gate 				/* NOTREACHED */
24180Sstevel@tonic-gate 			}
24190Sstevel@tonic-gate 		} else {
24200Sstevel@tonic-gate 			usage(*argv);
24210Sstevel@tonic-gate 		}
24220Sstevel@tonic-gate 		(void) close(s);
24230Sstevel@tonic-gate 		s = socket(PF_ROUTE, SOCK_RAW, af);
24240Sstevel@tonic-gate 		if (s < 0)
24250Sstevel@tonic-gate 			quit("socket", errno);
24260Sstevel@tonic-gate 	}
24270Sstevel@tonic-gate 	for (;;) {
24280Sstevel@tonic-gate 		n = read(s, msg, sizeof (msg));
24290Sstevel@tonic-gate 		if (n <= 0)
24300Sstevel@tonic-gate 			quit("read", errno);
24310Sstevel@tonic-gate 		(void) printf("got message of size %d\n", n);
24320Sstevel@tonic-gate 		print_rtmsg((struct rt_msghdr *)msg, n);
24330Sstevel@tonic-gate 	}
24340Sstevel@tonic-gate }
24350Sstevel@tonic-gate 
24360Sstevel@tonic-gate int
rtmsg(rtcmd_irep_t * newrt)24371291Sjl138328 rtmsg(rtcmd_irep_t *newrt)
24380Sstevel@tonic-gate {
24390Sstevel@tonic-gate 	static int seq;
24400Sstevel@tonic-gate 	int rlen;
24410Sstevel@tonic-gate 	char *cp = m_rtmsg.m_space;
24420Sstevel@tonic-gate 	int l;
24430Sstevel@tonic-gate 
24440Sstevel@tonic-gate 	errno = 0;
24450Sstevel@tonic-gate 	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
24461291Sjl138328 
24471291Sjl138328 	if (newrt->ri_cmd == RTM_GET) {
24481291Sjl138328 		newrt->ri_ifp.sa.sa_family = AF_LINK;
24491291Sjl138328 		newrt->ri_addrs |= RTA_IFP;
24500Sstevel@tonic-gate 	}
24511291Sjl138328 
24520Sstevel@tonic-gate #define	rtm m_rtmsg.m_rtm
24531291Sjl138328 	rtm.rtm_type = newrt->ri_cmd;
24541291Sjl138328 	rtm.rtm_flags = newrt->ri_flags;
24550Sstevel@tonic-gate 	rtm.rtm_version = RTM_VERSION;
24560Sstevel@tonic-gate 	rtm.rtm_seq = ++seq;
24571291Sjl138328 	rtm.rtm_addrs = newrt->ri_addrs;
24581291Sjl138328 	rtm.rtm_rmx = newrt->ri_metrics;
24591291Sjl138328 	rtm.rtm_inits = newrt->ri_inits;
24600Sstevel@tonic-gate 
24610Sstevel@tonic-gate #define	NEXTADDR(w, u) \
24621291Sjl138328 	if (newrt->ri_addrs & (w)) { \
24630Sstevel@tonic-gate 		l = ROUNDUP_LONG(salen(&u.sa)); \
24640Sstevel@tonic-gate 		(void) memmove(cp, &(u), l); \
24650Sstevel@tonic-gate 		cp += l; \
24660Sstevel@tonic-gate 		if (verbose) \
24670Sstevel@tonic-gate 			sodump(&(u), #u); \
24680Sstevel@tonic-gate 	}
24691291Sjl138328 	NEXTADDR(RTA_DST, newrt->ri_dst);
24701291Sjl138328 	NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
24711291Sjl138328 	NEXTADDR(RTA_NETMASK, newrt->ri_mask);
24721291Sjl138328 	NEXTADDR(RTA_IFP, newrt->ri_ifp);
24731291Sjl138328 	NEXTADDR(RTA_IFA, newrt->ri_ifa);
24740Sstevel@tonic-gate 	/*
24750Sstevel@tonic-gate 	 * RTA_SRC has overloaded meaning. It can represent the
24760Sstevel@tonic-gate 	 * src address of incoming or outgoing packets.
24770Sstevel@tonic-gate 	 */
24781291Sjl138328 	NEXTADDR(RTA_SRC, newrt->ri_src);
24790Sstevel@tonic-gate #undef	NEXTADDR
24801676Sjpk 
24811676Sjpk 	if (newrt->ri_rtsa_cnt > 0) {
24821676Sjpk 		/* LINTED: aligned */
24831676Sjpk 		rtm_ext_t *rtm_ext = (rtm_ext_t *)cp;
24841676Sjpk 		tsol_rtsecattr_t *rtsecattr;
24851676Sjpk 
24861676Sjpk 		rtm_ext->rtmex_type = RTMEX_GATEWAY_SECATTR;
24871676Sjpk 		rtm_ext->rtmex_len = TSOL_RTSECATTR_SIZE(1);
24881676Sjpk 
24891676Sjpk 		rtsecattr = (tsol_rtsecattr_t *)(rtm_ext + 1);
24901676Sjpk 		rtsecattr->rtsa_cnt = 1;
24911676Sjpk 
24921676Sjpk 		bcopy(&newrt->ri_rtsa, rtsecattr->rtsa_attr,
24931676Sjpk 		    sizeof (newrt->ri_rtsa));
24941676Sjpk 		cp = (char *)(rtsecattr->rtsa_attr + 1);
24951676Sjpk 	}
24961676Sjpk 
24970Sstevel@tonic-gate 	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
24981676Sjpk 
24990Sstevel@tonic-gate 	if (verbose)
25000Sstevel@tonic-gate 		print_rtmsg(&rtm, l);
25010Sstevel@tonic-gate 	if (debugonly)
25020Sstevel@tonic-gate 		return (0);
25030Sstevel@tonic-gate 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
25040Sstevel@tonic-gate 		switch (errno) {
25050Sstevel@tonic-gate 		case ESRCH:
25060Sstevel@tonic-gate 		case EBUSY:
25070Sstevel@tonic-gate 		case ENOBUFS:
25080Sstevel@tonic-gate 		case EEXIST:
25090Sstevel@tonic-gate 		case ENETUNREACH:
25100Sstevel@tonic-gate 		case EHOSTUNREACH:
25110Sstevel@tonic-gate 		case EPERM:
25120Sstevel@tonic-gate 			break;
25130Sstevel@tonic-gate 		default:
25140Sstevel@tonic-gate 			perror(gettext("writing to routing socket"));
25150Sstevel@tonic-gate 			break;
25160Sstevel@tonic-gate 		}
25170Sstevel@tonic-gate 		return (-1);
25180Sstevel@tonic-gate 	} else if (rlen < (int)rtm.rtm_msglen) {
25190Sstevel@tonic-gate 		(void) fprintf(stderr,
25200Sstevel@tonic-gate 		    gettext("route: write to routing socket got only %d for "
25210Sstevel@tonic-gate 		    "len\n"), rlen);
25220Sstevel@tonic-gate 		return (-1);
25230Sstevel@tonic-gate 	}
25241291Sjl138328 	if (newrt->ri_cmd == RTM_GET) {
25250Sstevel@tonic-gate 		do {
25260Sstevel@tonic-gate 			l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
25270Sstevel@tonic-gate 		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
25280Sstevel@tonic-gate 		if (l < 0) {
25290Sstevel@tonic-gate 			(void) fprintf(stderr,
25300Sstevel@tonic-gate 			    gettext("route: read from routing socket: %s\n"),
25310Sstevel@tonic-gate 			    strerror(errno));
25320Sstevel@tonic-gate 		} else {
25331291Sjl138328 			print_getmsg(newrt, &rtm, l);
25340Sstevel@tonic-gate 		}
25350Sstevel@tonic-gate 	}
25360Sstevel@tonic-gate #undef rtm
25370Sstevel@tonic-gate 	return (0);
25380Sstevel@tonic-gate }
25390Sstevel@tonic-gate 
25400Sstevel@tonic-gate static char *msgtypes[] = {
25410Sstevel@tonic-gate 	"",
25420Sstevel@tonic-gate 	"RTM_ADD: Add Route",
25430Sstevel@tonic-gate 	"RTM_DELETE: Delete Route",
25440Sstevel@tonic-gate 	"RTM_CHANGE: Change Metrics or flags",
25450Sstevel@tonic-gate 	"RTM_GET: Report Metrics",
25460Sstevel@tonic-gate 	"RTM_LOSING: Kernel Suspects Partitioning",
25470Sstevel@tonic-gate 	"RTM_REDIRECT: Told to use different route",
25480Sstevel@tonic-gate 	"RTM_MISS: Lookup failed on this address",
25490Sstevel@tonic-gate 	"RTM_LOCK: fix specified metrics",
25500Sstevel@tonic-gate 	"RTM_OLDADD: caused by SIOCADDRT",
25510Sstevel@tonic-gate 	"RTM_OLDDEL: caused by SIOCDELRT",
25520Sstevel@tonic-gate 	"RTM_RESOLVE: Route created by cloning",
255311076SCathy.Zhou@Sun.COM 	"RTM_NEWADDR: address being brought up on iface",
255411076SCathy.Zhou@Sun.COM 	"RTM_DELADDR: address being brought down on iface",
25550Sstevel@tonic-gate 	"RTM_IFINFO: iface status change",
255611076SCathy.Zhou@Sun.COM 	"RTM_CHGADDR: address being changed on iface",
255711076SCathy.Zhou@Sun.COM 	"RTM_FREEADDR: address being removed from iface",
25580Sstevel@tonic-gate 	0,
25590Sstevel@tonic-gate };
25600Sstevel@tonic-gate 
25610Sstevel@tonic-gate #define	NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
25620Sstevel@tonic-gate 
25630Sstevel@tonic-gate static char metricnames[] =
25640Sstevel@tonic-gate "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
25650Sstevel@tonic-gate 	"\1mtu";
25660Sstevel@tonic-gate static char routeflags[] =
25670Sstevel@tonic-gate "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
25680Sstevel@tonic-gate 	"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2569*12748SSowmini.Varadhan@oracle.COM 	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
2570*12748SSowmini.Varadhan@oracle.COM 	"\024KERNEL\025ZONE";
25710Sstevel@tonic-gate static char ifnetflags[] =
25720Sstevel@tonic-gate "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
25730Sstevel@tonic-gate 	"\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
25740Sstevel@tonic-gate 	"\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
25750Sstevel@tonic-gate 	"\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
25760Sstevel@tonic-gate 	"\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
25774823Sseb 	"\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
25784823Sseb 	"\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
25794823Sseb 	"\047DUPLICATE";
25800Sstevel@tonic-gate static char addrnames[] =
25810Sstevel@tonic-gate "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
25820Sstevel@tonic-gate 
25830Sstevel@tonic-gate void
print_rtmsg(struct rt_msghdr * rtm,int msglen)25840Sstevel@tonic-gate print_rtmsg(struct rt_msghdr *rtm, int msglen)
25850Sstevel@tonic-gate {
25860Sstevel@tonic-gate 	struct if_msghdr *ifm;
25870Sstevel@tonic-gate 	struct ifa_msghdr *ifam;
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 	if (!verbose)
25900Sstevel@tonic-gate 		return;
25910Sstevel@tonic-gate 	if (rtm->rtm_version != RTM_VERSION) {
25920Sstevel@tonic-gate 		(void) printf("routing message version %d not understood\n",
25930Sstevel@tonic-gate 		    rtm->rtm_version);
25940Sstevel@tonic-gate 		return;
25950Sstevel@tonic-gate 	}
25961676Sjpk 	if (rtm->rtm_msglen != msglen) {
25970Sstevel@tonic-gate 		(void) printf("message length mismatch, in packet %d, "
25980Sstevel@tonic-gate 		    "returned %d\n",
25990Sstevel@tonic-gate 		    rtm->rtm_msglen, msglen);
26001676Sjpk 		if (msglen > rtm->rtm_msglen)
26011676Sjpk 			msglen = rtm->rtm_msglen;
26020Sstevel@tonic-gate 	}
26030Sstevel@tonic-gate 	/*
26040Sstevel@tonic-gate 	 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
26050Sstevel@tonic-gate 	 * and the upper-bound of (NMSGTYPES - 1).
26060Sstevel@tonic-gate 	 */
26070Sstevel@tonic-gate 	if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
26080Sstevel@tonic-gate 		(void) printf("routing message type %d not understood\n",
26090Sstevel@tonic-gate 		    rtm->rtm_type);
26100Sstevel@tonic-gate 		return;
26110Sstevel@tonic-gate 	}
26121676Sjpk 	(void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
26130Sstevel@tonic-gate 	switch (rtm->rtm_type) {
26140Sstevel@tonic-gate 	case RTM_IFINFO:
26150Sstevel@tonic-gate 		ifm = (struct if_msghdr *)rtm;
26160Sstevel@tonic-gate 		(void) printf("if# %d, flags:", ifm->ifm_index);
26170Sstevel@tonic-gate 		bprintf(stdout, ifm->ifm_flags, ifnetflags);
26181676Sjpk 		pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm),
26191676Sjpk 		    ifm->ifm_addrs);
26200Sstevel@tonic-gate 		break;
26210Sstevel@tonic-gate 	case RTM_NEWADDR:
26220Sstevel@tonic-gate 	case RTM_DELADDR:
262311076SCathy.Zhou@Sun.COM 	case RTM_CHGADDR:
262411076SCathy.Zhou@Sun.COM 	case RTM_FREEADDR:
26250Sstevel@tonic-gate 		ifam = (struct ifa_msghdr *)rtm;
26260Sstevel@tonic-gate 		(void) printf("metric %d, flags:", ifam->ifam_metric);
26270Sstevel@tonic-gate 		bprintf(stdout, ifam->ifam_flags, routeflags);
26281676Sjpk 		pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam),
26291676Sjpk 		    ifam->ifam_addrs);
26300Sstevel@tonic-gate 		break;
26310Sstevel@tonic-gate 	default:
26320Sstevel@tonic-gate 		(void) printf("pid: %ld, seq %d, errno %d, flags:",
263311042SErik.Nordmark@Sun.COM 		    rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
26340Sstevel@tonic-gate 		bprintf(stdout, rtm->rtm_flags, routeflags);
26351676Sjpk 		pmsg_common(rtm, msglen);
26361676Sjpk 		break;
26370Sstevel@tonic-gate 	}
26380Sstevel@tonic-gate }
26390Sstevel@tonic-gate 
26400Sstevel@tonic-gate void
print_getmsg(rtcmd_irep_t * req_rt,struct rt_msghdr * rtm,int msglen)26411291Sjl138328 print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
26420Sstevel@tonic-gate {
26430Sstevel@tonic-gate 	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
26440Sstevel@tonic-gate 	struct sockaddr_dl *ifp = NULL;
26450Sstevel@tonic-gate 	struct sockaddr *sa;
26460Sstevel@tonic-gate 	char *cp;
26470Sstevel@tonic-gate 	int i;
26480Sstevel@tonic-gate 
26491291Sjl138328 	(void) printf("   route to: %s\n", routename(&req_rt->ri_dst.sa));
26500Sstevel@tonic-gate 	if (rtm->rtm_version != RTM_VERSION) {
26510Sstevel@tonic-gate 		(void) fprintf(stderr,
26520Sstevel@tonic-gate 		    gettext("routing message version %d not understood\n"),
26530Sstevel@tonic-gate 		    rtm->rtm_version);
26540Sstevel@tonic-gate 		return;
26550Sstevel@tonic-gate 	}
26560Sstevel@tonic-gate 	if (rtm->rtm_msglen > (ushort_t)msglen) {
26570Sstevel@tonic-gate 		(void) fprintf(stderr,
26580Sstevel@tonic-gate 		    gettext("message length mismatch, in packet %d, "
265911042SErik.Nordmark@Sun.COM 		    "returned %d\n"), rtm->rtm_msglen, msglen);
26600Sstevel@tonic-gate 	}
26610Sstevel@tonic-gate 	if (rtm->rtm_errno)  {
26620Sstevel@tonic-gate 		(void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
26630Sstevel@tonic-gate 		    strerror(rtm->rtm_errno), rtm->rtm_errno);
26640Sstevel@tonic-gate 		return;
26650Sstevel@tonic-gate 	}
26660Sstevel@tonic-gate 	cp = ((char *)(rtm + 1));
26670Sstevel@tonic-gate 	if (rtm->rtm_addrs != 0) {
26680Sstevel@tonic-gate 		for (i = 1; i != 0; i <<= 1) {
26690Sstevel@tonic-gate 			if (i & rtm->rtm_addrs) {
26700Sstevel@tonic-gate 				/* LINTED */
26710Sstevel@tonic-gate 				sa = (struct sockaddr *)cp;
26720Sstevel@tonic-gate 				switch (i) {
26730Sstevel@tonic-gate 				case RTA_DST:
26740Sstevel@tonic-gate 					dst = sa;
26750Sstevel@tonic-gate 					break;
26760Sstevel@tonic-gate 				case RTA_GATEWAY:
26770Sstevel@tonic-gate 					gate = sa;
26780Sstevel@tonic-gate 					break;
26790Sstevel@tonic-gate 				case RTA_NETMASK:
26800Sstevel@tonic-gate 					mask = sa;
26810Sstevel@tonic-gate 					break;
26820Sstevel@tonic-gate 				case RTA_IFP:
26830Sstevel@tonic-gate 					if (sa->sa_family == AF_LINK &&
26840Sstevel@tonic-gate 					    ((struct sockaddr_dl *)sa)->
268511042SErik.Nordmark@Sun.COM 					    sdl_nlen != 0)
26860Sstevel@tonic-gate 						ifp = (struct sockaddr_dl *)sa;
26870Sstevel@tonic-gate 					break;
26880Sstevel@tonic-gate 				case RTA_SRC:
26890Sstevel@tonic-gate 					src = sa;
26900Sstevel@tonic-gate 					break;
26910Sstevel@tonic-gate 				}
26920Sstevel@tonic-gate 				ADVANCE(cp, sa);
26930Sstevel@tonic-gate 			}
26940Sstevel@tonic-gate 		}
26950Sstevel@tonic-gate 	}
26960Sstevel@tonic-gate 	if (dst != NULL && mask != NULL)
26970Sstevel@tonic-gate 		mask->sa_family = dst->sa_family;	/* XXX */
26980Sstevel@tonic-gate 	if (dst != NULL)
26990Sstevel@tonic-gate 		(void) printf("destination: %s\n", routename(dst));
27000Sstevel@tonic-gate 	if (mask != NULL) {
27010Sstevel@tonic-gate 		boolean_t savenflag = nflag;
27020Sstevel@tonic-gate 
27030Sstevel@tonic-gate 		nflag = B_TRUE;
27040Sstevel@tonic-gate 		(void) printf("       mask: %s\n", routename(mask));
27050Sstevel@tonic-gate 		nflag = savenflag;
27060Sstevel@tonic-gate 	}
27070Sstevel@tonic-gate 	if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
27080Sstevel@tonic-gate 		(void) printf("    gateway: %s\n", routename(gate));
27090Sstevel@tonic-gate 	if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
27100Sstevel@tonic-gate 		(void) printf("     setsrc: %s\n", routename(src));
27110Sstevel@tonic-gate 	if (ifp != NULL) {
27120Sstevel@tonic-gate 		if (verbose) {
27130Sstevel@tonic-gate 			int i;
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 			(void) printf("  interface: %.*s index %d address ",
27160Sstevel@tonic-gate 			    ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
27170Sstevel@tonic-gate 			for (i = ifp->sdl_nlen;
27180Sstevel@tonic-gate 			    i < ifp->sdl_nlen + ifp->sdl_alen;
27190Sstevel@tonic-gate 			    i++) {
27200Sstevel@tonic-gate 				(void) printf("%02x ",
27210Sstevel@tonic-gate 				    ifp->sdl_data[i] & 0xFF);
27220Sstevel@tonic-gate 			}
27230Sstevel@tonic-gate 			(void) printf("\n");
27240Sstevel@tonic-gate 		} else {
27250Sstevel@tonic-gate 			(void) printf("  interface: %.*s\n",
27260Sstevel@tonic-gate 			    ifp->sdl_nlen, ifp->sdl_data);
27270Sstevel@tonic-gate 		}
27280Sstevel@tonic-gate 	}
27290Sstevel@tonic-gate 	(void) printf("      flags: ");
27300Sstevel@tonic-gate 	bprintf(stdout, rtm->rtm_flags, routeflags);
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate #define	lock(f)	((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
27330Sstevel@tonic-gate #define	msec(u)	(((u) + 500) / 1000)		/* usec to msec */
27340Sstevel@tonic-gate 
27350Sstevel@tonic-gate 	(void) printf("\n%s\n", " recvpipe  sendpipe  ssthresh    rtt,ms "
27360Sstevel@tonic-gate 	    "rttvar,ms  hopcount      mtu     expire");
27370Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
27380Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
27390Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
27400Sstevel@tonic-gate 	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
27410Sstevel@tonic-gate 	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
27420Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
27430Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
27440Sstevel@tonic-gate 	if (rtm->rtm_rmx.rmx_expire)
27450Sstevel@tonic-gate 		rtm->rtm_rmx.rmx_expire -= time(0);
27461676Sjpk 	(void) printf("%8d%c", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
27470Sstevel@tonic-gate #undef lock
27480Sstevel@tonic-gate #undef msec
27490Sstevel@tonic-gate #define	RTA_IGN	\
27500Sstevel@tonic-gate 	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
27510Sstevel@tonic-gate 	if (verbose) {
27521676Sjpk 		pmsg_common(rtm, msglen);
27531676Sjpk 	} else {
27541676Sjpk 		const char *sptr, *endptr;
27551676Sjpk 		const struct sockaddr *sa;
27561676Sjpk 		uint_t addrs;
27571676Sjpk 
27581676Sjpk 		/* Not verbose; just print out the exceptional cases */
27591676Sjpk 		if (rtm->rtm_addrs &~ RTA_IGN) {
27601676Sjpk 			(void) printf("\nsockaddrs: ");
27611676Sjpk 			bprintf(stdout, rtm->rtm_addrs, addrnames);
27621676Sjpk 		}
27631676Sjpk 		sptr = (const char *)(rtm + 1);
27641676Sjpk 		endptr = (const char *)rtm + msglen;
27651676Sjpk 		addrs = rtm->rtm_addrs;
27661676Sjpk 		while (addrs != 0 && sptr + sizeof (*sa) <= endptr) {
27671676Sjpk 			addrs &= addrs - 1;
27681676Sjpk 			/* LINTED */
27691676Sjpk 			sa = (const struct sockaddr *)sptr;
27701676Sjpk 			ADVANCE(sptr, sa);
27711676Sjpk 		}
27721676Sjpk 		if (addrs == 0)
27731676Sjpk 			pmsg_secattr(sptr, endptr - sptr, "    secattr: ");
27740Sstevel@tonic-gate 		(void) putchar('\n');
27750Sstevel@tonic-gate 	}
27760Sstevel@tonic-gate #undef	RTA_IGN
27770Sstevel@tonic-gate }
27780Sstevel@tonic-gate 
27791676Sjpk static void
pmsg_common(const struct rt_msghdr * rtm,size_t msglen)27801676Sjpk pmsg_common(const struct rt_msghdr *rtm, size_t msglen)
27810Sstevel@tonic-gate {
27820Sstevel@tonic-gate 	(void) printf("\nlocks: ");
27830Sstevel@tonic-gate 	bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
27840Sstevel@tonic-gate 	(void) printf(" inits: ");
27850Sstevel@tonic-gate 	bprintf(stdout, (int)rtm->rtm_inits, metricnames);
27861676Sjpk 	pmsg_addrs((const char *)(rtm + 1), msglen - sizeof (*rtm),
27871676Sjpk 	    rtm->rtm_addrs);
27880Sstevel@tonic-gate }
27890Sstevel@tonic-gate 
27901676Sjpk static void
pmsg_addrs(const char * cp,size_t msglen,uint_t addrs)27911676Sjpk pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
27920Sstevel@tonic-gate {
27931676Sjpk 	const struct sockaddr *sa;
27941676Sjpk 	const char *maxptr;
27950Sstevel@tonic-gate 	int i;
27960Sstevel@tonic-gate 
27971676Sjpk 	if (addrs != 0) {
27981676Sjpk 		(void) printf("\nsockaddrs: ");
27991676Sjpk 		bprintf(stdout, addrs, addrnames);
28001676Sjpk 		(void) putchar('\n');
28011676Sjpk 		maxptr = cp + msglen;
28021676Sjpk 		for (i = 1; i != 0 && cp + sizeof (*sa) <= maxptr; i <<= 1) {
28031676Sjpk 			if (i & addrs) {
28041676Sjpk 				/* LINTED */
28051676Sjpk 				sa = (const struct sockaddr *)cp;
28061676Sjpk 				(void) printf(" %s", routename(sa));
28071676Sjpk 				ADVANCE(cp, sa);
28081676Sjpk 			}
28090Sstevel@tonic-gate 		}
28101676Sjpk 		if (i != 0)
28111676Sjpk 			msglen = 0;
28121676Sjpk 		else
28131676Sjpk 			msglen = maxptr - cp;
28140Sstevel@tonic-gate 	}
28151676Sjpk 	pmsg_secattr(cp, msglen, "secattr: ");
28160Sstevel@tonic-gate 	(void) putchar('\n');
28170Sstevel@tonic-gate 	(void) fflush(stdout);
28180Sstevel@tonic-gate }
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate void
bprintf(FILE * fp,int b,char * s)28210Sstevel@tonic-gate bprintf(FILE *fp, int b, char *s)
28220Sstevel@tonic-gate {
28230Sstevel@tonic-gate 	int i;
28240Sstevel@tonic-gate 	boolean_t gotsome = B_FALSE;
28250Sstevel@tonic-gate 
28260Sstevel@tonic-gate 	if (b == 0)
28270Sstevel@tonic-gate 		return;
28280Sstevel@tonic-gate 	while ((i = *s++) != 0) {
28290Sstevel@tonic-gate 		if (b & (1 << (i - 1))) {
28300Sstevel@tonic-gate 			if (!gotsome)
28310Sstevel@tonic-gate 				i = '<';
28320Sstevel@tonic-gate 			else
28330Sstevel@tonic-gate 				i = ',';
28340Sstevel@tonic-gate 			(void) putc(i, fp);
28350Sstevel@tonic-gate 			gotsome = B_TRUE;
28360Sstevel@tonic-gate 			for (; (i = *s) > ' '; s++)
28370Sstevel@tonic-gate 				(void) putc(i, fp);
28380Sstevel@tonic-gate 		} else {
28390Sstevel@tonic-gate 			while (*s > ' ')
28400Sstevel@tonic-gate 				s++;
28410Sstevel@tonic-gate 		}
28420Sstevel@tonic-gate 	}
28430Sstevel@tonic-gate 	if (gotsome)
28440Sstevel@tonic-gate 		(void) putc('>', fp);
28450Sstevel@tonic-gate }
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate int
keyword(const char * cp)28481291Sjl138328 keyword(const char *cp)
28490Sstevel@tonic-gate {
28500Sstevel@tonic-gate 	struct keytab *kt = keywords;
28510Sstevel@tonic-gate 
28520Sstevel@tonic-gate 	while (kt->kt_cp && strcmp(kt->kt_cp, cp))
28530Sstevel@tonic-gate 		kt++;
28540Sstevel@tonic-gate 	return (kt->kt_i);
28550Sstevel@tonic-gate }
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate void
sodump(su_t * su,char * which)28581291Sjl138328 sodump(su_t *su, char *which)
28590Sstevel@tonic-gate {
28600Sstevel@tonic-gate 	static char obuf[INET6_ADDRSTRLEN];
28610Sstevel@tonic-gate 
28620Sstevel@tonic-gate 	switch (su->sa.sa_family) {
28630Sstevel@tonic-gate 	case AF_LINK:
28640Sstevel@tonic-gate 		(void) printf("%s: link %s; ",
28650Sstevel@tonic-gate 		    which, link_ntoa(&su->sdl));
28660Sstevel@tonic-gate 		break;
28670Sstevel@tonic-gate 	case AF_INET:
28680Sstevel@tonic-gate 		(void) printf("%s: inet %s; ",
28690Sstevel@tonic-gate 		    which, inet_ntoa(su->sin.sin_addr));
28700Sstevel@tonic-gate 		break;
28710Sstevel@tonic-gate 	case AF_INET6:
28720Sstevel@tonic-gate 		if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
28730Sstevel@tonic-gate 		    INET6_ADDRSTRLEN) != NULL) {
28740Sstevel@tonic-gate 			(void) printf("%s: inet6 %s; ", which, obuf);
28750Sstevel@tonic-gate 			break;
28760Sstevel@tonic-gate 		}
28770Sstevel@tonic-gate 		/* FALLTHROUGH */
28780Sstevel@tonic-gate 	default:
28790Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
28800Sstevel@tonic-gate 		/* NOTREACHED */
28810Sstevel@tonic-gate 	}
28820Sstevel@tonic-gate 	(void) fflush(stdout);
28830Sstevel@tonic-gate }
28840Sstevel@tonic-gate 
28850Sstevel@tonic-gate /* States */
28860Sstevel@tonic-gate #define	VIRGIN	0
28870Sstevel@tonic-gate #define	GOTONE	1
28880Sstevel@tonic-gate #define	GOTTWO	2
28890Sstevel@tonic-gate #define	RESET	3
28900Sstevel@tonic-gate /* Inputs */
28910Sstevel@tonic-gate #define	DIGIT	(4*0)
28920Sstevel@tonic-gate #define	END	(4*1)
28930Sstevel@tonic-gate #define	DELIM	(4*2)
28940Sstevel@tonic-gate #define	LETTER	(4*3)
28950Sstevel@tonic-gate 
28960Sstevel@tonic-gate void
sockaddr(char * addr,struct sockaddr * sa)28970Sstevel@tonic-gate sockaddr(char *addr, struct sockaddr *sa)
28980Sstevel@tonic-gate {
28990Sstevel@tonic-gate 	char *cp = (char *)sa;
29000Sstevel@tonic-gate 	int size = salen(sa);
29010Sstevel@tonic-gate 	char *cplim = cp + size;
29020Sstevel@tonic-gate 	int byte = 0, state = VIRGIN, new;
29030Sstevel@tonic-gate 
29040Sstevel@tonic-gate 	(void) memset(cp, 0, size);
29050Sstevel@tonic-gate 	cp++;
29060Sstevel@tonic-gate 	do {
29070Sstevel@tonic-gate 		if ((*addr >= '0') && (*addr <= '9')) {
29080Sstevel@tonic-gate 			new = *addr - '0';
29090Sstevel@tonic-gate 		} else if ((*addr >= 'a') && (*addr <= 'f')) {
29100Sstevel@tonic-gate 			new = *addr - 'a' + 10;
29110Sstevel@tonic-gate 		} else if ((*addr >= 'A') && (*addr <= 'F')) {
29120Sstevel@tonic-gate 			new = *addr - 'A' + 10;
29130Sstevel@tonic-gate 		} else if (*addr == 0) {
29140Sstevel@tonic-gate 			state |= END;
29150Sstevel@tonic-gate 		} else {
29160Sstevel@tonic-gate 			state |= DELIM;
29170Sstevel@tonic-gate 		}
29180Sstevel@tonic-gate 		addr++;
29190Sstevel@tonic-gate 		switch (state /* | INPUT */) {
29200Sstevel@tonic-gate 		case GOTTWO | DIGIT:
29210Sstevel@tonic-gate 			*cp++ = byte;
29220Sstevel@tonic-gate 			/* FALLTHROUGH */
29230Sstevel@tonic-gate 		case VIRGIN | DIGIT:
29240Sstevel@tonic-gate 			state = GOTONE; byte = new; continue;
29250Sstevel@tonic-gate 		case GOTONE | DIGIT:
29260Sstevel@tonic-gate 			state = GOTTWO; byte = new + (byte << 4); continue;
29270Sstevel@tonic-gate 		default: /* | DELIM */
29280Sstevel@tonic-gate 			state = VIRGIN; *cp++ = byte; byte = 0; continue;
29290Sstevel@tonic-gate 		case GOTONE | END:
29300Sstevel@tonic-gate 		case GOTTWO | END:
29310Sstevel@tonic-gate 			*cp++ = byte;
29320Sstevel@tonic-gate 			/* FALLTHROUGH */
29330Sstevel@tonic-gate 		case VIRGIN | END:
29340Sstevel@tonic-gate 			break;
29350Sstevel@tonic-gate 		}
29360Sstevel@tonic-gate 		break;
29370Sstevel@tonic-gate 	} while (cp < cplim);
29380Sstevel@tonic-gate }
29390Sstevel@tonic-gate 
29400Sstevel@tonic-gate int
salen(const struct sockaddr * sa)29411676Sjpk salen(const struct sockaddr *sa)
29420Sstevel@tonic-gate {
29430Sstevel@tonic-gate 	switch (sa->sa_family) {
29440Sstevel@tonic-gate 	case AF_INET:
29450Sstevel@tonic-gate 		return (sizeof (struct sockaddr_in));
29460Sstevel@tonic-gate 	case AF_LINK:
29470Sstevel@tonic-gate 		return (sizeof (struct sockaddr_dl));
29480Sstevel@tonic-gate 	case AF_INET6:
29490Sstevel@tonic-gate 		return (sizeof (struct sockaddr_in6));
29500Sstevel@tonic-gate 	default:
29510Sstevel@tonic-gate 		return (sizeof (struct sockaddr));
29520Sstevel@tonic-gate 	}
29530Sstevel@tonic-gate }
29540Sstevel@tonic-gate 
29550Sstevel@tonic-gate void
link_addr(const char * addr,struct sockaddr_dl * sdl)29560Sstevel@tonic-gate link_addr(const char *addr, struct sockaddr_dl *sdl)
29570Sstevel@tonic-gate {
29580Sstevel@tonic-gate 	char *cp = sdl->sdl_data;
29590Sstevel@tonic-gate 	char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
29600Sstevel@tonic-gate 	int byte = 0, state = VIRGIN, new;
29610Sstevel@tonic-gate 
29620Sstevel@tonic-gate 	(void) memset(sdl, 0, sizeof (struct sockaddr_dl));
29630Sstevel@tonic-gate 	sdl->sdl_family = AF_LINK;
29640Sstevel@tonic-gate 	do {
29650Sstevel@tonic-gate 		state &= ~LETTER;
29660Sstevel@tonic-gate 		if ((*addr >= '0') && (*addr <= '9')) {
29670Sstevel@tonic-gate 			new = *addr - '0';
29680Sstevel@tonic-gate 		} else if ((*addr >= 'a') && (*addr <= 'f')) {
29690Sstevel@tonic-gate 			new = *addr - 'a' + 10;
29700Sstevel@tonic-gate 		} else if ((*addr >= 'A') && (*addr <= 'F')) {
29710Sstevel@tonic-gate 			new = *addr - 'A' + 10;
29720Sstevel@tonic-gate 		} else if (*addr == 0) {
29730Sstevel@tonic-gate 			state |= END;
29740Sstevel@tonic-gate 		} else if (state == VIRGIN &&
29750Sstevel@tonic-gate 		    (((*addr >= 'A') && (*addr <= 'Z')) ||
29760Sstevel@tonic-gate 		    ((*addr >= 'a') && (*addr <= 'z')))) {
29770Sstevel@tonic-gate 			state |= LETTER;
29780Sstevel@tonic-gate 		} else {
29790Sstevel@tonic-gate 			state |= DELIM;
29800Sstevel@tonic-gate 		}
29810Sstevel@tonic-gate 		addr++;
29820Sstevel@tonic-gate 		switch (state /* | INPUT */) {
29830Sstevel@tonic-gate 		case VIRGIN | DIGIT:
29840Sstevel@tonic-gate 		case VIRGIN | LETTER:
29850Sstevel@tonic-gate 			*cp++ = addr[-1];
29860Sstevel@tonic-gate 			continue;
29870Sstevel@tonic-gate 		case VIRGIN | DELIM:
29880Sstevel@tonic-gate 			state = RESET;
29890Sstevel@tonic-gate 			sdl->sdl_nlen = cp - sdl->sdl_data;
29900Sstevel@tonic-gate 			continue;
29910Sstevel@tonic-gate 		case GOTTWO | DIGIT:
29920Sstevel@tonic-gate 			*cp++ = byte;
29930Sstevel@tonic-gate 			/* FALLTHROUGH */
29940Sstevel@tonic-gate 		case RESET | DIGIT:
29950Sstevel@tonic-gate 			state = GOTONE;
29960Sstevel@tonic-gate 			byte = new;
29970Sstevel@tonic-gate 			continue;
29980Sstevel@tonic-gate 		case GOTONE | DIGIT:
29990Sstevel@tonic-gate 			state = GOTTWO;
30000Sstevel@tonic-gate 			byte = new + (byte << 4);
30010Sstevel@tonic-gate 			continue;
30020Sstevel@tonic-gate 		default: /* | DELIM */
30030Sstevel@tonic-gate 			state = RESET;
30040Sstevel@tonic-gate 			*cp++ = byte;
30050Sstevel@tonic-gate 			byte = 0;
30060Sstevel@tonic-gate 			continue;
30070Sstevel@tonic-gate 		case GOTONE | END:
30080Sstevel@tonic-gate 		case GOTTWO | END:
30090Sstevel@tonic-gate 			*cp++ = byte;
30100Sstevel@tonic-gate 			/* FALLTHROUGH */
30110Sstevel@tonic-gate 		case RESET | END:
30120Sstevel@tonic-gate 			break;
30130Sstevel@tonic-gate 		}
30140Sstevel@tonic-gate 		break;
30150Sstevel@tonic-gate 	} while (cp < cplim);
30160Sstevel@tonic-gate 	sdl->sdl_alen = cp - LLADDR(sdl);
30170Sstevel@tonic-gate }
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate static char hexlist[] = "0123456789abcdef";
30200Sstevel@tonic-gate 
30210Sstevel@tonic-gate char *
link_ntoa(const struct sockaddr_dl * sdl)30220Sstevel@tonic-gate link_ntoa(const struct sockaddr_dl *sdl)
30230Sstevel@tonic-gate {
30240Sstevel@tonic-gate 	static char obuf[64];
30250Sstevel@tonic-gate 	char *out = obuf;
30260Sstevel@tonic-gate 	int i;
30270Sstevel@tonic-gate 	uchar_t *in = (uchar_t *)LLADDR(sdl);
30280Sstevel@tonic-gate 	uchar_t *inlim = in + sdl->sdl_alen;
30290Sstevel@tonic-gate 	boolean_t firsttime = B_TRUE;
30300Sstevel@tonic-gate 
30310Sstevel@tonic-gate 	if (sdl->sdl_nlen) {
30320Sstevel@tonic-gate 		(void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
30330Sstevel@tonic-gate 		out += sdl->sdl_nlen;
30340Sstevel@tonic-gate 		if (sdl->sdl_alen)
30350Sstevel@tonic-gate 			*out++ = ':';
30360Sstevel@tonic-gate 	}
30370Sstevel@tonic-gate 	while (in < inlim) {
30380Sstevel@tonic-gate 		if (firsttime)
30390Sstevel@tonic-gate 			firsttime = B_FALSE;
30400Sstevel@tonic-gate 		else
30410Sstevel@tonic-gate 			*out++ = '.';
30420Sstevel@tonic-gate 		i = *in++;
30430Sstevel@tonic-gate 		if (i > 0xf) {
30440Sstevel@tonic-gate 			out[1] = hexlist[i & 0xf];
30450Sstevel@tonic-gate 			i >>= 4;
30460Sstevel@tonic-gate 			out[0] = hexlist[i];
30470Sstevel@tonic-gate 			out += 2;
30480Sstevel@tonic-gate 		} else {
30490Sstevel@tonic-gate 			*out++ = hexlist[i];
30500Sstevel@tonic-gate 		}
30510Sstevel@tonic-gate 	}
30520Sstevel@tonic-gate 	*out = 0;
30530Sstevel@tonic-gate 	return (obuf);
30540Sstevel@tonic-gate }
30550Sstevel@tonic-gate 
30560Sstevel@tonic-gate static mib_item_t *
mibget(int sd)30570Sstevel@tonic-gate mibget(int sd)
30580Sstevel@tonic-gate {
30590Sstevel@tonic-gate 	intmax_t		buf[512 / sizeof (intmax_t)];
30600Sstevel@tonic-gate 	int			flags;
30610Sstevel@tonic-gate 	int			i, j, getcode;
30620Sstevel@tonic-gate 	struct strbuf		ctlbuf, databuf;
30630Sstevel@tonic-gate 	struct T_optmgmt_req	*tor = (struct T_optmgmt_req *)buf;
30640Sstevel@tonic-gate 	struct T_optmgmt_ack	*toa = (struct T_optmgmt_ack *)buf;
30650Sstevel@tonic-gate 	struct T_error_ack	*tea = (struct T_error_ack *)buf;
30660Sstevel@tonic-gate 	struct opthdr		*req;
30670Sstevel@tonic-gate 	mib_item_t		*first_item = NULL;
30680Sstevel@tonic-gate 	mib_item_t		*last_item  = NULL;
30690Sstevel@tonic-gate 	mib_item_t		*temp;
30700Sstevel@tonic-gate 
30710Sstevel@tonic-gate 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
30720Sstevel@tonic-gate 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
30730Sstevel@tonic-gate 	tor->OPT_length = sizeof (struct opthdr);
30740Sstevel@tonic-gate 	tor->MGMT_flags = T_CURRENT;
30750Sstevel@tonic-gate 	req = (struct opthdr *)&tor[1];
30760Sstevel@tonic-gate 	req->level = MIB2_IP;		/* any MIB2_xxx value ok here */
30770Sstevel@tonic-gate 	req->name  = 0;
30780Sstevel@tonic-gate 	req->len   = 0;
30790Sstevel@tonic-gate 
30800Sstevel@tonic-gate 	ctlbuf.buf = (char *)buf;
30810Sstevel@tonic-gate 	ctlbuf.len = tor->OPT_length + tor->OPT_offset;
30820Sstevel@tonic-gate 	flags = 0;
30830Sstevel@tonic-gate 	if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
30840Sstevel@tonic-gate 		perror("mibget: putmsg (ctl)");
30850Sstevel@tonic-gate 		return (NULL);
30860Sstevel@tonic-gate 	}
30870Sstevel@tonic-gate 	/*
30880Sstevel@tonic-gate 	 * each reply consists of a ctl part for one fixed structure
30890Sstevel@tonic-gate 	 * or table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK,
30900Sstevel@tonic-gate 	 * containing an opthdr structure.  level/name identify the entry,
30910Sstevel@tonic-gate 	 * len is the size of the data part of the message.
30920Sstevel@tonic-gate 	 */
30930Sstevel@tonic-gate 	req = (struct opthdr *)&toa[1];
30940Sstevel@tonic-gate 	ctlbuf.maxlen = sizeof (buf);
30950Sstevel@tonic-gate 	for (j = 1; ; j++) {
30960Sstevel@tonic-gate 		flags = 0;
30970Sstevel@tonic-gate 		getcode = getmsg(sd, &ctlbuf, NULL, &flags);
30980Sstevel@tonic-gate 		if (getcode < 0) {
30990Sstevel@tonic-gate 			perror("mibget: getmsg (ctl)");
31000Sstevel@tonic-gate 			if (verbose) {
31010Sstevel@tonic-gate 				(void) fprintf(stderr,
31020Sstevel@tonic-gate 				    "#   level   name    len\n");
31030Sstevel@tonic-gate 				i = 0;
31040Sstevel@tonic-gate 				for (last_item = first_item; last_item != NULL;
31050Sstevel@tonic-gate 				    last_item = last_item->next_item) {
31060Sstevel@tonic-gate 					(void) printf("%d  %4ld   %5ld   %ld\n",
31070Sstevel@tonic-gate 					    ++i, last_item->group,
31080Sstevel@tonic-gate 					    last_item->mib_id,
31090Sstevel@tonic-gate 					    last_item->length);
31100Sstevel@tonic-gate 				}
31110Sstevel@tonic-gate 			}
31120Sstevel@tonic-gate 			break;
31130Sstevel@tonic-gate 		}
31140Sstevel@tonic-gate 		if (getcode == 0 &&
31150Sstevel@tonic-gate 		    ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
31160Sstevel@tonic-gate 		    toa->PRIM_type == T_OPTMGMT_ACK &&
31170Sstevel@tonic-gate 		    toa->MGMT_flags == T_SUCCESS &&
31180Sstevel@tonic-gate 		    req->len == 0) {
31190Sstevel@tonic-gate 			if (verbose) {
31200Sstevel@tonic-gate 				(void) printf("mibget getmsg() %d returned EOD "
31210Sstevel@tonic-gate 				    "(level %lu, name %lu)\n", j, req->level,
31220Sstevel@tonic-gate 				    req->name);
31230Sstevel@tonic-gate 			}
31240Sstevel@tonic-gate 			return (first_item);		/* this is EOD msg */
31250Sstevel@tonic-gate 		}
31260Sstevel@tonic-gate 
31270Sstevel@tonic-gate 		if (ctlbuf.len >= sizeof (struct T_error_ack) &&
31280Sstevel@tonic-gate 		    tea->PRIM_type == T_ERROR_ACK) {
31290Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("mibget %d gives "
31300Sstevel@tonic-gate 			    "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
31310Sstevel@tonic-gate 			    "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
313211042SErik.Nordmark@Sun.COM 			errno = (tea->TLI_error == TSYSERR) ?
313311042SErik.Nordmark@Sun.COM 			    tea->UNIX_error : EPROTO;
31340Sstevel@tonic-gate 			break;
31350Sstevel@tonic-gate 		}
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate 		if (getcode != MOREDATA ||
31380Sstevel@tonic-gate 		    ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
31390Sstevel@tonic-gate 		    toa->PRIM_type != T_OPTMGMT_ACK ||
31400Sstevel@tonic-gate 		    toa->MGMT_flags != T_SUCCESS) {
31410Sstevel@tonic-gate 			(void) printf("mibget getmsg(ctl) %d returned %d, "
31420Sstevel@tonic-gate 			    "ctlbuf.len = %d, PRIM_type = %ld\n",
31430Sstevel@tonic-gate 			    j, getcode, ctlbuf.len, toa->PRIM_type);
31440Sstevel@tonic-gate 			if (toa->PRIM_type == T_OPTMGMT_ACK) {
31450Sstevel@tonic-gate 				(void) printf("T_OPTMGMT_ACK: "
31460Sstevel@tonic-gate 				    "MGMT_flags = 0x%lx, req->len = %ld\n",
31470Sstevel@tonic-gate 				    toa->MGMT_flags, req->len);
31480Sstevel@tonic-gate 			}
31490Sstevel@tonic-gate 			errno = ENOMSG;
31500Sstevel@tonic-gate 			break;
31510Sstevel@tonic-gate 		}
31520Sstevel@tonic-gate 
31530Sstevel@tonic-gate 		temp = malloc(sizeof (mib_item_t));
31540Sstevel@tonic-gate 		if (temp == NULL) {
31550Sstevel@tonic-gate 			perror("mibget: malloc");
31560Sstevel@tonic-gate 			break;
31570Sstevel@tonic-gate 		}
31580Sstevel@tonic-gate 		if (last_item != NULL)
31590Sstevel@tonic-gate 			last_item->next_item = temp;
31600Sstevel@tonic-gate 		else
31610Sstevel@tonic-gate 			first_item = temp;
31620Sstevel@tonic-gate 		last_item = temp;
31630Sstevel@tonic-gate 		last_item->next_item = NULL;
31640Sstevel@tonic-gate 		last_item->group = req->level;
31650Sstevel@tonic-gate 		last_item->mib_id = req->name;
31660Sstevel@tonic-gate 		last_item->length = req->len;
31670Sstevel@tonic-gate 		last_item->valp = malloc(req->len);
31680Sstevel@tonic-gate 		if (verbose) {
31690Sstevel@tonic-gate 			(void) printf("msg %d:  group = %4ld   mib_id = %5ld   "
31700Sstevel@tonic-gate 			    "length = %ld\n",
31710Sstevel@tonic-gate 			    j, last_item->group, last_item->mib_id,
31720Sstevel@tonic-gate 			    last_item->length);
31730Sstevel@tonic-gate 		}
31740Sstevel@tonic-gate 
31750Sstevel@tonic-gate 		databuf.maxlen = last_item->length;
31760Sstevel@tonic-gate 		databuf.buf    = (char *)last_item->valp;
31770Sstevel@tonic-gate 		databuf.len    = 0;
31780Sstevel@tonic-gate 		flags = 0;
31790Sstevel@tonic-gate 		getcode = getmsg(sd, NULL, &databuf, &flags);
31800Sstevel@tonic-gate 		if (getcode < 0) {
31810Sstevel@tonic-gate 			perror("mibget: getmsg (data)");
31820Sstevel@tonic-gate 			break;
31830Sstevel@tonic-gate 		} else if (getcode != 0) {
31840Sstevel@tonic-gate 			(void) printf("mibget getmsg(data) returned %d, "
31850Sstevel@tonic-gate 			    "databuf.maxlen = %d, databuf.len = %d\n",
31860Sstevel@tonic-gate 			    getcode, databuf.maxlen, databuf.len);
31870Sstevel@tonic-gate 			break;
31880Sstevel@tonic-gate 		}
31890Sstevel@tonic-gate 	}
31900Sstevel@tonic-gate 
31910Sstevel@tonic-gate 	/*
31920Sstevel@tonic-gate 	 * On error, free all the allocated mib_item_t objects.
31930Sstevel@tonic-gate 	 */
31940Sstevel@tonic-gate 	while (first_item != NULL) {
31950Sstevel@tonic-gate 		last_item = first_item;
31960Sstevel@tonic-gate 		first_item = first_item->next_item;
31970Sstevel@tonic-gate 		free(last_item);
31980Sstevel@tonic-gate 	}
31990Sstevel@tonic-gate 	return (NULL);
32000Sstevel@tonic-gate }
32011676Sjpk 
32021676Sjpk /*
32031676Sjpk  * print label security attributes for gateways.
32041676Sjpk  */
32051676Sjpk static void
pmsg_secattr(const char * sptr,size_t msglen,const char * labelstr)32061676Sjpk pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr)
32071676Sjpk {
32081676Sjpk 	rtm_ext_t rtm_ext;
32091676Sjpk 	tsol_rtsecattr_t sp;
32101676Sjpk 	struct rtsa_s *rtsa = &sp.rtsa_attr[0];
32111676Sjpk 	const char *endptr;
32121676Sjpk 	char buf[256];
32131676Sjpk 	int i;
32141676Sjpk 
32151676Sjpk 	if (!is_system_labeled())
32161676Sjpk 		return;
32171676Sjpk 
32181676Sjpk 	endptr = sptr + msglen;
32191676Sjpk 
32201676Sjpk 	for (;;) {
32211676Sjpk 		if (sptr + sizeof (rtm_ext_t) + sizeof (sp) > endptr)
32221676Sjpk 			return;
32231676Sjpk 
32241676Sjpk 		bcopy(sptr, &rtm_ext, sizeof (rtm_ext));
32251676Sjpk 		sptr += sizeof (rtm_ext);
32261676Sjpk 		if (rtm_ext.rtmex_type == RTMEX_GATEWAY_SECATTR)
32271676Sjpk 			break;
32281676Sjpk 		sptr += rtm_ext.rtmex_len;
32291676Sjpk 	}
32301676Sjpk 
32311676Sjpk 	/* bail if this entry is corrupt or overruns buffer length */
32321676Sjpk 	if (rtm_ext.rtmex_len < sizeof (sp) ||
32331676Sjpk 	    sptr + rtm_ext.rtmex_len > endptr)
32341676Sjpk 		return;
32351676Sjpk 
32361676Sjpk 	/* run up just to the end of this extension */
32371676Sjpk 	endptr = sptr + rtm_ext.rtmex_len;
32381676Sjpk 
32391676Sjpk 	bcopy(sptr, &sp, sizeof (sp));
32401676Sjpk 	sptr += sizeof (sp);
32411676Sjpk 
32421676Sjpk 	if (sptr + (sp.rtsa_cnt - 1) * sizeof (*rtsa) != endptr)
32431676Sjpk 		return;
32441676Sjpk 
32451676Sjpk 	for (i = 0; i < sp.rtsa_cnt; i++) {
32461676Sjpk 		if (i > 0) {
32471676Sjpk 			/* first element is part of sp initalized above */
32481676Sjpk 			bcopy(sptr, rtsa, sizeof (*rtsa));
32491676Sjpk 			sptr += sizeof (*rtsa);
32501676Sjpk 		}
32511676Sjpk 		(void) printf("\n%s%s", labelstr, rtsa_to_str(rtsa, buf,
32521676Sjpk 		    sizeof (buf)));
32531676Sjpk 	}
32541676Sjpk }
3255