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