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