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