1/*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
6/* All Rights Reserved	*/
7
8/* Copyright (c) 1990  Mentat Inc. */
9
10/*
11 *
12 * Copyright (c) 1983, 1989, 1991, 1993
13 *	The Regents of the University of California.  All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 * 3. All advertising materials mentioning features or use of this software
24 *    must display the following acknowledgement:
25 *	This product includes software developed by the University of
26 *	California, Berkeley and its contributors.
27 * 4. Neither the name of the University nor the names of its contributors
28 *    may be used to endorse or promote products derived from this software
29 *    without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * SUCH DAMAGE.
42 *
43 *	@(#)route.c	8.6 (Berkeley) 4/28/95
44 *	@(#)linkaddr.c	8.1 (Berkeley) 6/4/93
45 */
46
47#include <sys/param.h>
48#include <sys/file.h>
49#include <sys/socket.h>
50#include <sys/ioctl.h>
51#include <sys/stat.h>
52#include <sys/stream.h>
53#include <sys/sysmacros.h>
54#include <sys/tihdr.h>
55#include <sys/types.h>
56#include <sys/ccompile.h>
57
58#include <net/if.h>
59#include <net/route.h>
60#include <net/if_dl.h>
61#include <netinet/in.h>
62#include <arpa/inet.h>
63#include <netdb.h>
64#include <inet/mib2.h>
65#include <inet/ip.h>
66
67#include <limits.h>
68#include <locale.h>
69
70#include <errno.h>
71#include <unistd.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <stddef.h>
75#include <string.h>
76#include <stropts.h>
77#include <fcntl.h>
78#include <stdarg.h>
79#include <assert.h>
80#include <strings.h>
81
82#include <libtsnet.h>
83#include <tsol/label.h>
84
85static struct keytab {
86	char	*kt_cp;
87	int	kt_i;
88} keywords[] = {
89#define	K_ADD		1
90	{"add",		K_ADD},
91#define	K_BLACKHOLE	2
92	{"blackhole",	K_BLACKHOLE},
93#define	K_CHANGE	3
94	{"change",	K_CHANGE},
95#define	K_CLONING	4
96	{"cloning",	K_CLONING},
97#define	K_DELETE	5
98	{"delete",	K_DELETE},
99#define	K_DST		6
100	{"dst",		K_DST},
101#define	K_EXPIRE	7
102	{"expire",	K_EXPIRE},
103#define	K_FLUSH		8
104	{"flush",	K_FLUSH},
105#define	K_GATEWAY	9
106	{"gateway",	K_GATEWAY},
107#define	K_GET		11
108	{"get",		K_GET},
109#define	K_HOPCOUNT	12
110	{"hopcount",	K_HOPCOUNT},
111#define	K_HOST		13
112	{"host",	K_HOST},
113#define	K_IFA		14
114	{"ifa",		K_IFA},
115#define	K_IFACE		15
116	{"iface",	K_IFACE},
117#define	K_IFP		16
118	{"ifp",		K_IFP},
119#define	K_INET		17
120	{"inet",	K_INET},
121#define	K_INET6		18
122	{"inet6",	K_INET6},
123#define	K_INTERFACE	19
124	{"interface",	K_INTERFACE},
125#define	K_LINK		20
126	{"link",	K_LINK},
127#define	K_LOCK		21
128	{"lock",	K_LOCK},
129#define	K_LOCKREST	22
130	{"lockrest",	K_LOCKREST},
131#define	K_MASK		23
132	{"mask",	K_MASK},
133#define	K_MONITOR	24
134	{"monitor",	K_MONITOR},
135#define	K_MTU		25
136	{"mtu",		K_MTU},
137#define	K_NET		26
138	{"net",		K_NET},
139#define	K_NETMASK	27
140	{"netmask",	K_NETMASK},
141#define	K_NOSTATIC	28
142	{"nostatic",	K_NOSTATIC},
143#define	K_PRIVATE	29
144	{"private",	K_PRIVATE},
145#define	K_PROTO1	30
146	{"proto1",	K_PROTO1},
147#define	K_PROTO2	31
148	{"proto2",	K_PROTO2},
149#define	K_RECVPIPE	32
150	{"recvpipe",	K_RECVPIPE},
151#define	K_REJECT	33
152	{"reject",	K_REJECT},
153#define	K_RTT		34
154	{"rtt",		K_RTT},
155#define	K_RTTVAR	35
156	{"rttvar",	K_RTTVAR},
157#define	K_SA		36
158	{"sa",		K_SA},
159#define	K_SENDPIPE	37
160	{"sendpipe",	K_SENDPIPE},
161#define	K_SSTHRESH	38
162	{"ssthresh",	K_SSTHRESH},
163#define	K_STATIC	39
164	{"static",	K_STATIC},
165#define	K_XRESOLVE	40
166	{"xresolve",	K_XRESOLVE},
167#define	K_MULTIRT	41
168	{"multirt",	K_MULTIRT},
169#define	K_SETSRC	42
170	{"setsrc",	K_SETSRC},
171#define	K_SHOW		43
172	{"show",	K_SHOW},
173#define	K_SECATTR	43
174	{"secattr",	K_SECATTR},
175#define	K_INDIRECT	44
176	{"indirect",	K_INDIRECT},
177	{0, 0}
178};
179
180/*
181 * Size of buffers used to hold command lines from the saved route file as
182 * well as error strings.
183 */
184#define	BUF_SIZE 2048
185
186typedef union sockunion {
187	struct	sockaddr sa;
188	struct	sockaddr_in sin;
189	struct	sockaddr_dl sdl;
190	struct	sockaddr_in6 sin6;
191} su_t;
192
193/*
194 * This structure represents the digested information from parsing arguments
195 * to route add, change, delete, and get.
196 *
197 */
198typedef struct rtcmd_irep {
199	int ri_cmd;
200	int ri_flags;
201	int ri_af;
202	ulong_t	ri_inits;
203	struct rt_metrics ri_metrics;
204	int ri_addrs;
205	su_t ri_dst;
206	char *ri_dest_str;
207	su_t ri_src;
208	su_t ri_gate;
209	struct hostent *ri_gate_hp;
210	char *ri_gate_str;
211	su_t ri_mask;
212	su_t ri_ifa;
213	su_t ri_ifp;
214	char *ri_ifp_str;
215	int ri_rtsa_cnt;	/* number of gateway security attributes */
216	struct rtsa_s ri_rtsa;	/* enough space for one attribute */
217} rtcmd_irep_t;
218
219typedef struct	mib_item_s {
220	struct mib_item_s *next_item;
221	long group;
222	long mib_id;
223	long length;
224	intmax_t *valp;
225} mib_item_t;
226
227typedef enum {
228	ADDR_TYPE_ANY,
229	ADDR_TYPE_HOST,
230	ADDR_TYPE_NET
231} addr_type_t;
232
233typedef enum {
234	SEARCH_MODE_NULL,
235	SEARCH_MODE_PRINT,
236	SEARCH_MODE_DEL
237} search_mode_t;
238
239static boolean_t	args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
240    char *cmd_string);
241static void		bprintf(FILE *fp, int b, char *s);
242static boolean_t	compare_rtcmd(rtcmd_irep_t *srch_rt,
243    rtcmd_irep_t *file_rt);
244static void		delRouteEntry(mib2_ipRouteEntry_t *rp,
245    mib2_ipv6RouteEntry_t *rp6, int seqno);
246static void		del_rtcmd_irep(rtcmd_irep_t *rcip);
247static void		flushroutes(int argc, char *argv[]);
248static boolean_t	getaddr(rtcmd_irep_t *rcip, int which, char *s,
249    addr_type_t atype);
250static boolean_t	in6_getaddr(char *s, struct sockaddr_in6 *sin6,
251    int *plenp, struct hostent **hpp);
252static boolean_t	in_getaddr(char *s, struct sockaddr_in *sin,
253    int *plenp, int which, struct hostent **hpp, addr_type_t atype,
254    rtcmd_irep_t *rcip);
255static int		in_getprefixlen(char *addr, int max_plen);
256static boolean_t	in_prefixlentomask(int prefixlen, int maxlen,
257    uchar_t *mask);
258static void		inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
259    struct sockaddr_in *sin);
260static in_addr_t	inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
261static int		keyword(const char *cp);
262static void		link_addr(const char *addr, struct sockaddr_dl *sdl);
263static char		*link_ntoa(const struct sockaddr_dl *sdl);
264static mib_item_t	*mibget(int sd);
265static char		*netname(struct sockaddr *sa);
266static int		newroute(char **argv);
267static rtcmd_irep_t	*new_rtcmd_irep(void);
268static void		pmsg_addrs(const char *cp, size_t len, uint_t addrs);
269static void		pmsg_common(const struct rt_msghdr *rtm, size_t len);
270static void		print_getmsg(rtcmd_irep_t *req_rt,
271    struct rt_msghdr *rtm, int msglen);
272static void		print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
273    boolean_t gw_good, boolean_t to_saved);
274static void		print_rtmsg(struct rt_msghdr *rtm, int msglen);
275static void		quit(char *s, int err) __NORETURN;
276static char		*routename(const struct sockaddr *sa);
277static void		rtmonitor(int argc, char *argv[]);
278static int		rtmsg(rtcmd_irep_t *rcip);
279static int		salen(const struct sockaddr *sa);
280static void		save_route(int argc, char **argv, int do_flush);
281static void		save_string(char **dst, char *src);
282static int		search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
283    search_mode_t mode);
284static void		set_metric(rtcmd_irep_t *rcip, char *value, int key,
285    boolean_t lock);
286static int		show_saved_routes(int argc);
287static void		sockaddr(char *addr, struct sockaddr *sa);
288static void		sodump(su_t *su, char *which);
289static void		syntax_arg_missing(char *keyword);
290static void		syntax_bad_keyword(char *keyword);
291static void		syntax_error(char *err, ...);
292static void		usage(char *cp);
293static void		write_to_rtfile(FILE *fp, int argc, char **argv);
294static void		pmsg_secattr(const char *, size_t, const char *);
295
296static pid_t		pid;
297static int		s;
298static boolean_t	nflag;
299static int		af = AF_INET;
300static boolean_t	qflag, tflag;
301static boolean_t	verbose;
302static boolean_t	debugonly;
303static boolean_t	fflag;
304static boolean_t	update_table;
305static boolean_t	perm_flag;
306static boolean_t	early_v6_keyword;
307static char		perm_file_sfx[] = "/etc/inet/static_routes";
308static char		*perm_file;
309static char		temp_file_sfx[] = "/etc/inet/static_routes.tmp";
310static char		*temp_file;
311static struct in6_addr	in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
312    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
313/*
314 * WARNING:
315 * This next variable indicates whether certain functions exit when an error
316 * is detected in the user input.  Currently, exit_on_error is only set false
317 * in search_rtfile(), when argument are being parsed.  Only those functions
318 * used by search_rtfile() to parse its arguments are designed to work in
319 * both modes.  Take particular care in setting this false to ensure that any
320 * functions you call that might act on this flag properly return errors when
321 * exit_on_error is false.
322 */
323static int		exit_on_error = B_TRUE;
324
325static struct {
326	struct	rt_msghdr m_rtm;
327	char	m_space[BUF_SIZE];
328} m_rtmsg;
329
330/*
331 * Sizes of data structures extracted from the base mib.
332 * This allows the size of the tables entries to grow while preserving
333 * binary compatibility.
334 */
335static int ipRouteEntrySize;
336static int ipv6RouteEntrySize;
337
338#define	ROUNDUP_LONG(a) \
339	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
340#define	ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
341#define	C(x)	((x) & 0xff)
342
343/*
344 * return values from in_getprefixlen()
345 */
346#define	BAD_ADDR	-1	/* prefix is invalid */
347#define	NO_PREFIX	-2	/* no prefix was found */
348
349void
350usage(char *cp)
351{
352	if (cp != NULL) {
353		(void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
354		    cp);
355	}
356	(void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
357	    "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
358	exit(1);
359	/* NOTREACHED */
360}
361
362/*PRINTFLIKE1*/
363void
364syntax_error(char *err, ...)
365{
366	va_list args;
367
368	if (exit_on_error) {
369		va_start(args, err);
370		(void) vfprintf(stderr, err, args);
371		va_end(args);
372		exit(1);
373	}
374	/* NOTREACHED */
375}
376
377void
378syntax_bad_keyword(char *keyword)
379{
380	syntax_error(gettext("route: botched keyword: %s\n"), keyword);
381}
382
383void
384syntax_arg_missing(char *keyword)
385{
386	syntax_error(gettext("route: argument required following keyword %s\n"),
387	    keyword);
388}
389
390void
391quit(char *s, int sverrno)
392{
393	(void) fprintf(stderr, "route: ");
394	if (s != NULL)
395		(void) fprintf(stderr, "%s: ", s);
396	(void) fprintf(stderr, "%s\n", strerror(sverrno));
397	exit(sverrno);
398	/* NOTREACHED */
399}
400
401int
402main(int argc, char **argv)
403{
404	extern int optind;
405	extern char *optarg;
406	int ch;
407	int rval;
408	size_t size;
409	const char *root_dir = NULL;
410
411	(void) setlocale(LC_ALL, "");
412
413#if !defined(TEXT_DOMAIN)
414#define	TEXT_DOMAIN "SYS_TEST"
415#endif
416	(void) textdomain(TEXT_DOMAIN);
417
418	if (argc < 2)
419		usage(NULL);
420
421	while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
422		switch (ch) {
423		case 'n':
424			nflag = B_TRUE;
425			break;
426		case 'q':
427			qflag = B_TRUE;
428			break;
429		case 'v':
430			verbose = B_TRUE;
431			break;
432		case 't':
433			tflag = B_TRUE;
434			break;
435		case 'd':
436			debugonly = B_TRUE;
437			break;
438		case 'f':
439			fflag = B_TRUE;
440			break;
441		case 'p':
442			perm_flag = B_TRUE;
443			break;
444		case 'R':
445			root_dir = optarg;
446			break;
447		case '?':
448		default:
449			usage(NULL);
450			/* NOTREACHED */
451		}
452	}
453	argc -= optind;
454	argv += optind;
455
456	pid = getpid();
457	if (tflag)
458		s = open("/dev/null", O_WRONLY);
459	else
460		s = socket(PF_ROUTE, SOCK_RAW, 0);
461	if (s < 0)
462		quit("socket", errno);
463
464	/*
465	 * Handle the -p and -R flags.  The -R flag only applies
466	 * when the -p flag is set.
467	 */
468	if (root_dir == NULL) {
469		perm_file = perm_file_sfx;
470		temp_file = temp_file_sfx;
471	} else {
472		size = strlen(root_dir) + sizeof (perm_file_sfx);
473		perm_file = malloc(size);
474		if (perm_file == NULL)
475			quit("malloc", errno);
476		(void) snprintf(perm_file, size, "%s%s", root_dir,
477		    perm_file_sfx);
478		size = strlen(root_dir) + sizeof (temp_file_sfx);
479		temp_file = malloc(size);
480		if (temp_file == NULL)
481			quit("malloc", errno);
482		(void) snprintf(temp_file, size, "%s%s", root_dir,
483		    temp_file_sfx);
484	}
485	/*
486	 * Whether or not to act on the routing table.  The only time the
487	 * routing table is not modified is when both -p and -R are present.
488	 */
489	update_table = (!perm_flag || root_dir == NULL);
490	if (tflag)
491		perm_flag = 0;
492
493	if (fflag) {
494		/*
495		 * Accept an address family keyword after the -f.  Since the
496		 * default address family is AF_INET, reassign af only for the
497		 * other valid address families.
498		 */
499		if (*argv != NULL) {
500			switch (keyword(*argv)) {
501			case K_INET6:
502				af = AF_INET6;
503				early_v6_keyword = B_TRUE;
504				/* fallthrough */
505			case K_INET:
506				/* Skip over the address family parameter. */
507				argc--;
508				argv++;
509				break;
510			}
511		}
512		flushroutes(0, NULL);
513	}
514
515	if (*argv != NULL) {
516		switch (keyword(*argv)) {
517		case K_GET:
518		case K_CHANGE:
519		case K_ADD:
520		case K_DELETE:
521			rval = 0;
522			if (update_table) {
523				rval = newroute(argv);
524			}
525			if (perm_flag && (rval == 0 || rval == EEXIST ||
526			    rval == ESRCH)) {
527				save_route(argc, argv, B_FALSE);
528				return (0);
529			}
530			return (rval);
531		case K_SHOW:
532			if (perm_flag) {
533				return (show_saved_routes(argc));
534			} else {
535				syntax_error(gettext(
536				    "route: show command requires -p\n"));
537			}
538			/* NOTREACHED */
539		case K_MONITOR:
540			rtmonitor(argc, argv);
541			/* NOTREACHED */
542
543		case K_FLUSH:
544			flushroutes(argc, argv);
545			return (0);
546		}
547	}
548	if (!fflag)
549		usage(*argv);
550	return (0);
551}
552
553/*
554 * Purge all entries in the routing tables not
555 * associated with network interfaces.
556 */
557void
558flushroutes(int argc, char *argv[])
559{
560	int seqno;
561	int sd;	/* mib stream */
562	mib_item_t	*item;
563	mib2_ipRouteEntry_t *rp;
564	mib2_ipv6RouteEntry_t *rp6;
565	int oerrno;
566	int off = 0;
567	int on = 1;
568
569	if (argc > 1) {
570		argv++;
571		if (argc == 2 && **argv == '-') {
572			/*
573			 * The address family (preceded by a dash) may be used
574			 * to flush the routes of that particular family.
575			 */
576			switch (keyword(*argv + 1)) {
577			case K_INET:
578				af = AF_INET;
579				break;
580			case K_LINK:
581				af = AF_LINK;
582				break;
583			case K_INET6:
584				af = AF_INET6;
585				break;
586			default:
587				usage(*argv);
588				/* NOTREACHED */
589			}
590		} else {
591			usage(*argv);
592		}
593	}
594	if (perm_flag) {
595		/* This flushes the persistent route file */
596		save_route(0, NULL, B_TRUE);
597	}
598	if (!update_table) {
599		return;
600	}
601
602	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
603	    sizeof (off)) < 0)
604		quit("setsockopt", errno);
605
606	sd = open("/dev/ip", O_RDWR);
607	oerrno = errno;
608	if (sd < 0) {
609		switch (errno) {
610		case EACCES:
611			(void) fprintf(stderr,
612			    gettext("route: flush: insufficient privileges\n"));
613			exit(oerrno);
614			/* NOTREACHED */
615		default:
616			quit(gettext("can't open mib stream"), oerrno);
617			/* NOTREACHED */
618		}
619	}
620	if ((item = mibget(sd)) == NULL)
621		quit("mibget", errno);
622	if (verbose) {
623		(void) printf("Examining routing table from "
624		    "T_SVR4_OPTMGMT_REQ\n");
625	}
626	seqno = 0;		/* ??? */
627	switch (af) {
628	case AF_INET:
629		/* Extract ipRouteEntrySize */
630		for (; item != NULL; item = item->next_item) {
631			if (item->mib_id != 0)
632				continue;
633			if (item->group == MIB2_IP) {
634				ipRouteEntrySize =
635				    ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
636				assert(IS_P2ALIGNED(ipRouteEntrySize,
637				    sizeof (mib2_ipRouteEntry_t *)));
638				break;
639			}
640		}
641		if (ipRouteEntrySize == 0) {
642			(void) fprintf(stderr,
643			    gettext("ipRouteEntrySize can't be determined.\n"));
644			exit(1);
645		}
646		for (; item != NULL; item = item->next_item) {
647			/*
648			 * skip all the other trash that comes up the mib stream
649			 */
650			if (item->group != MIB2_IP ||
651			    item->mib_id != MIB2_IP_ROUTE)
652				continue;
653			for (rp = (mib2_ipRouteEntry_t *)item->valp;
654			    (char *)rp < (char *)item->valp + item->length;
655			    /* LINTED */
656			    rp = (mib2_ipRouteEntry_t *)
657			    ((char *)rp + ipRouteEntrySize)) {
658				delRouteEntry(rp, NULL, seqno);
659				seqno++;
660			}
661			break;
662		}
663		break;
664	case AF_INET6:
665		/* Extract ipv6RouteEntrySize */
666		for (; item != NULL; item = item->next_item) {
667			if (item->mib_id != 0)
668				continue;
669			if (item->group == MIB2_IP6) {
670				ipv6RouteEntrySize =
671				    ((mib2_ipv6IfStatsEntry_t *)item->valp)->
672				    ipv6RouteEntrySize;
673				assert(IS_P2ALIGNED(ipv6RouteEntrySize,
674				    sizeof (mib2_ipv6RouteEntry_t *)));
675				break;
676			}
677		}
678		if (ipv6RouteEntrySize == 0) {
679			(void) fprintf(stderr, gettext(
680			    "ipv6RouteEntrySize cannot be determined.\n"));
681			exit(1);
682		}
683		for (; item != NULL; item = item->next_item) {
684			/*
685			 * skip all the other trash that comes up the mib stream
686			 */
687			if (item->group != MIB2_IP6 ||
688			    item->mib_id != MIB2_IP6_ROUTE)
689				continue;
690			for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
691			    (char *)rp6 < (char *)item->valp + item->length;
692			    /* LINTED */
693			    rp6 = (mib2_ipv6RouteEntry_t *)
694			    ((char *)rp6 + ipv6RouteEntrySize)) {
695				delRouteEntry(NULL, rp6, seqno);
696				seqno++;
697			}
698			break;
699		}
700		break;
701	}
702
703	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
704	    sizeof (on)) < 0)
705		quit("setsockopt", errno);
706}
707
708/*
709 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
710 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
711 * order to facilitate the flushing of RTF_GATEWAY routes.
712 */
713static void
714delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
715{
716	char *cp;
717	int ire_type;
718	int rlen;
719	struct rt_msghdr *rtm;
720	struct sockaddr_in sin;
721	struct sockaddr_in6 sin6;
722	int slen;
723
724	if (rp != NULL)
725		ire_type = rp->ipRouteInfo.re_ire_type;
726	else
727		ire_type = rp6->ipv6RouteInfo.re_ire_type;
728	if (ire_type != IRE_DEFAULT &&
729	    ire_type != IRE_PREFIX &&
730	    ire_type != IRE_HOST &&
731	    ire_type != IRE_HOST_REDIRECT)
732		return;
733
734	rtm = &m_rtmsg.m_rtm;
735	(void) memset(rtm, 0, sizeof (m_rtmsg));
736	rtm->rtm_type = RTM_DELETE;
737	rtm->rtm_seq = seqno;
738	rtm->rtm_flags |= RTF_GATEWAY;
739	rtm->rtm_version = RTM_VERSION;
740	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
741	cp = m_rtmsg.m_space;
742	if (rp != NULL) {
743		slen = sizeof (struct sockaddr_in);
744		if (rp->ipRouteMask == IP_HOST_MASK)
745			rtm->rtm_flags |= RTF_HOST;
746		(void) memset(&sin, 0, slen);
747		sin.sin_family = AF_INET;
748		sin.sin_addr.s_addr = rp->ipRouteDest;
749		(void) memmove(cp, &sin, slen);
750		cp += slen;
751		sin.sin_addr.s_addr = rp->ipRouteNextHop;
752		(void) memmove(cp, &sin, slen);
753		cp += slen;
754		sin.sin_addr.s_addr = rp->ipRouteMask;
755		(void) memmove(cp, &sin, slen);
756		cp += slen;
757	} else {
758		slen = sizeof (struct sockaddr_in6);
759		if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
760			rtm->rtm_flags |= RTF_HOST;
761		(void) memset(&sin6, 0, slen);
762		sin6.sin6_family = AF_INET6;
763		sin6.sin6_addr = rp6->ipv6RouteDest;
764		(void) memmove(cp, &sin6, slen);
765		cp += slen;
766		sin6.sin6_addr = rp6->ipv6RouteNextHop;
767		(void) memmove(cp, &sin6, slen);
768		cp += slen;
769		(void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
770		(void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
771		    (uchar_t *)&sin6.sin6_addr.s6_addr);
772		(void) memmove(cp, &sin6, slen);
773		cp += slen;
774	}
775	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
776	if (debugonly) {
777		/*
778		 * In debugonly mode, the routing socket message to delete the
779		 * current entry is not actually sent.  However if verbose is
780		 * also set, the routing socket message that would have been
781		 * is printed.
782		 */
783		if (verbose)
784			print_rtmsg(rtm, rtm->rtm_msglen);
785		return;
786	}
787
788	rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
789	if (rlen < (int)rtm->rtm_msglen) {
790		if (rlen < 0) {
791			(void) fprintf(stderr,
792			    gettext("route: write to routing socket: %s\n"),
793			    strerror(errno));
794		} else {
795			(void) fprintf(stderr, gettext("route: write to "
796			    "routing socket got only %d for rlen\n"), rlen);
797		}
798		return;
799	}
800	if (qflag) {
801		/*
802		 * In quiet mode, nothing is printed at all (unless the write()
803		 * itself failed.
804		 */
805		return;
806	}
807	if (verbose) {
808		print_rtmsg(rtm, rlen);
809	} else {
810		struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
811
812		(void) printf("%-20.20s ",
813		    rtm->rtm_flags & RTF_HOST ? routename(sa) :
814		    netname(sa));
815		/* LINTED */
816		sa = (struct sockaddr *)(salen(sa) + (char *)sa);
817		(void) printf("%-20.20s ", routename(sa));
818		(void) printf("done\n");
819	}
820}
821
822/*
823 * Return the name of the host whose address is given.
824 */
825char *
826routename(const struct sockaddr *sa)
827{
828	char *cp;
829	static char line[MAXHOSTNAMELEN + 1];
830	struct hostent *hp = NULL;
831	static char domain[MAXHOSTNAMELEN + 1];
832	static boolean_t first = B_TRUE;
833	struct in_addr in;
834	struct in6_addr in6;
835	int error_num;
836	ushort_t *s;
837	ushort_t *slim;
838
839	if (first) {
840		first = B_FALSE;
841		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
842		    (cp = strchr(domain, '.')))
843			(void) strcpy(domain, cp + 1);
844		else
845			domain[0] = 0;
846	}
847
848	if (salen(sa) == 0) {
849		(void) strcpy(line, "default");
850		return (line);
851	}
852	switch (sa->sa_family) {
853
854	case AF_INET:
855		/* LINTED */
856		in = ((struct sockaddr_in *)sa)->sin_addr;
857
858		cp = NULL;
859		if (in.s_addr == INADDR_ANY)
860			cp = "default";
861		if (cp == NULL && !nflag) {
862			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
863			    AF_INET);
864			if (hp != NULL) {
865				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
866				    (strcmp(cp + 1, domain) == 0))
867					*cp = 0;
868				cp = hp->h_name;
869			}
870		}
871		if (cp != NULL) {
872			(void) strncpy(line, cp, MAXHOSTNAMELEN);
873			line[MAXHOSTNAMELEN] = '\0';
874		} else {
875			in.s_addr = ntohl(in.s_addr);
876			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
877			    C(in.s_addr >> 16), C(in.s_addr >> 8),
878			    C(in.s_addr));
879		}
880		break;
881
882	case AF_LINK:
883		return (link_ntoa((struct sockaddr_dl *)sa));
884
885	case AF_INET6:
886		/* LINTED */
887		in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
888
889		cp = NULL;
890		if (IN6_IS_ADDR_UNSPECIFIED(&in6))
891			cp = "default";
892		if (cp == NULL && !nflag) {
893			hp = getipnodebyaddr((char *)&in6,
894			    sizeof (struct in6_addr), AF_INET6, &error_num);
895			if (hp != NULL) {
896				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
897				    (strcmp(cp + 1, domain) == 0))
898					*cp = 0;
899				cp = hp->h_name;
900			}
901		}
902		if (cp != NULL) {
903			(void) strncpy(line, cp, MAXHOSTNAMELEN);
904			line[MAXHOSTNAMELEN] = '\0';
905		} else {
906			(void) inet_ntop(AF_INET6, (void *)&in6, line,
907			    INET6_ADDRSTRLEN);
908		}
909		if (hp != NULL)
910			freehostent(hp);
911
912		break;
913
914	default:
915		s = (ushort_t *)sa;
916
917		slim = s + ((salen(sa) + 1) >> 1);
918		cp = line + sprintf(line, "(%d)", sa->sa_family);
919
920		while (++s < slim) /* start with sa->sa_data */
921			cp += sprintf(cp, " %x", *s);
922		break;
923	}
924	return (line);
925}
926
927/*
928 * Return the name of the network whose address is given.
929 * The address is assumed to be that of a net or subnet, not a host.
930 */
931static char *
932netname(struct sockaddr *sa)
933{
934	char *cp = NULL;
935	static char line[MAXHOSTNAMELEN + 1];
936	struct netent *np;
937	in_addr_t net, mask;
938	int subnetshift;
939	struct in_addr in;
940	ushort_t *s;
941	ushort_t *slim;
942
943	switch (sa->sa_family) {
944
945	case AF_INET:
946		/* LINTED */
947		in = ((struct sockaddr_in *)sa)->sin_addr;
948
949		in.s_addr = ntohl(in.s_addr);
950		if (in.s_addr == INADDR_ANY) {
951			cp = "default";
952		} else if (!nflag) {
953			if (IN_CLASSA(in.s_addr)) {
954				mask = IN_CLASSA_NET;
955				subnetshift = 8;
956			} else if (IN_CLASSB(in.s_addr)) {
957				mask = IN_CLASSB_NET;
958				subnetshift = 8;
959			} else {
960				mask = IN_CLASSC_NET;
961				subnetshift = 4;
962			}
963			/*
964			 * If there are more bits than the standard mask
965			 * would suggest, subnets must be in use.
966			 * Guess at the subnet mask, assuming reasonable
967			 * width subnet fields.
968			 */
969			while (in.s_addr &~ mask)
970				mask = (long)mask >> subnetshift;
971			net = in.s_addr & mask;
972			while ((mask & 1) == 0)
973				mask >>= 1, net >>= 1;
974			np = getnetbyaddr(net, AF_INET);
975			if (np != NULL)
976				cp = np->n_name;
977		}
978		if (cp != NULL) {
979			(void) strncpy(line, cp, MAXHOSTNAMELEN);
980			line[MAXHOSTNAMELEN] = '\0';
981		} else if ((in.s_addr & 0xffffff) == 0) {
982			(void) sprintf(line, "%u", C(in.s_addr >> 24));
983		} else if ((in.s_addr & 0xffff) == 0) {
984			(void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
985			    C(in.s_addr >> 16));
986		} else if ((in.s_addr & 0xff) == 0) {
987			(void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
988			    C(in.s_addr >> 16), C(in.s_addr >> 8));
989		} else {
990			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
991			    C(in.s_addr >> 16), C(in.s_addr >> 8),
992			    C(in.s_addr));
993		}
994		break;
995
996	case AF_LINK:
997		return (link_ntoa((struct sockaddr_dl *)sa));
998
999	case AF_INET6:
1000		return (routename(sa));
1001
1002	default:
1003		/* LINTED */
1004		s = (ushort_t *)sa->sa_data;
1005
1006		slim = s + ((salen(sa) + 1) >> 1);
1007		cp = line + sprintf(line, "af %d:", sa->sa_family);
1008
1009		while (s < slim)
1010			cp += sprintf(cp, " %x", *s++);
1011		break;
1012	}
1013	return (line);
1014}
1015
1016/*
1017 * Initialize a new structure.  Keep in mind that ri_dst_str, ri_gate_str and
1018 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1019 * or point to dynamically allocated memory.
1020 */
1021rtcmd_irep_t *
1022new_rtcmd_irep(void)
1023{
1024	rtcmd_irep_t *rcip;
1025
1026	rcip = calloc(1, sizeof (rtcmd_irep_t));
1027	if (rcip == NULL) {
1028		quit("calloc", errno);
1029	}
1030	rcip->ri_af = af;
1031	rcip->ri_flags = RTF_STATIC;
1032	return (rcip);
1033}
1034
1035void
1036del_rtcmd_irep(rtcmd_irep_t *rcip)
1037{
1038	free(rcip->ri_dest_str);
1039	free(rcip->ri_gate_str);
1040	free(rcip->ri_ifp_str);
1041	/*
1042	 * IPv6 host entries come from getipnodebyname, which dynamically
1043	 * allocates memory.  IPv4 host entries come from gethostbyname, which
1044	 * returns static memory and cannot be freed with freehostent.
1045	 */
1046	if (rcip->ri_gate_hp != NULL &&
1047	    rcip->ri_gate_hp->h_addrtype == AF_INET6)
1048		freehostent(rcip->ri_gate_hp);
1049	free(rcip);
1050}
1051
1052void
1053save_string(char **dst, char *src)
1054{
1055	free(*dst);
1056	*dst = strdup(src);
1057	if (*dst == NULL) {
1058		quit("malloc", errno);
1059	}
1060}
1061
1062/*
1063 * Print the short form summary of a route command.
1064 * Eg. "add net default: gateway 10.0.0.1"
1065 * The final newline is not added, allowing the caller to append additional
1066 * information.
1067 */
1068void
1069print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
1070    boolean_t to_saved)
1071{
1072	char *cmd;
1073	char obuf[INET6_ADDRSTRLEN];
1074
1075	switch (rcip->ri_cmd) {
1076	case RTM_ADD:
1077		cmd = "add";
1078		break;
1079	case RTM_CHANGE:
1080		cmd = "change";
1081		break;
1082	case RTM_DELETE:
1083		cmd = "delete";
1084		break;
1085	case RTM_GET:
1086		cmd = "get";
1087		break;
1088	default:
1089		assert(0);
1090	}
1091
1092	(void) fprintf(to, "%s%s %s %s", cmd,
1093	    (to_saved) ? " persistent" : "",
1094	    (rcip->ri_flags & RTF_HOST) ? "host" : "net",
1095	    (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str);
1096
1097	if (rcip->ri_gate_str != NULL) {
1098		switch (rcip->ri_af) {
1099		case AF_INET:
1100			if (nflag) {
1101				(void) fprintf(to, ": gateway %s",
1102				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
1103			} else if (gw_good &&
1104			    rcip->ri_gate_hp != NULL &&
1105			    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1106				/*
1107				 * Print the actual address used in the case
1108				 * where there was more than one address
1109				 * available for the name, and one was used
1110				 * successfully.
1111				 */
1112				(void) fprintf(to, ": gateway %s (%s)",
1113				    rcip->ri_gate_str,
1114				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
1115			} else {
1116				(void) fprintf(to, ": gateway %s",
1117				    rcip->ri_gate_str);
1118			}
1119			break;
1120		case AF_INET6:
1121			if (inet_ntop(AF_INET6,
1122			    &rcip->ri_gate.sin6.sin6_addr, obuf,
1123			    INET6_ADDRSTRLEN) != NULL) {
1124				if (nflag) {
1125					(void) fprintf(to, ": gateway %s",
1126					    obuf);
1127					break;
1128				}
1129				if (gw_good &&
1130				    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1131					(void) fprintf(to, ": gateway %s (%s)",
1132					    rcip->ri_gate_str, obuf);
1133					break;
1134				}
1135			}
1136			/* FALLTHROUGH */
1137		default:
1138			(void) fprintf(to, ": gateway %s",
1139			    rcip->ri_gate_str);
1140			break;
1141		}
1142	}
1143}
1144
1145void
1146set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
1147{
1148	int flag = 0;
1149	uint_t noval, *valp = &noval;
1150
1151	switch (key) {
1152#define	caseof(x, y, z)	\
1153	case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1154
1155	caseof(K_MTU, RTV_MTU, rmx_mtu);
1156	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
1157	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
1158	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
1159	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
1160	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
1161	caseof(K_RTT, RTV_RTT, rmx_rtt);
1162	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
1163#undef	caseof
1164	}
1165	rcip->ri_inits |= flag;
1166	if (lock)
1167		rcip->ri_metrics.rmx_locks |= flag;
1168	*valp = atoi(value);
1169}
1170
1171/*
1172 * Parse the options give in argv[], filling in rcip with the results.
1173 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1174 * tokenized to produce the command line.  Cmd_string is tokenized using
1175 * strtok, which will overwrite whitespace in the string with nulls.
1176 *
1177 * Returns B_TRUE on success and B_FALSE on failure.
1178 */
1179boolean_t
1180args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string)
1181{
1182	const char *ws = "\f\n\r\t\v ";
1183	char *tok = cmd_string;
1184	char *keyword_str;
1185	addr_type_t atype = ADDR_TYPE_ANY;
1186	boolean_t iflag = B_FALSE;
1187	boolean_t locknext = B_FALSE;
1188	boolean_t lockrest = B_FALSE;
1189	boolean_t dash_keyword;
1190	int key;
1191	char *err;
1192
1193	if (cmd_string == NULL) {
1194		tok = argv[0];
1195	} else {
1196		tok = strtok(cmd_string, ws);
1197	}
1198
1199	/*
1200	 * The command keywords are already fully checked by main() or
1201	 * search_rtfile().
1202	 */
1203	switch (*tok) {
1204	case 'a':
1205		rcip->ri_cmd = RTM_ADD;
1206		break;
1207	case 'c':
1208		rcip->ri_cmd = RTM_CHANGE;
1209		break;
1210	case 'd':
1211		rcip->ri_cmd = RTM_DELETE;
1212		break;
1213	case 'g':
1214		rcip->ri_cmd = RTM_GET;
1215		break;
1216	default:
1217		/* NOTREACHED */
1218		quit(gettext("Internal Error"), EINVAL);
1219		/* NOTREACHED */
1220	}
1221
1222#define	NEXTTOKEN \
1223	((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1224
1225	while (NEXTTOKEN) {
1226		keyword_str = tok;
1227		if (*tok == '-') {
1228			dash_keyword = B_TRUE;
1229			key = keyword(tok + 1);
1230		} else {
1231			dash_keyword = B_FALSE;
1232			key = keyword(tok);
1233			if (key != K_HOST && key != K_NET) {
1234				/* All others must be preceded by '-' */
1235				key = 0;
1236			}
1237		}
1238		switch (key) {
1239		case K_HOST:
1240			if (atype == ADDR_TYPE_NET) {
1241				syntax_error(gettext("route: -host and -net "
1242				    "are mutually exclusive\n"));
1243				return (B_FALSE);
1244			}
1245			atype = ADDR_TYPE_HOST;
1246			break;
1247		case K_NET:
1248			if (atype == ADDR_TYPE_HOST) {
1249				syntax_error(gettext("route: -host and -net "
1250				    "are mutually exclusive\n"));
1251				return (B_FALSE);
1252			}
1253			atype = ADDR_TYPE_NET;
1254			break;
1255		case K_LINK:
1256			rcip->ri_af = AF_LINK;
1257			break;
1258		case K_INET:
1259			rcip->ri_af = AF_INET;
1260			break;
1261		case K_SA:
1262			rcip->ri_af = PF_ROUTE;
1263			break;
1264		case K_INET6:
1265			rcip->ri_af = AF_INET6;
1266			break;
1267		case K_IFACE:
1268		case K_INTERFACE:
1269			iflag = B_TRUE;
1270			/* fallthrough */
1271		case K_NOSTATIC:
1272			rcip->ri_flags &= ~RTF_STATIC;
1273			break;
1274		case K_LOCK:
1275			locknext = B_TRUE;
1276			break;
1277		case K_LOCKREST:
1278			lockrest = B_TRUE;
1279			break;
1280		case K_REJECT:
1281			rcip->ri_flags |= RTF_REJECT;
1282			break;
1283		case K_BLACKHOLE:
1284			rcip->ri_flags |= RTF_BLACKHOLE;
1285			break;
1286		case K_PROTO1:
1287			rcip->ri_flags |= RTF_PROTO1;
1288			break;
1289		case K_PROTO2:
1290			rcip->ri_flags |= RTF_PROTO2;
1291			break;
1292		case K_CLONING:
1293			rcip->ri_flags |= RTF_CLONING;
1294			break;
1295		case K_XRESOLVE:
1296			rcip->ri_flags |= RTF_XRESOLVE;
1297			break;
1298		case K_STATIC:
1299			rcip->ri_flags |= RTF_STATIC;
1300			break;
1301		case K_IFA:
1302			if (!NEXTTOKEN) {
1303				syntax_arg_missing(keyword_str);
1304				return (B_FALSE);
1305			}
1306			if (!getaddr(rcip, RTA_IFA, tok, atype)) {
1307				return (B_FALSE);
1308			}
1309			break;
1310		case K_IFP:
1311			if (!NEXTTOKEN) {
1312				syntax_arg_missing(keyword_str);
1313				return (B_FALSE);
1314			}
1315			if (!getaddr(rcip, RTA_IFP, tok, atype)) {
1316				return (B_FALSE);
1317			}
1318			break;
1319		case K_GATEWAY:
1320			if (!NEXTTOKEN) {
1321				syntax_arg_missing(keyword_str);
1322				return (B_FALSE);
1323			}
1324			if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1325				return (B_FALSE);
1326			}
1327			break;
1328		case K_DST:
1329			if (!NEXTTOKEN) {
1330				syntax_arg_missing(keyword_str);
1331				return (B_FALSE);
1332			}
1333			if (!getaddr(rcip, RTA_DST, tok, atype)) {
1334				return (B_FALSE);
1335			}
1336			break;
1337		case K_NETMASK:
1338			if (!NEXTTOKEN) {
1339				syntax_arg_missing(keyword_str);
1340				return (B_FALSE);
1341			}
1342			if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1343				return (B_FALSE);
1344			}
1345			atype = ADDR_TYPE_NET;
1346			break;
1347		case K_MTU:
1348		case K_HOPCOUNT:
1349		case K_EXPIRE:
1350		case K_RECVPIPE:
1351		case K_SENDPIPE:
1352		case K_SSTHRESH:
1353		case K_RTT:
1354		case K_RTTVAR:
1355			if (!NEXTTOKEN) {
1356				syntax_arg_missing(keyword_str);
1357				return (B_FALSE);
1358			}
1359			set_metric(rcip, tok, key, locknext || lockrest);
1360			locknext = B_FALSE;
1361			break;
1362		case K_PRIVATE:
1363			rcip->ri_flags |= RTF_PRIVATE;
1364			break;
1365		case K_MULTIRT:
1366			rcip->ri_flags |= RTF_MULTIRT;
1367			break;
1368		case K_SETSRC:
1369			if (!NEXTTOKEN) {
1370				syntax_arg_missing(keyword_str);
1371				return (B_FALSE);
1372			}
1373			if (!getaddr(rcip, RTA_SRC, tok, atype)) {
1374				return (B_FALSE);
1375			}
1376			rcip->ri_flags |= RTF_SETSRC;
1377			break;
1378		case K_SECATTR:
1379			if (!NEXTTOKEN) {
1380				syntax_arg_missing(keyword_str);
1381				return (B_FALSE);
1382			}
1383			if (is_system_labeled()) {
1384				int err;
1385
1386				if (rcip->ri_rtsa_cnt >= 1) {
1387					syntax_error(gettext("route: can't "
1388					    "specify more than one security "
1389					    "attribute\n"));
1390					return (B_FALSE);
1391				}
1392				if (!rtsa_keyword(tok, &rcip->ri_rtsa, &err,
1393				    NULL)) {
1394					syntax_error(gettext("route: "
1395					    "bad security attribute: %s\n"),
1396					    tsol_strerror(err, errno));
1397					return (B_FALSE);
1398				}
1399				rcip->ri_rtsa_cnt++;
1400			} else {
1401				syntax_error(gettext("route: "
1402				    "system is not labeled; cannot specify "
1403				    "security attributes.\n"));
1404				return (B_FALSE);
1405			}
1406			break;
1407		case K_INDIRECT:
1408			rcip->ri_flags |= RTF_INDIRECT;
1409			break;
1410		default:
1411			if (dash_keyword) {
1412				syntax_bad_keyword(tok + 1);
1413				return (B_FALSE);
1414			}
1415			if ((rcip->ri_addrs & RTA_DST) == 0) {
1416				if (!getaddr(rcip, RTA_DST, tok, atype)) {
1417					return (B_FALSE);
1418				}
1419			} else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
1420				/*
1421				 * For the gateway parameter, retrieve the
1422				 * pointer to the struct hostent so that all
1423				 * possible addresses can be tried until one
1424				 * is successful.
1425				 */
1426				if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1427					return (B_FALSE);
1428				}
1429			} else {
1430				ulong_t metric;
1431				/*
1432				 * Assume that a regular number is a metric.
1433				 * Needed for compatibility with old route
1434				 * command syntax.
1435				 */
1436				errno = 0;
1437				metric = strtoul(tok, &err, 10);
1438				if (errno == 0 && *err == '\0' &&
1439				    metric < 0x80000000ul) {
1440					iflag = (metric == 0);
1441					if (verbose) {
1442						(void) printf("old usage of "
1443						    "trailing number, assuming "
1444						    "route %s\n", iflag ?
1445						    "to if" : "via gateway");
1446					}
1447					continue;
1448				}
1449				if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1450					return (B_FALSE);
1451				}
1452			}
1453		}
1454	}
1455#undef NEXTTOKEN
1456
1457	if ((rcip->ri_addrs & RTA_DST) == 0) {
1458		syntax_error(gettext("route: destination required\n"));
1459		return (B_FALSE);
1460	} else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
1461	    (rcip->ri_addrs & RTA_GATEWAY) == 0) {
1462		syntax_error(gettext(
1463		    "route: gateway required for add or delete command\n"));
1464		return (B_FALSE);
1465	}
1466
1467	if (!iflag) {
1468		rcip->ri_flags |= RTF_GATEWAY;
1469	}
1470
1471	if (atype != ADDR_TYPE_NET) {
1472		if (rcip->ri_addrs & RTA_NETMASK) {
1473			/*
1474			 * We know the netmask, so we can set the host flag
1475			 * based on whether the netmask is the host netmask.
1476			 */
1477			if (rcip->ri_af == AF_INET &&
1478			    rcip->ri_mask.sin.sin_addr.s_addr ==
1479			    IP_HOST_MASK) {
1480				rcip->ri_flags |= RTF_HOST;
1481			}
1482			if (rcip->ri_af == AF_INET6 &&
1483			    memcmp(&rcip->ri_mask.sin6.sin6_addr,
1484			    &in6_host_mask,
1485			    sizeof (struct in6_addr)) == 0) {
1486				rcip->ri_flags |= RTF_HOST;
1487			}
1488		} else {
1489			/*
1490			 * If no prefix mask has been saved at this point, it
1491			 * only makes sense to treat the destination address
1492			 * as a host address.
1493			 */
1494			rcip->ri_flags |= RTF_HOST;
1495		}
1496	}
1497	return (B_TRUE);
1498}
1499
1500/*
1501 * This command always seeks to the end of the file prior to writing.
1502 */
1503void
1504write_to_rtfile(FILE *fp, int argc, char **argv)
1505{
1506	char file_line[BUF_SIZE];
1507	int len;
1508	int i;
1509
1510	len = 0;
1511	if (early_v6_keyword) {
1512		/*
1513		 * This flag is set when "inet6" was seen as an
1514		 * argument to the -f flag.  Normally, when writing
1515		 * routes to the persistent route file, everything on
1516		 * the command line after "add" is saved verbatim.
1517		 * In this case, the arguments after "add" may not be
1518		 * sufficient, as the ipv6 keyword came before "add",
1519		 * yet must be present in the persistent route file.
1520		 */
1521		len += snprintf(file_line, BUF_SIZE, "-inet6 ");
1522	}
1523	for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
1524		len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
1525		    argv[i]);
1526	}
1527	if (len >= BUF_SIZE)
1528		quit(gettext("Internal Error"), EINVAL);
1529	file_line[len - 1] = '\n';
1530	if (fseek(fp, 0, SEEK_END) != 0 ||
1531	    fputs(file_line, fp) == EOF) {
1532		quit(gettext("failed to write to route file"),
1533		    errno);
1534	}
1535}
1536
1537boolean_t
1538compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
1539{
1540	if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
1541	    memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
1542		return (B_FALSE);
1543	}
1544	return (srch_rt->ri_gate_str == NULL ||
1545	    strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
1546}
1547
1548/*
1549 * Search the route file for routes matching the supplied route.  There are 3
1550 * modes of operation:
1551 *    SEARCH_MODE_RET - no side effects.
1552 *    SEARCH_MODE_PRINT - prints each matching line.
1553 *    SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1554 *
1555 * In all cases, the number of matches is returned.  If rt is NULL, all routes
1556 * matching the global af value are considered matching.
1557 */
1558int
1559search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
1560{
1561	char *tmp_buf;
1562	int match_cnt;
1563	boolean_t match;
1564	char file_line[BUF_SIZE + 4] = "add ";
1565	rtcmd_irep_t *thisrt;
1566
1567	match_cnt = 0;
1568
1569	/*
1570	 * Leave space at the beginning of file_line for "add ".
1571	 */
1572	while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
1573
1574		if (file_line[4] == '#' || file_line[4] == '\n') {
1575			/* Handle comments and blank lines */
1576			if (mode == SEARCH_MODE_DEL &&
1577			    fputs(file_line + 4, temp_fp) == EOF) {
1578				quit(gettext(
1579				    "route: failed to write to temp file"),
1580				    errno);
1581			}
1582			continue;
1583		}
1584		thisrt = new_rtcmd_irep();
1585		/*
1586		 * thisrt->ri_af defaults to whatever address family happens
1587		 * to be set in the global af, but routes in the persistent
1588		 * route file must be treated as AF_INET by default.
1589		 */
1590		thisrt->ri_af = AF_INET;
1591
1592		exit_on_error = B_FALSE;
1593		tmp_buf = strdup(file_line);
1594		/* args_to_rtcmd() will mangle the string passed. */
1595		if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
1596			/* There was an error in args_to_rtcmd() or helpers */
1597			del_rtcmd_irep(thisrt);
1598			free(tmp_buf);
1599			continue;
1600		}
1601		exit_on_error = B_TRUE;
1602		free(tmp_buf);
1603
1604		if (thisrt->ri_gate_str == NULL) {
1605			del_rtcmd_irep(thisrt);
1606			continue;
1607		}
1608		match = (rt == NULL) ? (thisrt->ri_af == af) :
1609		    compare_rtcmd(rt, thisrt);
1610
1611		if (match) match_cnt++;
1612		if (match && mode == SEARCH_MODE_PRINT) {
1613			(void) printf("persistent: route %s", file_line);
1614		}
1615		if (match && mode == SEARCH_MODE_DEL) {
1616			thisrt->ri_cmd = RTM_DELETE;
1617			print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
1618			(void) printf("\n");
1619		}
1620		del_rtcmd_irep(thisrt);
1621
1622		if (!match && mode == SEARCH_MODE_DEL &&
1623		    fputs(file_line + 4, temp_fp) == EOF) {
1624			quit(gettext("failed to write to temp file"),
1625			    errno);
1626		}
1627	}
1628	return (match_cnt);
1629}
1630
1631/*
1632 * Perform the route operation given in argv on the persistent route file.
1633 * If do_flush is set, the persistent route file is flushed of all routes
1634 * matching the global family, and the arguments are ignored.
1635 */
1636void
1637save_route(int argc, char **argv, int do_flush)
1638{
1639	rtcmd_irep_t *rt;
1640	int perm_fd;
1641	FILE *perm_fp;
1642	FILE *temp_fp;
1643	mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1644	struct flock lock;
1645	struct stat st;
1646	const char commentstr[] =
1647	    "# File generated by route(1M) - do not edit.\n";
1648
1649	perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
1650	if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
1651		quit("failed to open route file", errno);
1652
1653	lock.l_type = F_WRLCK;
1654	lock.l_whence = SEEK_SET;
1655	lock.l_start = 0;
1656	lock.l_len = 0;
1657	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1658		quit(gettext("failed to lock route file"), errno);
1659		/* NOTREACHED */
1660	}
1661	if (st.st_size == 0 &&
1662	    write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
1663	    sizeof (commentstr) - 1)
1664		quit(gettext("failed to open route file"), errno);
1665
1666	if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
1667		quit(gettext("failed to open route file"), errno);
1668		/* NOTREACHED */
1669	}
1670
1671	if (!do_flush) {
1672		rt = new_rtcmd_irep();
1673		(void) args_to_rtcmd(rt, argv, NULL);
1674	}
1675	if (do_flush || rt->ri_cmd == RTM_DELETE) {
1676		if ((temp_fp = fopen(temp_file, "w")) == NULL) {
1677			quit(gettext("failed to open temp file"), errno);
1678			/* NOTREACHED */
1679		}
1680	}
1681	if (do_flush) {
1682		(void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
1683		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1684			quit(gettext("failed to update route file"), errno);
1685			/* NOTREACHED */
1686		}
1687		(void) fclose(perm_fp);
1688		return;
1689	}
1690
1691	switch (rt->ri_cmd) {
1692	case RTM_ADD:
1693		if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
1694			/* Route is already in the file */
1695			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1696			(void) fprintf(stderr, ": entry exists\n");
1697			exit(1);
1698		}
1699		write_to_rtfile(perm_fp, argc - 1, argv + 1);
1700		print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1701		(void) printf("\n");
1702		break;
1703
1704	case RTM_CHANGE:
1705		syntax_error(
1706		    gettext("route: change command not supported with -p\n"));
1707		/* NOTREACHED */
1708
1709	case RTM_DELETE:
1710		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
1711			/* Route not found */
1712			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1713			(void) fprintf(stderr, gettext(": not in file\n"));
1714			exit(1);
1715		}
1716		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1717			quit(gettext("failed to update route file"), errno);
1718			/* NOTREACHED */
1719		}
1720		break;
1721
1722	case RTM_GET:
1723		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1724		    0) {
1725			print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1726			(void) printf(gettext(": not in file\n"));
1727		}
1728		break;
1729
1730	default:
1731		quit(gettext("Internal Error"), EINVAL);
1732		/* NOTREACHED */
1733	}
1734
1735	/*
1736	 * Closing the file unlocks it.
1737	 */
1738	(void) fclose(perm_fp);
1739}
1740
1741int
1742show_saved_routes(int argc)
1743{
1744	int perm_fd;
1745	FILE *perm_fp;
1746	struct flock lock;
1747	int count = 0;
1748
1749	if (argc != 1) {
1750		syntax_error(gettext("route: invalid arguments for show\n"));
1751	}
1752
1753	perm_fd = open(perm_file, O_RDONLY, 0);
1754
1755	if (perm_fd == -1) {
1756		if (errno == ENOENT) {
1757			(void) printf("No persistent routes are defined\n");
1758			return (0);
1759		} else {
1760			quit(gettext("failed to open route file"), errno);
1761		}
1762	}
1763	lock.l_type = F_RDLCK;
1764	lock.l_whence = SEEK_SET;
1765	lock.l_start = 0;
1766	lock.l_len = 0;
1767	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1768		quit(gettext("failed to lock route file"),
1769		    errno);
1770		/* NOTREACHED */
1771	}
1772	if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1773		quit(gettext("failed to open route file"), errno);
1774		/* NOTREACHED */
1775	}
1776	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1777	(void) fseek(perm_fp, 0, SEEK_SET);
1778	af = AF_INET6;
1779	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1780
1781	if (count == 0)
1782		(void) printf("No persistent routes are defined\n");
1783
1784	(void) fclose(perm_fp);
1785	return (0);
1786}
1787
1788int
1789newroute(char **argv)
1790{
1791	rtcmd_irep_t *newrt;
1792	int ret, attempts, oerrno;
1793	char *err;
1794	char obuf[INET6_ADDRSTRLEN];
1795#define	hp (newrt->ri_gate_hp)
1796
1797	newrt = new_rtcmd_irep();
1798	(void) args_to_rtcmd(newrt, argv, NULL);
1799
1800	if (newrt->ri_cmd != RTM_GET && !tflag) {
1801		/* Don't want to read back our messages */
1802		(void) shutdown(s, 0);
1803	}
1804	if (newrt->ri_addrs & RTA_IFP) {
1805		newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
1806		if (newrt->ri_ifp.sdl.sdl_index == 0) {
1807			if (errno != ENXIO) {
1808				quit("if_nametoindex", errno);
1809			} else {
1810				(void) fprintf(stderr,
1811				    gettext("route: %s: no such interface\n"),
1812				    newrt->ri_ifp_str);
1813				exit(1);
1814			}
1815		}
1816		newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1817	}
1818	for (attempts = 1; ; attempts++) {
1819		errno = 0;
1820		if ((ret = rtmsg(newrt)) == 0)
1821			break;
1822		if (errno != ENETUNREACH && errno != ESRCH)
1823			break;
1824		if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
1825		    hp->h_addr_list[attempts] != NULL) {
1826			switch (af) {
1827			case AF_INET:
1828				(void) memmove(&newrt->ri_gate.sin.sin_addr,
1829				    hp->h_addr_list[attempts], hp->h_length);
1830				continue;
1831			case AF_INET6:
1832				(void) memmove(&newrt->ri_gate.sin6.sin6_addr,
1833				    hp->h_addr_list[attempts], hp->h_length);
1834				continue;
1835			}
1836		}
1837		break;
1838	}
1839	oerrno = errno;
1840
1841	if (newrt->ri_cmd != RTM_GET) {
1842		print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
1843		if (ret == 0)
1844			(void) printf("\n");
1845	} else if (ret != 0) {
1846		/*
1847		 * Note: there is nothing additional to print for get
1848		 * if ret == 0.
1849		 */
1850		if (nflag) {
1851			switch (newrt->ri_af) {
1852			case AF_INET:
1853				(void) printf(" %s",
1854				    inet_ntoa(newrt->ri_dst.sin.sin_addr));
1855				break;
1856			case AF_INET6:
1857				if (inet_ntop(AF_INET6,
1858				    (void *)&newrt->ri_dst.sin6.sin6_addr,
1859				    obuf, INET6_ADDRSTRLEN) != NULL) {
1860					(void) printf(" %s", obuf);
1861					break;
1862				}
1863				/* FALLTHROUGH */
1864			default:
1865				(void) printf("%s", newrt->ri_dest_str);
1866				break;
1867			}
1868		} else {
1869			(void) printf("%s", newrt->ri_dest_str);
1870		}
1871	}
1872
1873	if (ret != 0) {
1874		switch (oerrno) {
1875		case ESRCH:
1876			err = "not in table";
1877			break;
1878		case EBUSY:
1879			err = "entry in use";
1880			break;
1881		case ENOBUFS:
1882			err = "routing table overflow";
1883			break;
1884		case EEXIST:
1885			err = "entry exists";
1886			break;
1887		case EPERM:
1888			err = "insufficient privileges";
1889			break;
1890		default:
1891			err = strerror(oerrno);
1892			break;
1893		}
1894		(void) printf(": %s\n", err);
1895	}
1896
1897	del_rtcmd_irep(newrt);
1898
1899	return (oerrno);
1900#undef hp
1901}
1902
1903
1904/*
1905 * Convert a network number to the corresponding IP address.
1906 * If the RTA_NETMASK hasn't been specified yet set it based
1907 * on the class of address.
1908 */
1909static void
1910inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
1911{
1912	in_addr_t addr, mask;
1913
1914	if (net == 0) {
1915		mask = addr = 0;
1916	} else if (net < 128) {
1917		addr = net << IN_CLASSA_NSHIFT;
1918		mask = IN_CLASSA_NET;
1919	} else if (net < 65536) {
1920		addr = net << IN_CLASSB_NSHIFT;
1921		mask = IN_CLASSB_NET;
1922	} else if (net < 16777216L) {
1923		addr = net << IN_CLASSC_NSHIFT;
1924		mask = IN_CLASSC_NET;
1925	} else {
1926		addr = net;
1927		if ((addr & IN_CLASSA_HOST) == 0)
1928			mask =  IN_CLASSA_NET;
1929		else if ((addr & IN_CLASSB_HOST) == 0)
1930			mask =  IN_CLASSB_NET;
1931		else if ((addr & IN_CLASSC_HOST) == 0)
1932			mask =  IN_CLASSC_NET;
1933		else {
1934			if (IN_CLASSA(addr))
1935				mask =  IN_CLASSA_NET;
1936			else if (IN_CLASSB(addr))
1937				mask =  IN_CLASSB_NET;
1938			else if (IN_CLASSC(addr))
1939				mask =  IN_CLASSC_NET;
1940			else
1941				mask = IP_HOST_MASK;
1942			mask = inet_makesubnetmask(addr, mask);
1943		}
1944	}
1945	sin->sin_addr.s_addr = htonl(addr);
1946
1947	/* Class E default mask is 32 */
1948	if (IN_CLASSE(addr))
1949		mask = IN_CLASSE_NET;
1950
1951	if (!(rcip->ri_addrs & RTA_NETMASK)) {
1952		rcip->ri_addrs |= RTA_NETMASK;
1953		sin = &rcip->ri_mask.sin;
1954		sin->sin_addr.s_addr = htonl(mask);
1955		sin->sin_family = AF_INET;
1956	}
1957}
1958
1959static in_addr_t
1960inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
1961{
1962	int n;
1963	struct ifconf ifc;
1964	struct ifreq ifreq;
1965	struct ifreq *ifr;
1966	struct sockaddr_in *sin;
1967	char *buf;
1968	int numifs;
1969	size_t bufsize;
1970	int iosoc;
1971	in_addr_t if_addr, if_mask;
1972	in_addr_t if_subnetmask = 0;
1973	short if_flags;
1974
1975	if (mask == 0)
1976		return (0);
1977	if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1978		quit("socket", errno);
1979	if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
1980		quit("ioctl", errno);
1981	bufsize = numifs * sizeof (struct ifreq);
1982	buf = malloc(bufsize);
1983	if (buf == NULL)
1984		quit("malloc", errno);
1985	(void) memset(&ifc, 0, sizeof (ifc));
1986	ifc.ifc_len = bufsize;
1987	ifc.ifc_buf = buf;
1988	if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
1989		quit("ioctl (get interface configuration)", errno);
1990	/* Let's check to see if this is maybe a local subnet route. */
1991	ifr = ifc.ifc_req;
1992	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
1993		ifreq = *ifr;
1994		/* LINTED */
1995		sin = (struct sockaddr_in *)&ifr->ifr_addr;
1996		if_addr = ntohl(sin->sin_addr.s_addr);
1997
1998		if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
1999			quit("ioctl (get interface flags)", errno);
2000		if ((ifreq.ifr_flags & IFF_UP) == 0)
2001			continue;
2002		if_flags = ifreq.ifr_flags;
2003
2004		if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
2005			quit("ioctl (get netmask)", errno);
2006		/* LINTED */
2007		sin = (struct sockaddr_in *)&ifreq.ifr_addr;
2008		if_mask = ntohl(sin->sin_addr.s_addr);
2009		if ((if_addr & mask) == (addr & mask)) {
2010			/*
2011			 * Don't trust pt-pt interfaces if there are
2012			 * other interfaces.
2013			 */
2014			if (if_flags & IFF_POINTOPOINT) {
2015				if_subnetmask = if_mask;
2016				continue;
2017			}
2018			/*
2019			 * Fine.  Just assume the same net mask as the
2020			 * directly attached subnet interface is using.
2021			 */
2022			return (if_mask);
2023		}
2024	}
2025	if (if_subnetmask != 0)
2026		return (if_subnetmask);
2027	return (mask);
2028}
2029
2030/*
2031 * Interpret an argument as a network address of some kind.
2032 *
2033 * If the address family is one looked up in getaddr() using one of the
2034 * getipnodebyX() functions (currently only AF_INET6), then callers should
2035 * freehostent() the returned "struct hostent" pointer if one was passed in.
2036 *
2037 * If exit_on_error is true, this function will cause route to exit on error by
2038 * calling syntax_error().  Otherwise, it returns B_TRUE on success or B_FALSE
2039 * on failure.
2040 */
2041static boolean_t
2042getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
2043{
2044	su_t *su;
2045	struct hostent **hpp;
2046	struct hostent *hp;
2047	int masklen;
2048
2049	if (which == RTA_GATEWAY) {
2050		hpp = &(rcip->ri_gate_hp);
2051	} else {
2052		hpp = &hp;
2053	}
2054	*hpp = NULL;
2055
2056	rcip->ri_addrs |= which;
2057	switch (which) {
2058	case RTA_DST:
2059		save_string(&rcip->ri_dest_str, s);
2060		su = &rcip->ri_dst;
2061		su->sa.sa_family = rcip->ri_af;
2062		break;
2063	case RTA_GATEWAY:
2064		save_string(&rcip->ri_gate_str, s);
2065		su = &rcip->ri_gate;
2066		su->sa.sa_family = rcip->ri_af;
2067		break;
2068	case RTA_NETMASK:
2069		su = &rcip->ri_mask;
2070		su->sa.sa_family = rcip->ri_af;
2071		break;
2072	case RTA_IFP:
2073		save_string(&rcip->ri_ifp_str, s);
2074		return (B_TRUE);
2075		/*
2076		 * RTA_SRC has overloaded meaning. It can represent the
2077		 * src address of incoming or outgoing packets.
2078		 */
2079	case RTA_IFA:
2080		su = &rcip->ri_ifa;
2081		su->sa.sa_family = rcip->ri_af;
2082		break;
2083	case RTA_SRC:
2084		su = &rcip->ri_src;
2085		su->sa.sa_family = rcip->ri_af;
2086		break;
2087	default:
2088		/* NOTREACHED */
2089		quit(gettext("Internal Error"), EINVAL);
2090		/* NOTREACHED */
2091	}
2092	if (strcmp(s, "default") == 0) {
2093		if (which == RTA_DST) {
2094			return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
2095		}
2096		if (which == RTA_SRC) {
2097			return (B_TRUE);
2098		}
2099		return (B_TRUE);
2100	}
2101	switch (rcip->ri_af) {
2102	case AF_LINK:
2103		link_addr(s, &su->sdl);
2104		return (B_TRUE);
2105	case PF_ROUTE:
2106		sockaddr(s, &su->sa);
2107		return (B_TRUE);
2108	case AF_INET6:
2109		switch (which) {
2110		case RTA_DST:
2111			if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
2112				return (B_FALSE);
2113			}
2114			if (masklen != NO_PREFIX) {
2115				(void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
2116				    sizeof (rcip->ri_mask.sin6.sin6_addr));
2117				if (!in_prefixlentomask(masklen, IPV6_ABITS,
2118				    (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
2119					syntax_error(gettext(
2120					    "route: bad prefix length: %d\n"),
2121					    masklen);
2122					return (B_FALSE);
2123				}
2124				rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2125				rcip->ri_addrs |= RTA_NETMASK;
2126			}
2127			return (B_TRUE);
2128		case RTA_GATEWAY:
2129		case RTA_IFA:
2130		case RTA_SRC:
2131			return (in6_getaddr(s, &su->sin6, NULL, hpp));
2132		case RTA_NETMASK:
2133			syntax_error(
2134			    gettext("route: -netmask not supported for IPv6: "
2135			    "use <prefix>/<prefix-length> instead\n"));
2136			return (B_FALSE);
2137		default:
2138			quit(gettext("Internal Error"), EINVAL);
2139			/* NOTREACHED */
2140		}
2141	case AF_INET:
2142		switch (which) {
2143		case RTA_DST:
2144			if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
2145			    atype, rcip)) {
2146				return (B_FALSE);
2147			}
2148			if (masklen != NO_PREFIX) {
2149				(void) memset(&rcip->ri_mask.sin.sin_addr, 0,
2150				    sizeof (rcip->ri_mask.sin.sin_addr));
2151				if (!in_prefixlentomask(masklen, IP_ABITS,
2152				    (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
2153					syntax_error(gettext(
2154					    "route: bad prefix length: %d\n"),
2155					    masklen);
2156					return (B_FALSE);
2157				}
2158				rcip->ri_mask.sin.sin_family = rcip->ri_af;
2159				rcip->ri_addrs |= RTA_NETMASK;
2160			}
2161			return (B_TRUE);
2162		case RTA_GATEWAY:
2163		case RTA_IFA:
2164		case RTA_NETMASK:
2165		case RTA_SRC:
2166			return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2167			    rcip));
2168		default:
2169			quit(gettext("Internal Error"), EINVAL);
2170			/* NOTREACHED */
2171		}
2172	default:
2173		quit(gettext("Internal Error"), EINVAL);
2174		/* NOTREACHED */
2175	}
2176	return (B_TRUE);
2177}
2178
2179/*
2180 * Interpret an argument as an IPv4 network address of some kind,
2181 * returning B_TRUE on success or B_FALSE on failure.
2182 * This function will cause an exit() on failure if exit_on_failure is set.
2183 *
2184 * Note that this tries host interpretation before network interpretation,
2185 * except when -net has been given and the destination address is being parsed.
2186 *
2187 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2188 * pass out <n> in *plenp.
2189 * If <n> doesn't parse return BAD_ADDR as *plenp.
2190 * If no /<n> is present return NO_PREFIX as *plenp.
2191 */
2192static boolean_t
2193in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
2194    struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
2195{
2196	struct hostent *hp;
2197	struct netent *np;
2198	in_addr_t val;
2199	char str[BUFSIZ];
2200
2201	(void) strlcpy(str, s, sizeof (str));
2202
2203	/*
2204	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2205	 */
2206	if (plenp != NULL) {
2207		char *cp;
2208
2209		*plenp = in_getprefixlen(str, IP_ABITS);
2210		if (*plenp == BAD_ADDR)
2211			return (B_FALSE);
2212		cp = strchr(str, '/');
2213		if (cp != NULL)
2214			*cp = '\0';
2215	} else if (strchr(str, '/') != NULL) {
2216		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2217		return (B_FALSE);
2218	}
2219
2220	(void) memset(sin, 0, sizeof (*sin));
2221	sin->sin_family = AF_INET;
2222
2223	/*
2224	 * Handle 255.255.255.255 as a special case first.
2225	 */
2226	if (strcmp(str, "255.255.255.255") == 0) {
2227		sin->sin_addr.s_addr = INADDR_BROADCAST;
2228		return (B_TRUE);
2229	}
2230
2231	val = inet_addr(str);
2232	if (val != (in_addr_t)-1) {
2233		/* Numeric address */
2234		sin->sin_addr.s_addr = val;
2235		if (which == RTA_DST) {
2236			if (atype == ADDR_TYPE_NET ||
2237			    (atype == ADDR_TYPE_ANY &&
2238			    inet_lnaof(sin->sin_addr) == INADDR_ANY)) {
2239				/* This looks like a network address. */
2240				inet_makenetandmask(rcip, ntohl(val),
2241				    sin);
2242			}
2243		}
2244		return (B_TRUE);
2245	}
2246	/* Host or net name */
2247	if (which != RTA_DST || atype != ADDR_TYPE_NET) {
2248		/* A host name is allowed. */
2249		if ((hp = gethostbyname(str)) != NULL) {
2250			*hpp = hp;
2251			(void) memmove(&sin->sin_addr, hp->h_addr,
2252			    hp->h_length);
2253			return (B_TRUE);
2254		}
2255	}
2256	if (atype != ADDR_TYPE_HOST) {
2257		/* A network name is allowed */
2258		if ((np = getnetbyname(str)) != NULL &&
2259		    (val = np->n_net) != 0) {
2260			if (which == RTA_DST) {
2261				inet_makenetandmask(rcip, val, sin);
2262			}
2263			return (B_TRUE);
2264		}
2265	}
2266	syntax_error(gettext("%s: bad value\n"), s);
2267	return (B_FALSE);
2268}
2269
2270/*
2271 * Interpret an argument as an IPv6 network address of some kind,
2272 * returning B_TRUE on success or B_FALSE on failure.
2273 * This function will cause an exit() on failure if exit_on_failure is set.
2274 *
2275 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2276 * pass out <n> in *plenp.
2277 * If <n> doesn't parse return BAD_ADDR as *plenp.
2278 * If no /<n> is present return NO_PREFIX as *plenp.
2279 */
2280static boolean_t
2281in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
2282    struct hostent **hpp)
2283{
2284	struct hostent *hp;
2285	char str[BUFSIZ];
2286	int error_num;
2287
2288	(void) strlcpy(str, s, sizeof (str));
2289
2290	/*
2291	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2292	 */
2293	if (plenp != NULL) {
2294		char *cp;
2295
2296		*plenp = in_getprefixlen(str, IPV6_ABITS);
2297		if (*plenp == BAD_ADDR)
2298			return (B_FALSE);
2299		cp = strchr(str, '/');
2300		if (cp != NULL)
2301			*cp = '\0';
2302	} else if (strchr(str, '/') != NULL) {
2303		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2304		return (B_FALSE);
2305	}
2306
2307	(void) memset(sin6, 0, sizeof (struct sockaddr_in6));
2308	sin6->sin6_family = AF_INET6;
2309
2310	hp = getipnodebyname(str, AF_INET6, 0, &error_num);
2311	if (hp != NULL) {
2312		*hpp = hp;
2313		(void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
2314		return (B_TRUE);
2315	}
2316	if (error_num == TRY_AGAIN) {
2317		/*
2318		 * This isn't a problem if we aren't going to use the address
2319		 * right away.
2320		 */
2321		if (!exit_on_error) {
2322			return (B_TRUE);
2323		}
2324		syntax_error(gettext("route: %s: bad address (try "
2325		    "again later)\n"), s);
2326		return (B_FALSE);
2327	}
2328	syntax_error(gettext("route: %s: bad address\n"), s);
2329	return (B_FALSE);
2330}
2331
2332/*
2333 * Parse <addr>/<n> syntax and return the integer n.
2334 * If <addr> is missing or <n> is not a valid integer, this function calls
2335 * syntax_error() and returns BAD_ADDR.
2336 * if n is not between 0 and max_plen inclusive, this functions calls
2337 * syntax_error() and returns BAD_ADDR.
2338 * If /<n> is not present, this function returns NO_PREFIX.
2339 * The string addr is not modified.
2340 */
2341int
2342in_getprefixlen(char *addr, int max_plen)
2343{
2344	int prefixlen;
2345	char *str, *end;
2346
2347	str = strchr(addr, '/');
2348	if (str == addr) {
2349		syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
2350		return (BAD_ADDR);
2351	}
2352	if (str == NULL)
2353		return (NO_PREFIX);
2354	str++;
2355
2356	errno = 0;
2357	prefixlen = strtoul(str, &end, 10);
2358	if (errno != 0 || str == end) {
2359		syntax_error(gettext("route: bad prefix length %s\n"), str);
2360		return (BAD_ADDR);
2361	}
2362	if (prefixlen > max_plen) {
2363		syntax_error(gettext("route: prefix length %s out of range\n"),
2364		    str);
2365		return (BAD_ADDR);
2366	}
2367	return (prefixlen);
2368}
2369
2370/*
2371 * Convert a prefix length to a mask.
2372 * Returns B_TRUE if ok. B_FALSE otherwise.
2373 * Assumes the mask array is zeroed by the caller.
2374 */
2375boolean_t
2376in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
2377{
2378	if (prefixlen < 0 || prefixlen > maxlen)
2379		return (B_FALSE);
2380
2381	while (prefixlen > 0) {
2382		if (prefixlen >= 8) {
2383			*mask++ = 0xFF;
2384			prefixlen -= 8;
2385			continue;
2386		}
2387		*mask |= 1 << (8 - prefixlen);
2388		prefixlen--;
2389	}
2390	return (B_TRUE);
2391}
2392
2393void
2394rtmonitor(int argc, char *argv[])
2395{
2396	int n;
2397	intmax_t msg[2048 / sizeof (intmax_t)];
2398
2399	if (tflag)
2400		exit(0);
2401	verbose = B_TRUE;
2402	if (argc > 1) {
2403		argv++;
2404		if (argc == 2 && **argv == '-') {
2405			switch (keyword(*argv + 1)) {
2406			case K_INET:
2407				af = AF_INET;
2408				break;
2409			case K_LINK:
2410				af = AF_LINK;
2411				break;
2412			case K_INET6:
2413				af = AF_INET6;
2414				break;
2415			default:
2416				usage(*argv);
2417				/* NOTREACHED */
2418			}
2419		} else {
2420			usage(*argv);
2421		}
2422		(void) close(s);
2423		s = socket(PF_ROUTE, SOCK_RAW, af);
2424		if (s < 0)
2425			quit("socket", errno);
2426	}
2427	for (;;) {
2428		n = read(s, msg, sizeof (msg));
2429		if (n <= 0)
2430			quit("read", errno);
2431		(void) printf("got message of size %d\n", n);
2432		print_rtmsg((struct rt_msghdr *)msg, n);
2433	}
2434}
2435
2436int
2437rtmsg(rtcmd_irep_t *newrt)
2438{
2439	static int seq;
2440	int rlen;
2441	char *cp = m_rtmsg.m_space;
2442	int l;
2443
2444	errno = 0;
2445	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
2446
2447	if (newrt->ri_cmd == RTM_GET) {
2448		newrt->ri_ifp.sa.sa_family = AF_LINK;
2449		newrt->ri_addrs |= RTA_IFP;
2450	}
2451
2452#define	rtm m_rtmsg.m_rtm
2453	rtm.rtm_type = newrt->ri_cmd;
2454	rtm.rtm_flags = newrt->ri_flags;
2455	rtm.rtm_version = RTM_VERSION;
2456	rtm.rtm_seq = ++seq;
2457	rtm.rtm_addrs = newrt->ri_addrs;
2458	rtm.rtm_rmx = newrt->ri_metrics;
2459	rtm.rtm_inits = newrt->ri_inits;
2460
2461#define	NEXTADDR(w, u) \
2462	if (newrt->ri_addrs & (w)) { \
2463		l = ROUNDUP_LONG(salen(&u.sa)); \
2464		(void) memmove(cp, &(u), l); \
2465		cp += l; \
2466		if (verbose) \
2467			sodump(&(u), #u); \
2468	}
2469	NEXTADDR(RTA_DST, newrt->ri_dst);
2470	NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
2471	NEXTADDR(RTA_NETMASK, newrt->ri_mask);
2472	NEXTADDR(RTA_IFP, newrt->ri_ifp);
2473	NEXTADDR(RTA_IFA, newrt->ri_ifa);
2474	/*
2475	 * RTA_SRC has overloaded meaning. It can represent the
2476	 * src address of incoming or outgoing packets.
2477	 */
2478	NEXTADDR(RTA_SRC, newrt->ri_src);
2479#undef	NEXTADDR
2480
2481	if (newrt->ri_rtsa_cnt > 0) {
2482		/* LINTED: aligned */
2483		rtm_ext_t *rtm_ext = (rtm_ext_t *)cp;
2484		tsol_rtsecattr_t *rtsecattr;
2485
2486		rtm_ext->rtmex_type = RTMEX_GATEWAY_SECATTR;
2487		rtm_ext->rtmex_len = TSOL_RTSECATTR_SIZE(1);
2488
2489		rtsecattr = (tsol_rtsecattr_t *)(rtm_ext + 1);
2490		rtsecattr->rtsa_cnt = 1;
2491
2492		bcopy(&newrt->ri_rtsa, rtsecattr->rtsa_attr,
2493		    sizeof (newrt->ri_rtsa));
2494		cp = (char *)(rtsecattr->rtsa_attr + 1);
2495	}
2496
2497	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
2498
2499	if (verbose)
2500		print_rtmsg(&rtm, l);
2501	if (debugonly)
2502		return (0);
2503	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
2504		switch (errno) {
2505		case ESRCH:
2506		case EBUSY:
2507		case ENOBUFS:
2508		case EEXIST:
2509		case ENETUNREACH:
2510		case EHOSTUNREACH:
2511		case EPERM:
2512			break;
2513		default:
2514			perror(gettext("writing to routing socket"));
2515			break;
2516		}
2517		return (-1);
2518	} else if (rlen < (int)rtm.rtm_msglen) {
2519		(void) fprintf(stderr,
2520		    gettext("route: write to routing socket got only %d for "
2521		    "len\n"), rlen);
2522		return (-1);
2523	}
2524	if (newrt->ri_cmd == RTM_GET) {
2525		do {
2526			l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
2527		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
2528		if (l < 0) {
2529			(void) fprintf(stderr,
2530			    gettext("route: read from routing socket: %s\n"),
2531			    strerror(errno));
2532		} else {
2533			print_getmsg(newrt, &rtm, l);
2534		}
2535	}
2536#undef rtm
2537	return (0);
2538}
2539
2540static char *msgtypes[] = {
2541	"",
2542	"RTM_ADD: Add Route",
2543	"RTM_DELETE: Delete Route",
2544	"RTM_CHANGE: Change Metrics or flags",
2545	"RTM_GET: Report Metrics",
2546	"RTM_LOSING: Kernel Suspects Partitioning",
2547	"RTM_REDIRECT: Told to use different route",
2548	"RTM_MISS: Lookup failed on this address",
2549	"RTM_LOCK: fix specified metrics",
2550	"RTM_OLDADD: caused by SIOCADDRT",
2551	"RTM_OLDDEL: caused by SIOCDELRT",
2552	"RTM_RESOLVE: Route created by cloning",
2553	"RTM_NEWADDR: address being brought up on iface",
2554	"RTM_DELADDR: address being brought down on iface",
2555	"RTM_IFINFO: iface status change",
2556	"RTM_CHGADDR: address being changed on iface",
2557	"RTM_FREEADDR: address being removed from iface",
2558	0,
2559};
2560
2561#define	NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2562
2563static char metricnames[] =
2564"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2565	"\1mtu";
2566static char routeflags[] =
2567"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2568	"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2569	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
2570	"\024KERNEL\025ZONE";
2571static char ifnetflags[] =
2572"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2573	"\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2574	"\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2575	"\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2576	"\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2577	"\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2578	"\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2579	"\047DUPLICATE";
2580static char addrnames[] =
2581"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2582
2583void
2584print_rtmsg(struct rt_msghdr *rtm, int msglen)
2585{
2586	struct if_msghdr *ifm;
2587	struct ifa_msghdr *ifam;
2588
2589	if (!verbose)
2590		return;
2591	if (rtm->rtm_version != RTM_VERSION) {
2592		(void) printf("routing message version %d not understood\n",
2593		    rtm->rtm_version);
2594		return;
2595	}
2596	if (rtm->rtm_msglen != msglen) {
2597		(void) printf("message length mismatch, in packet %d, "
2598		    "returned %d\n",
2599		    rtm->rtm_msglen, msglen);
2600		if (msglen > rtm->rtm_msglen)
2601			msglen = rtm->rtm_msglen;
2602	}
2603	/*
2604	 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2605	 * and the upper-bound of (NMSGTYPES - 1).
2606	 */
2607	if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
2608		(void) printf("routing message type %d not understood\n",
2609		    rtm->rtm_type);
2610		return;
2611	}
2612	(void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
2613	switch (rtm->rtm_type) {
2614	case RTM_IFINFO:
2615		ifm = (struct if_msghdr *)rtm;
2616		(void) printf("if# %d, flags:", ifm->ifm_index);
2617		bprintf(stdout, ifm->ifm_flags, ifnetflags);
2618		pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm),
2619		    ifm->ifm_addrs);
2620		break;
2621	case RTM_NEWADDR:
2622	case RTM_DELADDR:
2623	case RTM_CHGADDR:
2624	case RTM_FREEADDR:
2625		ifam = (struct ifa_msghdr *)rtm;
2626		(void) printf("metric %d, flags:", ifam->ifam_metric);
2627		bprintf(stdout, ifam->ifam_flags, routeflags);
2628		pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam),
2629		    ifam->ifam_addrs);
2630		break;
2631	default:
2632		(void) printf("pid: %ld, seq %d, errno %d, flags:",
2633		    rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
2634		bprintf(stdout, rtm->rtm_flags, routeflags);
2635		pmsg_common(rtm, msglen);
2636		break;
2637	}
2638}
2639
2640void
2641print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
2642{
2643	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
2644	struct sockaddr_dl *ifp = NULL;
2645	struct sockaddr *sa;
2646	char *cp;
2647	int i;
2648
2649	(void) printf("   route to: %s\n", routename(&req_rt->ri_dst.sa));
2650	if (rtm->rtm_version != RTM_VERSION) {
2651		(void) fprintf(stderr,
2652		    gettext("routing message version %d not understood\n"),
2653		    rtm->rtm_version);
2654		return;
2655	}
2656	if (rtm->rtm_msglen > (ushort_t)msglen) {
2657		(void) fprintf(stderr,
2658		    gettext("message length mismatch, in packet %d, "
2659		    "returned %d\n"), rtm->rtm_msglen, msglen);
2660	}
2661	if (rtm->rtm_errno)  {
2662		(void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
2663		    strerror(rtm->rtm_errno), rtm->rtm_errno);
2664		return;
2665	}
2666	cp = ((char *)(rtm + 1));
2667	if (rtm->rtm_addrs != 0) {
2668		for (i = 1; i != 0; i <<= 1) {
2669			if (i & rtm->rtm_addrs) {
2670				/* LINTED */
2671				sa = (struct sockaddr *)cp;
2672				switch (i) {
2673				case RTA_DST:
2674					dst = sa;
2675					break;
2676				case RTA_GATEWAY:
2677					gate = sa;
2678					break;
2679				case RTA_NETMASK:
2680					mask = sa;
2681					break;
2682				case RTA_IFP:
2683					if (sa->sa_family == AF_LINK &&
2684					    ((struct sockaddr_dl *)sa)->
2685					    sdl_nlen != 0)
2686						ifp = (struct sockaddr_dl *)sa;
2687					break;
2688				case RTA_SRC:
2689					src = sa;
2690					break;
2691				}
2692				ADVANCE(cp, sa);
2693			}
2694		}
2695	}
2696	if (dst != NULL && mask != NULL)
2697		mask->sa_family = dst->sa_family;	/* XXX */
2698	if (dst != NULL)
2699		(void) printf("destination: %s\n", routename(dst));
2700	if (mask != NULL) {
2701		boolean_t savenflag = nflag;
2702
2703		nflag = B_TRUE;
2704		(void) printf("       mask: %s\n", routename(mask));
2705		nflag = savenflag;
2706	}
2707	if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
2708		(void) printf("    gateway: %s\n", routename(gate));
2709	if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
2710		(void) printf("     setsrc: %s\n", routename(src));
2711	if (ifp != NULL) {
2712		if (verbose) {
2713			int i;
2714
2715			(void) printf("  interface: %.*s index %d address ",
2716			    ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
2717			for (i = ifp->sdl_nlen;
2718			    i < ifp->sdl_nlen + ifp->sdl_alen;
2719			    i++) {
2720				(void) printf("%02x ",
2721				    ifp->sdl_data[i] & 0xFF);
2722			}
2723			(void) printf("\n");
2724		} else {
2725			(void) printf("  interface: %.*s\n",
2726			    ifp->sdl_nlen, ifp->sdl_data);
2727		}
2728	}
2729	(void) printf("      flags: ");
2730	bprintf(stdout, rtm->rtm_flags, routeflags);
2731
2732#define	lock(f)	((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2733#define	msec(u)	(((u) + 500) / 1000)		/* usec to msec */
2734
2735	(void) printf("\n%s\n", " recvpipe  sendpipe  ssthresh    rtt,ms "
2736	    "rttvar,ms  hopcount      mtu     expire");
2737	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
2738	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
2739	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
2740	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
2741	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
2742	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
2743	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
2744	if (rtm->rtm_rmx.rmx_expire)
2745		rtm->rtm_rmx.rmx_expire -= time(0);
2746	(void) printf("%8d%c", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
2747#undef lock
2748#undef msec
2749#define	RTA_IGN	\
2750	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2751	if (verbose) {
2752		pmsg_common(rtm, msglen);
2753	} else {
2754		const char *sptr, *endptr;
2755		const struct sockaddr *sa;
2756		uint_t addrs;
2757
2758		/* Not verbose; just print out the exceptional cases */
2759		if (rtm->rtm_addrs &~ RTA_IGN) {
2760			(void) printf("\nsockaddrs: ");
2761			bprintf(stdout, rtm->rtm_addrs, addrnames);
2762		}
2763		sptr = (const char *)(rtm + 1);
2764		endptr = (const char *)rtm + msglen;
2765		addrs = rtm->rtm_addrs;
2766		while (addrs != 0 && sptr + sizeof (*sa) <= endptr) {
2767			addrs &= addrs - 1;
2768			/* LINTED */
2769			sa = (const struct sockaddr *)sptr;
2770			ADVANCE(sptr, sa);
2771		}
2772		if (addrs == 0)
2773			pmsg_secattr(sptr, endptr - sptr, "    secattr: ");
2774		(void) putchar('\n');
2775	}
2776#undef	RTA_IGN
2777}
2778
2779static void
2780pmsg_common(const struct rt_msghdr *rtm, size_t msglen)
2781{
2782	(void) printf("\nlocks: ");
2783	bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
2784	(void) printf(" inits: ");
2785	bprintf(stdout, (int)rtm->rtm_inits, metricnames);
2786	pmsg_addrs((const char *)(rtm + 1), msglen - sizeof (*rtm),
2787	    rtm->rtm_addrs);
2788}
2789
2790static void
2791pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
2792{
2793	const struct sockaddr *sa;
2794	const char *maxptr;
2795	int i;
2796
2797	if (addrs != 0) {
2798		(void) printf("\nsockaddrs: ");
2799		bprintf(stdout, addrs, addrnames);
2800		(void) putchar('\n');
2801		maxptr = cp + msglen;
2802		for (i = 1; i != 0 && cp + sizeof (*sa) <= maxptr; i <<= 1) {
2803			if (i & addrs) {
2804				/* LINTED */
2805				sa = (const struct sockaddr *)cp;
2806				(void) printf(" %s", routename(sa));
2807				ADVANCE(cp, sa);
2808			}
2809		}
2810		if (i != 0)
2811			msglen = 0;
2812		else
2813			msglen = maxptr - cp;
2814	}
2815	pmsg_secattr(cp, msglen, "secattr: ");
2816	(void) putchar('\n');
2817	(void) fflush(stdout);
2818}
2819
2820void
2821bprintf(FILE *fp, int b, char *s)
2822{
2823	int i;
2824	boolean_t gotsome = B_FALSE;
2825
2826	if (b == 0)
2827		return;
2828	while ((i = *s++) != 0) {
2829		if (b & (1 << (i - 1))) {
2830			if (!gotsome)
2831				i = '<';
2832			else
2833				i = ',';
2834			(void) putc(i, fp);
2835			gotsome = B_TRUE;
2836			for (; (i = *s) > ' '; s++)
2837				(void) putc(i, fp);
2838		} else {
2839			while (*s > ' ')
2840				s++;
2841		}
2842	}
2843	if (gotsome)
2844		(void) putc('>', fp);
2845}
2846
2847int
2848keyword(const char *cp)
2849{
2850	struct keytab *kt = keywords;
2851
2852	while (kt->kt_cp && strcmp(kt->kt_cp, cp))
2853		kt++;
2854	return (kt->kt_i);
2855}
2856
2857void
2858sodump(su_t *su, char *which)
2859{
2860	static char obuf[INET6_ADDRSTRLEN];
2861
2862	switch (su->sa.sa_family) {
2863	case AF_LINK:
2864		(void) printf("%s: link %s; ",
2865		    which, link_ntoa(&su->sdl));
2866		break;
2867	case AF_INET:
2868		(void) printf("%s: inet %s; ",
2869		    which, inet_ntoa(su->sin.sin_addr));
2870		break;
2871	case AF_INET6:
2872		if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
2873		    INET6_ADDRSTRLEN) != NULL) {
2874			(void) printf("%s: inet6 %s; ", which, obuf);
2875			break;
2876		}
2877		/* FALLTHROUGH */
2878	default:
2879		quit(gettext("Internal Error"), EINVAL);
2880		/* NOTREACHED */
2881	}
2882	(void) fflush(stdout);
2883}
2884
2885/* States */
2886#define	VIRGIN	0
2887#define	GOTONE	1
2888#define	GOTTWO	2
2889#define	RESET	3
2890/* Inputs */
2891#define	DIGIT	(4*0)
2892#define	END	(4*1)
2893#define	DELIM	(4*2)
2894#define	LETTER	(4*3)
2895
2896void
2897sockaddr(char *addr, struct sockaddr *sa)
2898{
2899	char *cp = (char *)sa;
2900	int size = salen(sa);
2901	char *cplim = cp + size;
2902	int byte = 0, state = VIRGIN, new;
2903
2904	(void) memset(cp, 0, size);
2905	cp++;
2906	do {
2907		if ((*addr >= '0') && (*addr <= '9')) {
2908			new = *addr - '0';
2909		} else if ((*addr >= 'a') && (*addr <= 'f')) {
2910			new = *addr - 'a' + 10;
2911		} else if ((*addr >= 'A') && (*addr <= 'F')) {
2912			new = *addr - 'A' + 10;
2913		} else if (*addr == 0) {
2914			state |= END;
2915		} else {
2916			state |= DELIM;
2917		}
2918		addr++;
2919		switch (state /* | INPUT */) {
2920		case GOTTWO | DIGIT:
2921			*cp++ = byte;
2922			/* FALLTHROUGH */
2923		case VIRGIN | DIGIT:
2924			state = GOTONE; byte = new; continue;
2925		case GOTONE | DIGIT:
2926			state = GOTTWO; byte = new + (byte << 4); continue;
2927		default: /* | DELIM */
2928			state = VIRGIN; *cp++ = byte; byte = 0; continue;
2929		case GOTONE | END:
2930		case GOTTWO | END:
2931			*cp++ = byte;
2932			/* FALLTHROUGH */
2933		case VIRGIN | END:
2934			break;
2935		}
2936		break;
2937	} while (cp < cplim);
2938}
2939
2940int
2941salen(const struct sockaddr *sa)
2942{
2943	switch (sa->sa_family) {
2944	case AF_INET:
2945		return (sizeof (struct sockaddr_in));
2946	case AF_LINK:
2947		return (sizeof (struct sockaddr_dl));
2948	case AF_INET6:
2949		return (sizeof (struct sockaddr_in6));
2950	default:
2951		return (sizeof (struct sockaddr));
2952	}
2953}
2954
2955void
2956link_addr(const char *addr, struct sockaddr_dl *sdl)
2957{
2958	char *cp = sdl->sdl_data;
2959	char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
2960	int byte = 0, state = VIRGIN, new;
2961
2962	(void) memset(sdl, 0, sizeof (struct sockaddr_dl));
2963	sdl->sdl_family = AF_LINK;
2964	do {
2965		state &= ~LETTER;
2966		if ((*addr >= '0') && (*addr <= '9')) {
2967			new = *addr - '0';
2968		} else if ((*addr >= 'a') && (*addr <= 'f')) {
2969			new = *addr - 'a' + 10;
2970		} else if ((*addr >= 'A') && (*addr <= 'F')) {
2971			new = *addr - 'A' + 10;
2972		} else if (*addr == 0) {
2973			state |= END;
2974		} else if (state == VIRGIN &&
2975		    (((*addr >= 'A') && (*addr <= 'Z')) ||
2976		    ((*addr >= 'a') && (*addr <= 'z')))) {
2977			state |= LETTER;
2978		} else {
2979			state |= DELIM;
2980		}
2981		addr++;
2982		switch (state /* | INPUT */) {
2983		case VIRGIN | DIGIT:
2984		case VIRGIN | LETTER:
2985			*cp++ = addr[-1];
2986			continue;
2987		case VIRGIN | DELIM:
2988			state = RESET;
2989			sdl->sdl_nlen = cp - sdl->sdl_data;
2990			continue;
2991		case GOTTWO | DIGIT:
2992			*cp++ = byte;
2993			/* FALLTHROUGH */
2994		case RESET | DIGIT:
2995			state = GOTONE;
2996			byte = new;
2997			continue;
2998		case GOTONE | DIGIT:
2999			state = GOTTWO;
3000			byte = new + (byte << 4);
3001			continue;
3002		default: /* | DELIM */
3003			state = RESET;
3004			*cp++ = byte;
3005			byte = 0;
3006			continue;
3007		case GOTONE | END:
3008		case GOTTWO | END:
3009			*cp++ = byte;
3010			/* FALLTHROUGH */
3011		case RESET | END:
3012			break;
3013		}
3014		break;
3015	} while (cp < cplim);
3016	sdl->sdl_alen = cp - LLADDR(sdl);
3017}
3018
3019static char hexlist[] = "0123456789abcdef";
3020
3021char *
3022link_ntoa(const struct sockaddr_dl *sdl)
3023{
3024	static char obuf[64];
3025	char *out = obuf;
3026	int i;
3027	uchar_t *in = (uchar_t *)LLADDR(sdl);
3028	uchar_t *inlim = in + sdl->sdl_alen;
3029	boolean_t firsttime = B_TRUE;
3030
3031	if (sdl->sdl_nlen) {
3032		(void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
3033		out += sdl->sdl_nlen;
3034		if (sdl->sdl_alen)
3035			*out++ = ':';
3036	}
3037	while (in < inlim) {
3038		if (firsttime)
3039			firsttime = B_FALSE;
3040		else
3041			*out++ = '.';
3042		i = *in++;
3043		if (i > 0xf) {
3044			out[1] = hexlist[i & 0xf];
3045			i >>= 4;
3046			out[0] = hexlist[i];
3047			out += 2;
3048		} else {
3049			*out++ = hexlist[i];
3050		}
3051	}
3052	*out = 0;
3053	return (obuf);
3054}
3055
3056static mib_item_t *
3057mibget(int sd)
3058{
3059	intmax_t		buf[512 / sizeof (intmax_t)];
3060	int			flags;
3061	int			i, j, getcode;
3062	struct strbuf		ctlbuf, databuf;
3063	struct T_optmgmt_req	*tor = (struct T_optmgmt_req *)buf;
3064	struct T_optmgmt_ack	*toa = (struct T_optmgmt_ack *)buf;
3065	struct T_error_ack	*tea = (struct T_error_ack *)buf;
3066	struct opthdr		*req;
3067	mib_item_t		*first_item = NULL;
3068	mib_item_t		*last_item  = NULL;
3069	mib_item_t		*temp;
3070
3071	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
3072	tor->OPT_offset = sizeof (struct T_optmgmt_req);
3073	tor->OPT_length = sizeof (struct opthdr);
3074	tor->MGMT_flags = T_CURRENT;
3075	req = (struct opthdr *)&tor[1];
3076	req->level = MIB2_IP;		/* any MIB2_xxx value ok here */
3077	req->name  = 0;
3078	req->len   = 0;
3079
3080	ctlbuf.buf = (char *)buf;
3081	ctlbuf.len = tor->OPT_length + tor->OPT_offset;
3082	flags = 0;
3083	if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
3084		perror("mibget: putmsg (ctl)");
3085		return (NULL);
3086	}
3087	/*
3088	 * each reply consists of a ctl part for one fixed structure
3089	 * or table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK,
3090	 * containing an opthdr structure.  level/name identify the entry,
3091	 * len is the size of the data part of the message.
3092	 */
3093	req = (struct opthdr *)&toa[1];
3094	ctlbuf.maxlen = sizeof (buf);
3095	for (j = 1; ; j++) {
3096		flags = 0;
3097		getcode = getmsg(sd, &ctlbuf, NULL, &flags);
3098		if (getcode < 0) {
3099			perror("mibget: getmsg (ctl)");
3100			if (verbose) {
3101				(void) fprintf(stderr,
3102				    "#   level   name    len\n");
3103				i = 0;
3104				for (last_item = first_item; last_item != NULL;
3105				    last_item = last_item->next_item) {
3106					(void) printf("%d  %4ld   %5ld   %ld\n",
3107					    ++i, last_item->group,
3108					    last_item->mib_id,
3109					    last_item->length);
3110				}
3111			}
3112			break;
3113		}
3114		if (getcode == 0 &&
3115		    ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
3116		    toa->PRIM_type == T_OPTMGMT_ACK &&
3117		    toa->MGMT_flags == T_SUCCESS &&
3118		    req->len == 0) {
3119			if (verbose) {
3120				(void) printf("mibget getmsg() %d returned EOD "
3121				    "(level %lu, name %lu)\n", j, req->level,
3122				    req->name);
3123			}
3124			return (first_item);		/* this is EOD msg */
3125		}
3126
3127		if (ctlbuf.len >= sizeof (struct T_error_ack) &&
3128		    tea->PRIM_type == T_ERROR_ACK) {
3129			(void) fprintf(stderr, gettext("mibget %d gives "
3130			    "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3131			    "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
3132			errno = (tea->TLI_error == TSYSERR) ?
3133			    tea->UNIX_error : EPROTO;
3134			break;
3135		}
3136
3137		if (getcode != MOREDATA ||
3138		    ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
3139		    toa->PRIM_type != T_OPTMGMT_ACK ||
3140		    toa->MGMT_flags != T_SUCCESS) {
3141			(void) printf("mibget getmsg(ctl) %d returned %d, "
3142			    "ctlbuf.len = %d, PRIM_type = %ld\n",
3143			    j, getcode, ctlbuf.len, toa->PRIM_type);
3144			if (toa->PRIM_type == T_OPTMGMT_ACK) {
3145				(void) printf("T_OPTMGMT_ACK: "
3146				    "MGMT_flags = 0x%lx, req->len = %ld\n",
3147				    toa->MGMT_flags, req->len);
3148			}
3149			errno = ENOMSG;
3150			break;
3151		}
3152
3153		temp = malloc(sizeof (mib_item_t));
3154		if (temp == NULL) {
3155			perror("mibget: malloc");
3156			break;
3157		}
3158		if (last_item != NULL)
3159			last_item->next_item = temp;
3160		else
3161			first_item = temp;
3162		last_item = temp;
3163		last_item->next_item = NULL;
3164		last_item->group = req->level;
3165		last_item->mib_id = req->name;
3166		last_item->length = req->len;
3167		last_item->valp = malloc(req->len);
3168		if (verbose) {
3169			(void) printf("msg %d:  group = %4ld   mib_id = %5ld   "
3170			    "length = %ld\n",
3171			    j, last_item->group, last_item->mib_id,
3172			    last_item->length);
3173		}
3174
3175		databuf.maxlen = last_item->length;
3176		databuf.buf    = (char *)last_item->valp;
3177		databuf.len    = 0;
3178		flags = 0;
3179		getcode = getmsg(sd, NULL, &databuf, &flags);
3180		if (getcode < 0) {
3181			perror("mibget: getmsg (data)");
3182			break;
3183		} else if (getcode != 0) {
3184			(void) printf("mibget getmsg(data) returned %d, "
3185			    "databuf.maxlen = %d, databuf.len = %d\n",
3186			    getcode, databuf.maxlen, databuf.len);
3187			break;
3188		}
3189	}
3190
3191	/*
3192	 * On error, free all the allocated mib_item_t objects.
3193	 */
3194	while (first_item != NULL) {
3195		last_item = first_item;
3196		first_item = first_item->next_item;
3197		free(last_item);
3198	}
3199	return (NULL);
3200}
3201
3202/*
3203 * print label security attributes for gateways.
3204 */
3205static void
3206pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr)
3207{
3208	rtm_ext_t rtm_ext;
3209	tsol_rtsecattr_t sp;
3210	struct rtsa_s *rtsa = &sp.rtsa_attr[0];
3211	const char *endptr;
3212	char buf[256];
3213	int i;
3214
3215	if (!is_system_labeled())
3216		return;
3217
3218	endptr = sptr + msglen;
3219
3220	for (;;) {
3221		if (sptr + sizeof (rtm_ext_t) + sizeof (sp) > endptr)
3222			return;
3223
3224		bcopy(sptr, &rtm_ext, sizeof (rtm_ext));
3225		sptr += sizeof (rtm_ext);
3226		if (rtm_ext.rtmex_type == RTMEX_GATEWAY_SECATTR)
3227			break;
3228		sptr += rtm_ext.rtmex_len;
3229	}
3230
3231	/* bail if this entry is corrupt or overruns buffer length */
3232	if (rtm_ext.rtmex_len < sizeof (sp) ||
3233	    sptr + rtm_ext.rtmex_len > endptr)
3234		return;
3235
3236	/* run up just to the end of this extension */
3237	endptr = sptr + rtm_ext.rtmex_len;
3238
3239	bcopy(sptr, &sp, sizeof (sp));
3240	sptr += sizeof (sp);
3241
3242	if (sptr + (sp.rtsa_cnt - 1) * sizeof (*rtsa) != endptr)
3243		return;
3244
3245	for (i = 0; i < sp.rtsa_cnt; i++) {
3246		if (i > 0) {
3247			/* first element is part of sp initalized above */
3248			bcopy(sptr, rtsa, sizeof (*rtsa));
3249			sptr += sizeof (*rtsa);
3250		}
3251		(void) printf("\n%s%s", labelstr, rtsa_to_str(rtsa, buf,
3252		    sizeof (buf)));
3253	}
3254}
3255