route.c revision 11042:2d6e217af1b4
1254721Semaste/*
2254721Semaste * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3254721Semaste * Use is subject to license terms.
4254721Semaste */
5254721Semaste
6254721Semaste/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
7254721Semaste/* All Rights Reserved	*/
8254721Semaste
9254721Semaste/* Copyright (c) 1990  Mentat Inc. */
10254721Semaste
11254721Semaste/*
12254721Semaste *
13269024Semaste * Copyright (c) 1983, 1989, 1991, 1993
14254721Semaste *	The Regents of the University of California.  All rights reserved.
15254721Semaste *
16254721Semaste * Redistribution and use in source and binary forms, with or without
17254721Semaste * modification, are permitted provided that the following conditions
18254721Semaste * are met:
19254721Semaste * 1. Redistributions of source code must retain the above copyright
20254721Semaste *    notice, this list of conditions and the following disclaimer.
21254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
22254721Semaste *    notice, this list of conditions and the following disclaimer in the
23254721Semaste *    documentation and/or other materials provided with the distribution.
24254721Semaste * 3. All advertising materials mentioning features or use of this software
25254721Semaste *    must display the following acknowledgement:
26254721Semaste *	This product includes software developed by the University of
27254721Semaste *	California, Berkeley and its contributors.
28254721Semaste * 4. Neither the name of the University nor the names of its contributors
29254721Semaste *    may be used to endorse or promote products derived from this software
30254721Semaste *    without specific prior written permission.
31254721Semaste *
32254721Semaste * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42254721Semaste * SUCH DAMAGE.
43254721Semaste *
44254721Semaste *	@(#)route.c	8.6 (Berkeley) 4/28/95
45254721Semaste *	@(#)linkaddr.c	8.1 (Berkeley) 6/4/93
46254721Semaste */
47254721Semaste
48254721Semaste#include <sys/param.h>
49254721Semaste#include <sys/file.h>
50254721Semaste#include <sys/socket.h>
51254721Semaste#include <sys/ioctl.h>
52254721Semaste#include <sys/stat.h>
53254721Semaste#include <sys/stream.h>
54254721Semaste#include <sys/sysmacros.h>
55254721Semaste#include <sys/tihdr.h>
56254721Semaste#include <sys/types.h>
57254721Semaste#include <sys/ccompile.h>
58254721Semaste
59254721Semaste#include <net/if.h>
60254721Semaste#include <net/route.h>
61254721Semaste#include <net/if_dl.h>
62254721Semaste#include <netinet/in.h>
63254721Semaste#include <arpa/inet.h>
64254721Semaste#include <netdb.h>
65254721Semaste#include <inet/mib2.h>
66254721Semaste#include <inet/ip.h>
67254721Semaste
68254721Semaste#include <limits.h>
69254721Semaste#include <locale.h>
70254721Semaste
71254721Semaste#include <errno.h>
72254721Semaste#include <unistd.h>
73254721Semaste#include <stdio.h>
74254721Semaste#include <stdlib.h>
75254721Semaste#include <stddef.h>
76254721Semaste#include <string.h>
77254721Semaste#include <stropts.h>
78254721Semaste#include <fcntl.h>
79254721Semaste#include <stdarg.h>
80254721Semaste#include <assert.h>
81254721Semaste#include <strings.h>
82254721Semaste
83254721Semaste#include <libtsnet.h>
84254721Semaste#include <tsol/label.h>
85254721Semaste
86254721Semastestatic struct keytab {
87254721Semaste	char	*kt_cp;
88254721Semaste	int	kt_i;
89254721Semaste} keywords[] = {
90254721Semaste#define	K_ADD		1
91254721Semaste	{"add",		K_ADD},
92254721Semaste#define	K_BLACKHOLE	2
93254721Semaste	{"blackhole",	K_BLACKHOLE},
94254721Semaste#define	K_CHANGE	3
95254721Semaste	{"change",	K_CHANGE},
96254721Semaste#define	K_CLONING	4
97254721Semaste	{"cloning",	K_CLONING},
98254721Semaste#define	K_DELETE	5
99254721Semaste	{"delete",	K_DELETE},
100254721Semaste#define	K_DST		6
101254721Semaste	{"dst",		K_DST},
102254721Semaste#define	K_EXPIRE	7
103254721Semaste	{"expire",	K_EXPIRE},
104254721Semaste#define	K_FLUSH		8
105254721Semaste	{"flush",	K_FLUSH},
106254721Semaste#define	K_GATEWAY	9
107254721Semaste	{"gateway",	K_GATEWAY},
108254721Semaste#define	K_GET		11
109254721Semaste	{"get",		K_GET},
110254721Semaste#define	K_HOPCOUNT	12
111254721Semaste	{"hopcount",	K_HOPCOUNT},
112254721Semaste#define	K_HOST		13
113254721Semaste	{"host",	K_HOST},
114254721Semaste#define	K_IFA		14
115254721Semaste	{"ifa",		K_IFA},
116254721Semaste#define	K_IFACE		15
117254721Semaste	{"iface",	K_IFACE},
118254721Semaste#define	K_IFP		16
119254721Semaste	{"ifp",		K_IFP},
120254721Semaste#define	K_INET		17
121254721Semaste	{"inet",	K_INET},
122254721Semaste#define	K_INET6		18
123254721Semaste	{"inet6",	K_INET6},
124254721Semaste#define	K_INTERFACE	19
125254721Semaste	{"interface",	K_INTERFACE},
126254721Semaste#define	K_LINK		20
127254721Semaste	{"link",	K_LINK},
128254721Semaste#define	K_LOCK		21
129254721Semaste	{"lock",	K_LOCK},
130254721Semaste#define	K_LOCKREST	22
131254721Semaste	{"lockrest",	K_LOCKREST},
132254721Semaste#define	K_MASK		23
133254721Semaste	{"mask",	K_MASK},
134254721Semaste#define	K_MONITOR	24
135254721Semaste	{"monitor",	K_MONITOR},
136254721Semaste#define	K_MTU		25
137254721Semaste	{"mtu",		K_MTU},
138254721Semaste#define	K_NET		26
139254721Semaste	{"net",		K_NET},
140254721Semaste#define	K_NETMASK	27
141254721Semaste	{"netmask",	K_NETMASK},
142254721Semaste#define	K_NOSTATIC	28
143254721Semaste	{"nostatic",	K_NOSTATIC},
144254721Semaste#define	K_PRIVATE	29
145254721Semaste	{"private",	K_PRIVATE},
146254721Semaste#define	K_PROTO1	30
147254721Semaste	{"proto1",	K_PROTO1},
148254721Semaste#define	K_PROTO2	31
149254721Semaste	{"proto2",	K_PROTO2},
150254721Semaste#define	K_RECVPIPE	32
151254721Semaste	{"recvpipe",	K_RECVPIPE},
152254721Semaste#define	K_REJECT	33
153254721Semaste	{"reject",	K_REJECT},
154254721Semaste#define	K_RTT		34
155254721Semaste	{"rtt",		K_RTT},
156254721Semaste#define	K_RTTVAR	35
157254721Semaste	{"rttvar",	K_RTTVAR},
158254721Semaste#define	K_SA		36
159254721Semaste	{"sa",		K_SA},
160254721Semaste#define	K_SENDPIPE	37
161254721Semaste	{"sendpipe",	K_SENDPIPE},
162254721Semaste#define	K_SSTHRESH	38
163254721Semaste	{"ssthresh",	K_SSTHRESH},
164254721Semaste#define	K_STATIC	39
165254721Semaste	{"static",	K_STATIC},
166254721Semaste#define	K_XRESOLVE	40
167254721Semaste	{"xresolve",	K_XRESOLVE},
168254721Semaste#define	K_MULTIRT	41
169254721Semaste	{"multirt",	K_MULTIRT},
170254721Semaste#define	K_SETSRC	42
171254721Semaste	{"setsrc",	K_SETSRC},
172254721Semaste#define	K_SHOW		43
173254721Semaste	{"show",	K_SHOW},
174254721Semaste#define	K_SECATTR	43
175254721Semaste	{"secattr",	K_SECATTR},
176254721Semaste#define	K_INDIRECT	44
177254721Semaste	{"indirect",	K_INDIRECT},
178254721Semaste	{0, 0}
179254721Semaste};
180254721Semaste
181254721Semaste/*
182254721Semaste * Size of buffers used to hold command lines from the saved route file as
183254721Semaste * well as error strings.
184254721Semaste */
185254721Semaste#define	BUF_SIZE 2048
186254721Semaste
187254721Semastetypedef union sockunion {
188254721Semaste	struct	sockaddr sa;
189254721Semaste	struct	sockaddr_in sin;
190254721Semaste	struct	sockaddr_dl sdl;
191254721Semaste	struct	sockaddr_in6 sin6;
192254721Semaste} su_t;
193254721Semaste
194254721Semaste/*
195254721Semaste * This structure represents the digested information from parsing arguments
196254721Semaste * to route add, change, delete, and get.
197254721Semaste *
198254721Semaste */
199254721Semastetypedef struct rtcmd_irep {
200254721Semaste	int ri_cmd;
201254721Semaste	int ri_flags;
202254721Semaste	int ri_af;
203254721Semaste	ulong_t	ri_inits;
204254721Semaste	struct rt_metrics ri_metrics;
205254721Semaste	int ri_addrs;
206254721Semaste	su_t ri_dst;
207254721Semaste	char *ri_dest_str;
208254721Semaste	su_t ri_src;
209254721Semaste	su_t ri_gate;
210254721Semaste	struct hostent *ri_gate_hp;
211254721Semaste	char *ri_gate_str;
212254721Semaste	su_t ri_mask;
213254721Semaste	su_t ri_ifa;
214254721Semaste	su_t ri_ifp;
215254721Semaste	char *ri_ifp_str;
216254721Semaste	int ri_rtsa_cnt;	/* number of gateway security attributes */
217254721Semaste	struct rtsa_s ri_rtsa;	/* enough space for one attribute */
218254721Semaste} rtcmd_irep_t;
219254721Semaste
220254721Semastetypedef struct	mib_item_s {
221254721Semaste	struct mib_item_s *next_item;
222254721Semaste	long group;
223254721Semaste	long mib_id;
224254721Semaste	long length;
225254721Semaste	intmax_t *valp;
226254721Semaste} mib_item_t;
227254721Semaste
228254721Semastetypedef enum {
229254721Semaste	ADDR_TYPE_ANY,
230254721Semaste	ADDR_TYPE_HOST,
231254721Semaste	ADDR_TYPE_NET
232254721Semaste} addr_type_t;
233254721Semaste
234254721Semastetypedef enum {
235254721Semaste	SEARCH_MODE_NULL,
236254721Semaste	SEARCH_MODE_PRINT,
237254721Semaste	SEARCH_MODE_DEL
238254721Semaste} search_mode_t;
239254721Semaste
240254721Semastestatic boolean_t	args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
241254721Semaste    char *cmd_string);
242254721Semastestatic void		bprintf(FILE *fp, int b, char *s);
243254721Semastestatic boolean_t	compare_rtcmd(rtcmd_irep_t *srch_rt,
244254721Semaste    rtcmd_irep_t *file_rt);
245254721Semastestatic void		delRouteEntry(mib2_ipRouteEntry_t *rp,
246254721Semaste    mib2_ipv6RouteEntry_t *rp6, int seqno);
247254721Semastestatic void		del_rtcmd_irep(rtcmd_irep_t *rcip);
248254721Semastestatic void		flushroutes(int argc, char *argv[]);
249254721Semastestatic boolean_t	getaddr(rtcmd_irep_t *rcip, int which, char *s,
250254721Semaste    addr_type_t atype);
251254721Semastestatic boolean_t	in6_getaddr(char *s, struct sockaddr_in6 *sin6,
252254721Semaste    int *plenp, struct hostent **hpp);
253254721Semastestatic boolean_t	in_getaddr(char *s, struct sockaddr_in *sin,
254254721Semaste    int *plenp, int which, struct hostent **hpp, addr_type_t atype,
255254721Semaste    rtcmd_irep_t *rcip);
256254721Semastestatic int		in_getprefixlen(char *addr, int max_plen);
257254721Semastestatic boolean_t	in_prefixlentomask(int prefixlen, int maxlen,
258254721Semaste    uchar_t *mask);
259254721Semastestatic void		inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
260254721Semaste    struct sockaddr_in *sin);
261254721Semastestatic in_addr_t	inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
262254721Semastestatic int		keyword(const char *cp);
263254721Semastestatic void		link_addr(const char *addr, struct sockaddr_dl *sdl);
264254721Semastestatic char		*link_ntoa(const struct sockaddr_dl *sdl);
265254721Semastestatic mib_item_t	*mibget(int sd);
266254721Semastestatic char		*netname(struct sockaddr *sa);
267254721Semastestatic int		newroute(char **argv);
268254721Semastestatic rtcmd_irep_t	*new_rtcmd_irep(void);
269254721Semastestatic void		pmsg_addrs(const char *cp, size_t len, uint_t addrs);
270254721Semastestatic void		pmsg_common(const struct rt_msghdr *rtm, size_t len);
271254721Semastestatic void		print_getmsg(rtcmd_irep_t *req_rt,
272254721Semaste    struct rt_msghdr *rtm, int msglen);
273254721Semastestatic void		print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
274254721Semaste    boolean_t gw_good, boolean_t to_saved);
275254721Semastestatic void		print_rtmsg(struct rt_msghdr *rtm, int msglen);
276254721Semastestatic void		quit(char *s, int err) __NORETURN;
277254721Semastestatic char		*routename(const struct sockaddr *sa);
278254721Semastestatic void		rtmonitor(int argc, char *argv[]);
279254721Semastestatic int		rtmsg(rtcmd_irep_t *rcip);
280254721Semastestatic int		salen(const struct sockaddr *sa);
281254721Semastestatic void		save_route(int argc, char **argv, int do_flush);
282254721Semastestatic void		save_string(char **dst, char *src);
283254721Semastestatic int		search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
284254721Semaste    search_mode_t mode);
285254721Semastestatic void		set_metric(rtcmd_irep_t *rcip, char *value, int key,
286254721Semaste    boolean_t lock);
287254721Semastestatic int		show_saved_routes(int argc);
288254721Semastestatic void		sockaddr(char *addr, struct sockaddr *sa);
289254721Semastestatic void		sodump(su_t *su, char *which);
290254721Semastestatic void		syntax_arg_missing(char *keyword);
291254721Semastestatic void		syntax_bad_keyword(char *keyword);
292254721Semastestatic void		syntax_error(char *err, ...);
293254721Semastestatic void		usage(char *cp);
294254721Semastestatic void		write_to_rtfile(FILE *fp, int argc, char **argv);
295254721Semastestatic void		pmsg_secattr(const char *, size_t, const char *);
296254721Semaste
297254721Semastestatic pid_t		pid;
298254721Semastestatic int		s;
299254721Semastestatic boolean_t	nflag;
300254721Semastestatic int		af = AF_INET;
301254721Semastestatic boolean_t	qflag, tflag;
302254721Semastestatic boolean_t	verbose;
303254721Semastestatic boolean_t	debugonly;
304254721Semastestatic boolean_t	fflag;
305254721Semastestatic boolean_t	update_table;
306254721Semastestatic boolean_t	perm_flag;
307254721Semastestatic boolean_t	early_v6_keyword;
308254721Semastestatic char		perm_file_sfx[] = "/etc/inet/static_routes";
309254721Semastestatic char		*perm_file;
310254721Semastestatic char		temp_file_sfx[] = "/etc/inet/static_routes.tmp";
311254721Semastestatic char		*temp_file;
312254721Semastestatic struct in6_addr	in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
313254721Semaste    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
314254721Semaste/*
315254721Semaste * WARNING:
316254721Semaste * This next variable indicates whether certain functions exit when an error
317254721Semaste * is detected in the user input.  Currently, exit_on_error is only set false
318254721Semaste * in search_rtfile(), when argument are being parsed.  Only those functions
319254721Semaste * used by search_rtfile() to parse its arguments are designed to work in
320254721Semaste * both modes.  Take particular care in setting this false to ensure that any
321254721Semaste * functions you call that might act on this flag properly return errors when
322254721Semaste * exit_on_error is false.
323254721Semaste */
324254721Semastestatic int		exit_on_error = B_TRUE;
325254721Semaste
326254721Semastestatic struct {
327254721Semaste	struct	rt_msghdr m_rtm;
328254721Semaste	char	m_space[BUF_SIZE];
329254721Semaste} m_rtmsg;
330254721Semaste
331254721Semaste/*
332254721Semaste * Sizes of data structures extracted from the base mib.
333254721Semaste * This allows the size of the tables entries to grow while preserving
334254721Semaste * binary compatibility.
335254721Semaste */
336254721Semastestatic int ipRouteEntrySize;
337254721Semastestatic int ipv6RouteEntrySize;
338254721Semaste
339254721Semaste#define	ROUNDUP_LONG(a) \
340254721Semaste	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
341254721Semaste#define	ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
342254721Semaste#define	C(x)	((x) & 0xff)
343254721Semaste
344254721Semaste/*
345254721Semaste * return values from in_getprefixlen()
346254721Semaste */
347254721Semaste#define	BAD_ADDR	-1	/* prefix is invalid */
348254721Semaste#define	NO_PREFIX	-2	/* no prefix was found */
349254721Semaste
350254721Semastevoid
351254721Semasteusage(char *cp)
352254721Semaste{
353254721Semaste	if (cp != NULL) {
354254721Semaste		(void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
355254721Semaste		    cp);
356254721Semaste	}
357254721Semaste	(void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
358254721Semaste	    "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
359254721Semaste	exit(1);
360254721Semaste	/* NOTREACHED */
361254721Semaste}
362254721Semaste
363254721Semaste/*PRINTFLIKE1*/
364254721Semastevoid
365254721Semastesyntax_error(char *err, ...)
366254721Semaste{
367254721Semaste	va_list args;
368254721Semaste
369254721Semaste	if (exit_on_error) {
370254721Semaste		va_start(args, err);
371254721Semaste		(void) vfprintf(stderr, err, args);
372254721Semaste		va_end(args);
373254721Semaste		exit(1);
374254721Semaste	}
375254721Semaste	/* NOTREACHED */
376254721Semaste}
377254721Semaste
378254721Semastevoid
379254721Semastesyntax_bad_keyword(char *keyword)
380254721Semaste{
381254721Semaste	syntax_error(gettext("route: botched keyword: %s\n"), keyword);
382254721Semaste}
383254721Semaste
384254721Semastevoid
385254721Semastesyntax_arg_missing(char *keyword)
386254721Semaste{
387254721Semaste	syntax_error(gettext("route: argument required following keyword %s\n"),
388254721Semaste	    keyword);
389254721Semaste}
390254721Semaste
391254721Semastevoid
392254721Semastequit(char *s, int sverrno)
393254721Semaste{
394254721Semaste	(void) fprintf(stderr, "route: ");
395254721Semaste	if (s != NULL)
396254721Semaste		(void) fprintf(stderr, "%s: ", s);
397254721Semaste	(void) fprintf(stderr, "%s\n", strerror(sverrno));
398254721Semaste	exit(sverrno);
399254721Semaste	/* NOTREACHED */
400254721Semaste}
401254721Semaste
402254721Semasteint
403254721Semastemain(int argc, char **argv)
404254721Semaste{
405254721Semaste	extern int optind;
406254721Semaste	extern char *optarg;
407254721Semaste	int ch;
408254721Semaste	int rval;
409254721Semaste	size_t size;
410254721Semaste	const char *root_dir = NULL;
411254721Semaste
412254721Semaste	(void) setlocale(LC_ALL, "");
413254721Semaste
414254721Semaste#if !defined(TEXT_DOMAIN)
415254721Semaste#define	TEXT_DOMAIN "SYS_TEST"
416254721Semaste#endif
417254721Semaste	(void) textdomain(TEXT_DOMAIN);
418254721Semaste
419254721Semaste	if (argc < 2)
420254721Semaste		usage(NULL);
421254721Semaste
422254721Semaste	while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
423254721Semaste		switch (ch) {
424254721Semaste		case 'n':
425254721Semaste			nflag = B_TRUE;
426254721Semaste			break;
427254721Semaste		case 'q':
428254721Semaste			qflag = B_TRUE;
429254721Semaste			break;
430254721Semaste		case 'v':
431254721Semaste			verbose = B_TRUE;
432254721Semaste			break;
433254721Semaste		case 't':
434254721Semaste			tflag = B_TRUE;
435254721Semaste			break;
436254721Semaste		case 'd':
437254721Semaste			debugonly = B_TRUE;
438254721Semaste			break;
439254721Semaste		case 'f':
440254721Semaste			fflag = B_TRUE;
441254721Semaste			break;
442254721Semaste		case 'p':
443254721Semaste			perm_flag = B_TRUE;
444254721Semaste			break;
445254721Semaste		case 'R':
446254721Semaste			root_dir = optarg;
447254721Semaste			break;
448254721Semaste		case '?':
449254721Semaste		default:
450254721Semaste			usage(NULL);
451254721Semaste			/* NOTREACHED */
452254721Semaste		}
453254721Semaste	}
454254721Semaste	argc -= optind;
455254721Semaste	argv += optind;
456254721Semaste
457254721Semaste	pid = getpid();
458254721Semaste	if (tflag)
459254721Semaste		s = open("/dev/null", O_WRONLY);
460254721Semaste	else
461254721Semaste		s = socket(PF_ROUTE, SOCK_RAW, 0);
462254721Semaste	if (s < 0)
463254721Semaste		quit("socket", errno);
464254721Semaste
465254721Semaste	/*
466254721Semaste	 * Handle the -p and -R flags.  The -R flag only applies
467254721Semaste	 * when the -p flag is set.
468254721Semaste	 */
469254721Semaste	if (root_dir == NULL) {
470254721Semaste		perm_file = perm_file_sfx;
471254721Semaste		temp_file = temp_file_sfx;
472254721Semaste	} else {
473254721Semaste		size = strlen(root_dir) + sizeof (perm_file_sfx);
474254721Semaste		perm_file = malloc(size);
475254721Semaste		if (perm_file == NULL)
476254721Semaste			quit("malloc", errno);
477254721Semaste		(void) snprintf(perm_file, size, "%s%s", root_dir,
478254721Semaste		    perm_file_sfx);
479254721Semaste		size = strlen(root_dir) + sizeof (temp_file_sfx);
480254721Semaste		temp_file = malloc(size);
481254721Semaste		if (temp_file == NULL)
482254721Semaste			quit("malloc", errno);
483254721Semaste		(void) snprintf(temp_file, size, "%s%s", root_dir,
484254721Semaste		    temp_file_sfx);
485254721Semaste	}
486254721Semaste	/*
487254721Semaste	 * Whether or not to act on the routing table.  The only time the
488254721Semaste	 * routing table is not modified is when both -p and -R are present.
489254721Semaste	 */
490254721Semaste	update_table = (!perm_flag || root_dir == NULL);
491254721Semaste	if (tflag)
492254721Semaste		perm_flag = 0;
493254721Semaste
494254721Semaste	if (fflag) {
495254721Semaste		/*
496254721Semaste		 * Accept an address family keyword after the -f.  Since the
497254721Semaste		 * default address family is AF_INET, reassign af only for the
498254721Semaste		 * other valid address families.
499254721Semaste		 */
500254721Semaste		if (*argv != NULL) {
501254721Semaste			switch (keyword(*argv)) {
502254721Semaste			case K_INET6:
503254721Semaste				af = AF_INET6;
504254721Semaste				early_v6_keyword = B_TRUE;
505254721Semaste				/* fallthrough */
506254721Semaste			case K_INET:
507254721Semaste				/* Skip over the address family parameter. */
508254721Semaste				argc--;
509254721Semaste				argv++;
510254721Semaste				break;
511254721Semaste			}
512254721Semaste		}
513254721Semaste		flushroutes(0, NULL);
514254721Semaste	}
515254721Semaste
516254721Semaste	if (*argv != NULL) {
517254721Semaste		switch (keyword(*argv)) {
518254721Semaste		case K_GET:
519254721Semaste		case K_CHANGE:
520254721Semaste		case K_ADD:
521254721Semaste		case K_DELETE:
522254721Semaste			rval = 0;
523254721Semaste			if (update_table) {
524254721Semaste				rval = newroute(argv);
525254721Semaste			}
526254721Semaste			if (perm_flag && (rval == 0 || rval == EEXIST ||
527254721Semaste			    rval == ESRCH)) {
528254721Semaste				save_route(argc, argv, B_FALSE);
529254721Semaste				return (0);
530254721Semaste			}
531254721Semaste			return (rval);
532254721Semaste		case K_SHOW:
533254721Semaste			if (perm_flag) {
534254721Semaste				return (show_saved_routes(argc));
535254721Semaste			} else {
536254721Semaste				syntax_error(gettext(
537254721Semaste				    "route: show command requires -p\n"));
538254721Semaste			}
539254721Semaste			/* NOTREACHED */
540254721Semaste		case K_MONITOR:
541254721Semaste			rtmonitor(argc, argv);
542254721Semaste			/* NOTREACHED */
543254721Semaste
544254721Semaste		case K_FLUSH:
545254721Semaste			flushroutes(argc, argv);
546254721Semaste			return (0);
547254721Semaste		}
548254721Semaste	}
549254721Semaste	if (!fflag)
550254721Semaste		usage(*argv);
551254721Semaste	return (0);
552254721Semaste}
553254721Semaste
554254721Semaste/*
555254721Semaste * Purge all entries in the routing tables not
556254721Semaste * associated with network interfaces.
557254721Semaste */
558254721Semastevoid
559254721Semasteflushroutes(int argc, char *argv[])
560254721Semaste{
561254721Semaste	int seqno;
562254721Semaste	int sd;	/* mib stream */
563254721Semaste	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 */
714static void
715delRouteEntry(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 */
826char *
827routename(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 */
932static char *
933netname(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 */
1022rtcmd_irep_t *
1023new_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
1036void
1037del_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
1053void
1054save_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 */
1069void
1070print_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
1146void
1147set_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 */
1180boolean_t
1181args_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		case K_INDIRECT:
1409			rcip->ri_flags |= RTF_INDIRECT;
1410			break;
1411		default:
1412			if (dash_keyword) {
1413				syntax_bad_keyword(tok + 1);
1414				return (B_FALSE);
1415			}
1416			if ((rcip->ri_addrs & RTA_DST) == 0) {
1417				if (!getaddr(rcip, RTA_DST, tok, atype)) {
1418					return (B_FALSE);
1419				}
1420			} else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
1421				/*
1422				 * For the gateway parameter, retrieve the
1423				 * pointer to the struct hostent so that all
1424				 * possible addresses can be tried until one
1425				 * is successful.
1426				 */
1427				if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1428					return (B_FALSE);
1429				}
1430			} else {
1431				ulong_t metric;
1432				/*
1433				 * Assume that a regular number is a metric.
1434				 * Needed for compatibility with old route
1435				 * command syntax.
1436				 */
1437				errno = 0;
1438				metric = strtoul(tok, &err, 10);
1439				if (errno == 0 && *err == '\0' &&
1440				    metric < 0x80000000ul) {
1441					iflag = (metric == 0);
1442					if (verbose) {
1443						(void) printf("old usage of "
1444						    "trailing number, assuming "
1445						    "route %s\n", iflag ?
1446						    "to if" : "via gateway");
1447					}
1448					continue;
1449				}
1450				if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1451					return (B_FALSE);
1452				}
1453			}
1454		}
1455	}
1456#undef NEXTTOKEN
1457
1458	if ((rcip->ri_addrs & RTA_DST) == 0) {
1459		syntax_error(gettext("route: destination required\n"));
1460		return (B_FALSE);
1461	} else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
1462	    (rcip->ri_addrs & RTA_GATEWAY) == 0) {
1463		syntax_error(gettext(
1464		    "route: gateway required for add or delete command\n"));
1465		return (B_FALSE);
1466	}
1467
1468	if (!iflag) {
1469		rcip->ri_flags |= RTF_GATEWAY;
1470	}
1471
1472	if (atype != ADDR_TYPE_NET) {
1473		if (rcip->ri_addrs & RTA_NETMASK) {
1474			/*
1475			 * We know the netmask, so we can set the host flag
1476			 * based on whether the netmask is the host netmask.
1477			 */
1478			if (rcip->ri_af == AF_INET &&
1479			    rcip->ri_mask.sin.sin_addr.s_addr ==
1480			    IP_HOST_MASK) {
1481				rcip->ri_flags |= RTF_HOST;
1482			}
1483			if (rcip->ri_af == AF_INET6 &&
1484			    memcmp(&rcip->ri_mask.sin6.sin6_addr,
1485			    &in6_host_mask,
1486			    sizeof (struct in6_addr)) == 0) {
1487				rcip->ri_flags |= RTF_HOST;
1488			}
1489		} else {
1490			/*
1491			 * If no prefix mask has been saved at this point, it
1492			 * only makes sense to treat the destination address
1493			 * as a host address.
1494			 */
1495			rcip->ri_flags |= RTF_HOST;
1496		}
1497	}
1498	return (B_TRUE);
1499}
1500
1501/*
1502 * This command always seeks to the end of the file prior to writing.
1503 */
1504void
1505write_to_rtfile(FILE *fp, int argc, char **argv)
1506{
1507	char file_line[BUF_SIZE];
1508	int len;
1509	int i;
1510
1511	len = 0;
1512	if (early_v6_keyword) {
1513		/*
1514		 * This flag is set when "inet6" was seen as an
1515		 * argument to the -f flag.  Normally, when writing
1516		 * routes to the persistent route file, everything on
1517		 * the command line after "add" is saved verbatim.
1518		 * In this case, the arguments after "add" may not be
1519		 * sufficient, as the ipv6 keyword came before "add",
1520		 * yet must be present in the persistent route file.
1521		 */
1522		len += snprintf(file_line, BUF_SIZE, "-inet6 ");
1523	}
1524	for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
1525		len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
1526		    argv[i]);
1527	}
1528	if (len >= BUF_SIZE)
1529		quit(gettext("Internal Error"), EINVAL);
1530	file_line[len - 1] = '\n';
1531	if (fseek(fp, 0, SEEK_END) != 0 ||
1532	    fputs(file_line, fp) == EOF) {
1533		quit(gettext("failed to write to route file"),
1534		    errno);
1535	}
1536}
1537
1538boolean_t
1539compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
1540{
1541	if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
1542	    memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
1543		return (B_FALSE);
1544	}
1545	return (srch_rt->ri_gate_str == NULL ||
1546	    strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
1547}
1548
1549/*
1550 * Search the route file for routes matching the supplied route.  There are 3
1551 * modes of operation:
1552 *    SEARCH_MODE_RET - no side effects.
1553 *    SEARCH_MODE_PRINT - prints each matching line.
1554 *    SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1555 *
1556 * In all cases, the number of matches is returned.  If rt is NULL, all routes
1557 * matching the global af value are considered matching.
1558 */
1559int
1560search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
1561{
1562	char *tmp_buf;
1563	int match_cnt;
1564	boolean_t match;
1565	char file_line[BUF_SIZE + 4] = "add ";
1566	rtcmd_irep_t *thisrt;
1567
1568	match_cnt = 0;
1569
1570	/*
1571	 * Leave space at the beginning of file_line for "add ".
1572	 */
1573	while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
1574
1575		if (file_line[4] == '#' || file_line[4] == '\n') {
1576			/* Handle comments and blank lines */
1577			if (mode == SEARCH_MODE_DEL &&
1578			    fputs(file_line + 4, temp_fp) == EOF) {
1579				quit(gettext(
1580				    "route: failed to write to temp file"),
1581				    errno);
1582			}
1583			continue;
1584		}
1585		thisrt = new_rtcmd_irep();
1586		/*
1587		 * thisrt->ri_af defaults to whatever address family happens
1588		 * to be set in the global af, but routes in the persistent
1589		 * route file must be treated as AF_INET by default.
1590		 */
1591		thisrt->ri_af = AF_INET;
1592
1593		exit_on_error = B_FALSE;
1594		tmp_buf = strdup(file_line);
1595		/* args_to_rtcmd() will mangle the string passed. */
1596		if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
1597			/* There was an error in args_to_rtcmd() or helpers */
1598			del_rtcmd_irep(thisrt);
1599			free(tmp_buf);
1600			continue;
1601		}
1602		exit_on_error = B_TRUE;
1603		free(tmp_buf);
1604
1605		if (thisrt->ri_gate_str == NULL) {
1606			del_rtcmd_irep(thisrt);
1607			continue;
1608		}
1609		match = (rt == NULL) ? (thisrt->ri_af == af) :
1610		    compare_rtcmd(rt, thisrt);
1611
1612		if (match) match_cnt++;
1613		if (match && mode == SEARCH_MODE_PRINT) {
1614			(void) printf("persistent: route %s", file_line);
1615		}
1616		if (match && mode == SEARCH_MODE_DEL) {
1617			thisrt->ri_cmd = RTM_DELETE;
1618			print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
1619			(void) printf("\n");
1620		}
1621		del_rtcmd_irep(thisrt);
1622
1623		if (!match && mode == SEARCH_MODE_DEL &&
1624		    fputs(file_line + 4, temp_fp) == EOF) {
1625			quit(gettext("failed to write to temp file"),
1626			    errno);
1627		}
1628	}
1629	return (match_cnt);
1630}
1631
1632/*
1633 * Perform the route operation given in argv on the persistent route file.
1634 * If do_flush is set, the persistent route file is flushed of all routes
1635 * matching the global family, and the arguments are ignored.
1636 */
1637void
1638save_route(int argc, char **argv, int do_flush)
1639{
1640	rtcmd_irep_t *rt;
1641	int perm_fd;
1642	FILE *perm_fp;
1643	FILE *temp_fp;
1644	mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1645	struct flock lock;
1646	struct stat st;
1647	const char commentstr[] =
1648	    "# File generated by route(1M) - do not edit.\n";
1649
1650	perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
1651	if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
1652		quit("failed to open route file", errno);
1653
1654	lock.l_type = F_WRLCK;
1655	lock.l_whence = SEEK_SET;
1656	lock.l_start = 0;
1657	lock.l_len = 0;
1658	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1659		quit(gettext("failed to lock route file"), errno);
1660		/* NOTREACHED */
1661	}
1662	if (st.st_size == 0 &&
1663	    write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
1664	    sizeof (commentstr) - 1)
1665		quit(gettext("failed to open route file"), errno);
1666
1667	if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
1668		quit(gettext("failed to open route file"), errno);
1669		/* NOTREACHED */
1670	}
1671
1672	if (!do_flush) {
1673		rt = new_rtcmd_irep();
1674		(void) args_to_rtcmd(rt, argv, NULL);
1675	}
1676	if (do_flush || rt->ri_cmd == RTM_DELETE) {
1677		if ((temp_fp = fopen(temp_file, "w")) == NULL) {
1678			quit(gettext("failed to open temp file"), errno);
1679			/* NOTREACHED */
1680		}
1681	}
1682	if (do_flush) {
1683		(void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
1684		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1685			quit(gettext("failed to update route file"), errno);
1686			/* NOTREACHED */
1687		}
1688		(void) fclose(perm_fp);
1689		return;
1690	}
1691
1692	switch (rt->ri_cmd) {
1693	case RTM_ADD:
1694		if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
1695			/* Route is already in the file */
1696			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1697			(void) fprintf(stderr, ": entry exists\n");
1698			exit(1);
1699		}
1700		write_to_rtfile(perm_fp, argc - 1, argv + 1);
1701		print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1702		(void) printf("\n");
1703		break;
1704
1705	case RTM_CHANGE:
1706		syntax_error(
1707		    gettext("route: change command not supported with -p\n"));
1708		/* NOTREACHED */
1709
1710	case RTM_DELETE:
1711		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
1712			/* Route not found */
1713			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1714			(void) fprintf(stderr, gettext(": not in file\n"));
1715			exit(1);
1716		}
1717		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1718			quit(gettext("failed to update route file"), errno);
1719			/* NOTREACHED */
1720		}
1721		break;
1722
1723	case RTM_GET:
1724		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1725		    0) {
1726			print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1727			(void) printf(gettext(": not in file\n"));
1728		}
1729		break;
1730
1731	default:
1732		quit(gettext("Internal Error"), EINVAL);
1733		/* NOTREACHED */
1734	}
1735
1736	/*
1737	 * Closing the file unlocks it.
1738	 */
1739	(void) fclose(perm_fp);
1740}
1741
1742int
1743show_saved_routes(int argc)
1744{
1745	int perm_fd;
1746	FILE *perm_fp;
1747	struct flock lock;
1748	int count = 0;
1749
1750	if (argc != 1) {
1751		syntax_error(gettext("route: invalid arguments for show\n"));
1752	}
1753
1754	perm_fd = open(perm_file, O_RDONLY, 0);
1755
1756	if (perm_fd == -1) {
1757		if (errno == ENOENT) {
1758			(void) printf("No persistent routes are defined\n");
1759			return (0);
1760		} else {
1761			quit(gettext("failed to open route file"), errno);
1762		}
1763	}
1764	lock.l_type = F_RDLCK;
1765	lock.l_whence = SEEK_SET;
1766	lock.l_start = 0;
1767	lock.l_len = 0;
1768	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1769		quit(gettext("failed to lock route file"),
1770		    errno);
1771		/* NOTREACHED */
1772	}
1773	if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1774		quit(gettext("failed to open route file"), errno);
1775		/* NOTREACHED */
1776	}
1777	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1778	(void) fseek(perm_fp, 0, SEEK_SET);
1779	af = AF_INET6;
1780	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1781
1782	if (count == 0)
1783		(void) printf("No persistent routes are defined\n");
1784
1785	(void) fclose(perm_fp);
1786	return (0);
1787}
1788
1789int
1790newroute(char **argv)
1791{
1792	rtcmd_irep_t *newrt;
1793	int ret, attempts, oerrno;
1794	char *err;
1795	char obuf[INET6_ADDRSTRLEN];
1796#define	hp (newrt->ri_gate_hp)
1797
1798	newrt = new_rtcmd_irep();
1799	(void) args_to_rtcmd(newrt, argv, NULL);
1800
1801	if (newrt->ri_cmd != RTM_GET && !tflag) {
1802		/* Don't want to read back our messages */
1803		(void) shutdown(s, 0);
1804	}
1805	if (newrt->ri_addrs & RTA_IFP) {
1806		newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
1807		if (newrt->ri_ifp.sdl.sdl_index == 0) {
1808			if (errno != ENXIO) {
1809				quit("if_nametoindex", errno);
1810			} else {
1811				(void) fprintf(stderr,
1812				    gettext("route: %s: no such interface\n"),
1813				    newrt->ri_ifp_str);
1814				exit(1);
1815			}
1816		}
1817		newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1818	}
1819	for (attempts = 1; ; attempts++) {
1820		errno = 0;
1821		if ((ret = rtmsg(newrt)) == 0)
1822			break;
1823		if (errno != ENETUNREACH && errno != ESRCH)
1824			break;
1825		if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
1826		    hp->h_addr_list[attempts] != NULL) {
1827			switch (af) {
1828			case AF_INET:
1829				(void) memmove(&newrt->ri_gate.sin.sin_addr,
1830				    hp->h_addr_list[attempts], hp->h_length);
1831				continue;
1832			case AF_INET6:
1833				(void) memmove(&newrt->ri_gate.sin6.sin6_addr,
1834				    hp->h_addr_list[attempts], hp->h_length);
1835				continue;
1836			}
1837		}
1838		break;
1839	}
1840	oerrno = errno;
1841
1842	if (newrt->ri_cmd != RTM_GET) {
1843		print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
1844		if (ret == 0)
1845			(void) printf("\n");
1846	} else if (ret != 0) {
1847		/*
1848		 * Note: there is nothing additional to print for get
1849		 * if ret == 0.
1850		 */
1851		if (nflag) {
1852			switch (newrt->ri_af) {
1853			case AF_INET:
1854				(void) printf(" %s",
1855				    inet_ntoa(newrt->ri_dst.sin.sin_addr));
1856				break;
1857			case AF_INET6:
1858				if (inet_ntop(AF_INET6,
1859				    (void *)&newrt->ri_dst.sin6.sin6_addr,
1860				    obuf, INET6_ADDRSTRLEN) != NULL) {
1861					(void) printf(" %s", obuf);
1862					break;
1863				}
1864				/* FALLTHROUGH */
1865			default:
1866				(void) printf("%s", newrt->ri_dest_str);
1867				break;
1868			}
1869		} else {
1870			(void) printf("%s", newrt->ri_dest_str);
1871		}
1872	}
1873
1874	if (ret != 0) {
1875		switch (oerrno) {
1876		case ESRCH:
1877			err = "not in table";
1878			break;
1879		case EBUSY:
1880			err = "entry in use";
1881			break;
1882		case ENOBUFS:
1883			err = "routing table overflow";
1884			break;
1885		case EEXIST:
1886			err = "entry exists";
1887			break;
1888		case EPERM:
1889			err = "insufficient privileges";
1890			break;
1891		default:
1892			err = strerror(oerrno);
1893			break;
1894		}
1895		(void) printf(": %s\n", err);
1896	}
1897
1898	del_rtcmd_irep(newrt);
1899
1900	return (oerrno);
1901#undef hp
1902}
1903
1904
1905/*
1906 * Convert a network number to the corresponding IP address.
1907 * If the RTA_NETMASK hasn't been specified yet set it based
1908 * on the class of address.
1909 */
1910static void
1911inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
1912{
1913	in_addr_t addr, mask;
1914
1915	if (net == 0) {
1916		mask = addr = 0;
1917	} else if (net < 128) {
1918		addr = net << IN_CLASSA_NSHIFT;
1919		mask = IN_CLASSA_NET;
1920	} else if (net < 65536) {
1921		addr = net << IN_CLASSB_NSHIFT;
1922		mask = IN_CLASSB_NET;
1923	} else if (net < 16777216L) {
1924		addr = net << IN_CLASSC_NSHIFT;
1925		mask = IN_CLASSC_NET;
1926	} else {
1927		addr = net;
1928		if ((addr & IN_CLASSA_HOST) == 0)
1929			mask =  IN_CLASSA_NET;
1930		else if ((addr & IN_CLASSB_HOST) == 0)
1931			mask =  IN_CLASSB_NET;
1932		else if ((addr & IN_CLASSC_HOST) == 0)
1933			mask =  IN_CLASSC_NET;
1934		else {
1935			if (IN_CLASSA(addr))
1936				mask =  IN_CLASSA_NET;
1937			else if (IN_CLASSB(addr))
1938				mask =  IN_CLASSB_NET;
1939			else if (IN_CLASSC(addr))
1940				mask =  IN_CLASSC_NET;
1941			else
1942				mask = IP_HOST_MASK;
1943			mask = inet_makesubnetmask(addr, mask);
1944		}
1945	}
1946	sin->sin_addr.s_addr = htonl(addr);
1947
1948	/* Class E default mask is 32 */
1949	if (IN_CLASSE(addr))
1950		mask = IN_CLASSE_NET;
1951
1952	if (!(rcip->ri_addrs & RTA_NETMASK)) {
1953		rcip->ri_addrs |= RTA_NETMASK;
1954		sin = &rcip->ri_mask.sin;
1955		sin->sin_addr.s_addr = htonl(mask);
1956		sin->sin_family = AF_INET;
1957	}
1958}
1959
1960static in_addr_t
1961inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
1962{
1963	int n;
1964	struct ifconf ifc;
1965	struct ifreq ifreq;
1966	struct ifreq *ifr;
1967	struct sockaddr_in *sin;
1968	char *buf;
1969	int numifs;
1970	size_t bufsize;
1971	int iosoc;
1972	in_addr_t if_addr, if_mask;
1973	in_addr_t if_subnetmask = 0;
1974	short if_flags;
1975
1976	if (mask == 0)
1977		return (0);
1978	if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1979		quit("socket", errno);
1980	if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
1981		quit("ioctl", errno);
1982	bufsize = numifs * sizeof (struct ifreq);
1983	buf = malloc(bufsize);
1984	if (buf == NULL)
1985		quit("malloc", errno);
1986	(void) memset(&ifc, 0, sizeof (ifc));
1987	ifc.ifc_len = bufsize;
1988	ifc.ifc_buf = buf;
1989	if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
1990		quit("ioctl (get interface configuration)", errno);
1991	/* Let's check to see if this is maybe a local subnet route. */
1992	ifr = ifc.ifc_req;
1993	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
1994		ifreq = *ifr;
1995		/* LINTED */
1996		sin = (struct sockaddr_in *)&ifr->ifr_addr;
1997		if_addr = ntohl(sin->sin_addr.s_addr);
1998
1999		if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
2000			quit("ioctl (get interface flags)", errno);
2001		if ((ifreq.ifr_flags & IFF_UP) == 0)
2002			continue;
2003		if_flags = ifreq.ifr_flags;
2004
2005		if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
2006			quit("ioctl (get netmask)", errno);
2007		/* LINTED */
2008		sin = (struct sockaddr_in *)&ifreq.ifr_addr;
2009		if_mask = ntohl(sin->sin_addr.s_addr);
2010		if ((if_addr & mask) == (addr & mask)) {
2011			/*
2012			 * Don't trust pt-pt interfaces if there are
2013			 * other interfaces.
2014			 */
2015			if (if_flags & IFF_POINTOPOINT) {
2016				if_subnetmask = if_mask;
2017				continue;
2018			}
2019			/*
2020			 * Fine.  Just assume the same net mask as the
2021			 * directly attached subnet interface is using.
2022			 */
2023			return (if_mask);
2024		}
2025	}
2026	if (if_subnetmask != 0)
2027		return (if_subnetmask);
2028	return (mask);
2029}
2030
2031/*
2032 * Interpret an argument as a network address of some kind.
2033 *
2034 * If the address family is one looked up in getaddr() using one of the
2035 * getipnodebyX() functions (currently only AF_INET6), then callers should
2036 * freehostent() the returned "struct hostent" pointer if one was passed in.
2037 *
2038 * If exit_on_error is true, this function will cause route to exit on error by
2039 * calling syntax_error().  Otherwise, it returns B_TRUE on success or B_FALSE
2040 * on failure.
2041 */
2042static boolean_t
2043getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
2044{
2045	su_t *su;
2046	struct hostent **hpp;
2047	struct hostent *hp;
2048	int masklen;
2049
2050	if (which == RTA_GATEWAY) {
2051		hpp = &(rcip->ri_gate_hp);
2052	} else {
2053		hpp = &hp;
2054	}
2055	*hpp = NULL;
2056
2057	rcip->ri_addrs |= which;
2058	switch (which) {
2059	case RTA_DST:
2060		save_string(&rcip->ri_dest_str, s);
2061		su = &rcip->ri_dst;
2062		su->sa.sa_family = rcip->ri_af;
2063		break;
2064	case RTA_GATEWAY:
2065		save_string(&rcip->ri_gate_str, s);
2066		su = &rcip->ri_gate;
2067		su->sa.sa_family = rcip->ri_af;
2068		break;
2069	case RTA_NETMASK:
2070		su = &rcip->ri_mask;
2071		su->sa.sa_family = rcip->ri_af;
2072		break;
2073	case RTA_IFP:
2074		save_string(&rcip->ri_ifp_str, s);
2075		return (B_TRUE);
2076		/*
2077		 * RTA_SRC has overloaded meaning. It can represent the
2078		 * src address of incoming or outgoing packets.
2079		 */
2080	case RTA_IFA:
2081		su = &rcip->ri_ifa;
2082		su->sa.sa_family = rcip->ri_af;
2083		break;
2084	case RTA_SRC:
2085		su = &rcip->ri_src;
2086		su->sa.sa_family = rcip->ri_af;
2087		break;
2088	default:
2089		/* NOTREACHED */
2090		quit(gettext("Internal Error"), EINVAL);
2091		/* NOTREACHED */
2092	}
2093	if (strcmp(s, "default") == 0) {
2094		if (which == RTA_DST) {
2095			return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
2096		}
2097		if (which == RTA_SRC) {
2098			return (B_TRUE);
2099		}
2100		return (B_TRUE);
2101	}
2102	switch (rcip->ri_af) {
2103	case AF_LINK:
2104		link_addr(s, &su->sdl);
2105		return (B_TRUE);
2106	case PF_ROUTE:
2107		sockaddr(s, &su->sa);
2108		return (B_TRUE);
2109	case AF_INET6:
2110		switch (which) {
2111		case RTA_DST:
2112			if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
2113				return (B_FALSE);
2114			}
2115			if (masklen != NO_PREFIX) {
2116				(void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
2117				    sizeof (rcip->ri_mask.sin6.sin6_addr));
2118				if (!in_prefixlentomask(masklen, IPV6_ABITS,
2119				    (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
2120					syntax_error(gettext(
2121					    "route: bad prefix length: %d\n"),
2122					    masklen);
2123					return (B_FALSE);
2124				}
2125				rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2126				rcip->ri_addrs |= RTA_NETMASK;
2127			}
2128			return (B_TRUE);
2129		case RTA_GATEWAY:
2130		case RTA_IFA:
2131		case RTA_SRC:
2132			return (in6_getaddr(s, &su->sin6, NULL, hpp));
2133		case RTA_NETMASK:
2134			syntax_error(
2135			    gettext("route: -netmask not supported for IPv6: "
2136			    "use <prefix>/<prefix-length> instead\n"));
2137			return (B_FALSE);
2138		default:
2139			quit(gettext("Internal Error"), EINVAL);
2140			/* NOTREACHED */
2141		}
2142	case AF_INET:
2143		switch (which) {
2144		case RTA_DST:
2145			if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
2146			    atype, rcip)) {
2147				return (B_FALSE);
2148			}
2149			if (masklen != NO_PREFIX) {
2150				(void) memset(&rcip->ri_mask.sin.sin_addr, 0,
2151				    sizeof (rcip->ri_mask.sin.sin_addr));
2152				if (!in_prefixlentomask(masklen, IP_ABITS,
2153				    (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
2154					syntax_error(gettext(
2155					    "route: bad prefix length: %d\n"),
2156					    masklen);
2157					return (B_FALSE);
2158				}
2159				rcip->ri_mask.sin.sin_family = rcip->ri_af;
2160				rcip->ri_addrs |= RTA_NETMASK;
2161			}
2162			return (B_TRUE);
2163		case RTA_GATEWAY:
2164		case RTA_IFA:
2165		case RTA_NETMASK:
2166		case RTA_SRC:
2167			return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2168			    rcip));
2169		default:
2170			quit(gettext("Internal Error"), EINVAL);
2171			/* NOTREACHED */
2172		}
2173	default:
2174		quit(gettext("Internal Error"), EINVAL);
2175		/* NOTREACHED */
2176	}
2177	return (B_TRUE);
2178}
2179
2180/*
2181 * Interpret an argument as an IPv4 network address of some kind,
2182 * returning B_TRUE on success or B_FALSE on failure.
2183 * This function will cause an exit() on failure if exit_on_failure is set.
2184 *
2185 * Note that this tries host interpretation before network interpretation,
2186 * except when -net has been given and the destination address is being parsed.
2187 *
2188 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2189 * pass out <n> in *plenp.
2190 * If <n> doesn't parse return BAD_ADDR as *plenp.
2191 * If no /<n> is present return NO_PREFIX as *plenp.
2192 */
2193static boolean_t
2194in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
2195    struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
2196{
2197	struct hostent *hp;
2198	struct netent *np;
2199	in_addr_t val;
2200	char str[BUFSIZ];
2201
2202	(void) strlcpy(str, s, sizeof (str));
2203
2204	/*
2205	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2206	 */
2207	if (plenp != NULL) {
2208		char *cp;
2209
2210		*plenp = in_getprefixlen(str, IP_ABITS);
2211		if (*plenp == BAD_ADDR)
2212			return (B_FALSE);
2213		cp = strchr(str, '/');
2214		if (cp != NULL)
2215			*cp = '\0';
2216	} else if (strchr(str, '/') != NULL) {
2217		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2218		return (B_FALSE);
2219	}
2220
2221	(void) memset(sin, 0, sizeof (*sin));
2222	sin->sin_family = AF_INET;
2223
2224	/*
2225	 * Handle 255.255.255.255 as a special case first.
2226	 */
2227	if (strcmp(str, "255.255.255.255") == 0) {
2228		sin->sin_addr.s_addr = INADDR_BROADCAST;
2229		return (B_TRUE);
2230	}
2231
2232	val = inet_addr(str);
2233	if (val != (in_addr_t)-1) {
2234		/* Numeric address */
2235		sin->sin_addr.s_addr = val;
2236		if (which == RTA_DST) {
2237			if (atype == ADDR_TYPE_NET ||
2238			    (atype == ADDR_TYPE_ANY &&
2239			    inet_lnaof(sin->sin_addr) == INADDR_ANY)) {
2240				/* This looks like a network address. */
2241				inet_makenetandmask(rcip, ntohl(val),
2242				    sin);
2243			}
2244		}
2245		return (B_TRUE);
2246	}
2247	/* Host or net name */
2248	if (which != RTA_DST || atype != ADDR_TYPE_NET) {
2249		/* A host name is allowed. */
2250		if ((hp = gethostbyname(str)) != NULL) {
2251			*hpp = hp;
2252			(void) memmove(&sin->sin_addr, hp->h_addr,
2253			    hp->h_length);
2254			return (B_TRUE);
2255		}
2256	}
2257	if (atype != ADDR_TYPE_HOST) {
2258		/* A network name is allowed */
2259		if ((np = getnetbyname(str)) != NULL &&
2260		    (val = np->n_net) != 0) {
2261			if (which == RTA_DST) {
2262				inet_makenetandmask(rcip, val, sin);
2263			}
2264			return (B_TRUE);
2265		}
2266	}
2267	syntax_error(gettext("%s: bad value\n"), s);
2268	return (B_FALSE);
2269}
2270
2271/*
2272 * Interpret an argument as an IPv6 network address of some kind,
2273 * returning B_TRUE on success or B_FALSE on failure.
2274 * This function will cause an exit() on failure if exit_on_failure is set.
2275 *
2276 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2277 * pass out <n> in *plenp.
2278 * If <n> doesn't parse return BAD_ADDR as *plenp.
2279 * If no /<n> is present return NO_PREFIX as *plenp.
2280 */
2281static boolean_t
2282in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
2283    struct hostent **hpp)
2284{
2285	struct hostent *hp;
2286	char str[BUFSIZ];
2287	int error_num;
2288
2289	(void) strlcpy(str, s, sizeof (str));
2290
2291	/*
2292	 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2293	 */
2294	if (plenp != NULL) {
2295		char *cp;
2296
2297		*plenp = in_getprefixlen(str, IPV6_ABITS);
2298		if (*plenp == BAD_ADDR)
2299			return (B_FALSE);
2300		cp = strchr(str, '/');
2301		if (cp != NULL)
2302			*cp = '\0';
2303	} else if (strchr(str, '/') != NULL) {
2304		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2305		return (B_FALSE);
2306	}
2307
2308	(void) memset(sin6, 0, sizeof (struct sockaddr_in6));
2309	sin6->sin6_family = AF_INET6;
2310
2311	hp = getipnodebyname(str, AF_INET6, 0, &error_num);
2312	if (hp != NULL) {
2313		*hpp = hp;
2314		(void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
2315		return (B_TRUE);
2316	}
2317	if (error_num == TRY_AGAIN) {
2318		/*
2319		 * This isn't a problem if we aren't going to use the address
2320		 * right away.
2321		 */
2322		if (!exit_on_error) {
2323			return (B_TRUE);
2324		}
2325		syntax_error(gettext("route: %s: bad address (try "
2326		    "again later)\n"), s);
2327		return (B_FALSE);
2328	}
2329	syntax_error(gettext("route: %s: bad address\n"), s);
2330	return (B_FALSE);
2331}
2332
2333/*
2334 * Parse <addr>/<n> syntax and return the integer n.
2335 * If <addr> is missing or <n> is not a valid integer, this function calls
2336 * syntax_error() and returns BAD_ADDR.
2337 * if n is not between 0 and max_plen inclusive, this functions calls
2338 * syntax_error() and returns BAD_ADDR.
2339 * If /<n> is not present, this function returns NO_PREFIX.
2340 * The string addr is not modified.
2341 */
2342int
2343in_getprefixlen(char *addr, int max_plen)
2344{
2345	int prefixlen;
2346	char *str, *end;
2347
2348	str = strchr(addr, '/');
2349	if (str == addr) {
2350		syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
2351		return (BAD_ADDR);
2352	}
2353	if (str == NULL)
2354		return (NO_PREFIX);
2355	str++;
2356
2357	errno = 0;
2358	prefixlen = strtoul(str, &end, 10);
2359	if (errno != 0 || str == end) {
2360		syntax_error(gettext("route: bad prefix length %s\n"), str);
2361		return (BAD_ADDR);
2362	}
2363	if (prefixlen > max_plen) {
2364		syntax_error(gettext("route: prefix length %s out of range\n"),
2365		    str);
2366		return (BAD_ADDR);
2367	}
2368	return (prefixlen);
2369}
2370
2371/*
2372 * Convert a prefix length to a mask.
2373 * Returns B_TRUE if ok. B_FALSE otherwise.
2374 * Assumes the mask array is zeroed by the caller.
2375 */
2376boolean_t
2377in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
2378{
2379	if (prefixlen < 0 || prefixlen > maxlen)
2380		return (B_FALSE);
2381
2382	while (prefixlen > 0) {
2383		if (prefixlen >= 8) {
2384			*mask++ = 0xFF;
2385			prefixlen -= 8;
2386			continue;
2387		}
2388		*mask |= 1 << (8 - prefixlen);
2389		prefixlen--;
2390	}
2391	return (B_TRUE);
2392}
2393
2394void
2395rtmonitor(int argc, char *argv[])
2396{
2397	int n;
2398	intmax_t msg[2048 / sizeof (intmax_t)];
2399
2400	if (tflag)
2401		exit(0);
2402	verbose = B_TRUE;
2403	if (argc > 1) {
2404		argv++;
2405		if (argc == 2 && **argv == '-') {
2406			switch (keyword(*argv + 1)) {
2407			case K_INET:
2408				af = AF_INET;
2409				break;
2410			case K_LINK:
2411				af = AF_LINK;
2412				break;
2413			case K_INET6:
2414				af = AF_INET6;
2415				break;
2416			default:
2417				usage(*argv);
2418				/* NOTREACHED */
2419			}
2420		} else {
2421			usage(*argv);
2422		}
2423		(void) close(s);
2424		s = socket(PF_ROUTE, SOCK_RAW, af);
2425		if (s < 0)
2426			quit("socket", errno);
2427	}
2428	for (;;) {
2429		n = read(s, msg, sizeof (msg));
2430		if (n <= 0)
2431			quit("read", errno);
2432		(void) printf("got message of size %d\n", n);
2433		print_rtmsg((struct rt_msghdr *)msg, n);
2434	}
2435}
2436
2437int
2438rtmsg(rtcmd_irep_t *newrt)
2439{
2440	static int seq;
2441	int rlen;
2442	char *cp = m_rtmsg.m_space;
2443	int l;
2444
2445	errno = 0;
2446	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
2447
2448	if (newrt->ri_cmd == RTM_GET) {
2449		newrt->ri_ifp.sa.sa_family = AF_LINK;
2450		newrt->ri_addrs |= RTA_IFP;
2451	}
2452
2453#define	rtm m_rtmsg.m_rtm
2454	rtm.rtm_type = newrt->ri_cmd;
2455	rtm.rtm_flags = newrt->ri_flags;
2456	rtm.rtm_version = RTM_VERSION;
2457	rtm.rtm_seq = ++seq;
2458	rtm.rtm_addrs = newrt->ri_addrs;
2459	rtm.rtm_rmx = newrt->ri_metrics;
2460	rtm.rtm_inits = newrt->ri_inits;
2461
2462#define	NEXTADDR(w, u) \
2463	if (newrt->ri_addrs & (w)) { \
2464		l = ROUNDUP_LONG(salen(&u.sa)); \
2465		(void) memmove(cp, &(u), l); \
2466		cp += l; \
2467		if (verbose) \
2468			sodump(&(u), #u); \
2469	}
2470	NEXTADDR(RTA_DST, newrt->ri_dst);
2471	NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
2472	NEXTADDR(RTA_NETMASK, newrt->ri_mask);
2473	NEXTADDR(RTA_IFP, newrt->ri_ifp);
2474	NEXTADDR(RTA_IFA, newrt->ri_ifa);
2475	/*
2476	 * RTA_SRC has overloaded meaning. It can represent the
2477	 * src address of incoming or outgoing packets.
2478	 */
2479	NEXTADDR(RTA_SRC, newrt->ri_src);
2480#undef	NEXTADDR
2481
2482	if (newrt->ri_rtsa_cnt > 0) {
2483		/* LINTED: aligned */
2484		rtm_ext_t *rtm_ext = (rtm_ext_t *)cp;
2485		tsol_rtsecattr_t *rtsecattr;
2486
2487		rtm_ext->rtmex_type = RTMEX_GATEWAY_SECATTR;
2488		rtm_ext->rtmex_len = TSOL_RTSECATTR_SIZE(1);
2489
2490		rtsecattr = (tsol_rtsecattr_t *)(rtm_ext + 1);
2491		rtsecattr->rtsa_cnt = 1;
2492
2493		bcopy(&newrt->ri_rtsa, rtsecattr->rtsa_attr,
2494		    sizeof (newrt->ri_rtsa));
2495		cp = (char *)(rtsecattr->rtsa_attr + 1);
2496	}
2497
2498	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
2499
2500	if (verbose)
2501		print_rtmsg(&rtm, l);
2502	if (debugonly)
2503		return (0);
2504	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
2505		switch (errno) {
2506		case ESRCH:
2507		case EBUSY:
2508		case ENOBUFS:
2509		case EEXIST:
2510		case ENETUNREACH:
2511		case EHOSTUNREACH:
2512		case EPERM:
2513			break;
2514		default:
2515			perror(gettext("writing to routing socket"));
2516			break;
2517		}
2518		return (-1);
2519	} else if (rlen < (int)rtm.rtm_msglen) {
2520		(void) fprintf(stderr,
2521		    gettext("route: write to routing socket got only %d for "
2522		    "len\n"), rlen);
2523		return (-1);
2524	}
2525	if (newrt->ri_cmd == RTM_GET) {
2526		do {
2527			l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
2528		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
2529		if (l < 0) {
2530			(void) fprintf(stderr,
2531			    gettext("route: read from routing socket: %s\n"),
2532			    strerror(errno));
2533		} else {
2534			print_getmsg(newrt, &rtm, l);
2535		}
2536	}
2537#undef rtm
2538	return (0);
2539}
2540
2541static char *msgtypes[] = {
2542	"",
2543	"RTM_ADD: Add Route",
2544	"RTM_DELETE: Delete Route",
2545	"RTM_CHANGE: Change Metrics or flags",
2546	"RTM_GET: Report Metrics",
2547	"RTM_LOSING: Kernel Suspects Partitioning",
2548	"RTM_REDIRECT: Told to use different route",
2549	"RTM_MISS: Lookup failed on this address",
2550	"RTM_LOCK: fix specified metrics",
2551	"RTM_OLDADD: caused by SIOCADDRT",
2552	"RTM_OLDDEL: caused by SIOCDELRT",
2553	"RTM_RESOLVE: Route created by cloning",
2554	"RTM_NEWADDR: address being added to iface",
2555	"RTM_DELADDR: address being removed from iface",
2556	"RTM_IFINFO: iface status change",
2557	0,
2558};
2559
2560#define	NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2561
2562static char metricnames[] =
2563"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2564	"\1mtu";
2565static char routeflags[] =
2566"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2567	"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2568	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT";
2569static char ifnetflags[] =
2570"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2571	"\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2572	"\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2573	"\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2574	"\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2575	"\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2576	"\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2577	"\047DUPLICATE";
2578static char addrnames[] =
2579"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2580
2581void
2582print_rtmsg(struct rt_msghdr *rtm, int msglen)
2583{
2584	struct if_msghdr *ifm;
2585	struct ifa_msghdr *ifam;
2586
2587	if (!verbose)
2588		return;
2589	if (rtm->rtm_version != RTM_VERSION) {
2590		(void) printf("routing message version %d not understood\n",
2591		    rtm->rtm_version);
2592		return;
2593	}
2594	if (rtm->rtm_msglen != msglen) {
2595		(void) printf("message length mismatch, in packet %d, "
2596		    "returned %d\n",
2597		    rtm->rtm_msglen, msglen);
2598		if (msglen > rtm->rtm_msglen)
2599			msglen = rtm->rtm_msglen;
2600	}
2601	/*
2602	 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2603	 * and the upper-bound of (NMSGTYPES - 1).
2604	 */
2605	if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
2606		(void) printf("routing message type %d not understood\n",
2607		    rtm->rtm_type);
2608		return;
2609	}
2610	(void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
2611	switch (rtm->rtm_type) {
2612	case RTM_IFINFO:
2613		ifm = (struct if_msghdr *)rtm;
2614		(void) printf("if# %d, flags:", ifm->ifm_index);
2615		bprintf(stdout, ifm->ifm_flags, ifnetflags);
2616		pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm),
2617		    ifm->ifm_addrs);
2618		break;
2619	case RTM_NEWADDR:
2620	case RTM_DELADDR:
2621		ifam = (struct ifa_msghdr *)rtm;
2622		(void) printf("metric %d, flags:", ifam->ifam_metric);
2623		bprintf(stdout, ifam->ifam_flags, routeflags);
2624		pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam),
2625		    ifam->ifam_addrs);
2626		break;
2627	default:
2628		(void) printf("pid: %ld, seq %d, errno %d, flags:",
2629		    rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
2630		bprintf(stdout, rtm->rtm_flags, routeflags);
2631		pmsg_common(rtm, msglen);
2632		break;
2633	}
2634}
2635
2636void
2637print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
2638{
2639	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
2640	struct sockaddr_dl *ifp = NULL;
2641	struct sockaddr *sa;
2642	char *cp;
2643	int i;
2644
2645	(void) printf("   route to: %s\n", routename(&req_rt->ri_dst.sa));
2646	if (rtm->rtm_version != RTM_VERSION) {
2647		(void) fprintf(stderr,
2648		    gettext("routing message version %d not understood\n"),
2649		    rtm->rtm_version);
2650		return;
2651	}
2652	if (rtm->rtm_msglen > (ushort_t)msglen) {
2653		(void) fprintf(stderr,
2654		    gettext("message length mismatch, in packet %d, "
2655		    "returned %d\n"), rtm->rtm_msglen, msglen);
2656	}
2657	if (rtm->rtm_errno)  {
2658		(void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
2659		    strerror(rtm->rtm_errno), rtm->rtm_errno);
2660		return;
2661	}
2662	cp = ((char *)(rtm + 1));
2663	if (rtm->rtm_addrs != 0) {
2664		for (i = 1; i != 0; i <<= 1) {
2665			if (i & rtm->rtm_addrs) {
2666				/* LINTED */
2667				sa = (struct sockaddr *)cp;
2668				switch (i) {
2669				case RTA_DST:
2670					dst = sa;
2671					break;
2672				case RTA_GATEWAY:
2673					gate = sa;
2674					break;
2675				case RTA_NETMASK:
2676					mask = sa;
2677					break;
2678				case RTA_IFP:
2679					if (sa->sa_family == AF_LINK &&
2680					    ((struct sockaddr_dl *)sa)->
2681					    sdl_nlen != 0)
2682						ifp = (struct sockaddr_dl *)sa;
2683					break;
2684				case RTA_SRC:
2685					src = sa;
2686					break;
2687				}
2688				ADVANCE(cp, sa);
2689			}
2690		}
2691	}
2692	if (dst != NULL && mask != NULL)
2693		mask->sa_family = dst->sa_family;	/* XXX */
2694	if (dst != NULL)
2695		(void) printf("destination: %s\n", routename(dst));
2696	if (mask != NULL) {
2697		boolean_t savenflag = nflag;
2698
2699		nflag = B_TRUE;
2700		(void) printf("       mask: %s\n", routename(mask));
2701		nflag = savenflag;
2702	}
2703	if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
2704		(void) printf("    gateway: %s\n", routename(gate));
2705	if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
2706		(void) printf("     setsrc: %s\n", routename(src));
2707	if (ifp != NULL) {
2708		if (verbose) {
2709			int i;
2710
2711			(void) printf("  interface: %.*s index %d address ",
2712			    ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
2713			for (i = ifp->sdl_nlen;
2714			    i < ifp->sdl_nlen + ifp->sdl_alen;
2715			    i++) {
2716				(void) printf("%02x ",
2717				    ifp->sdl_data[i] & 0xFF);
2718			}
2719			(void) printf("\n");
2720		} else {
2721			(void) printf("  interface: %.*s\n",
2722			    ifp->sdl_nlen, ifp->sdl_data);
2723		}
2724	}
2725	(void) printf("      flags: ");
2726	bprintf(stdout, rtm->rtm_flags, routeflags);
2727
2728#define	lock(f)	((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2729#define	msec(u)	(((u) + 500) / 1000)		/* usec to msec */
2730
2731	(void) printf("\n%s\n", " recvpipe  sendpipe  ssthresh    rtt,ms "
2732	    "rttvar,ms  hopcount      mtu     expire");
2733	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
2734	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
2735	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
2736	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
2737	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
2738	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
2739	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
2740	if (rtm->rtm_rmx.rmx_expire)
2741		rtm->rtm_rmx.rmx_expire -= time(0);
2742	(void) printf("%8d%c", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
2743#undef lock
2744#undef msec
2745#define	RTA_IGN	\
2746	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2747	if (verbose) {
2748		pmsg_common(rtm, msglen);
2749	} else {
2750		const char *sptr, *endptr;
2751		const struct sockaddr *sa;
2752		uint_t addrs;
2753
2754		/* Not verbose; just print out the exceptional cases */
2755		if (rtm->rtm_addrs &~ RTA_IGN) {
2756			(void) printf("\nsockaddrs: ");
2757			bprintf(stdout, rtm->rtm_addrs, addrnames);
2758		}
2759		sptr = (const char *)(rtm + 1);
2760		endptr = (const char *)rtm + msglen;
2761		addrs = rtm->rtm_addrs;
2762		while (addrs != 0 && sptr + sizeof (*sa) <= endptr) {
2763			addrs &= addrs - 1;
2764			/* LINTED */
2765			sa = (const struct sockaddr *)sptr;
2766			ADVANCE(sptr, sa);
2767		}
2768		if (addrs == 0)
2769			pmsg_secattr(sptr, endptr - sptr, "    secattr: ");
2770		(void) putchar('\n');
2771	}
2772#undef	RTA_IGN
2773}
2774
2775static void
2776pmsg_common(const struct rt_msghdr *rtm, size_t msglen)
2777{
2778	(void) printf("\nlocks: ");
2779	bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
2780	(void) printf(" inits: ");
2781	bprintf(stdout, (int)rtm->rtm_inits, metricnames);
2782	pmsg_addrs((const char *)(rtm + 1), msglen - sizeof (*rtm),
2783	    rtm->rtm_addrs);
2784}
2785
2786static void
2787pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
2788{
2789	const struct sockaddr *sa;
2790	const char *maxptr;
2791	int i;
2792
2793	if (addrs != 0) {
2794		(void) printf("\nsockaddrs: ");
2795		bprintf(stdout, addrs, addrnames);
2796		(void) putchar('\n');
2797		maxptr = cp + msglen;
2798		for (i = 1; i != 0 && cp + sizeof (*sa) <= maxptr; i <<= 1) {
2799			if (i & addrs) {
2800				/* LINTED */
2801				sa = (const struct sockaddr *)cp;
2802				(void) printf(" %s", routename(sa));
2803				ADVANCE(cp, sa);
2804			}
2805		}
2806		if (i != 0)
2807			msglen = 0;
2808		else
2809			msglen = maxptr - cp;
2810	}
2811	pmsg_secattr(cp, msglen, "secattr: ");
2812	(void) putchar('\n');
2813	(void) fflush(stdout);
2814}
2815
2816void
2817bprintf(FILE *fp, int b, char *s)
2818{
2819	int i;
2820	boolean_t gotsome = B_FALSE;
2821
2822	if (b == 0)
2823		return;
2824	while ((i = *s++) != 0) {
2825		if (b & (1 << (i - 1))) {
2826			if (!gotsome)
2827				i = '<';
2828			else
2829				i = ',';
2830			(void) putc(i, fp);
2831			gotsome = B_TRUE;
2832			for (; (i = *s) > ' '; s++)
2833				(void) putc(i, fp);
2834		} else {
2835			while (*s > ' ')
2836				s++;
2837		}
2838	}
2839	if (gotsome)
2840		(void) putc('>', fp);
2841}
2842
2843int
2844keyword(const char *cp)
2845{
2846	struct keytab *kt = keywords;
2847
2848	while (kt->kt_cp && strcmp(kt->kt_cp, cp))
2849		kt++;
2850	return (kt->kt_i);
2851}
2852
2853void
2854sodump(su_t *su, char *which)
2855{
2856	static char obuf[INET6_ADDRSTRLEN];
2857
2858	switch (su->sa.sa_family) {
2859	case AF_LINK:
2860		(void) printf("%s: link %s; ",
2861		    which, link_ntoa(&su->sdl));
2862		break;
2863	case AF_INET:
2864		(void) printf("%s: inet %s; ",
2865		    which, inet_ntoa(su->sin.sin_addr));
2866		break;
2867	case AF_INET6:
2868		if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
2869		    INET6_ADDRSTRLEN) != NULL) {
2870			(void) printf("%s: inet6 %s; ", which, obuf);
2871			break;
2872		}
2873		/* FALLTHROUGH */
2874	default:
2875		quit(gettext("Internal Error"), EINVAL);
2876		/* NOTREACHED */
2877	}
2878	(void) fflush(stdout);
2879}
2880
2881/* States */
2882#define	VIRGIN	0
2883#define	GOTONE	1
2884#define	GOTTWO	2
2885#define	RESET	3
2886/* Inputs */
2887#define	DIGIT	(4*0)
2888#define	END	(4*1)
2889#define	DELIM	(4*2)
2890#define	LETTER	(4*3)
2891
2892void
2893sockaddr(char *addr, struct sockaddr *sa)
2894{
2895	char *cp = (char *)sa;
2896	int size = salen(sa);
2897	char *cplim = cp + size;
2898	int byte = 0, state = VIRGIN, new;
2899
2900	(void) memset(cp, 0, size);
2901	cp++;
2902	do {
2903		if ((*addr >= '0') && (*addr <= '9')) {
2904			new = *addr - '0';
2905		} else if ((*addr >= 'a') && (*addr <= 'f')) {
2906			new = *addr - 'a' + 10;
2907		} else if ((*addr >= 'A') && (*addr <= 'F')) {
2908			new = *addr - 'A' + 10;
2909		} else if (*addr == 0) {
2910			state |= END;
2911		} else {
2912			state |= DELIM;
2913		}
2914		addr++;
2915		switch (state /* | INPUT */) {
2916		case GOTTWO | DIGIT:
2917			*cp++ = byte;
2918			/* FALLTHROUGH */
2919		case VIRGIN | DIGIT:
2920			state = GOTONE; byte = new; continue;
2921		case GOTONE | DIGIT:
2922			state = GOTTWO; byte = new + (byte << 4); continue;
2923		default: /* | DELIM */
2924			state = VIRGIN; *cp++ = byte; byte = 0; continue;
2925		case GOTONE | END:
2926		case GOTTWO | END:
2927			*cp++ = byte;
2928			/* FALLTHROUGH */
2929		case VIRGIN | END:
2930			break;
2931		}
2932		break;
2933	} while (cp < cplim);
2934}
2935
2936int
2937salen(const struct sockaddr *sa)
2938{
2939	switch (sa->sa_family) {
2940	case AF_INET:
2941		return (sizeof (struct sockaddr_in));
2942	case AF_LINK:
2943		return (sizeof (struct sockaddr_dl));
2944	case AF_INET6:
2945		return (sizeof (struct sockaddr_in6));
2946	default:
2947		return (sizeof (struct sockaddr));
2948	}
2949}
2950
2951void
2952link_addr(const char *addr, struct sockaddr_dl *sdl)
2953{
2954	char *cp = sdl->sdl_data;
2955	char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
2956	int byte = 0, state = VIRGIN, new;
2957
2958	(void) memset(sdl, 0, sizeof (struct sockaddr_dl));
2959	sdl->sdl_family = AF_LINK;
2960	do {
2961		state &= ~LETTER;
2962		if ((*addr >= '0') && (*addr <= '9')) {
2963			new = *addr - '0';
2964		} else if ((*addr >= 'a') && (*addr <= 'f')) {
2965			new = *addr - 'a' + 10;
2966		} else if ((*addr >= 'A') && (*addr <= 'F')) {
2967			new = *addr - 'A' + 10;
2968		} else if (*addr == 0) {
2969			state |= END;
2970		} else if (state == VIRGIN &&
2971		    (((*addr >= 'A') && (*addr <= 'Z')) ||
2972		    ((*addr >= 'a') && (*addr <= 'z')))) {
2973			state |= LETTER;
2974		} else {
2975			state |= DELIM;
2976		}
2977		addr++;
2978		switch (state /* | INPUT */) {
2979		case VIRGIN | DIGIT:
2980		case VIRGIN | LETTER:
2981			*cp++ = addr[-1];
2982			continue;
2983		case VIRGIN | DELIM:
2984			state = RESET;
2985			sdl->sdl_nlen = cp - sdl->sdl_data;
2986			continue;
2987		case GOTTWO | DIGIT:
2988			*cp++ = byte;
2989			/* FALLTHROUGH */
2990		case RESET | DIGIT:
2991			state = GOTONE;
2992			byte = new;
2993			continue;
2994		case GOTONE | DIGIT:
2995			state = GOTTWO;
2996			byte = new + (byte << 4);
2997			continue;
2998		default: /* | DELIM */
2999			state = RESET;
3000			*cp++ = byte;
3001			byte = 0;
3002			continue;
3003		case GOTONE | END:
3004		case GOTTWO | END:
3005			*cp++ = byte;
3006			/* FALLTHROUGH */
3007		case RESET | END:
3008			break;
3009		}
3010		break;
3011	} while (cp < cplim);
3012	sdl->sdl_alen = cp - LLADDR(sdl);
3013}
3014
3015static char hexlist[] = "0123456789abcdef";
3016
3017char *
3018link_ntoa(const struct sockaddr_dl *sdl)
3019{
3020	static char obuf[64];
3021	char *out = obuf;
3022	int i;
3023	uchar_t *in = (uchar_t *)LLADDR(sdl);
3024	uchar_t *inlim = in + sdl->sdl_alen;
3025	boolean_t firsttime = B_TRUE;
3026
3027	if (sdl->sdl_nlen) {
3028		(void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
3029		out += sdl->sdl_nlen;
3030		if (sdl->sdl_alen)
3031			*out++ = ':';
3032	}
3033	while (in < inlim) {
3034		if (firsttime)
3035			firsttime = B_FALSE;
3036		else
3037			*out++ = '.';
3038		i = *in++;
3039		if (i > 0xf) {
3040			out[1] = hexlist[i & 0xf];
3041			i >>= 4;
3042			out[0] = hexlist[i];
3043			out += 2;
3044		} else {
3045			*out++ = hexlist[i];
3046		}
3047	}
3048	*out = 0;
3049	return (obuf);
3050}
3051
3052static mib_item_t *
3053mibget(int sd)
3054{
3055	intmax_t		buf[512 / sizeof (intmax_t)];
3056	int			flags;
3057	int			i, j, getcode;
3058	struct strbuf		ctlbuf, databuf;
3059	struct T_optmgmt_req	*tor = (struct T_optmgmt_req *)buf;
3060	struct T_optmgmt_ack	*toa = (struct T_optmgmt_ack *)buf;
3061	struct T_error_ack	*tea = (struct T_error_ack *)buf;
3062	struct opthdr		*req;
3063	mib_item_t		*first_item = NULL;
3064	mib_item_t		*last_item  = NULL;
3065	mib_item_t		*temp;
3066
3067	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
3068	tor->OPT_offset = sizeof (struct T_optmgmt_req);
3069	tor->OPT_length = sizeof (struct opthdr);
3070	tor->MGMT_flags = T_CURRENT;
3071	req = (struct opthdr *)&tor[1];
3072	req->level = MIB2_IP;		/* any MIB2_xxx value ok here */
3073	req->name  = 0;
3074	req->len   = 0;
3075
3076	ctlbuf.buf = (char *)buf;
3077	ctlbuf.len = tor->OPT_length + tor->OPT_offset;
3078	flags = 0;
3079	if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
3080		perror("mibget: putmsg (ctl)");
3081		return (NULL);
3082	}
3083	/*
3084	 * each reply consists of a ctl part for one fixed structure
3085	 * or table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK,
3086	 * containing an opthdr structure.  level/name identify the entry,
3087	 * len is the size of the data part of the message.
3088	 */
3089	req = (struct opthdr *)&toa[1];
3090	ctlbuf.maxlen = sizeof (buf);
3091	for (j = 1; ; j++) {
3092		flags = 0;
3093		getcode = getmsg(sd, &ctlbuf, NULL, &flags);
3094		if (getcode < 0) {
3095			perror("mibget: getmsg (ctl)");
3096			if (verbose) {
3097				(void) fprintf(stderr,
3098				    "#   level   name    len\n");
3099				i = 0;
3100				for (last_item = first_item; last_item != NULL;
3101				    last_item = last_item->next_item) {
3102					(void) printf("%d  %4ld   %5ld   %ld\n",
3103					    ++i, last_item->group,
3104					    last_item->mib_id,
3105					    last_item->length);
3106				}
3107			}
3108			break;
3109		}
3110		if (getcode == 0 &&
3111		    ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
3112		    toa->PRIM_type == T_OPTMGMT_ACK &&
3113		    toa->MGMT_flags == T_SUCCESS &&
3114		    req->len == 0) {
3115			if (verbose) {
3116				(void) printf("mibget getmsg() %d returned EOD "
3117				    "(level %lu, name %lu)\n", j, req->level,
3118				    req->name);
3119			}
3120			return (first_item);		/* this is EOD msg */
3121		}
3122
3123		if (ctlbuf.len >= sizeof (struct T_error_ack) &&
3124		    tea->PRIM_type == T_ERROR_ACK) {
3125			(void) fprintf(stderr, gettext("mibget %d gives "
3126			    "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3127			    "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
3128			errno = (tea->TLI_error == TSYSERR) ?
3129			    tea->UNIX_error : EPROTO;
3130			break;
3131		}
3132
3133		if (getcode != MOREDATA ||
3134		    ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
3135		    toa->PRIM_type != T_OPTMGMT_ACK ||
3136		    toa->MGMT_flags != T_SUCCESS) {
3137			(void) printf("mibget getmsg(ctl) %d returned %d, "
3138			    "ctlbuf.len = %d, PRIM_type = %ld\n",
3139			    j, getcode, ctlbuf.len, toa->PRIM_type);
3140			if (toa->PRIM_type == T_OPTMGMT_ACK) {
3141				(void) printf("T_OPTMGMT_ACK: "
3142				    "MGMT_flags = 0x%lx, req->len = %ld\n",
3143				    toa->MGMT_flags, req->len);
3144			}
3145			errno = ENOMSG;
3146			break;
3147		}
3148
3149		temp = malloc(sizeof (mib_item_t));
3150		if (temp == NULL) {
3151			perror("mibget: malloc");
3152			break;
3153		}
3154		if (last_item != NULL)
3155			last_item->next_item = temp;
3156		else
3157			first_item = temp;
3158		last_item = temp;
3159		last_item->next_item = NULL;
3160		last_item->group = req->level;
3161		last_item->mib_id = req->name;
3162		last_item->length = req->len;
3163		last_item->valp = malloc(req->len);
3164		if (verbose) {
3165			(void) printf("msg %d:  group = %4ld   mib_id = %5ld   "
3166			    "length = %ld\n",
3167			    j, last_item->group, last_item->mib_id,
3168			    last_item->length);
3169		}
3170
3171		databuf.maxlen = last_item->length;
3172		databuf.buf    = (char *)last_item->valp;
3173		databuf.len    = 0;
3174		flags = 0;
3175		getcode = getmsg(sd, NULL, &databuf, &flags);
3176		if (getcode < 0) {
3177			perror("mibget: getmsg (data)");
3178			break;
3179		} else if (getcode != 0) {
3180			(void) printf("mibget getmsg(data) returned %d, "
3181			    "databuf.maxlen = %d, databuf.len = %d\n",
3182			    getcode, databuf.maxlen, databuf.len);
3183			break;
3184		}
3185	}
3186
3187	/*
3188	 * On error, free all the allocated mib_item_t objects.
3189	 */
3190	while (first_item != NULL) {
3191		last_item = first_item;
3192		first_item = first_item->next_item;
3193		free(last_item);
3194	}
3195	return (NULL);
3196}
3197
3198/*
3199 * print label security attributes for gateways.
3200 */
3201static void
3202pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr)
3203{
3204	rtm_ext_t rtm_ext;
3205	tsol_rtsecattr_t sp;
3206	struct rtsa_s *rtsa = &sp.rtsa_attr[0];
3207	const char *endptr;
3208	char buf[256];
3209	int i;
3210
3211	if (!is_system_labeled())
3212		return;
3213
3214	endptr = sptr + msglen;
3215
3216	for (;;) {
3217		if (sptr + sizeof (rtm_ext_t) + sizeof (sp) > endptr)
3218			return;
3219
3220		bcopy(sptr, &rtm_ext, sizeof (rtm_ext));
3221		sptr += sizeof (rtm_ext);
3222		if (rtm_ext.rtmex_type == RTMEX_GATEWAY_SECATTR)
3223			break;
3224		sptr += rtm_ext.rtmex_len;
3225	}
3226
3227	/* bail if this entry is corrupt or overruns buffer length */
3228	if (rtm_ext.rtmex_len < sizeof (sp) ||
3229	    sptr + rtm_ext.rtmex_len > endptr)
3230		return;
3231
3232	/* run up just to the end of this extension */
3233	endptr = sptr + rtm_ext.rtmex_len;
3234
3235	bcopy(sptr, &sp, sizeof (sp));
3236	sptr += sizeof (sp);
3237
3238	if (sptr + (sp.rtsa_cnt - 1) * sizeof (*rtsa) != endptr)
3239		return;
3240
3241	for (i = 0; i < sp.rtsa_cnt; i++) {
3242		if (i > 0) {
3243			/* first element is part of sp initalized above */
3244			bcopy(sptr, rtsa, sizeof (*rtsa));
3245			sptr += sizeof (*rtsa);
3246		}
3247		(void) printf("\n%s%s", labelstr, rtsa_to_str(rtsa, buf,
3248		    sizeof (buf)));
3249	}
3250}
3251