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