xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/route.c (revision 3f33f4f79cd49923b042b1d7c50a69056e05d3e6)
17c478bd9Sstevel@tonic-gate /*
2*3f33f4f7Sjl138328  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
77c478bd9Sstevel@tonic-gate /* All Rights Reserved	*/
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate /* Copyright (c) 1990  Mentat Inc. */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate /*
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1989, 1991, 1993
147c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
157c478bd9Sstevel@tonic-gate  *
167c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
177c478bd9Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
187c478bd9Sstevel@tonic-gate  * are met:
197c478bd9Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
207c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
217c478bd9Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
227c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
237c478bd9Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
247c478bd9Sstevel@tonic-gate  * 3. All advertising materials mentioning features or use of this software
257c478bd9Sstevel@tonic-gate  *    must display the following acknowledgement:
267c478bd9Sstevel@tonic-gate  *	This product includes software developed by the University of
277c478bd9Sstevel@tonic-gate  *	California, Berkeley and its contributors.
287c478bd9Sstevel@tonic-gate  * 4. Neither the name of the University nor the names of its contributors
297c478bd9Sstevel@tonic-gate  *    may be used to endorse or promote products derived from this software
307c478bd9Sstevel@tonic-gate  *    without specific prior written permission.
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
337c478bd9Sstevel@tonic-gate  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
347c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
357c478bd9Sstevel@tonic-gate  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
367c478bd9Sstevel@tonic-gate  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
377c478bd9Sstevel@tonic-gate  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
387c478bd9Sstevel@tonic-gate  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
397c478bd9Sstevel@tonic-gate  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
407c478bd9Sstevel@tonic-gate  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
417c478bd9Sstevel@tonic-gate  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
427c478bd9Sstevel@tonic-gate  * SUCH DAMAGE.
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  *	@(#)route.c	8.6 (Berkeley) 4/28/95
457c478bd9Sstevel@tonic-gate  *	@(#)linkaddr.c	8.1 (Berkeley) 6/4/93
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include <sys/param.h>
517c478bd9Sstevel@tonic-gate #include <sys/file.h>
527c478bd9Sstevel@tonic-gate #include <sys/socket.h>
537c478bd9Sstevel@tonic-gate #include <sys/ioctl.h>
54*3f33f4f7Sjl138328 #include <sys/stat.h>
557c478bd9Sstevel@tonic-gate #include <sys/stream.h>
567c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
57*3f33f4f7Sjl138328 #include <sys/tihdr.h>
58*3f33f4f7Sjl138328 #include <sys/types.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #include <net/if.h>
617c478bd9Sstevel@tonic-gate #include <net/route.h>
627c478bd9Sstevel@tonic-gate #include <net/if_dl.h>
637c478bd9Sstevel@tonic-gate #include <netinet/in.h>
647c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
657c478bd9Sstevel@tonic-gate #include <netdb.h>
667c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
677c478bd9Sstevel@tonic-gate #include <inet/ip.h>
687c478bd9Sstevel@tonic-gate 
69*3f33f4f7Sjl138328 #include <limits.h>
707c478bd9Sstevel@tonic-gate #include <locale.h>
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #include <errno.h>
737c478bd9Sstevel@tonic-gate #include <unistd.h>
747c478bd9Sstevel@tonic-gate #include <stdio.h>
757c478bd9Sstevel@tonic-gate #include <stdlib.h>
767c478bd9Sstevel@tonic-gate #include <string.h>
777c478bd9Sstevel@tonic-gate #include <stropts.h>
787c478bd9Sstevel@tonic-gate #include <fcntl.h>
79*3f33f4f7Sjl138328 #include <setjmp.h>
80*3f33f4f7Sjl138328 #include <stdarg.h>
817c478bd9Sstevel@tonic-gate #include <assert.h>
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static struct keytab {
847c478bd9Sstevel@tonic-gate 	char	*kt_cp;
857c478bd9Sstevel@tonic-gate 	int	kt_i;
867c478bd9Sstevel@tonic-gate } keywords[] = {
877c478bd9Sstevel@tonic-gate #define	K_ADD		1
887c478bd9Sstevel@tonic-gate 	{"add",		K_ADD},
897c478bd9Sstevel@tonic-gate #define	K_BLACKHOLE	2
907c478bd9Sstevel@tonic-gate 	{"blackhole",	K_BLACKHOLE},
917c478bd9Sstevel@tonic-gate #define	K_CHANGE	3
927c478bd9Sstevel@tonic-gate 	{"change",	K_CHANGE},
937c478bd9Sstevel@tonic-gate #define	K_CLONING	4
947c478bd9Sstevel@tonic-gate 	{"cloning",	K_CLONING},
957c478bd9Sstevel@tonic-gate #define	K_DELETE	5
967c478bd9Sstevel@tonic-gate 	{"delete",	K_DELETE},
977c478bd9Sstevel@tonic-gate #define	K_DST		6
987c478bd9Sstevel@tonic-gate 	{"dst",		K_DST},
997c478bd9Sstevel@tonic-gate #define	K_EXPIRE	7
1007c478bd9Sstevel@tonic-gate 	{"expire",	K_EXPIRE},
1017c478bd9Sstevel@tonic-gate #define	K_FLUSH		8
1027c478bd9Sstevel@tonic-gate 	{"flush",	K_FLUSH},
1037c478bd9Sstevel@tonic-gate #define	K_GATEWAY	9
1047c478bd9Sstevel@tonic-gate 	{"gateway",	K_GATEWAY},
1057c478bd9Sstevel@tonic-gate #define	K_GET		11
1067c478bd9Sstevel@tonic-gate 	{"get",		K_GET},
1077c478bd9Sstevel@tonic-gate #define	K_HOPCOUNT	12
1087c478bd9Sstevel@tonic-gate 	{"hopcount",	K_HOPCOUNT},
1097c478bd9Sstevel@tonic-gate #define	K_HOST		13
1107c478bd9Sstevel@tonic-gate 	{"host",	K_HOST},
1117c478bd9Sstevel@tonic-gate #define	K_IFA		14
1127c478bd9Sstevel@tonic-gate 	{"ifa",		K_IFA},
1137c478bd9Sstevel@tonic-gate #define	K_IFACE		15
1147c478bd9Sstevel@tonic-gate 	{"iface",	K_IFACE},
1157c478bd9Sstevel@tonic-gate #define	K_IFP		16
1167c478bd9Sstevel@tonic-gate 	{"ifp",		K_IFP},
1177c478bd9Sstevel@tonic-gate #define	K_INET		17
1187c478bd9Sstevel@tonic-gate 	{"inet",	K_INET},
1197c478bd9Sstevel@tonic-gate #define	K_INET6		18
1207c478bd9Sstevel@tonic-gate 	{"inet6",	K_INET6},
1217c478bd9Sstevel@tonic-gate #define	K_INTERFACE	19
1227c478bd9Sstevel@tonic-gate 	{"interface",	K_INTERFACE},
1237c478bd9Sstevel@tonic-gate #define	K_LINK		20
1247c478bd9Sstevel@tonic-gate 	{"link",	K_LINK},
1257c478bd9Sstevel@tonic-gate #define	K_LOCK		21
1267c478bd9Sstevel@tonic-gate 	{"lock",	K_LOCK},
1277c478bd9Sstevel@tonic-gate #define	K_LOCKREST	22
1287c478bd9Sstevel@tonic-gate 	{"lockrest",	K_LOCKREST},
1297c478bd9Sstevel@tonic-gate #define	K_MASK		23
1307c478bd9Sstevel@tonic-gate 	{"mask",	K_MASK},
1317c478bd9Sstevel@tonic-gate #define	K_MONITOR	24
1327c478bd9Sstevel@tonic-gate 	{"monitor",	K_MONITOR},
1337c478bd9Sstevel@tonic-gate #define	K_MTU		25
1347c478bd9Sstevel@tonic-gate 	{"mtu",		K_MTU},
1357c478bd9Sstevel@tonic-gate #define	K_NET		26
1367c478bd9Sstevel@tonic-gate 	{"net",		K_NET},
1377c478bd9Sstevel@tonic-gate #define	K_NETMASK	27
1387c478bd9Sstevel@tonic-gate 	{"netmask",	K_NETMASK},
1397c478bd9Sstevel@tonic-gate #define	K_NOSTATIC	28
1407c478bd9Sstevel@tonic-gate 	{"nostatic",	K_NOSTATIC},
1417c478bd9Sstevel@tonic-gate #define	K_PRIVATE	29
1427c478bd9Sstevel@tonic-gate 	{"private",	K_PRIVATE},
1437c478bd9Sstevel@tonic-gate #define	K_PROTO1	30
1447c478bd9Sstevel@tonic-gate 	{"proto1",	K_PROTO1},
1457c478bd9Sstevel@tonic-gate #define	K_PROTO2	31
1467c478bd9Sstevel@tonic-gate 	{"proto2",	K_PROTO2},
1477c478bd9Sstevel@tonic-gate #define	K_RECVPIPE	32
1487c478bd9Sstevel@tonic-gate 	{"recvpipe",	K_RECVPIPE},
1497c478bd9Sstevel@tonic-gate #define	K_REJECT	33
1507c478bd9Sstevel@tonic-gate 	{"reject",	K_REJECT},
1517c478bd9Sstevel@tonic-gate #define	K_RTT		34
1527c478bd9Sstevel@tonic-gate 	{"rtt",		K_RTT},
1537c478bd9Sstevel@tonic-gate #define	K_RTTVAR	35
1547c478bd9Sstevel@tonic-gate 	{"rttvar",	K_RTTVAR},
1557c478bd9Sstevel@tonic-gate #define	K_SA		36
1567c478bd9Sstevel@tonic-gate 	{"sa",		K_SA},
1577c478bd9Sstevel@tonic-gate #define	K_SENDPIPE	37
1587c478bd9Sstevel@tonic-gate 	{"sendpipe",	K_SENDPIPE},
1597c478bd9Sstevel@tonic-gate #define	K_SSTHRESH	38
1607c478bd9Sstevel@tonic-gate 	{"ssthresh",	K_SSTHRESH},
1617c478bd9Sstevel@tonic-gate #define	K_STATIC	39
1627c478bd9Sstevel@tonic-gate 	{"static",	K_STATIC},
1637c478bd9Sstevel@tonic-gate #define	K_XRESOLVE	40
1647c478bd9Sstevel@tonic-gate 	{"xresolve",	K_XRESOLVE},
1657c478bd9Sstevel@tonic-gate #define	K_MULTIRT	41
1667c478bd9Sstevel@tonic-gate 	{"multirt",	K_MULTIRT},
1677c478bd9Sstevel@tonic-gate #define	K_SETSRC	42
1687c478bd9Sstevel@tonic-gate 	{"setsrc",	K_SETSRC},
169*3f33f4f7Sjl138328 #define	K_SHOW		43
170*3f33f4f7Sjl138328 	{"show",	K_SHOW},
1717c478bd9Sstevel@tonic-gate 	{0, 0}
1727c478bd9Sstevel@tonic-gate };
1737c478bd9Sstevel@tonic-gate 
174*3f33f4f7Sjl138328 /*
175*3f33f4f7Sjl138328  * Size of buffers used to hold command lines from the saved route file as
176*3f33f4f7Sjl138328  * well as error strings.
177*3f33f4f7Sjl138328  */
178*3f33f4f7Sjl138328 #define	BUF_SIZE 2048
179*3f33f4f7Sjl138328 
180*3f33f4f7Sjl138328 typedef union sockunion {
1817c478bd9Sstevel@tonic-gate 	struct	sockaddr sa;
1827c478bd9Sstevel@tonic-gate 	struct	sockaddr_in sin;
1837c478bd9Sstevel@tonic-gate 	struct	sockaddr_dl sdl;
1847c478bd9Sstevel@tonic-gate 	struct	sockaddr_in6 sin6;
185*3f33f4f7Sjl138328 } su_t;
186*3f33f4f7Sjl138328 
187*3f33f4f7Sjl138328 /*
188*3f33f4f7Sjl138328  * This structure represents the digested information from parsing arguments
189*3f33f4f7Sjl138328  * to route add, change, delete, and get.
190*3f33f4f7Sjl138328  *
191*3f33f4f7Sjl138328  */
192*3f33f4f7Sjl138328 typedef struct rtcmd_irep {
193*3f33f4f7Sjl138328 	int ri_cmd;
194*3f33f4f7Sjl138328 	int ri_flags;
195*3f33f4f7Sjl138328 	int ri_af;
196*3f33f4f7Sjl138328 	int ri_masklen;
197*3f33f4f7Sjl138328 	ulong_t	ri_inits;
198*3f33f4f7Sjl138328 	struct rt_metrics ri_metrics;
199*3f33f4f7Sjl138328 	int ri_addrs;
200*3f33f4f7Sjl138328 	su_t ri_dst;
201*3f33f4f7Sjl138328 	char *ri_dest_str;
202*3f33f4f7Sjl138328 	su_t ri_src;
203*3f33f4f7Sjl138328 	su_t ri_gate;
204*3f33f4f7Sjl138328 	struct hostent *ri_gate_hp;
205*3f33f4f7Sjl138328 	char *ri_gate_str;
206*3f33f4f7Sjl138328 	su_t ri_mask;
207*3f33f4f7Sjl138328 	su_t ri_ifa;
208*3f33f4f7Sjl138328 	su_t ri_ifp;
209*3f33f4f7Sjl138328 	char *ri_ifp_str;
210*3f33f4f7Sjl138328 } rtcmd_irep_t;
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate typedef struct	mib_item_s {
2137c478bd9Sstevel@tonic-gate 	struct mib_item_s	*next_item;
2147c478bd9Sstevel@tonic-gate 	long			group;
2157c478bd9Sstevel@tonic-gate 	long			mib_id;
2167c478bd9Sstevel@tonic-gate 	long			length;
2177c478bd9Sstevel@tonic-gate 	intmax_t		*valp;
2187c478bd9Sstevel@tonic-gate } mib_item_t;
2197c478bd9Sstevel@tonic-gate 
220*3f33f4f7Sjl138328 typedef enum {
221*3f33f4f7Sjl138328 	ADDR_TYPE_ANY,
222*3f33f4f7Sjl138328 	ADDR_TYPE_HOST,
223*3f33f4f7Sjl138328 	ADDR_TYPE_NET
224*3f33f4f7Sjl138328 } addr_type_t;
2257c478bd9Sstevel@tonic-gate 
226*3f33f4f7Sjl138328 typedef enum {
227*3f33f4f7Sjl138328 	SEARCH_MODE_NULL,
228*3f33f4f7Sjl138328 	SEARCH_MODE_PRINT,
229*3f33f4f7Sjl138328 	SEARCH_MODE_DEL
230*3f33f4f7Sjl138328 } search_mode_t;
231*3f33f4f7Sjl138328 
232*3f33f4f7Sjl138328 static boolean_t	args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
233*3f33f4f7Sjl138328     char *cmd_string);
2347c478bd9Sstevel@tonic-gate static void		bprintf(FILE *fp, int b, char *s);
235*3f33f4f7Sjl138328 static boolean_t	compare_rtcmd(rtcmd_irep_t *srch_rt,
236*3f33f4f7Sjl138328     rtcmd_irep_t *file_rt);
2377c478bd9Sstevel@tonic-gate static void		delRouteEntry(mib2_ipRouteEntry_t *rp,
2387c478bd9Sstevel@tonic-gate     mib2_ipv6RouteEntry_t *rp6, int seqno);
239*3f33f4f7Sjl138328 static void		del_rtcmd_irep(rtcmd_irep_t *rcip);
2407c478bd9Sstevel@tonic-gate static void		flushroutes(int argc, char *argv[]);
241*3f33f4f7Sjl138328 static boolean_t	getaddr(rtcmd_irep_t *rcip, int which, char *s,
242*3f33f4f7Sjl138328     addr_type_t atype);
2437c478bd9Sstevel@tonic-gate static boolean_t	in6_getaddr(char *s, struct sockaddr_in6 *sin6,
2447c478bd9Sstevel@tonic-gate     int *plenp, struct hostent **hpp);
2457c478bd9Sstevel@tonic-gate static boolean_t	in_getaddr(char *s, struct sockaddr_in *sin,
246*3f33f4f7Sjl138328     int *plenp, int which, struct hostent **hpp, addr_type_t atype,
247*3f33f4f7Sjl138328     rtcmd_irep_t *rcip);
2487c478bd9Sstevel@tonic-gate static int		in_getprefixlen(char *addr, int max_plen);
2497c478bd9Sstevel@tonic-gate static boolean_t	in_prefixlentomask(int prefixlen, int maxlen,
2507c478bd9Sstevel@tonic-gate     uchar_t *mask);
251*3f33f4f7Sjl138328 static void		inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
2527c478bd9Sstevel@tonic-gate     struct sockaddr_in *sin);
2537c478bd9Sstevel@tonic-gate static in_addr_t	inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
2547c478bd9Sstevel@tonic-gate static int		keyword(char *cp);
2557c478bd9Sstevel@tonic-gate static void		link_addr(const char *addr, struct sockaddr_dl *sdl);
2567c478bd9Sstevel@tonic-gate static char		*link_ntoa(const struct sockaddr_dl *sdl);
2577c478bd9Sstevel@tonic-gate static mib_item_t	*mibget(int sd);
2587c478bd9Sstevel@tonic-gate static char		*netname(struct sockaddr *sa);
259*3f33f4f7Sjl138328 static int		newroute(char **argv);
260*3f33f4f7Sjl138328 static rtcmd_irep_t	*new_rtcmd_irep(void);
2617c478bd9Sstevel@tonic-gate static void		pmsg_addrs(char *cp, int addrs);
2627c478bd9Sstevel@tonic-gate static void		pmsg_common(struct rt_msghdr *rtm);
263*3f33f4f7Sjl138328 static void		print_getmsg(rtcmd_irep_t *req_rt,
264*3f33f4f7Sjl138328     struct rt_msghdr *rtm, int msglen);
265*3f33f4f7Sjl138328 static void		print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
266*3f33f4f7Sjl138328     boolean_t gw_good, boolean_t to_saved);
2677c478bd9Sstevel@tonic-gate static void		print_rtmsg(struct rt_msghdr *rtm, int msglen);
2687c478bd9Sstevel@tonic-gate static void		quit(char *s, int err);
2697c478bd9Sstevel@tonic-gate static char		*routename(struct sockaddr *sa);
2707c478bd9Sstevel@tonic-gate static void		rtmonitor(int argc, char *argv[]);
271*3f33f4f7Sjl138328 static int		rtmsg(rtcmd_irep_t *rcip);
2727c478bd9Sstevel@tonic-gate static int		salen(struct sockaddr *sa);
273*3f33f4f7Sjl138328 static void		save_route(int argc, char **argv);
274*3f33f4f7Sjl138328 static void		save_string(char **dst, char *src);
275*3f33f4f7Sjl138328 static int		search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
276*3f33f4f7Sjl138328     search_mode_t mode);
277*3f33f4f7Sjl138328 static void		set_metric(rtcmd_irep_t *rcip, char *value, int key,
278*3f33f4f7Sjl138328     boolean_t lock);
279*3f33f4f7Sjl138328 static int		show_saved_routes(int argc);
2807c478bd9Sstevel@tonic-gate static void		sockaddr(char *addr, struct sockaddr *sa);
281*3f33f4f7Sjl138328 static void		sodump(su_t *su, char *which);
282*3f33f4f7Sjl138328 static void		syntax_arg_missing(char *keyword);
283*3f33f4f7Sjl138328 static void		syntax_bad_keyword(char *keyword);
284*3f33f4f7Sjl138328 static void		syntax_error(char *err, ...);
2857c478bd9Sstevel@tonic-gate static void		usage(char *cp);
286*3f33f4f7Sjl138328 static void		write_to_rtfile(FILE *fp, int argc, char **argv);
2877c478bd9Sstevel@tonic-gate 
288*3f33f4f7Sjl138328 static pid_t		pid;
2897c478bd9Sstevel@tonic-gate static int		s;
290*3f33f4f7Sjl138328 static boolean_t	nflag;
2917c478bd9Sstevel@tonic-gate static int		af = AF_INET;
2927c478bd9Sstevel@tonic-gate static boolean_t	qflag, tflag;
293*3f33f4f7Sjl138328 static boolean_t	verbose;
294*3f33f4f7Sjl138328 static boolean_t	debugonly;
2957c478bd9Sstevel@tonic-gate static boolean_t	fflag;
296*3f33f4f7Sjl138328 static boolean_t	perm_flag;
297*3f33f4f7Sjl138328 static char		perm_file_sfx[] = "/etc/inet/static_routes";
298*3f33f4f7Sjl138328 static char		*perm_file;
299*3f33f4f7Sjl138328 static char		*root_dir;
300*3f33f4f7Sjl138328 static char		temp_file_sfx[] = "/etc/inet/static_routes.tmp";
301*3f33f4f7Sjl138328 static char		*temp_file;
302*3f33f4f7Sjl138328 
303*3f33f4f7Sjl138328 /*
304*3f33f4f7Sjl138328  * WARNING:
305*3f33f4f7Sjl138328  * This next variable indicates whether certain functions exit when an error
306*3f33f4f7Sjl138328  * is detected in the user input.  Currently, exit_on_error is only set false
307*3f33f4f7Sjl138328  * in search_rtfile(), when argument are being parsed.  Only those functions
308*3f33f4f7Sjl138328  * used by search_rtfile() to parse its arguments are designed to work in
309*3f33f4f7Sjl138328  * both modes.  Take particular care in setting this false to ensure that any
310*3f33f4f7Sjl138328  * functions you call that might act on this flag properly return errors when
311*3f33f4f7Sjl138328  * exit_on_error is false.
312*3f33f4f7Sjl138328  */
313*3f33f4f7Sjl138328 static int		exit_on_error = B_TRUE;
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate static struct {
3167c478bd9Sstevel@tonic-gate 	struct	rt_msghdr m_rtm;
3177c478bd9Sstevel@tonic-gate 	char	m_space[512];
3187c478bd9Sstevel@tonic-gate } m_rtmsg;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate /*
3217c478bd9Sstevel@tonic-gate  * Sizes of data structures extracted from the base mib.
3227c478bd9Sstevel@tonic-gate  * This allows the size of the tables entries to grow while preserving
3237c478bd9Sstevel@tonic-gate  * binary compatibility.
3247c478bd9Sstevel@tonic-gate  */
3257c478bd9Sstevel@tonic-gate static int ipRouteEntrySize;
3267c478bd9Sstevel@tonic-gate static int ipv6RouteEntrySize;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate #define	ROUNDUP_LONG(a) \
3297c478bd9Sstevel@tonic-gate 	((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
3307c478bd9Sstevel@tonic-gate #define	ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
3317c478bd9Sstevel@tonic-gate #define	C(x)	((x) & 0xff)
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate /*
3347c478bd9Sstevel@tonic-gate  * return values from in_getprefixlen()
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate #define	BAD_ADDR	-1	/* prefix is invalid */
3377c478bd9Sstevel@tonic-gate #define	NO_PREFIX	-2	/* no prefix was found */
3387c478bd9Sstevel@tonic-gate 
339*3f33f4f7Sjl138328 
3407c478bd9Sstevel@tonic-gate void
3417c478bd9Sstevel@tonic-gate usage(char *cp)
3427c478bd9Sstevel@tonic-gate {
343*3f33f4f7Sjl138328 	if (cp != NULL) {
3447c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
3457c478bd9Sstevel@tonic-gate 		    cp);
346*3f33f4f7Sjl138328 	}
347*3f33f4f7Sjl138328 	(void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
348*3f33f4f7Sjl138328 	    "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
3497c478bd9Sstevel@tonic-gate 	exit(1);
3507c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate 
353*3f33f4f7Sjl138328 /*PRINTFLIKE1*/
354*3f33f4f7Sjl138328 void
355*3f33f4f7Sjl138328 syntax_error(char *err, ...)
356*3f33f4f7Sjl138328 {
357*3f33f4f7Sjl138328 	va_list args;
358*3f33f4f7Sjl138328 
359*3f33f4f7Sjl138328 	if (exit_on_error) {
360*3f33f4f7Sjl138328 		va_start(args, err);
361*3f33f4f7Sjl138328 		(void) vfprintf(stderr, err, args);
362*3f33f4f7Sjl138328 		va_end(args);
363*3f33f4f7Sjl138328 		exit(1);
364*3f33f4f7Sjl138328 	}
365*3f33f4f7Sjl138328 	/* NOTREACHED */
366*3f33f4f7Sjl138328 }
367*3f33f4f7Sjl138328 
368*3f33f4f7Sjl138328 void
369*3f33f4f7Sjl138328 syntax_bad_keyword(char *keyword)
370*3f33f4f7Sjl138328 {
371*3f33f4f7Sjl138328 	syntax_error(gettext("route: botched keyword: %s\n"), keyword);
372*3f33f4f7Sjl138328 }
373*3f33f4f7Sjl138328 
374*3f33f4f7Sjl138328 void
375*3f33f4f7Sjl138328 syntax_arg_missing(char *keyword)
376*3f33f4f7Sjl138328 {
377*3f33f4f7Sjl138328 	syntax_error(gettext("route: argument required following keyword %s\n"),
378*3f33f4f7Sjl138328 	    keyword);
379*3f33f4f7Sjl138328 }
380*3f33f4f7Sjl138328 
3817c478bd9Sstevel@tonic-gate void
3827c478bd9Sstevel@tonic-gate quit(char *s, int sverrno)
3837c478bd9Sstevel@tonic-gate {
3847c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "route: ");
3857c478bd9Sstevel@tonic-gate 	if (s != NULL)
3867c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", s);
3877c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "%s\n", strerror(sverrno));
3887c478bd9Sstevel@tonic-gate 	exit(sverrno);
3897c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate int
3937c478bd9Sstevel@tonic-gate main(int argc, char **argv)
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 	extern int optind;
396*3f33f4f7Sjl138328 	extern char *optarg;
3977c478bd9Sstevel@tonic-gate 	int ch;
3987c478bd9Sstevel@tonic-gate 	int key;
399*3f33f4f7Sjl138328 	int rval;
400*3f33f4f7Sjl138328 	size_t size;
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
4057c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
4067c478bd9Sstevel@tonic-gate #endif
4077c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	if (argc < 2)
4107c478bd9Sstevel@tonic-gate 		usage((char *)NULL);
4117c478bd9Sstevel@tonic-gate 
412*3f33f4f7Sjl138328 	while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
4137c478bd9Sstevel@tonic-gate 		switch (ch) {
4147c478bd9Sstevel@tonic-gate 		case 'n':
4157c478bd9Sstevel@tonic-gate 			nflag = B_TRUE;
4167c478bd9Sstevel@tonic-gate 			break;
4177c478bd9Sstevel@tonic-gate 		case 'q':
4187c478bd9Sstevel@tonic-gate 			qflag = B_TRUE;
4197c478bd9Sstevel@tonic-gate 			break;
4207c478bd9Sstevel@tonic-gate 		case 'v':
4217c478bd9Sstevel@tonic-gate 			verbose = B_TRUE;
4227c478bd9Sstevel@tonic-gate 			break;
4237c478bd9Sstevel@tonic-gate 		case 't':
4247c478bd9Sstevel@tonic-gate 			tflag = B_TRUE;
4257c478bd9Sstevel@tonic-gate 			break;
4267c478bd9Sstevel@tonic-gate 		case 'd':
4277c478bd9Sstevel@tonic-gate 			debugonly = B_TRUE;
4287c478bd9Sstevel@tonic-gate 			break;
4297c478bd9Sstevel@tonic-gate 		case 'f':
4307c478bd9Sstevel@tonic-gate 			fflag = B_TRUE;
4317c478bd9Sstevel@tonic-gate 			break;
432*3f33f4f7Sjl138328 		case 'p':
433*3f33f4f7Sjl138328 			perm_flag = B_TRUE;
434*3f33f4f7Sjl138328 			break;
435*3f33f4f7Sjl138328 		case 'R':
436*3f33f4f7Sjl138328 			root_dir = optarg;
437*3f33f4f7Sjl138328 			break;
4387c478bd9Sstevel@tonic-gate 		case '?':
4397c478bd9Sstevel@tonic-gate 		default:
4407c478bd9Sstevel@tonic-gate 			usage((char *)NULL);
4417c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4427c478bd9Sstevel@tonic-gate 		}
4437c478bd9Sstevel@tonic-gate 	}
4447c478bd9Sstevel@tonic-gate 	argc -= optind;
4457c478bd9Sstevel@tonic-gate 	argv += optind;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	pid = getpid();
4487c478bd9Sstevel@tonic-gate 	if (tflag)
4497c478bd9Sstevel@tonic-gate 		s = open("/dev/null", O_WRONLY);
4507c478bd9Sstevel@tonic-gate 	else
4517c478bd9Sstevel@tonic-gate 		s = socket(PF_ROUTE, SOCK_RAW, 0);
4527c478bd9Sstevel@tonic-gate 	if (s < 0)
4537c478bd9Sstevel@tonic-gate 		quit("socket", errno);
454*3f33f4f7Sjl138328 
455*3f33f4f7Sjl138328 	/*
456*3f33f4f7Sjl138328 	 * Handle the -p and -R flags.  The -R flag only applies
457*3f33f4f7Sjl138328 	 * when the -p flag is set.
458*3f33f4f7Sjl138328 	 */
459*3f33f4f7Sjl138328 	if (root_dir == NULL) {
460*3f33f4f7Sjl138328 		perm_file = perm_file_sfx;
461*3f33f4f7Sjl138328 		temp_file = temp_file_sfx;
462*3f33f4f7Sjl138328 	} else {
463*3f33f4f7Sjl138328 		size = strlen(root_dir) + sizeof (perm_file_sfx);
464*3f33f4f7Sjl138328 		perm_file = malloc(size);
465*3f33f4f7Sjl138328 		if (perm_file == NULL)
466*3f33f4f7Sjl138328 			quit("malloc", errno);
467*3f33f4f7Sjl138328 		(void) snprintf(perm_file, size, "%s%s", root_dir,
468*3f33f4f7Sjl138328 		    perm_file_sfx);
469*3f33f4f7Sjl138328 		size = strlen(root_dir) + sizeof (temp_file_sfx);
470*3f33f4f7Sjl138328 		temp_file = malloc(size);
471*3f33f4f7Sjl138328 		if (temp_file == NULL)
472*3f33f4f7Sjl138328 			quit("malloc", errno);
473*3f33f4f7Sjl138328 		(void) snprintf(temp_file, size, "%s%s", root_dir,
474*3f33f4f7Sjl138328 		    temp_file_sfx);
475*3f33f4f7Sjl138328 	}
476*3f33f4f7Sjl138328 
4777c478bd9Sstevel@tonic-gate 	if (fflag) {
4787c478bd9Sstevel@tonic-gate 		/*
4797c478bd9Sstevel@tonic-gate 		 * Accept an address family keyword after the -f.  Since the
4807c478bd9Sstevel@tonic-gate 		 * default address family is AF_INET, reassign af only for the
4817c478bd9Sstevel@tonic-gate 		 * other valid address families.
4827c478bd9Sstevel@tonic-gate 		 */
4837c478bd9Sstevel@tonic-gate 		if (*argv != NULL) {
4847c478bd9Sstevel@tonic-gate 			switch (key = keyword(*argv)) {
4857c478bd9Sstevel@tonic-gate 			case K_INET:
4867c478bd9Sstevel@tonic-gate 			case K_INET6:
4877c478bd9Sstevel@tonic-gate 				if (key == K_INET6)
4887c478bd9Sstevel@tonic-gate 					af = AF_INET6;
4897c478bd9Sstevel@tonic-gate 				/* Skip over the address family parameter. */
4907c478bd9Sstevel@tonic-gate 				argc--;
4917c478bd9Sstevel@tonic-gate 				argv++;
4927c478bd9Sstevel@tonic-gate 				break;
4937c478bd9Sstevel@tonic-gate 			}
4947c478bd9Sstevel@tonic-gate 		}
495*3f33f4f7Sjl138328 		if (perm_flag && root_dir != NULL) {
496*3f33f4f7Sjl138328 			/*
497*3f33f4f7Sjl138328 			 * Act only on the file.
498*3f33f4f7Sjl138328 			 */
499*3f33f4f7Sjl138328 			save_route(argc, argv);
500*3f33f4f7Sjl138328 		} else {
5017c478bd9Sstevel@tonic-gate 			flushroutes(0, NULL);
5027c478bd9Sstevel@tonic-gate 		}
503*3f33f4f7Sjl138328 	}
504*3f33f4f7Sjl138328 
5057c478bd9Sstevel@tonic-gate 	if (*argv != NULL) {
506*3f33f4f7Sjl138328 		fflag = 0;
5077c478bd9Sstevel@tonic-gate 		switch (keyword(*argv)) {
5087c478bd9Sstevel@tonic-gate 		case K_GET:
5097c478bd9Sstevel@tonic-gate 		case K_CHANGE:
5107c478bd9Sstevel@tonic-gate 		case K_ADD:
5117c478bd9Sstevel@tonic-gate 		case K_DELETE:
512*3f33f4f7Sjl138328 			if (perm_flag && root_dir != NULL) {
513*3f33f4f7Sjl138328 				/*
514*3f33f4f7Sjl138328 				 * Act only on the file.
515*3f33f4f7Sjl138328 				 */
516*3f33f4f7Sjl138328 				rval = 0;
517*3f33f4f7Sjl138328 			} else {
518*3f33f4f7Sjl138328 				rval = newroute(argv);
519*3f33f4f7Sjl138328 			}
520*3f33f4f7Sjl138328 			if (perm_flag && (rval == 0 || rval == EEXIST ||
521*3f33f4f7Sjl138328 			    rval == ESRCH)) {
522*3f33f4f7Sjl138328 				save_route(argc, argv);
523*3f33f4f7Sjl138328 				return (0);
524*3f33f4f7Sjl138328 			}
525*3f33f4f7Sjl138328 			return (rval);
526*3f33f4f7Sjl138328 		case K_SHOW:
527*3f33f4f7Sjl138328 			if (perm_flag) {
528*3f33f4f7Sjl138328 				return (show_saved_routes(argc));
529*3f33f4f7Sjl138328 			} else {
530*3f33f4f7Sjl138328 				syntax_error(gettext(
531*3f33f4f7Sjl138328 				    "route: show command requires -p"));
532*3f33f4f7Sjl138328 			}
533*3f33f4f7Sjl138328 			/* NOTREACHED */
5347c478bd9Sstevel@tonic-gate 		case K_MONITOR:
5357c478bd9Sstevel@tonic-gate 			rtmonitor(argc, argv);
5367c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 		case K_FLUSH:
5397c478bd9Sstevel@tonic-gate 			flushroutes(argc, argv);
540*3f33f4f7Sjl138328 			if (perm_flag) {
541*3f33f4f7Sjl138328 				fflag = 1;
542*3f33f4f7Sjl138328 				save_route(argc, argv);
543*3f33f4f7Sjl138328 			}
544*3f33f4f7Sjl138328 			return (0);
5457c478bd9Sstevel@tonic-gate 		}
5467c478bd9Sstevel@tonic-gate 	}
5477c478bd9Sstevel@tonic-gate 	if (!fflag)
5487c478bd9Sstevel@tonic-gate 		usage(*argv);
5497c478bd9Sstevel@tonic-gate 	return (0);
5507c478bd9Sstevel@tonic-gate }
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate /*
5537c478bd9Sstevel@tonic-gate  * Purge all entries in the routing tables not
5547c478bd9Sstevel@tonic-gate  * associated with network interfaces.
5557c478bd9Sstevel@tonic-gate  */
5567c478bd9Sstevel@tonic-gate void
5577c478bd9Sstevel@tonic-gate flushroutes(int argc, char *argv[])
5587c478bd9Sstevel@tonic-gate {
5597c478bd9Sstevel@tonic-gate 	int seqno;
5607c478bd9Sstevel@tonic-gate 	int sd;	/* mib stream */
5617c478bd9Sstevel@tonic-gate 	mib_item_t	*item;
5627c478bd9Sstevel@tonic-gate 	mib2_ipRouteEntry_t *rp;
5637c478bd9Sstevel@tonic-gate 	mib2_ipv6RouteEntry_t *rp6;
5647c478bd9Sstevel@tonic-gate 	int oerrno;
5657c478bd9Sstevel@tonic-gate 	int off = 0;
5667c478bd9Sstevel@tonic-gate 	int on = 1;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
5697c478bd9Sstevel@tonic-gate 	    sizeof (off)) < 0)
5707c478bd9Sstevel@tonic-gate 		quit("setsockopt", errno);
5717c478bd9Sstevel@tonic-gate 	if (argc > 1) {
5727c478bd9Sstevel@tonic-gate 		argv++;
5737c478bd9Sstevel@tonic-gate 		if (argc == 2 && **argv == '-') {
5747c478bd9Sstevel@tonic-gate 			/*
5757c478bd9Sstevel@tonic-gate 			 * The address family (preceded by a dash) may be used
5767c478bd9Sstevel@tonic-gate 			 * to flush the routes of that particular family.
5777c478bd9Sstevel@tonic-gate 			 */
5787c478bd9Sstevel@tonic-gate 			switch (keyword(*argv + 1)) {
5797c478bd9Sstevel@tonic-gate 			case K_INET:
5807c478bd9Sstevel@tonic-gate 				af = AF_INET;
5817c478bd9Sstevel@tonic-gate 				break;
5827c478bd9Sstevel@tonic-gate 			case K_LINK:
5837c478bd9Sstevel@tonic-gate 				af = AF_LINK;
5847c478bd9Sstevel@tonic-gate 				break;
5857c478bd9Sstevel@tonic-gate 			case K_INET6:
5867c478bd9Sstevel@tonic-gate 				af = AF_INET6;
5877c478bd9Sstevel@tonic-gate 				break;
5887c478bd9Sstevel@tonic-gate 			default:
5897c478bd9Sstevel@tonic-gate 				usage(*argv);
5907c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
5917c478bd9Sstevel@tonic-gate 			}
5927c478bd9Sstevel@tonic-gate 		} else {
5937c478bd9Sstevel@tonic-gate 			usage(*argv);
5947c478bd9Sstevel@tonic-gate 		}
5957c478bd9Sstevel@tonic-gate 	}
5967c478bd9Sstevel@tonic-gate 	sd = open("/dev/ip", O_RDWR);
5977c478bd9Sstevel@tonic-gate 	oerrno = errno;
5987c478bd9Sstevel@tonic-gate 	if (sd < 0) {
5997c478bd9Sstevel@tonic-gate 		switch (errno) {
6007c478bd9Sstevel@tonic-gate 		case EACCES:
6017c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
6027c478bd9Sstevel@tonic-gate 			    gettext("route: flush: insufficient privileges\n"));
6037c478bd9Sstevel@tonic-gate 			exit(oerrno);
6047c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
6057c478bd9Sstevel@tonic-gate 		default:
6067c478bd9Sstevel@tonic-gate 			quit(gettext("can't open mib stream"), oerrno);
6077c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
6087c478bd9Sstevel@tonic-gate 		}
6097c478bd9Sstevel@tonic-gate 	}
6107c478bd9Sstevel@tonic-gate 	if ((item = mibget(sd)) == NULL)
6117c478bd9Sstevel@tonic-gate 		quit("mibget", errno);
6127c478bd9Sstevel@tonic-gate 	if (verbose) {
6137c478bd9Sstevel@tonic-gate 		(void) printf("Examining routing table from "
6147c478bd9Sstevel@tonic-gate 		    "T_SVR4_OPTMGMT_REQ\n");
6157c478bd9Sstevel@tonic-gate 	}
6167c478bd9Sstevel@tonic-gate 	seqno = 0;		/* ??? */
6177c478bd9Sstevel@tonic-gate 	switch (af) {
6187c478bd9Sstevel@tonic-gate 	case AF_INET:
6197c478bd9Sstevel@tonic-gate 		/* Extract ipRouteEntrySize */
6207c478bd9Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6217c478bd9Sstevel@tonic-gate 			if (item->mib_id != 0)
6227c478bd9Sstevel@tonic-gate 				continue;
6237c478bd9Sstevel@tonic-gate 			if (item->group == MIB2_IP) {
6247c478bd9Sstevel@tonic-gate 				ipRouteEntrySize =
6257c478bd9Sstevel@tonic-gate 				    ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
6267c478bd9Sstevel@tonic-gate 				assert(IS_P2ALIGNED(ipRouteEntrySize,
6277c478bd9Sstevel@tonic-gate 				    sizeof (mib2_ipRouteEntry_t *)));
6287c478bd9Sstevel@tonic-gate 				break;
6297c478bd9Sstevel@tonic-gate 			}
6307c478bd9Sstevel@tonic-gate 		}
6317c478bd9Sstevel@tonic-gate 		if (ipRouteEntrySize == 0) {
6327c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
6337c478bd9Sstevel@tonic-gate 			    gettext("ipRouteEntrySize can't be determined.\n"));
6347c478bd9Sstevel@tonic-gate 			exit(1);
6357c478bd9Sstevel@tonic-gate 		}
6367c478bd9Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6377c478bd9Sstevel@tonic-gate 			/*
6387c478bd9Sstevel@tonic-gate 			 * skip all the other trash that comes up the mib stream
6397c478bd9Sstevel@tonic-gate 			 */
6407c478bd9Sstevel@tonic-gate 			if (item->group != MIB2_IP ||
6417c478bd9Sstevel@tonic-gate 			    item->mib_id != MIB2_IP_ROUTE)
6427c478bd9Sstevel@tonic-gate 				continue;
6437c478bd9Sstevel@tonic-gate 			for (rp = (mib2_ipRouteEntry_t *)item->valp;
6447c478bd9Sstevel@tonic-gate 			    (char *)rp < (char *)item->valp + item->length;
6457c478bd9Sstevel@tonic-gate 			    /* LINTED */
6467c478bd9Sstevel@tonic-gate 			    rp = (mib2_ipRouteEntry_t *)
6477c478bd9Sstevel@tonic-gate 				((char *)rp + ipRouteEntrySize)) {
6487c478bd9Sstevel@tonic-gate 				delRouteEntry(rp, NULL, seqno);
6497c478bd9Sstevel@tonic-gate 				seqno++;
6507c478bd9Sstevel@tonic-gate 			}
6517c478bd9Sstevel@tonic-gate 			break;
6527c478bd9Sstevel@tonic-gate 		}
6537c478bd9Sstevel@tonic-gate 		break;
6547c478bd9Sstevel@tonic-gate 	case AF_INET6:
6557c478bd9Sstevel@tonic-gate 		/* Extract ipv6RouteEntrySize */
6567c478bd9Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6577c478bd9Sstevel@tonic-gate 			if (item->mib_id != 0)
6587c478bd9Sstevel@tonic-gate 				continue;
6597c478bd9Sstevel@tonic-gate 			if (item->group == MIB2_IP6) {
6607c478bd9Sstevel@tonic-gate 				ipv6RouteEntrySize =
6617c478bd9Sstevel@tonic-gate 				    ((mib2_ipv6IfStatsEntry_t *)item->valp)->
6627c478bd9Sstevel@tonic-gate 					ipv6RouteEntrySize;
6637c478bd9Sstevel@tonic-gate 				assert(IS_P2ALIGNED(ipv6RouteEntrySize,
6647c478bd9Sstevel@tonic-gate 				    sizeof (mib2_ipv6RouteEntry_t *)));
6657c478bd9Sstevel@tonic-gate 				break;
6667c478bd9Sstevel@tonic-gate 			}
6677c478bd9Sstevel@tonic-gate 		}
6687c478bd9Sstevel@tonic-gate 		if (ipv6RouteEntrySize == 0) {
6697c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
6707c478bd9Sstevel@tonic-gate 			    "ipv6RouteEntrySize cannot be determined.\n"));
6717c478bd9Sstevel@tonic-gate 			exit(1);
6727c478bd9Sstevel@tonic-gate 		}
6737c478bd9Sstevel@tonic-gate 		for (; item != NULL; item = item->next_item) {
6747c478bd9Sstevel@tonic-gate 			/*
6757c478bd9Sstevel@tonic-gate 			 * skip all the other trash that comes up the mib stream
6767c478bd9Sstevel@tonic-gate 			 */
6777c478bd9Sstevel@tonic-gate 			if (item->group != MIB2_IP6 ||
6787c478bd9Sstevel@tonic-gate 			    item->mib_id != MIB2_IP6_ROUTE)
6797c478bd9Sstevel@tonic-gate 				continue;
6807c478bd9Sstevel@tonic-gate 			for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
6817c478bd9Sstevel@tonic-gate 			    (char *)rp6 < (char *)item->valp + item->length;
6827c478bd9Sstevel@tonic-gate 			    /* LINTED */
6837c478bd9Sstevel@tonic-gate 			    rp6 = (mib2_ipv6RouteEntry_t *)
6847c478bd9Sstevel@tonic-gate 				((char *)rp6 + ipv6RouteEntrySize)) {
6857c478bd9Sstevel@tonic-gate 				delRouteEntry(NULL, rp6, seqno);
6867c478bd9Sstevel@tonic-gate 				seqno++;
6877c478bd9Sstevel@tonic-gate 			}
6887c478bd9Sstevel@tonic-gate 			break;
6897c478bd9Sstevel@tonic-gate 		}
6907c478bd9Sstevel@tonic-gate 		break;
6917c478bd9Sstevel@tonic-gate 	}
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
6947c478bd9Sstevel@tonic-gate 	    sizeof (on)) < 0)
6957c478bd9Sstevel@tonic-gate 		quit("setsockopt", errno);
6967c478bd9Sstevel@tonic-gate }
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate /*
6997c478bd9Sstevel@tonic-gate  * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
7007c478bd9Sstevel@tonic-gate  * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
7017c478bd9Sstevel@tonic-gate  * order to facilitate the flushing of RTF_GATEWAY routes.
7027c478bd9Sstevel@tonic-gate  */
7037c478bd9Sstevel@tonic-gate static void
7047c478bd9Sstevel@tonic-gate delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
7057c478bd9Sstevel@tonic-gate {
7067c478bd9Sstevel@tonic-gate 	char *cp;
7077c478bd9Sstevel@tonic-gate 	int ire_type;
7087c478bd9Sstevel@tonic-gate 	int rlen;
7097c478bd9Sstevel@tonic-gate 	struct rt_msghdr *rtm;
7107c478bd9Sstevel@tonic-gate 	struct sockaddr_in sin;
7117c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 sin6;
7127c478bd9Sstevel@tonic-gate 	int slen;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	if (rp != NULL)
7157c478bd9Sstevel@tonic-gate 		ire_type = rp->ipRouteInfo.re_ire_type;
7167c478bd9Sstevel@tonic-gate 	else
7177c478bd9Sstevel@tonic-gate 		ire_type = rp6->ipv6RouteInfo.re_ire_type;
7187c478bd9Sstevel@tonic-gate 	if (ire_type != IRE_DEFAULT &&
7197c478bd9Sstevel@tonic-gate 	    ire_type != IRE_PREFIX &&
7207c478bd9Sstevel@tonic-gate 	    ire_type != IRE_HOST &&
7217c478bd9Sstevel@tonic-gate 	    ire_type != IRE_HOST_REDIRECT)
7227c478bd9Sstevel@tonic-gate 		return;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	rtm = &m_rtmsg.m_rtm;
7257c478bd9Sstevel@tonic-gate 	(void) memset(rtm, 0, sizeof (m_rtmsg));
7267c478bd9Sstevel@tonic-gate 	rtm->rtm_type = RTM_DELETE;
7277c478bd9Sstevel@tonic-gate 	rtm->rtm_seq = seqno;
7287c478bd9Sstevel@tonic-gate 	rtm->rtm_flags |= RTF_GATEWAY;
7297c478bd9Sstevel@tonic-gate 	rtm->rtm_version = RTM_VERSION;
7307c478bd9Sstevel@tonic-gate 	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
7317c478bd9Sstevel@tonic-gate 	cp = m_rtmsg.m_space;
7327c478bd9Sstevel@tonic-gate 	if (rp != NULL) {
7337c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in);
7347c478bd9Sstevel@tonic-gate 		if (rp->ipRouteMask == IP_HOST_MASK)
7357c478bd9Sstevel@tonic-gate 			rtm->rtm_flags |= RTF_HOST;
7367c478bd9Sstevel@tonic-gate 		(void) memset(&sin, 0, slen);
7377c478bd9Sstevel@tonic-gate 		sin.sin_family = AF_INET;
7387c478bd9Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteDest;
7397c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7407c478bd9Sstevel@tonic-gate 		cp += slen;
7417c478bd9Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteNextHop;
7427c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7437c478bd9Sstevel@tonic-gate 		cp += slen;
7447c478bd9Sstevel@tonic-gate 		sin.sin_addr.s_addr = rp->ipRouteMask;
7457c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin, slen);
7467c478bd9Sstevel@tonic-gate 		cp += slen;
7477c478bd9Sstevel@tonic-gate 	} else {
7487c478bd9Sstevel@tonic-gate 		slen = sizeof (struct sockaddr_in6);
7497c478bd9Sstevel@tonic-gate 		if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
7507c478bd9Sstevel@tonic-gate 			rtm->rtm_flags |= RTF_HOST;
7517c478bd9Sstevel@tonic-gate 		(void) memset(&sin6, 0, slen);
7527c478bd9Sstevel@tonic-gate 		sin6.sin6_family = AF_INET6;
7537c478bd9Sstevel@tonic-gate 		sin6.sin6_addr = rp6->ipv6RouteDest;
7547c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7557c478bd9Sstevel@tonic-gate 		cp += slen;
7567c478bd9Sstevel@tonic-gate 		sin6.sin6_addr = rp6->ipv6RouteNextHop;
7577c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7587c478bd9Sstevel@tonic-gate 		cp += slen;
7597c478bd9Sstevel@tonic-gate 		(void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
7607c478bd9Sstevel@tonic-gate 		(void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
7617c478bd9Sstevel@tonic-gate 		    (uchar_t *)&sin6.sin6_addr.s6_addr);
7627c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &sin6, slen);
7637c478bd9Sstevel@tonic-gate 		cp += slen;
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
7667c478bd9Sstevel@tonic-gate 	if (debugonly) {
7677c478bd9Sstevel@tonic-gate 		/*
7687c478bd9Sstevel@tonic-gate 		 * In debugonly mode, the routing socket message to delete the
7697c478bd9Sstevel@tonic-gate 		 * current entry is not actually sent.  However if verbose is
7707c478bd9Sstevel@tonic-gate 		 * also set, the routing socket message that would have been
7717c478bd9Sstevel@tonic-gate 		 * is printed.
7727c478bd9Sstevel@tonic-gate 		 */
7737c478bd9Sstevel@tonic-gate 		if (verbose)
7747c478bd9Sstevel@tonic-gate 			print_rtmsg(rtm, rtm->rtm_msglen);
7757c478bd9Sstevel@tonic-gate 		return;
7767c478bd9Sstevel@tonic-gate 	}
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
7797c478bd9Sstevel@tonic-gate 	if (rlen < (int)rtm->rtm_msglen) {
7807c478bd9Sstevel@tonic-gate 		if (rlen < 0) {
7817c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
7827c478bd9Sstevel@tonic-gate 			    gettext("route: write to routing socket: %s\n"),
7837c478bd9Sstevel@tonic-gate 			    strerror(errno));
7847c478bd9Sstevel@tonic-gate 		} else {
7857c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("route: write to "
7867c478bd9Sstevel@tonic-gate 			    "routing socket got only %d for rlen\n"), rlen);
7877c478bd9Sstevel@tonic-gate 		}
7887c478bd9Sstevel@tonic-gate 		return;
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 	if (qflag) {
7917c478bd9Sstevel@tonic-gate 		/*
7927c478bd9Sstevel@tonic-gate 		 * In quiet mode, nothing is printed at all (unless the write()
7937c478bd9Sstevel@tonic-gate 		 * itself failed.
7947c478bd9Sstevel@tonic-gate 		 */
7957c478bd9Sstevel@tonic-gate 		return;
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 	if (verbose) {
7987c478bd9Sstevel@tonic-gate 		print_rtmsg(rtm, rlen);
7997c478bd9Sstevel@tonic-gate 	} else {
8007c478bd9Sstevel@tonic-gate 		struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 		(void) printf("%-20.20s ",
8037c478bd9Sstevel@tonic-gate 		    rtm->rtm_flags & RTF_HOST ? routename(sa) :
8047c478bd9Sstevel@tonic-gate 			netname(sa));
8057c478bd9Sstevel@tonic-gate 		/* LINTED */
8067c478bd9Sstevel@tonic-gate 		sa = (struct sockaddr *)(salen(sa) + (char *)sa);
8077c478bd9Sstevel@tonic-gate 		(void) printf("%-20.20s ", routename(sa));
8087c478bd9Sstevel@tonic-gate 		(void) printf("done\n");
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate }
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate /*
8137c478bd9Sstevel@tonic-gate  * Return the name of the host whose address is given.
8147c478bd9Sstevel@tonic-gate  */
8157c478bd9Sstevel@tonic-gate char *
8167c478bd9Sstevel@tonic-gate routename(struct sockaddr *sa)
8177c478bd9Sstevel@tonic-gate {
8187c478bd9Sstevel@tonic-gate 	char *cp;
8197c478bd9Sstevel@tonic-gate 	static char line[MAXHOSTNAMELEN + 1];
8207c478bd9Sstevel@tonic-gate 	struct hostent *hp = NULL;
8217c478bd9Sstevel@tonic-gate 	static char domain[MAXHOSTNAMELEN + 1];
8227c478bd9Sstevel@tonic-gate 	static boolean_t first = B_TRUE;
8237c478bd9Sstevel@tonic-gate 	struct in_addr in;
8247c478bd9Sstevel@tonic-gate 	struct in6_addr in6;
8257c478bd9Sstevel@tonic-gate 	int error_num;
8267c478bd9Sstevel@tonic-gate 	ushort_t *s;
8277c478bd9Sstevel@tonic-gate 	ushort_t *slim;
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	if (first) {
8307c478bd9Sstevel@tonic-gate 		first = B_FALSE;
8317c478bd9Sstevel@tonic-gate 		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
8327c478bd9Sstevel@tonic-gate 		    (cp = strchr(domain, '.')))
8337c478bd9Sstevel@tonic-gate 			(void) strcpy(domain, cp + 1);
8347c478bd9Sstevel@tonic-gate 		else
8357c478bd9Sstevel@tonic-gate 			domain[0] = 0;
8367c478bd9Sstevel@tonic-gate 	}
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	if (salen(sa) == 0) {
8397c478bd9Sstevel@tonic-gate 		(void) strcpy(line, "default");
8407c478bd9Sstevel@tonic-gate 		return (line);
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate 	switch (sa->sa_family) {
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	case AF_INET:
8457c478bd9Sstevel@tonic-gate 		/* LINTED */
8467c478bd9Sstevel@tonic-gate 		in = ((struct sockaddr_in *)sa)->sin_addr;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		cp = NULL;
8497c478bd9Sstevel@tonic-gate 		if (in.s_addr == INADDR_ANY)
8507c478bd9Sstevel@tonic-gate 			cp = "default";
8517c478bd9Sstevel@tonic-gate 		if (cp == NULL && !nflag) {
8527c478bd9Sstevel@tonic-gate 			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
8537c478bd9Sstevel@tonic-gate 				AF_INET);
8547c478bd9Sstevel@tonic-gate 			if (hp != NULL) {
8557c478bd9Sstevel@tonic-gate 				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
8567c478bd9Sstevel@tonic-gate 				    (strcmp(cp + 1, domain) == 0))
8577c478bd9Sstevel@tonic-gate 					*cp = 0;
8587c478bd9Sstevel@tonic-gate 				cp = hp->h_name;
8597c478bd9Sstevel@tonic-gate 			}
8607c478bd9Sstevel@tonic-gate 		}
8617c478bd9Sstevel@tonic-gate 		if (cp != NULL) {
8627c478bd9Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
8637c478bd9Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
8647c478bd9Sstevel@tonic-gate 		} else {
8657c478bd9Sstevel@tonic-gate 			in.s_addr = ntohl(in.s_addr);
8667c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
8677c478bd9Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8),
8687c478bd9Sstevel@tonic-gate 			    C(in.s_addr));
8697c478bd9Sstevel@tonic-gate 		}
8707c478bd9Sstevel@tonic-gate 		break;
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	case AF_LINK:
8737c478bd9Sstevel@tonic-gate 		return (link_ntoa((struct sockaddr_dl *)sa));
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	case AF_INET6:
8767c478bd9Sstevel@tonic-gate 		/* LINTED */
8777c478bd9Sstevel@tonic-gate 		in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 		cp = NULL;
8807c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&in6))
8817c478bd9Sstevel@tonic-gate 			cp = "default";
8827c478bd9Sstevel@tonic-gate 		if (cp == NULL && !nflag) {
8837c478bd9Sstevel@tonic-gate 			hp = getipnodebyaddr((char *)&in6,
8847c478bd9Sstevel@tonic-gate 				sizeof (struct in6_addr), AF_INET6, &error_num);
8857c478bd9Sstevel@tonic-gate 			if (hp != NULL) {
8867c478bd9Sstevel@tonic-gate 				if (((cp = strchr(hp->h_name, '.')) != NULL) &&
8877c478bd9Sstevel@tonic-gate 				    (strcmp(cp + 1, domain) == 0))
8887c478bd9Sstevel@tonic-gate 					*cp = 0;
8897c478bd9Sstevel@tonic-gate 				cp = hp->h_name;
8907c478bd9Sstevel@tonic-gate 			}
8917c478bd9Sstevel@tonic-gate 		}
8927c478bd9Sstevel@tonic-gate 		if (cp != NULL) {
8937c478bd9Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
8947c478bd9Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
8957c478bd9Sstevel@tonic-gate 		} else {
8967c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6, (void *)&in6, line,
8977c478bd9Sstevel@tonic-gate 			    INET6_ADDRSTRLEN);
8987c478bd9Sstevel@tonic-gate 		}
8997c478bd9Sstevel@tonic-gate 		if (hp != NULL)
9007c478bd9Sstevel@tonic-gate 			freehostent(hp);
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 		break;
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	default:
9057c478bd9Sstevel@tonic-gate 		s = (ushort_t *)sa;
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 		slim = s + ((salen(sa) + 1) >> 1);
9087c478bd9Sstevel@tonic-gate 		cp = line + sprintf(line, "(%d)", sa->sa_family);
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 		while (++s < slim) /* start with sa->sa_data */
9117c478bd9Sstevel@tonic-gate 			cp += sprintf(cp, " %x", *s);
9127c478bd9Sstevel@tonic-gate 		break;
9137c478bd9Sstevel@tonic-gate 	}
9147c478bd9Sstevel@tonic-gate 	return (line);
9157c478bd9Sstevel@tonic-gate }
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate /*
9187c478bd9Sstevel@tonic-gate  * Return the name of the network whose address is given.
9197c478bd9Sstevel@tonic-gate  * The address is assumed to be that of a net or subnet, not a host.
9207c478bd9Sstevel@tonic-gate  */
9217c478bd9Sstevel@tonic-gate static char *
9227c478bd9Sstevel@tonic-gate netname(struct sockaddr *sa)
9237c478bd9Sstevel@tonic-gate {
9247c478bd9Sstevel@tonic-gate 	char *cp = NULL;
9257c478bd9Sstevel@tonic-gate 	static char line[MAXHOSTNAMELEN + 1];
9267c478bd9Sstevel@tonic-gate 	struct netent *np;
9277c478bd9Sstevel@tonic-gate 	in_addr_t net, mask;
9287c478bd9Sstevel@tonic-gate 	int subnetshift;
9297c478bd9Sstevel@tonic-gate 	struct in_addr in;
9307c478bd9Sstevel@tonic-gate 	ushort_t *s;
9317c478bd9Sstevel@tonic-gate 	ushort_t *slim;
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	switch (sa->sa_family) {
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	case AF_INET:
9367c478bd9Sstevel@tonic-gate 		/* LINTED */
9377c478bd9Sstevel@tonic-gate 		in = ((struct sockaddr_in *)sa)->sin_addr;
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 		in.s_addr = ntohl(in.s_addr);
9407c478bd9Sstevel@tonic-gate 		if (in.s_addr == INADDR_ANY) {
9417c478bd9Sstevel@tonic-gate 			cp = "default";
9427c478bd9Sstevel@tonic-gate 		} else if (!nflag) {
9437c478bd9Sstevel@tonic-gate 			if (IN_CLASSA(in.s_addr)) {
9447c478bd9Sstevel@tonic-gate 				mask = IN_CLASSA_NET;
9457c478bd9Sstevel@tonic-gate 				subnetshift = 8;
9467c478bd9Sstevel@tonic-gate 			} else if (IN_CLASSB(in.s_addr)) {
9477c478bd9Sstevel@tonic-gate 				mask = IN_CLASSB_NET;
9487c478bd9Sstevel@tonic-gate 				subnetshift = 8;
9497c478bd9Sstevel@tonic-gate 			} else {
9507c478bd9Sstevel@tonic-gate 				mask = IN_CLASSC_NET;
9517c478bd9Sstevel@tonic-gate 				subnetshift = 4;
9527c478bd9Sstevel@tonic-gate 			}
9537c478bd9Sstevel@tonic-gate 			/*
9547c478bd9Sstevel@tonic-gate 			 * If there are more bits than the standard mask
9557c478bd9Sstevel@tonic-gate 			 * would suggest, subnets must be in use.
9567c478bd9Sstevel@tonic-gate 			 * Guess at the subnet mask, assuming reasonable
9577c478bd9Sstevel@tonic-gate 			 * width subnet fields.
9587c478bd9Sstevel@tonic-gate 			 */
9597c478bd9Sstevel@tonic-gate 			while (in.s_addr &~ mask)
9607c478bd9Sstevel@tonic-gate 				mask = (long)mask >> subnetshift;
9617c478bd9Sstevel@tonic-gate 			net = in.s_addr & mask;
9627c478bd9Sstevel@tonic-gate 			while ((mask & 1) == 0)
9637c478bd9Sstevel@tonic-gate 				mask >>= 1, net >>= 1;
9647c478bd9Sstevel@tonic-gate 			np = getnetbyaddr(net, AF_INET);
9657c478bd9Sstevel@tonic-gate 			if (np != NULL)
9667c478bd9Sstevel@tonic-gate 				cp = np->n_name;
9677c478bd9Sstevel@tonic-gate 		}
9687c478bd9Sstevel@tonic-gate 		if (cp != NULL) {
9697c478bd9Sstevel@tonic-gate 			(void) strncpy(line, cp, MAXHOSTNAMELEN);
9707c478bd9Sstevel@tonic-gate 			line[MAXHOSTNAMELEN] = '\0';
9717c478bd9Sstevel@tonic-gate 		} else if ((in.s_addr & 0xffffff) == 0) {
9727c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "%u", C(in.s_addr >> 24));
9737c478bd9Sstevel@tonic-gate 		} else if ((in.s_addr & 0xffff) == 0) {
9747c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
9757c478bd9Sstevel@tonic-gate 			    C(in.s_addr >> 16));
9767c478bd9Sstevel@tonic-gate 		} else if ((in.s_addr & 0xff) == 0) {
9777c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
9787c478bd9Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8));
9797c478bd9Sstevel@tonic-gate 		} else {
9807c478bd9Sstevel@tonic-gate 			(void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
9817c478bd9Sstevel@tonic-gate 			    C(in.s_addr >> 16), C(in.s_addr >> 8),
9827c478bd9Sstevel@tonic-gate 			    C(in.s_addr));
9837c478bd9Sstevel@tonic-gate 		}
9847c478bd9Sstevel@tonic-gate 		break;
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	case AF_LINK:
9877c478bd9Sstevel@tonic-gate 		return (link_ntoa((struct sockaddr_dl *)sa));
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	case AF_INET6:
9907c478bd9Sstevel@tonic-gate 		return (routename(sa));
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	default:
9937c478bd9Sstevel@tonic-gate 		/* LINTED */
9947c478bd9Sstevel@tonic-gate 		s = (ushort_t *)sa->sa_data;
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 		slim = s + ((salen(sa) + 1) >> 1);
9977c478bd9Sstevel@tonic-gate 		cp = line + sprintf(line, "af %d:", sa->sa_family);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		while (s < slim)
10007c478bd9Sstevel@tonic-gate 			cp += sprintf(cp, " %x", *s++);
10017c478bd9Sstevel@tonic-gate 		break;
10027c478bd9Sstevel@tonic-gate 	}
10037c478bd9Sstevel@tonic-gate 	return (line);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate 
1006*3f33f4f7Sjl138328 /*
1007*3f33f4f7Sjl138328  * Initialize a new structure.  Keep in mind that ri_dst_str, ri_gate_str and
1008*3f33f4f7Sjl138328  * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1009*3f33f4f7Sjl138328  * or point to dynamically allocated memory.
1010*3f33f4f7Sjl138328  */
1011*3f33f4f7Sjl138328 rtcmd_irep_t *
1012*3f33f4f7Sjl138328 new_rtcmd_irep(void)
1013*3f33f4f7Sjl138328 {
1014*3f33f4f7Sjl138328 	rtcmd_irep_t *rcip;
1015*3f33f4f7Sjl138328 
1016*3f33f4f7Sjl138328 	rcip = calloc(1, sizeof (rtcmd_irep_t));
1017*3f33f4f7Sjl138328 	if (rcip == NULL) {
1018*3f33f4f7Sjl138328 		quit("calloc", errno);
1019*3f33f4f7Sjl138328 	}
1020*3f33f4f7Sjl138328 	rcip->ri_af = AF_INET;
1021*3f33f4f7Sjl138328 	rcip->ri_flags = RTF_STATIC;
1022*3f33f4f7Sjl138328 	return (rcip);
1023*3f33f4f7Sjl138328 }
1024*3f33f4f7Sjl138328 
10257c478bd9Sstevel@tonic-gate void
1026*3f33f4f7Sjl138328 del_rtcmd_irep(rtcmd_irep_t *rcip)
1027*3f33f4f7Sjl138328 {
1028*3f33f4f7Sjl138328 	free(rcip->ri_dest_str);
1029*3f33f4f7Sjl138328 	free(rcip->ri_gate_str);
1030*3f33f4f7Sjl138328 	free(rcip->ri_ifp_str);
1031*3f33f4f7Sjl138328 	if (rcip->ri_gate_hp != NULL) {
1032*3f33f4f7Sjl138328 		freehostent(rcip->ri_gate_hp);
1033*3f33f4f7Sjl138328 	}
1034*3f33f4f7Sjl138328 	free(rcip);
1035*3f33f4f7Sjl138328 }
1036*3f33f4f7Sjl138328 
1037*3f33f4f7Sjl138328 void
1038*3f33f4f7Sjl138328 save_string(char **dst, char *src)
1039*3f33f4f7Sjl138328 {
1040*3f33f4f7Sjl138328 	free(*dst);
1041*3f33f4f7Sjl138328 	*dst = strdup(src);
1042*3f33f4f7Sjl138328 	if (*dst == NULL) {
1043*3f33f4f7Sjl138328 		quit("malloc", errno);
1044*3f33f4f7Sjl138328 	}
1045*3f33f4f7Sjl138328 }
1046*3f33f4f7Sjl138328 
1047*3f33f4f7Sjl138328 /*
1048*3f33f4f7Sjl138328  * Print the short form summary of a route command.
1049*3f33f4f7Sjl138328  * Eg. "add net default: gateway 10.0.0.1"
1050*3f33f4f7Sjl138328  * The final newline is not added, allowing the caller to append additional
1051*3f33f4f7Sjl138328  * information.
1052*3f33f4f7Sjl138328  */
1053*3f33f4f7Sjl138328 void
1054*3f33f4f7Sjl138328 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
1055*3f33f4f7Sjl138328     boolean_t to_saved)
1056*3f33f4f7Sjl138328 {
1057*3f33f4f7Sjl138328 	char *cmd;
1058*3f33f4f7Sjl138328 	char obuf[INET6_ADDRSTRLEN];
1059*3f33f4f7Sjl138328 
1060*3f33f4f7Sjl138328 	switch (rcip->ri_cmd) {
1061*3f33f4f7Sjl138328 	case RTM_ADD:
1062*3f33f4f7Sjl138328 		cmd = "add";
1063*3f33f4f7Sjl138328 		break;
1064*3f33f4f7Sjl138328 	case RTM_CHANGE:
1065*3f33f4f7Sjl138328 		cmd = "change";
1066*3f33f4f7Sjl138328 		break;
1067*3f33f4f7Sjl138328 	case RTM_DELETE:
1068*3f33f4f7Sjl138328 		cmd = "delete";
1069*3f33f4f7Sjl138328 		break;
1070*3f33f4f7Sjl138328 	case RTM_GET:
1071*3f33f4f7Sjl138328 		cmd = "get";
1072*3f33f4f7Sjl138328 		break;
1073*3f33f4f7Sjl138328 	default:
1074*3f33f4f7Sjl138328 		assert(0);
1075*3f33f4f7Sjl138328 	}
1076*3f33f4f7Sjl138328 
1077*3f33f4f7Sjl138328 	(void) fprintf(to, "%s%s %s %s", cmd,
1078*3f33f4f7Sjl138328 	    (to_saved) ? " persistent" : "",
1079*3f33f4f7Sjl138328 	    (rcip->ri_flags & RTF_HOST) ? "host" : "net",
1080*3f33f4f7Sjl138328 	    (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str);
1081*3f33f4f7Sjl138328 
1082*3f33f4f7Sjl138328 	if (rcip->ri_gate_str != NULL) {
1083*3f33f4f7Sjl138328 		switch (rcip->ri_af) {
1084*3f33f4f7Sjl138328 		case AF_INET:
1085*3f33f4f7Sjl138328 			if (nflag) {
1086*3f33f4f7Sjl138328 				(void) fprintf(to, ": gateway %s",
1087*3f33f4f7Sjl138328 				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
1088*3f33f4f7Sjl138328 			} else if (gw_good &&
1089*3f33f4f7Sjl138328 			    rcip->ri_gate_hp != NULL &&
1090*3f33f4f7Sjl138328 			    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1091*3f33f4f7Sjl138328 				/*
1092*3f33f4f7Sjl138328 				 * Print the actual address used in the case
1093*3f33f4f7Sjl138328 				 * where there was more than one address
1094*3f33f4f7Sjl138328 				 * available for the name, and one was used
1095*3f33f4f7Sjl138328 				 * successfully.
1096*3f33f4f7Sjl138328 				 */
1097*3f33f4f7Sjl138328 				(void) fprintf(to, ": gateway %s (%s)",
1098*3f33f4f7Sjl138328 				    rcip->ri_gate_str,
1099*3f33f4f7Sjl138328 				    inet_ntoa(rcip->ri_gate.sin.sin_addr));
1100*3f33f4f7Sjl138328 			} else {
1101*3f33f4f7Sjl138328 				(void) fprintf(to, ": gateway %s",
1102*3f33f4f7Sjl138328 				    rcip->ri_gate_str);
1103*3f33f4f7Sjl138328 			}
1104*3f33f4f7Sjl138328 			break;
1105*3f33f4f7Sjl138328 		case AF_INET6:
1106*3f33f4f7Sjl138328 			if (inet_ntop(AF_INET6,
1107*3f33f4f7Sjl138328 				&rcip->ri_gate.sin6.sin6_addr, obuf,
1108*3f33f4f7Sjl138328 				INET6_ADDRSTRLEN) != NULL) {
1109*3f33f4f7Sjl138328 				if (nflag) {
1110*3f33f4f7Sjl138328 					(void) fprintf(to, ": gateway %s",
1111*3f33f4f7Sjl138328 					    obuf);
1112*3f33f4f7Sjl138328 					break;
1113*3f33f4f7Sjl138328 				}
1114*3f33f4f7Sjl138328 				if (gw_good &&
1115*3f33f4f7Sjl138328 				    rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1116*3f33f4f7Sjl138328 					(void) fprintf(to, ": gateway %s (%s)",
1117*3f33f4f7Sjl138328 					    rcip->ri_gate_str, obuf);
1118*3f33f4f7Sjl138328 					break;
1119*3f33f4f7Sjl138328 				}
1120*3f33f4f7Sjl138328 			}
1121*3f33f4f7Sjl138328 			/* FALLTHROUGH */
1122*3f33f4f7Sjl138328 		default:
1123*3f33f4f7Sjl138328 			(void) fprintf(to, ": gateway %s",
1124*3f33f4f7Sjl138328 			    rcip->ri_gate_str);
1125*3f33f4f7Sjl138328 			break;
1126*3f33f4f7Sjl138328 		}
1127*3f33f4f7Sjl138328 	}
1128*3f33f4f7Sjl138328 }
1129*3f33f4f7Sjl138328 
1130*3f33f4f7Sjl138328 void
1131*3f33f4f7Sjl138328 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
11327c478bd9Sstevel@tonic-gate {
11337c478bd9Sstevel@tonic-gate 	int flag = 0;
11347c478bd9Sstevel@tonic-gate 	uint_t noval, *valp = &noval;
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	switch (key) {
1137*3f33f4f7Sjl138328 #define	caseof(x, y, z)	\
1138*3f33f4f7Sjl138328 	case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1139*3f33f4f7Sjl138328 
11407c478bd9Sstevel@tonic-gate 	caseof(K_MTU, RTV_MTU, rmx_mtu);
11417c478bd9Sstevel@tonic-gate 	caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
11427c478bd9Sstevel@tonic-gate 	caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
11437c478bd9Sstevel@tonic-gate 	caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
11447c478bd9Sstevel@tonic-gate 	caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
11457c478bd9Sstevel@tonic-gate 	caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
11467c478bd9Sstevel@tonic-gate 	caseof(K_RTT, RTV_RTT, rmx_rtt);
11477c478bd9Sstevel@tonic-gate 	caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
11487c478bd9Sstevel@tonic-gate #undef	caseof
11497c478bd9Sstevel@tonic-gate 	}
1150*3f33f4f7Sjl138328 	rcip->ri_inits |= flag;
1151*3f33f4f7Sjl138328 	if (lock)
1152*3f33f4f7Sjl138328 		rcip->ri_metrics.rmx_locks |= flag;
11537c478bd9Sstevel@tonic-gate 	*valp = atoi(value);
11547c478bd9Sstevel@tonic-gate }
11557c478bd9Sstevel@tonic-gate 
1156*3f33f4f7Sjl138328 /*
1157*3f33f4f7Sjl138328  * Parse the options give in argv[], filling in rcip with the results.
1158*3f33f4f7Sjl138328  * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1159*3f33f4f7Sjl138328  * tokenized to produce the command line.  Cmd_string is tokenized using
1160*3f33f4f7Sjl138328  * strtok, which will overwrite whitespace in the string with nulls.
1161*3f33f4f7Sjl138328  *
1162*3f33f4f7Sjl138328  * Returns B_TRUE on success and B_FALSE on failure.
1163*3f33f4f7Sjl138328  */
1164*3f33f4f7Sjl138328 boolean_t
1165*3f33f4f7Sjl138328 args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string)
11667c478bd9Sstevel@tonic-gate {
1167*3f33f4f7Sjl138328 	const char *ws = "\f\n\r\t\v ";
1168*3f33f4f7Sjl138328 	char *tok = cmd_string;
1169*3f33f4f7Sjl138328 	char *keyword_str;
1170*3f33f4f7Sjl138328 	addr_type_t atype = ADDR_TYPE_ANY;
1171*3f33f4f7Sjl138328 	boolean_t iflag = B_FALSE;
1172*3f33f4f7Sjl138328 	boolean_t locknext = B_FALSE;
1173*3f33f4f7Sjl138328 	boolean_t lockrest = B_FALSE;
1174*3f33f4f7Sjl138328 	boolean_t dash_keyword;
11757c478bd9Sstevel@tonic-gate 	int key;
1176*3f33f4f7Sjl138328 	char *err;
11777c478bd9Sstevel@tonic-gate 
1178*3f33f4f7Sjl138328 	if (cmd_string == NULL) {
1179*3f33f4f7Sjl138328 		tok = argv[0];
1180*3f33f4f7Sjl138328 	} else {
1181*3f33f4f7Sjl138328 		tok = strtok(cmd_string, ws);
11827c478bd9Sstevel@tonic-gate 	}
1183*3f33f4f7Sjl138328 
1184*3f33f4f7Sjl138328 	/*
1185*3f33f4f7Sjl138328 	 * The command keywords are already fully checked by main() or
1186*3f33f4f7Sjl138328 	 * search_rtfile().
1187*3f33f4f7Sjl138328 	 */
1188*3f33f4f7Sjl138328 	switch (*tok) {
1189*3f33f4f7Sjl138328 	case 'a':
1190*3f33f4f7Sjl138328 		rcip->ri_cmd = RTM_ADD;
1191*3f33f4f7Sjl138328 		break;
1192*3f33f4f7Sjl138328 	case 'c':
1193*3f33f4f7Sjl138328 		rcip->ri_cmd = RTM_CHANGE;
1194*3f33f4f7Sjl138328 		break;
1195*3f33f4f7Sjl138328 	case 'd':
1196*3f33f4f7Sjl138328 		rcip->ri_cmd = RTM_DELETE;
1197*3f33f4f7Sjl138328 		break;
1198*3f33f4f7Sjl138328 	case 'g':
1199*3f33f4f7Sjl138328 		rcip->ri_cmd = RTM_GET;
1200*3f33f4f7Sjl138328 		break;
1201*3f33f4f7Sjl138328 	default:
1202*3f33f4f7Sjl138328 		/* NOTREACHED */
1203*3f33f4f7Sjl138328 		quit(gettext("Internal Error"), EINVAL);
1204*3f33f4f7Sjl138328 		/* NOTREACHED */
1205*3f33f4f7Sjl138328 	}
1206*3f33f4f7Sjl138328 
1207*3f33f4f7Sjl138328 #define	NEXTTOKEN \
1208*3f33f4f7Sjl138328 	((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1209*3f33f4f7Sjl138328 
1210*3f33f4f7Sjl138328 	while (NEXTTOKEN) {
1211*3f33f4f7Sjl138328 		keyword_str = tok;
1212*3f33f4f7Sjl138328 		if (*tok == '-') {
1213*3f33f4f7Sjl138328 			dash_keyword = B_TRUE;
1214*3f33f4f7Sjl138328 			key = keyword(tok + 1);
1215*3f33f4f7Sjl138328 		} else {
1216*3f33f4f7Sjl138328 			dash_keyword = B_FALSE;
1217*3f33f4f7Sjl138328 			key = keyword(tok);
1218*3f33f4f7Sjl138328 			if (key != K_HOST && key != K_NET) {
1219*3f33f4f7Sjl138328 				/* All others must be preceded by '-' */
1220*3f33f4f7Sjl138328 				key = 0;
1221*3f33f4f7Sjl138328 			}
1222*3f33f4f7Sjl138328 		}
1223*3f33f4f7Sjl138328 		switch (key) {
1224*3f33f4f7Sjl138328 		case K_HOST:
1225*3f33f4f7Sjl138328 			if (atype == ADDR_TYPE_NET) {
1226*3f33f4f7Sjl138328 				syntax_error(gettext("route: -host and -net "
1227*3f33f4f7Sjl138328 				    "are mutually exclusive\n"));
1228*3f33f4f7Sjl138328 				return (B_FALSE);
1229*3f33f4f7Sjl138328 			}
1230*3f33f4f7Sjl138328 			atype = ADDR_TYPE_HOST;
1231*3f33f4f7Sjl138328 			break;
1232*3f33f4f7Sjl138328 		case K_NET:
1233*3f33f4f7Sjl138328 			if (atype == ADDR_TYPE_HOST) {
1234*3f33f4f7Sjl138328 				syntax_error(gettext("route: -host and -net "
1235*3f33f4f7Sjl138328 				    "are mutually exclusive\n"));
1236*3f33f4f7Sjl138328 				return (B_FALSE);
1237*3f33f4f7Sjl138328 			}
1238*3f33f4f7Sjl138328 			atype = ADDR_TYPE_NET;
1239*3f33f4f7Sjl138328 			break;
12407c478bd9Sstevel@tonic-gate 		case K_LINK:
1241*3f33f4f7Sjl138328 			rcip->ri_af = AF_LINK;
12427c478bd9Sstevel@tonic-gate 			break;
12437c478bd9Sstevel@tonic-gate 		case K_INET:
1244*3f33f4f7Sjl138328 			rcip->ri_af = AF_INET;
12457c478bd9Sstevel@tonic-gate 			break;
12467c478bd9Sstevel@tonic-gate 		case K_SA:
1247*3f33f4f7Sjl138328 			rcip->ri_af = PF_ROUTE;
12487c478bd9Sstevel@tonic-gate 			break;
12497c478bd9Sstevel@tonic-gate 		case K_INET6:
1250*3f33f4f7Sjl138328 			rcip->ri_af = AF_INET6;
12517c478bd9Sstevel@tonic-gate 			break;
12527c478bd9Sstevel@tonic-gate 		case K_IFACE:
12537c478bd9Sstevel@tonic-gate 		case K_INTERFACE:
12547c478bd9Sstevel@tonic-gate 			iflag = B_TRUE;
1255*3f33f4f7Sjl138328 			break;
12567c478bd9Sstevel@tonic-gate 		case K_NOSTATIC:
1257*3f33f4f7Sjl138328 			rcip->ri_flags &= ~RTF_STATIC;
12587c478bd9Sstevel@tonic-gate 			break;
12597c478bd9Sstevel@tonic-gate 		case K_LOCK:
1260*3f33f4f7Sjl138328 			locknext = B_TRUE;
12617c478bd9Sstevel@tonic-gate 			break;
12627c478bd9Sstevel@tonic-gate 		case K_LOCKREST:
12637c478bd9Sstevel@tonic-gate 			lockrest = B_TRUE;
12647c478bd9Sstevel@tonic-gate 			break;
12657c478bd9Sstevel@tonic-gate 		case K_REJECT:
1266*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_REJECT;
12677c478bd9Sstevel@tonic-gate 			break;
12687c478bd9Sstevel@tonic-gate 		case K_BLACKHOLE:
1269*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_BLACKHOLE;
12707c478bd9Sstevel@tonic-gate 			break;
12717c478bd9Sstevel@tonic-gate 		case K_PROTO1:
1272*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_PROTO1;
12737c478bd9Sstevel@tonic-gate 			break;
12747c478bd9Sstevel@tonic-gate 		case K_PROTO2:
1275*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_PROTO2;
12767c478bd9Sstevel@tonic-gate 			break;
12777c478bd9Sstevel@tonic-gate 		case K_CLONING:
1278*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_CLONING;
12797c478bd9Sstevel@tonic-gate 			break;
12807c478bd9Sstevel@tonic-gate 		case K_XRESOLVE:
1281*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_XRESOLVE;
12827c478bd9Sstevel@tonic-gate 			break;
12837c478bd9Sstevel@tonic-gate 		case K_STATIC:
1284*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_STATIC;
12857c478bd9Sstevel@tonic-gate 			break;
12867c478bd9Sstevel@tonic-gate 		case K_IFA:
1287*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1288*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1289*3f33f4f7Sjl138328 				return (B_FALSE);
1290*3f33f4f7Sjl138328 			}
1291*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_IFA, tok, atype)) {
1292*3f33f4f7Sjl138328 				return (B_FALSE);
1293*3f33f4f7Sjl138328 			}
12947c478bd9Sstevel@tonic-gate 			break;
12957c478bd9Sstevel@tonic-gate 		case K_IFP:
1296*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1297*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1298*3f33f4f7Sjl138328 				return (B_FALSE);
1299*3f33f4f7Sjl138328 			}
1300*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_IFP, tok, atype)) {
1301*3f33f4f7Sjl138328 				return (B_FALSE);
1302*3f33f4f7Sjl138328 			}
13037c478bd9Sstevel@tonic-gate 			break;
13047c478bd9Sstevel@tonic-gate 		case K_GATEWAY:
1305*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1306*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1307*3f33f4f7Sjl138328 				return (B_FALSE);
1308*3f33f4f7Sjl138328 			}
1309*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1310*3f33f4f7Sjl138328 				return (B_FALSE);
1311*3f33f4f7Sjl138328 			}
13127c478bd9Sstevel@tonic-gate 			break;
13137c478bd9Sstevel@tonic-gate 		case K_DST:
1314*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1315*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1316*3f33f4f7Sjl138328 				return (B_FALSE);
1317*3f33f4f7Sjl138328 			}
1318*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_DST, tok, atype)) {
1319*3f33f4f7Sjl138328 				return (B_FALSE);
1320*3f33f4f7Sjl138328 			}
13217c478bd9Sstevel@tonic-gate 			break;
13227c478bd9Sstevel@tonic-gate 		case K_NETMASK:
1323*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1324*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1325*3f33f4f7Sjl138328 				return (B_FALSE);
1326*3f33f4f7Sjl138328 			}
1327*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1328*3f33f4f7Sjl138328 				return (B_FALSE);
1329*3f33f4f7Sjl138328 			}
1330*3f33f4f7Sjl138328 			atype = ADDR_TYPE_NET;
13317c478bd9Sstevel@tonic-gate 			break;
13327c478bd9Sstevel@tonic-gate 		case K_MTU:
13337c478bd9Sstevel@tonic-gate 		case K_HOPCOUNT:
13347c478bd9Sstevel@tonic-gate 		case K_EXPIRE:
13357c478bd9Sstevel@tonic-gate 		case K_RECVPIPE:
13367c478bd9Sstevel@tonic-gate 		case K_SENDPIPE:
13377c478bd9Sstevel@tonic-gate 		case K_SSTHRESH:
13387c478bd9Sstevel@tonic-gate 		case K_RTT:
13397c478bd9Sstevel@tonic-gate 		case K_RTTVAR:
1340*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1341*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1342*3f33f4f7Sjl138328 				return (B_FALSE);
1343*3f33f4f7Sjl138328 			}
1344*3f33f4f7Sjl138328 			set_metric(rcip, tok, key, locknext || lockrest);
1345*3f33f4f7Sjl138328 			locknext = B_FALSE;
13467c478bd9Sstevel@tonic-gate 			break;
13477c478bd9Sstevel@tonic-gate 		case K_PRIVATE:
1348*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_PRIVATE;
13497c478bd9Sstevel@tonic-gate 			break;
13507c478bd9Sstevel@tonic-gate 		case K_MULTIRT:
1351*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_MULTIRT;
13527c478bd9Sstevel@tonic-gate 			break;
13537c478bd9Sstevel@tonic-gate 		case K_SETSRC:
1354*3f33f4f7Sjl138328 			if (!NEXTTOKEN) {
1355*3f33f4f7Sjl138328 				syntax_arg_missing(keyword_str);
1356*3f33f4f7Sjl138328 				return (B_FALSE);
1357*3f33f4f7Sjl138328 			}
1358*3f33f4f7Sjl138328 			if (!getaddr(rcip, RTA_SRC, tok, atype)) {
1359*3f33f4f7Sjl138328 				return (B_FALSE);
1360*3f33f4f7Sjl138328 			}
1361*3f33f4f7Sjl138328 			rcip->ri_flags |= RTF_SETSRC;
13627c478bd9Sstevel@tonic-gate 			break;
13637c478bd9Sstevel@tonic-gate 		default:
1364*3f33f4f7Sjl138328 			if (dash_keyword) {
1365*3f33f4f7Sjl138328 				syntax_bad_keyword(tok + 1);
1366*3f33f4f7Sjl138328 				return (B_FALSE);
13677c478bd9Sstevel@tonic-gate 			}
1368*3f33f4f7Sjl138328 			if ((rcip->ri_addrs & RTA_DST) == 0) {
1369*3f33f4f7Sjl138328 				if (!getaddr(rcip, RTA_DST, tok, atype)) {
1370*3f33f4f7Sjl138328 					return (B_FALSE);
1371*3f33f4f7Sjl138328 				}
1372*3f33f4f7Sjl138328 			} else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
13737c478bd9Sstevel@tonic-gate 				/*
13747c478bd9Sstevel@tonic-gate 				 * For the gateway parameter, retrieve the
13757c478bd9Sstevel@tonic-gate 				 * pointer to the struct hostent so that all
13767c478bd9Sstevel@tonic-gate 				 * possible addresses can be tried until one
13777c478bd9Sstevel@tonic-gate 				 * is successful.
13787c478bd9Sstevel@tonic-gate 				 */
1379*3f33f4f7Sjl138328 				if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1380*3f33f4f7Sjl138328 					return (B_FALSE);
1381*3f33f4f7Sjl138328 				}
13827c478bd9Sstevel@tonic-gate 			} else {
1383*3f33f4f7Sjl138328 				ulong_t metric;
13847c478bd9Sstevel@tonic-gate 				/*
13857c478bd9Sstevel@tonic-gate 				 * Assume that a regular number is a metric.
13867c478bd9Sstevel@tonic-gate 				 * Needed for compatibility with old route
13877c478bd9Sstevel@tonic-gate 				 * command syntax.
13887c478bd9Sstevel@tonic-gate 				 */
1389*3f33f4f7Sjl138328 				errno = 0;
1390*3f33f4f7Sjl138328 				metric = strtoul(tok, &err, 10);
1391*3f33f4f7Sjl138328 				if (errno == 0 && *err == '\0' &&
13927c478bd9Sstevel@tonic-gate 				    metric < 0x80000000ul) {
13937c478bd9Sstevel@tonic-gate 					iflag = (metric == 0);
13947c478bd9Sstevel@tonic-gate 					if (verbose) {
13957c478bd9Sstevel@tonic-gate 						(void) printf("old usage of "
13967c478bd9Sstevel@tonic-gate 						    "trailing number, assuming "
13977c478bd9Sstevel@tonic-gate 						    "route %s\n", iflag ?
13987c478bd9Sstevel@tonic-gate 						    "to if" : "via gateway");
13997c478bd9Sstevel@tonic-gate 					}
14007c478bd9Sstevel@tonic-gate 					continue;
14017c478bd9Sstevel@tonic-gate 				}
1402*3f33f4f7Sjl138328 				if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1403*3f33f4f7Sjl138328 					return (B_FALSE);
14047c478bd9Sstevel@tonic-gate 				}
14057c478bd9Sstevel@tonic-gate 			}
14067c478bd9Sstevel@tonic-gate 		}
1407*3f33f4f7Sjl138328 	}
1408*3f33f4f7Sjl138328 #undef NEXTTOKEN
1409*3f33f4f7Sjl138328 
1410*3f33f4f7Sjl138328 	if ((rcip->ri_addrs & RTA_DST) == 0) {
1411*3f33f4f7Sjl138328 		syntax_error(gettext("route: destination required\n"));
1412*3f33f4f7Sjl138328 		return (B_FALSE);
1413*3f33f4f7Sjl138328 	} else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
1414*3f33f4f7Sjl138328 	    (rcip->ri_addrs & RTA_GATEWAY) == 0) {
1415*3f33f4f7Sjl138328 		syntax_error(gettext(
1416*3f33f4f7Sjl138328 		    "route: gateway required for add or delete command\n"));
1417*3f33f4f7Sjl138328 		return (B_FALSE);
1418*3f33f4f7Sjl138328 	}
1419*3f33f4f7Sjl138328 
1420*3f33f4f7Sjl138328 	if (!iflag) {
1421*3f33f4f7Sjl138328 		rcip->ri_flags |= RTF_GATEWAY;
1422*3f33f4f7Sjl138328 	}
1423*3f33f4f7Sjl138328 
1424*3f33f4f7Sjl138328 	if (atype != ADDR_TYPE_NET && (rcip->ri_addrs & RTA_NETMASK)) {
1425*3f33f4f7Sjl138328 		if ((rcip->ri_af == AF_INET &&
1426*3f33f4f7Sjl138328 			rcip->ri_mask.sin.sin_addr.s_addr == IP_HOST_MASK) ||
1427*3f33f4f7Sjl138328 		    (rcip->ri_af == AF_INET6 &&
1428*3f33f4f7Sjl138328 			rcip->ri_masklen == IPV6_ABITS)) {
1429*3f33f4f7Sjl138328 			atype = ADDR_TYPE_HOST;
1430*3f33f4f7Sjl138328 		} else {
1431*3f33f4f7Sjl138328 			atype = ADDR_TYPE_NET;
1432*3f33f4f7Sjl138328 		}
1433*3f33f4f7Sjl138328 	}
1434*3f33f4f7Sjl138328 	if (atype == ADDR_TYPE_HOST) {
1435*3f33f4f7Sjl138328 		rcip->ri_flags |= RTF_HOST;
1436*3f33f4f7Sjl138328 	}
1437*3f33f4f7Sjl138328 	return (B_TRUE);
14387c478bd9Sstevel@tonic-gate }
14397c478bd9Sstevel@tonic-gate 
14407c478bd9Sstevel@tonic-gate /*
1441*3f33f4f7Sjl138328  * This command always seeks to the end of the file prior to writing.
14427c478bd9Sstevel@tonic-gate  */
1443*3f33f4f7Sjl138328 void
1444*3f33f4f7Sjl138328 write_to_rtfile(FILE *fp, int argc, char **argv)
1445*3f33f4f7Sjl138328 {
1446*3f33f4f7Sjl138328 	char file_line[BUF_SIZE];
1447*3f33f4f7Sjl138328 	int len;
1448*3f33f4f7Sjl138328 	int i;
1449*3f33f4f7Sjl138328 
1450*3f33f4f7Sjl138328 	len = 0;
1451*3f33f4f7Sjl138328 	for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
1452*3f33f4f7Sjl138328 		len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
1453*3f33f4f7Sjl138328 		    argv[i]);
14547c478bd9Sstevel@tonic-gate 	}
1455*3f33f4f7Sjl138328 	if (len >= BUF_SIZE)
1456*3f33f4f7Sjl138328 		quit(gettext("Internal Error"), EINVAL);
1457*3f33f4f7Sjl138328 	file_line[len - 1] = '\n';
1458*3f33f4f7Sjl138328 	if (fseek(fp, 0, SEEK_END) != 0 ||
1459*3f33f4f7Sjl138328 	    fputs(file_line, fp) == EOF) {
1460*3f33f4f7Sjl138328 		quit(gettext("failed to write to route file"),
1461*3f33f4f7Sjl138328 		    errno);
1462*3f33f4f7Sjl138328 	}
1463*3f33f4f7Sjl138328 }
1464*3f33f4f7Sjl138328 
1465*3f33f4f7Sjl138328 boolean_t
1466*3f33f4f7Sjl138328 compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
1467*3f33f4f7Sjl138328 {
1468*3f33f4f7Sjl138328 	if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
1469*3f33f4f7Sjl138328 	    memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
1470*3f33f4f7Sjl138328 		return (B_FALSE);
1471*3f33f4f7Sjl138328 	}
1472*3f33f4f7Sjl138328 	return (srch_rt->ri_gate_str == NULL ||
1473*3f33f4f7Sjl138328 	    strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
1474*3f33f4f7Sjl138328 }
1475*3f33f4f7Sjl138328 
1476*3f33f4f7Sjl138328 /*
1477*3f33f4f7Sjl138328  * Search the route file for routes matching the supplied route.  There are 3
1478*3f33f4f7Sjl138328  * modes of operation:
1479*3f33f4f7Sjl138328  *    SEARCH_MODE_RET - no side effects.
1480*3f33f4f7Sjl138328  *    SEARCH_MODE_PRINT - prints each matching line.
1481*3f33f4f7Sjl138328  *    SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1482*3f33f4f7Sjl138328  *
1483*3f33f4f7Sjl138328  * In all cases, the number of matches is returned.  If rt is NULL, all routes
1484*3f33f4f7Sjl138328  * matching the global af value are considered matching.
1485*3f33f4f7Sjl138328  */
1486*3f33f4f7Sjl138328 int
1487*3f33f4f7Sjl138328 search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
1488*3f33f4f7Sjl138328 {
1489*3f33f4f7Sjl138328 	char *tmp_buf;
1490*3f33f4f7Sjl138328 	int match_cnt;
1491*3f33f4f7Sjl138328 	boolean_t match;
1492*3f33f4f7Sjl138328 	char file_line[BUF_SIZE + 4] = "add ";
1493*3f33f4f7Sjl138328 	rtcmd_irep_t *thisrt;
1494*3f33f4f7Sjl138328 
1495*3f33f4f7Sjl138328 	match_cnt = 0;
1496*3f33f4f7Sjl138328 
1497*3f33f4f7Sjl138328 	/*
1498*3f33f4f7Sjl138328 	 * Leave space at the beginning of file_line for "add ".
1499*3f33f4f7Sjl138328 	 */
1500*3f33f4f7Sjl138328 	while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
1501*3f33f4f7Sjl138328 
1502*3f33f4f7Sjl138328 		if (file_line[4] == '#' || file_line[4] == '\n') {
1503*3f33f4f7Sjl138328 			/* Handle comments and blank lines */
1504*3f33f4f7Sjl138328 			if (mode == SEARCH_MODE_DEL &&
1505*3f33f4f7Sjl138328 			    fputs(file_line + 4, temp_fp) == EOF) {
1506*3f33f4f7Sjl138328 				quit(gettext(
1507*3f33f4f7Sjl138328 				    "route: failed to write to temp file"),
1508*3f33f4f7Sjl138328 				    errno);
1509*3f33f4f7Sjl138328 			}
1510*3f33f4f7Sjl138328 			continue;
1511*3f33f4f7Sjl138328 		}
1512*3f33f4f7Sjl138328 		thisrt = new_rtcmd_irep();
1513*3f33f4f7Sjl138328 
1514*3f33f4f7Sjl138328 		exit_on_error = B_FALSE;
1515*3f33f4f7Sjl138328 		tmp_buf = strdup(file_line);
1516*3f33f4f7Sjl138328 		/* args_to_rtcmd() will mangle the string passed. */
1517*3f33f4f7Sjl138328 		if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
1518*3f33f4f7Sjl138328 			/* There was an error in args_to_rtcmd() or helpers */
1519*3f33f4f7Sjl138328 			del_rtcmd_irep(thisrt);
1520*3f33f4f7Sjl138328 			free(tmp_buf);
1521*3f33f4f7Sjl138328 			continue;
1522*3f33f4f7Sjl138328 		}
1523*3f33f4f7Sjl138328 		exit_on_error = B_TRUE;
1524*3f33f4f7Sjl138328 		free(tmp_buf);
1525*3f33f4f7Sjl138328 
1526*3f33f4f7Sjl138328 		if (thisrt->ri_gate_str == NULL) {
1527*3f33f4f7Sjl138328 			del_rtcmd_irep(thisrt);
1528*3f33f4f7Sjl138328 			continue;
1529*3f33f4f7Sjl138328 		}
1530*3f33f4f7Sjl138328 		match = (rt == NULL) ? (thisrt->ri_af == af) :
1531*3f33f4f7Sjl138328 		    compare_rtcmd(rt, thisrt);
1532*3f33f4f7Sjl138328 
1533*3f33f4f7Sjl138328 		if (match) match_cnt++;
1534*3f33f4f7Sjl138328 		if (match && mode == SEARCH_MODE_PRINT) {
1535*3f33f4f7Sjl138328 			(void) printf("persistent: route %s", file_line);
1536*3f33f4f7Sjl138328 		}
1537*3f33f4f7Sjl138328 		if (match && mode == SEARCH_MODE_DEL) {
1538*3f33f4f7Sjl138328 			thisrt->ri_cmd = RTM_DELETE;
1539*3f33f4f7Sjl138328 			print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
1540*3f33f4f7Sjl138328 			(void) printf("\n");
1541*3f33f4f7Sjl138328 		}
1542*3f33f4f7Sjl138328 		del_rtcmd_irep(thisrt);
1543*3f33f4f7Sjl138328 
1544*3f33f4f7Sjl138328 		if (!match && mode == SEARCH_MODE_DEL &&
1545*3f33f4f7Sjl138328 		    fputs(file_line + 4, temp_fp) == EOF) {
1546*3f33f4f7Sjl138328 			quit(gettext("failed to write to temp file"),
1547*3f33f4f7Sjl138328 			    errno);
1548*3f33f4f7Sjl138328 		}
1549*3f33f4f7Sjl138328 	}
1550*3f33f4f7Sjl138328 	return (match_cnt);
1551*3f33f4f7Sjl138328 }
1552*3f33f4f7Sjl138328 
1553*3f33f4f7Sjl138328 /*
1554*3f33f4f7Sjl138328  * Perform the route operation given in argv on the persistent route file.
1555*3f33f4f7Sjl138328  * If fflag is set, argc and argv are ignored, and the persisten route file
1556*3f33f4f7Sjl138328  * is flushed of all routes matching the global family.
1557*3f33f4f7Sjl138328  */
1558*3f33f4f7Sjl138328 void
1559*3f33f4f7Sjl138328 save_route(int argc, char **argv)
1560*3f33f4f7Sjl138328 {
1561*3f33f4f7Sjl138328 	rtcmd_irep_t *rt;
1562*3f33f4f7Sjl138328 	int perm_fd;
1563*3f33f4f7Sjl138328 	FILE *perm_fp;
1564*3f33f4f7Sjl138328 	FILE *temp_fp;
1565*3f33f4f7Sjl138328 	mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1566*3f33f4f7Sjl138328 	struct flock lock;
1567*3f33f4f7Sjl138328 	struct stat st;
1568*3f33f4f7Sjl138328 	const char commentstr[] =
1569*3f33f4f7Sjl138328 	    "# File generated by route(1M) - do not edit.\n";
1570*3f33f4f7Sjl138328 
1571*3f33f4f7Sjl138328 	perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
1572*3f33f4f7Sjl138328 	if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
1573*3f33f4f7Sjl138328 		quit("failed to open route file", errno);
1574*3f33f4f7Sjl138328 
1575*3f33f4f7Sjl138328 	lock.l_type = F_WRLCK;
1576*3f33f4f7Sjl138328 	lock.l_whence = SEEK_SET;
1577*3f33f4f7Sjl138328 	lock.l_start = 0;
1578*3f33f4f7Sjl138328 	lock.l_len = 0;
1579*3f33f4f7Sjl138328 	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1580*3f33f4f7Sjl138328 		quit(gettext("failed to lock route file"), errno);
1581*3f33f4f7Sjl138328 		/* NOTREACHED */
1582*3f33f4f7Sjl138328 	}
1583*3f33f4f7Sjl138328 	if (st.st_size == 0 &&
1584*3f33f4f7Sjl138328 	    write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
1585*3f33f4f7Sjl138328 	    sizeof (commentstr) - 1)
1586*3f33f4f7Sjl138328 		quit(gettext("failed to open route file"), errno);
1587*3f33f4f7Sjl138328 
1588*3f33f4f7Sjl138328 	if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
1589*3f33f4f7Sjl138328 		quit(gettext("failed to open route file"), errno);
1590*3f33f4f7Sjl138328 		/* NOTREACHED */
1591*3f33f4f7Sjl138328 	}
1592*3f33f4f7Sjl138328 
1593*3f33f4f7Sjl138328 	if (!fflag) {
1594*3f33f4f7Sjl138328 		rt = new_rtcmd_irep();
1595*3f33f4f7Sjl138328 		(void) args_to_rtcmd(rt, argv, NULL);
1596*3f33f4f7Sjl138328 	}
1597*3f33f4f7Sjl138328 	if (fflag || rt->ri_cmd == RTM_DELETE) {
1598*3f33f4f7Sjl138328 		if ((temp_fp = fopen(temp_file, "w")) == NULL) {
1599*3f33f4f7Sjl138328 			quit(gettext("failed to open temp file"), errno);
1600*3f33f4f7Sjl138328 			/* NOTREACHED */
1601*3f33f4f7Sjl138328 		}
1602*3f33f4f7Sjl138328 	}
1603*3f33f4f7Sjl138328 	if (fflag) {
1604*3f33f4f7Sjl138328 		(void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
1605*3f33f4f7Sjl138328 		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1606*3f33f4f7Sjl138328 			quit(gettext("failed to update route file"), errno);
1607*3f33f4f7Sjl138328 			/* NOTREACHED */
1608*3f33f4f7Sjl138328 		}
1609*3f33f4f7Sjl138328 		(void) fclose(perm_fp);
1610*3f33f4f7Sjl138328 		return;
1611*3f33f4f7Sjl138328 	}
1612*3f33f4f7Sjl138328 
1613*3f33f4f7Sjl138328 	switch (rt->ri_cmd) {
1614*3f33f4f7Sjl138328 	case RTM_ADD:
1615*3f33f4f7Sjl138328 		if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
1616*3f33f4f7Sjl138328 			/* Route is already in the file */
1617*3f33f4f7Sjl138328 			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1618*3f33f4f7Sjl138328 			(void) fprintf(stderr, ": entry exists\n");
1619*3f33f4f7Sjl138328 			exit(1);
1620*3f33f4f7Sjl138328 		}
1621*3f33f4f7Sjl138328 		write_to_rtfile(perm_fp, argc - 1, argv + 1);
1622*3f33f4f7Sjl138328 		print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1623*3f33f4f7Sjl138328 		(void) printf("\n");
1624*3f33f4f7Sjl138328 		break;
1625*3f33f4f7Sjl138328 
1626*3f33f4f7Sjl138328 	case RTM_CHANGE:
1627*3f33f4f7Sjl138328 		syntax_error(
1628*3f33f4f7Sjl138328 		    gettext("route: change command not supported with -p\n"));
1629*3f33f4f7Sjl138328 		/* NOTREACHED */
1630*3f33f4f7Sjl138328 
1631*3f33f4f7Sjl138328 	case RTM_DELETE:
1632*3f33f4f7Sjl138328 		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
1633*3f33f4f7Sjl138328 			/* Route not found */
1634*3f33f4f7Sjl138328 			print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1635*3f33f4f7Sjl138328 			(void) fprintf(stderr, gettext(": not in file\n"));
1636*3f33f4f7Sjl138328 			exit(1);
1637*3f33f4f7Sjl138328 		}
1638*3f33f4f7Sjl138328 		if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1639*3f33f4f7Sjl138328 			quit(gettext("failed to update route file"), errno);
1640*3f33f4f7Sjl138328 			/* NOTREACHED */
1641*3f33f4f7Sjl138328 		}
1642*3f33f4f7Sjl138328 		break;
1643*3f33f4f7Sjl138328 
1644*3f33f4f7Sjl138328 	case RTM_GET:
1645*3f33f4f7Sjl138328 		if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1646*3f33f4f7Sjl138328 		    0) {
1647*3f33f4f7Sjl138328 			print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1648*3f33f4f7Sjl138328 			(void) printf(gettext(": not in file\n"));
1649*3f33f4f7Sjl138328 		}
1650*3f33f4f7Sjl138328 		break;
1651*3f33f4f7Sjl138328 
1652*3f33f4f7Sjl138328 	default:
1653*3f33f4f7Sjl138328 		quit(gettext("Internal Error"), EINVAL);
1654*3f33f4f7Sjl138328 		/* NOTREACHED */
1655*3f33f4f7Sjl138328 	}
1656*3f33f4f7Sjl138328 
1657*3f33f4f7Sjl138328 	/*
1658*3f33f4f7Sjl138328 	 * Closing the file unlocks it.
1659*3f33f4f7Sjl138328 	 */
1660*3f33f4f7Sjl138328 	(void) fclose(perm_fp);
1661*3f33f4f7Sjl138328 }
1662*3f33f4f7Sjl138328 
1663*3f33f4f7Sjl138328 int
1664*3f33f4f7Sjl138328 show_saved_routes(int argc)
1665*3f33f4f7Sjl138328 {
1666*3f33f4f7Sjl138328 	int perm_fd;
1667*3f33f4f7Sjl138328 	FILE *perm_fp;
1668*3f33f4f7Sjl138328 	struct flock lock;
1669*3f33f4f7Sjl138328 	int count = 0;
1670*3f33f4f7Sjl138328 
1671*3f33f4f7Sjl138328 	if (argc != 1) {
1672*3f33f4f7Sjl138328 		syntax_error(gettext("route: invalid arguments for show\n"));
1673*3f33f4f7Sjl138328 	}
1674*3f33f4f7Sjl138328 
1675*3f33f4f7Sjl138328 	perm_fd = open(perm_file, O_RDONLY, 0);
1676*3f33f4f7Sjl138328 
1677*3f33f4f7Sjl138328 	if (perm_fd == -1) {
1678*3f33f4f7Sjl138328 		if (errno == ENOENT) {
1679*3f33f4f7Sjl138328 			(void) printf("No persistent routes are defined\n");
1680*3f33f4f7Sjl138328 			return (0);
1681*3f33f4f7Sjl138328 		} else {
1682*3f33f4f7Sjl138328 			quit(gettext("failed to open route file"), errno);
1683*3f33f4f7Sjl138328 		}
1684*3f33f4f7Sjl138328 	}
1685*3f33f4f7Sjl138328 	lock.l_type = F_RDLCK;
1686*3f33f4f7Sjl138328 	lock.l_whence = SEEK_SET;
1687*3f33f4f7Sjl138328 	lock.l_start = 0;
1688*3f33f4f7Sjl138328 	lock.l_len = 0;
1689*3f33f4f7Sjl138328 	if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1690*3f33f4f7Sjl138328 		quit(gettext("failed to lock route file"),
1691*3f33f4f7Sjl138328 		    errno);
1692*3f33f4f7Sjl138328 		/* NOTREACHED */
1693*3f33f4f7Sjl138328 	}
1694*3f33f4f7Sjl138328 	if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1695*3f33f4f7Sjl138328 		quit(gettext("failed to open route file"), errno);
1696*3f33f4f7Sjl138328 		/* NOTREACHED */
1697*3f33f4f7Sjl138328 	}
1698*3f33f4f7Sjl138328 	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1699*3f33f4f7Sjl138328 	(void) fseek(perm_fp, 0, SEEK_SET);
1700*3f33f4f7Sjl138328 	af = AF_INET6;
1701*3f33f4f7Sjl138328 	count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1702*3f33f4f7Sjl138328 
1703*3f33f4f7Sjl138328 	if (count == 0)
1704*3f33f4f7Sjl138328 		(void) printf("No persistent routes are defined\n");
1705*3f33f4f7Sjl138328 
1706*3f33f4f7Sjl138328 	(void) fclose(perm_fp);
1707*3f33f4f7Sjl138328 	return (0);
1708*3f33f4f7Sjl138328 }
1709*3f33f4f7Sjl138328 
1710*3f33f4f7Sjl138328 int
1711*3f33f4f7Sjl138328 newroute(char **argv)
1712*3f33f4f7Sjl138328 {
1713*3f33f4f7Sjl138328 	rtcmd_irep_t *newrt;
1714*3f33f4f7Sjl138328 	int ret, attempts, oerrno;
1715*3f33f4f7Sjl138328 	char *err;
1716*3f33f4f7Sjl138328 	char obuf[INET6_ADDRSTRLEN];
1717*3f33f4f7Sjl138328 #define	hp (newrt->ri_gate_hp)
1718*3f33f4f7Sjl138328 
1719*3f33f4f7Sjl138328 	newrt = new_rtcmd_irep();
1720*3f33f4f7Sjl138328 	(void) args_to_rtcmd(newrt, argv, NULL);
1721*3f33f4f7Sjl138328 
1722*3f33f4f7Sjl138328 	if (newrt->ri_cmd != RTM_GET && !tflag) {
1723*3f33f4f7Sjl138328 		/* Don't want to read back our messages */
1724*3f33f4f7Sjl138328 		(void) shutdown(s, 0);
1725*3f33f4f7Sjl138328 	}
1726*3f33f4f7Sjl138328 	if (newrt->ri_addrs & RTA_IFP) {
1727*3f33f4f7Sjl138328 		newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
1728*3f33f4f7Sjl138328 		if (newrt->ri_ifp.sdl.sdl_index == 0) {
1729*3f33f4f7Sjl138328 			if (errno != ENXIO) {
1730*3f33f4f7Sjl138328 				quit("if_nametoindex", errno);
1731*3f33f4f7Sjl138328 			} else {
1732*3f33f4f7Sjl138328 				(void) fprintf(stderr,
1733*3f33f4f7Sjl138328 				    gettext("route: %s: no such interface\n"),
1734*3f33f4f7Sjl138328 				    newrt->ri_ifp_str);
1735*3f33f4f7Sjl138328 				exit(1);
1736*3f33f4f7Sjl138328 			}
1737*3f33f4f7Sjl138328 		}
1738*3f33f4f7Sjl138328 		newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1739*3f33f4f7Sjl138328 	}
17407c478bd9Sstevel@tonic-gate 	for (attempts = 1; ; attempts++) {
17417c478bd9Sstevel@tonic-gate 		errno = 0;
1742*3f33f4f7Sjl138328 		if ((ret = rtmsg(newrt)) == 0)
17437c478bd9Sstevel@tonic-gate 			break;
17447c478bd9Sstevel@tonic-gate 		if (errno != ENETUNREACH && errno != ESRCH)
17457c478bd9Sstevel@tonic-gate 			break;
1746*3f33f4f7Sjl138328 		if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
17477c478bd9Sstevel@tonic-gate 		    hp->h_addr_list[attempts] != NULL) {
17487c478bd9Sstevel@tonic-gate 			switch (af) {
17497c478bd9Sstevel@tonic-gate 			case AF_INET:
1750*3f33f4f7Sjl138328 				(void) memmove(&newrt->ri_gate.sin.sin_addr,
17517c478bd9Sstevel@tonic-gate 				    hp->h_addr_list[attempts], hp->h_length);
17527c478bd9Sstevel@tonic-gate 				continue;
17537c478bd9Sstevel@tonic-gate 			case AF_INET6:
1754*3f33f4f7Sjl138328 				(void) memmove(&newrt->ri_gate.sin6.sin6_addr,
17557c478bd9Sstevel@tonic-gate 				    hp->h_addr_list[attempts], hp->h_length);
17567c478bd9Sstevel@tonic-gate 				continue;
17577c478bd9Sstevel@tonic-gate 			}
17587c478bd9Sstevel@tonic-gate 		}
17597c478bd9Sstevel@tonic-gate 		break;
17607c478bd9Sstevel@tonic-gate 	}
17617c478bd9Sstevel@tonic-gate 	oerrno = errno;
1762*3f33f4f7Sjl138328 
1763*3f33f4f7Sjl138328 	if (newrt->ri_cmd != RTM_GET) {
1764*3f33f4f7Sjl138328 		print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
17657c478bd9Sstevel@tonic-gate 		if (ret == 0)
17667c478bd9Sstevel@tonic-gate 			(void) printf("\n");
1767*3f33f4f7Sjl138328 	} else if (ret != 0) {
1768*3f33f4f7Sjl138328 		/*
1769*3f33f4f7Sjl138328 		 * Note: there is nothing additional to print for get
1770*3f33f4f7Sjl138328 		 * if ret == 0.
1771*3f33f4f7Sjl138328 		 */
17727c478bd9Sstevel@tonic-gate 		if (nflag) {
1773*3f33f4f7Sjl138328 			switch (newrt->ri_af) {
17747c478bd9Sstevel@tonic-gate 			case AF_INET:
17757c478bd9Sstevel@tonic-gate 				(void) printf(" %s",
1776*3f33f4f7Sjl138328 				    inet_ntoa(newrt->ri_dst.sin.sin_addr));
17777c478bd9Sstevel@tonic-gate 				break;
17787c478bd9Sstevel@tonic-gate 			case AF_INET6:
17797c478bd9Sstevel@tonic-gate 				if (inet_ntop(AF_INET6,
1780*3f33f4f7Sjl138328 					(void *)&newrt->ri_dst.sin6.sin6_addr,
17817c478bd9Sstevel@tonic-gate 					obuf, INET6_ADDRSTRLEN) != NULL) {
17827c478bd9Sstevel@tonic-gate 					(void) printf(" %s", obuf);
17837c478bd9Sstevel@tonic-gate 					break;
17847c478bd9Sstevel@tonic-gate 				}
17857c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
17867c478bd9Sstevel@tonic-gate 			default:
1787*3f33f4f7Sjl138328 				(void) printf("%s", newrt->ri_dest_str);
17887c478bd9Sstevel@tonic-gate 				break;
17897c478bd9Sstevel@tonic-gate 			}
17907c478bd9Sstevel@tonic-gate 		} else {
1791*3f33f4f7Sjl138328 			(void) printf("%s", newrt->ri_dest_str);
17927c478bd9Sstevel@tonic-gate 		}
17937c478bd9Sstevel@tonic-gate 	}
1794*3f33f4f7Sjl138328 
1795*3f33f4f7Sjl138328 	if (ret != 0) {
17967c478bd9Sstevel@tonic-gate 		switch (oerrno) {
17977c478bd9Sstevel@tonic-gate 		case ESRCH:
17987c478bd9Sstevel@tonic-gate 			err = "not in table";
17997c478bd9Sstevel@tonic-gate 			break;
18007c478bd9Sstevel@tonic-gate 		case EBUSY:
18017c478bd9Sstevel@tonic-gate 			err = "entry in use";
18027c478bd9Sstevel@tonic-gate 			break;
18037c478bd9Sstevel@tonic-gate 		case ENOBUFS:
18047c478bd9Sstevel@tonic-gate 			err = "routing table overflow";
18057c478bd9Sstevel@tonic-gate 			break;
18067c478bd9Sstevel@tonic-gate 		case EEXIST:
18077c478bd9Sstevel@tonic-gate 			err = "entry exists";
18087c478bd9Sstevel@tonic-gate 			break;
18097c478bd9Sstevel@tonic-gate 		case EPERM:
18107c478bd9Sstevel@tonic-gate 			err = "insufficient privileges";
18117c478bd9Sstevel@tonic-gate 			break;
18127c478bd9Sstevel@tonic-gate 		default:
18137c478bd9Sstevel@tonic-gate 			err = strerror(oerrno);
18147c478bd9Sstevel@tonic-gate 			break;
18157c478bd9Sstevel@tonic-gate 		}
18167c478bd9Sstevel@tonic-gate 		(void) printf(": %s\n", err);
18177c478bd9Sstevel@tonic-gate 	}
1818*3f33f4f7Sjl138328 
1819*3f33f4f7Sjl138328 	del_rtcmd_irep(newrt);
18207c478bd9Sstevel@tonic-gate 
18217c478bd9Sstevel@tonic-gate 	return (oerrno);
1822*3f33f4f7Sjl138328 #undef hp
18237c478bd9Sstevel@tonic-gate }
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate /*
18277c478bd9Sstevel@tonic-gate  * Convert a network number to the corresponding IP address.
18287c478bd9Sstevel@tonic-gate  * If the RTA_NETMASK hasn't been specified yet set it based
18297c478bd9Sstevel@tonic-gate  * on the class of address.
18307c478bd9Sstevel@tonic-gate  */
18317c478bd9Sstevel@tonic-gate static void
1832*3f33f4f7Sjl138328 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
18337c478bd9Sstevel@tonic-gate {
18347c478bd9Sstevel@tonic-gate 	in_addr_t addr, mask;
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate 	if (net == 0) {
18377c478bd9Sstevel@tonic-gate 		mask = addr = 0;
18387c478bd9Sstevel@tonic-gate 	} else if (net < 128) {
18397c478bd9Sstevel@tonic-gate 		addr = net << IN_CLASSA_NSHIFT;
18407c478bd9Sstevel@tonic-gate 		mask = IN_CLASSA_NET;
18417c478bd9Sstevel@tonic-gate 	} else if (net < 65536) {
18427c478bd9Sstevel@tonic-gate 		addr = net << IN_CLASSB_NSHIFT;
18437c478bd9Sstevel@tonic-gate 		mask = IN_CLASSB_NET;
18447c478bd9Sstevel@tonic-gate 	} else if (net < 16777216L) {
18457c478bd9Sstevel@tonic-gate 		addr = net << IN_CLASSC_NSHIFT;
18467c478bd9Sstevel@tonic-gate 		mask = IN_CLASSC_NET;
18477c478bd9Sstevel@tonic-gate 	} else {
18487c478bd9Sstevel@tonic-gate 		addr = net;
18497c478bd9Sstevel@tonic-gate 		if ((addr & IN_CLASSA_HOST) == 0)
18507c478bd9Sstevel@tonic-gate 			mask =  IN_CLASSA_NET;
18517c478bd9Sstevel@tonic-gate 		else if ((addr & IN_CLASSB_HOST) == 0)
18527c478bd9Sstevel@tonic-gate 			mask =  IN_CLASSB_NET;
18537c478bd9Sstevel@tonic-gate 		else if ((addr & IN_CLASSC_HOST) == 0)
18547c478bd9Sstevel@tonic-gate 			mask =  IN_CLASSC_NET;
18557c478bd9Sstevel@tonic-gate 		else {
18567c478bd9Sstevel@tonic-gate 			if (IN_CLASSA(addr))
18577c478bd9Sstevel@tonic-gate 				mask =  IN_CLASSA_NET;
18587c478bd9Sstevel@tonic-gate 			else if (IN_CLASSB(addr))
18597c478bd9Sstevel@tonic-gate 				mask =  IN_CLASSB_NET;
18607c478bd9Sstevel@tonic-gate 			else if (IN_CLASSC(addr))
18617c478bd9Sstevel@tonic-gate 				mask =  IN_CLASSC_NET;
18627c478bd9Sstevel@tonic-gate 			else
18637c478bd9Sstevel@tonic-gate 				mask = IP_HOST_MASK;
18647c478bd9Sstevel@tonic-gate 			mask = inet_makesubnetmask(addr, mask);
18657c478bd9Sstevel@tonic-gate 		}
18667c478bd9Sstevel@tonic-gate 	}
18677c478bd9Sstevel@tonic-gate 	sin->sin_addr.s_addr = htonl(addr);
18687c478bd9Sstevel@tonic-gate 
1869*3f33f4f7Sjl138328 	if (!(rcip->ri_addrs & RTA_NETMASK)) {
1870*3f33f4f7Sjl138328 		rcip->ri_addrs |= RTA_NETMASK;
1871*3f33f4f7Sjl138328 		sin = &rcip->ri_mask.sin;
18727c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = htonl(mask);
18737c478bd9Sstevel@tonic-gate 		sin->sin_family = AF_INET;
18747c478bd9Sstevel@tonic-gate 	}
18757c478bd9Sstevel@tonic-gate }
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate static in_addr_t
18787c478bd9Sstevel@tonic-gate inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
18797c478bd9Sstevel@tonic-gate {
18807c478bd9Sstevel@tonic-gate 	int n;
18817c478bd9Sstevel@tonic-gate 	struct ifconf ifc;
18827c478bd9Sstevel@tonic-gate 	struct ifreq ifreq;
18837c478bd9Sstevel@tonic-gate 	struct ifreq *ifr;
18847c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
18857c478bd9Sstevel@tonic-gate 	char *buf;
18867c478bd9Sstevel@tonic-gate 	int numifs;
18877c478bd9Sstevel@tonic-gate 	size_t bufsize;
18887c478bd9Sstevel@tonic-gate 	int iosoc;
18897c478bd9Sstevel@tonic-gate 	in_addr_t if_addr, if_mask;
18907c478bd9Sstevel@tonic-gate 	in_addr_t if_subnetmask = 0;
18917c478bd9Sstevel@tonic-gate 	short if_flags;
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	if (mask == 0)
18947c478bd9Sstevel@tonic-gate 		return (0);
18957c478bd9Sstevel@tonic-gate 	if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
18967c478bd9Sstevel@tonic-gate 		quit("socket", errno);
18977c478bd9Sstevel@tonic-gate 	if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
18987c478bd9Sstevel@tonic-gate 		quit("ioctl", errno);
18997c478bd9Sstevel@tonic-gate 	bufsize = numifs * sizeof (struct ifreq);
19007c478bd9Sstevel@tonic-gate 	buf = malloc(bufsize);
19017c478bd9Sstevel@tonic-gate 	if (buf == NULL)
19027c478bd9Sstevel@tonic-gate 		quit("malloc", errno);
19037c478bd9Sstevel@tonic-gate 	(void) memset(&ifc, 0, sizeof (ifc));
19047c478bd9Sstevel@tonic-gate 	ifc.ifc_len = bufsize;
19057c478bd9Sstevel@tonic-gate 	ifc.ifc_buf = buf;
19067c478bd9Sstevel@tonic-gate 	if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
19077c478bd9Sstevel@tonic-gate 		quit("ioctl (get interface configuration)", errno);
19087c478bd9Sstevel@tonic-gate 	/* Let's check to see if this is maybe a local subnet route. */
19097c478bd9Sstevel@tonic-gate 	ifr = ifc.ifc_req;
19107c478bd9Sstevel@tonic-gate 	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
19117c478bd9Sstevel@tonic-gate 		ifreq = *ifr;
19127c478bd9Sstevel@tonic-gate 		/* LINTED */
19137c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)&ifr->ifr_addr;
19147c478bd9Sstevel@tonic-gate 		if_addr = ntohl(sin->sin_addr.s_addr);
19157c478bd9Sstevel@tonic-gate 
19167c478bd9Sstevel@tonic-gate 		if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
19177c478bd9Sstevel@tonic-gate 			quit("ioctl (get interface flags)", errno);
19187c478bd9Sstevel@tonic-gate 		if ((ifreq.ifr_flags & IFF_UP) == 0)
19197c478bd9Sstevel@tonic-gate 			continue;
19207c478bd9Sstevel@tonic-gate 		if_flags = ifreq.ifr_flags;
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate 		if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
19237c478bd9Sstevel@tonic-gate 			quit("ioctl (get netmask)", errno);
19247c478bd9Sstevel@tonic-gate 		/* LINTED */
19257c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)&ifreq.ifr_addr;
19267c478bd9Sstevel@tonic-gate 		if_mask = ntohl(sin->sin_addr.s_addr);
19277c478bd9Sstevel@tonic-gate 		if ((if_addr & mask) == (addr & mask)) {
19287c478bd9Sstevel@tonic-gate 			/*
19297c478bd9Sstevel@tonic-gate 			 * Don't trust pt-pt interfaces if there are
19307c478bd9Sstevel@tonic-gate 			 * other interfaces.
19317c478bd9Sstevel@tonic-gate 			 */
19327c478bd9Sstevel@tonic-gate 			if (if_flags & IFF_POINTOPOINT) {
19337c478bd9Sstevel@tonic-gate 				if_subnetmask = if_mask;
19347c478bd9Sstevel@tonic-gate 				continue;
19357c478bd9Sstevel@tonic-gate 			}
19367c478bd9Sstevel@tonic-gate 			/*
19377c478bd9Sstevel@tonic-gate 			 * Fine.  Just assume the same net mask as the
19387c478bd9Sstevel@tonic-gate 			 * directly attached subnet interface is using.
19397c478bd9Sstevel@tonic-gate 			 */
19407c478bd9Sstevel@tonic-gate 			return (if_mask);
19417c478bd9Sstevel@tonic-gate 		}
19427c478bd9Sstevel@tonic-gate 	}
19437c478bd9Sstevel@tonic-gate 	if (if_subnetmask != 0)
19447c478bd9Sstevel@tonic-gate 		return (if_subnetmask);
19457c478bd9Sstevel@tonic-gate 	return (mask);
19467c478bd9Sstevel@tonic-gate }
19477c478bd9Sstevel@tonic-gate 
19487c478bd9Sstevel@tonic-gate /*
1949*3f33f4f7Sjl138328  * Interpret an argument as a network address of some kind.
19507c478bd9Sstevel@tonic-gate  *
19517c478bd9Sstevel@tonic-gate  * If the address family is one looked up in getaddr() using one of the
19527c478bd9Sstevel@tonic-gate  * getipnodebyX() functions (currently only AF_INET6), then callers should
19537c478bd9Sstevel@tonic-gate  * freehostent() the returned "struct hostent" pointer if one was passed in.
1954*3f33f4f7Sjl138328  *
1955*3f33f4f7Sjl138328  * If exit_on_error is true, this function will cause route to exit on error by
1956*3f33f4f7Sjl138328  * calling syntax_error().  Otherwise, it returns B_TRUE on success or B_FALSE
1957*3f33f4f7Sjl138328  * on failure.
19587c478bd9Sstevel@tonic-gate  */
19597c478bd9Sstevel@tonic-gate static boolean_t
1960*3f33f4f7Sjl138328 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
19617c478bd9Sstevel@tonic-gate {
1962*3f33f4f7Sjl138328 	su_t *su;
1963*3f33f4f7Sjl138328 	struct hostent **hpp;
19647c478bd9Sstevel@tonic-gate 	struct hostent *hp;
19657c478bd9Sstevel@tonic-gate 	boolean_t ret;
19667c478bd9Sstevel@tonic-gate 
1967*3f33f4f7Sjl138328 	if (which == RTA_GATEWAY) {
1968*3f33f4f7Sjl138328 		hpp = &(rcip->ri_gate_hp);
1969*3f33f4f7Sjl138328 	} else {
19707c478bd9Sstevel@tonic-gate 		hpp = &hp;
1971*3f33f4f7Sjl138328 	}
19727c478bd9Sstevel@tonic-gate 	*hpp = NULL;
1973*3f33f4f7Sjl138328 
1974*3f33f4f7Sjl138328 	rcip->ri_addrs |= which;
19757c478bd9Sstevel@tonic-gate 	switch (which) {
19767c478bd9Sstevel@tonic-gate 	case RTA_DST:
1977*3f33f4f7Sjl138328 		save_string(&rcip->ri_dest_str, s);
1978*3f33f4f7Sjl138328 		su = &rcip->ri_dst;
1979*3f33f4f7Sjl138328 		su->sa.sa_family = rcip->ri_af;
19807c478bd9Sstevel@tonic-gate 		break;
19817c478bd9Sstevel@tonic-gate 	case RTA_GATEWAY:
1982*3f33f4f7Sjl138328 		save_string(&rcip->ri_gate_str, s);
1983*3f33f4f7Sjl138328 		su = &rcip->ri_gate;
1984*3f33f4f7Sjl138328 		su->sa.sa_family = rcip->ri_af;
19857c478bd9Sstevel@tonic-gate 		break;
19867c478bd9Sstevel@tonic-gate 	case RTA_NETMASK:
1987*3f33f4f7Sjl138328 		su = &rcip->ri_mask;
1988*3f33f4f7Sjl138328 		su->sa.sa_family = rcip->ri_af;
19897c478bd9Sstevel@tonic-gate 		break;
19907c478bd9Sstevel@tonic-gate 	case RTA_IFP:
1991*3f33f4f7Sjl138328 		save_string(&rcip->ri_ifp_str, s);
1992*3f33f4f7Sjl138328 		return (B_TRUE);
19937c478bd9Sstevel@tonic-gate 		/*
19947c478bd9Sstevel@tonic-gate 		 * RTA_SRC has overloaded meaning. It can represent the
19957c478bd9Sstevel@tonic-gate 		 * src address of incoming or outgoing packets.
19967c478bd9Sstevel@tonic-gate 		 */
19977c478bd9Sstevel@tonic-gate 	case RTA_IFA:
1998*3f33f4f7Sjl138328 		su = &rcip->ri_ifa;
1999*3f33f4f7Sjl138328 		su->sa.sa_family = rcip->ri_af;
20007c478bd9Sstevel@tonic-gate 		break;
20017c478bd9Sstevel@tonic-gate 	case RTA_SRC:
2002*3f33f4f7Sjl138328 		su = &rcip->ri_src;
2003*3f33f4f7Sjl138328 		su->sa.sa_family = rcip->ri_af;
20047c478bd9Sstevel@tonic-gate 		break;
20057c478bd9Sstevel@tonic-gate 	default:
20067c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
20077c478bd9Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
20087c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
20097c478bd9Sstevel@tonic-gate 	}
20107c478bd9Sstevel@tonic-gate 	if (strcmp(s, "default") == 0) {
20117c478bd9Sstevel@tonic-gate 		if (which == RTA_DST) {
2012*3f33f4f7Sjl138328 			return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
20137c478bd9Sstevel@tonic-gate 		}
20147c478bd9Sstevel@tonic-gate 		if (which == RTA_SRC) {
20157c478bd9Sstevel@tonic-gate 			return (B_TRUE);
20167c478bd9Sstevel@tonic-gate 		}
2017*3f33f4f7Sjl138328 		return (B_TRUE);
20187c478bd9Sstevel@tonic-gate 	}
2019*3f33f4f7Sjl138328 	switch (rcip->ri_af) {
20207c478bd9Sstevel@tonic-gate 	case AF_LINK:
20217c478bd9Sstevel@tonic-gate 		link_addr(s, &su->sdl);
20227c478bd9Sstevel@tonic-gate 		return (B_TRUE);
20237c478bd9Sstevel@tonic-gate 	case PF_ROUTE:
20247c478bd9Sstevel@tonic-gate 		sockaddr(s, &su->sa);
20257c478bd9Sstevel@tonic-gate 		return (B_TRUE);
20267c478bd9Sstevel@tonic-gate 	case AF_INET6:
20277c478bd9Sstevel@tonic-gate 		switch (which) {
20287c478bd9Sstevel@tonic-gate 		case RTA_DST:
20297c478bd9Sstevel@tonic-gate 			if (s[0] == '/') {
2030*3f33f4f7Sjl138328 				syntax_error(gettext(
2031*3f33f4f7Sjl138328 				    "route: %s: unexpected '/'\n"), s);
2032*3f33f4f7Sjl138328 				return (B_FALSE);
20337c478bd9Sstevel@tonic-gate 			}
2034*3f33f4f7Sjl138328 			ret = in6_getaddr(s, &su->sin6, &rcip->ri_masklen, hpp);
2035*3f33f4f7Sjl138328 			if (ret == B_FALSE) {
2036*3f33f4f7Sjl138328 				return (B_FALSE);
2037*3f33f4f7Sjl138328 			}
2038*3f33f4f7Sjl138328 			switch (rcip->ri_masklen) {
20397c478bd9Sstevel@tonic-gate 			case NO_PREFIX:
20407c478bd9Sstevel@tonic-gate 				/* Nothing there - ok */
2041*3f33f4f7Sjl138328 				return (B_TRUE);
20427c478bd9Sstevel@tonic-gate 			case BAD_ADDR:
2043*3f33f4f7Sjl138328 				syntax_error(gettext(
2044*3f33f4f7Sjl138328 				    "route: bad prefix length in %s\n"), s);
2045*3f33f4f7Sjl138328 				return (B_FALSE);
20467c478bd9Sstevel@tonic-gate 			default:
2047*3f33f4f7Sjl138328 				(void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
2048*3f33f4f7Sjl138328 				    sizeof (rcip->ri_mask.sin6.sin6_addr));
2049*3f33f4f7Sjl138328 				if (!in_prefixlentomask(rcip->ri_masklen,
2050*3f33f4f7Sjl138328 				    IPV6_ABITS,
2051*3f33f4f7Sjl138328 				    (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
2052*3f33f4f7Sjl138328 					syntax_error(gettext(
2053*3f33f4f7Sjl138328 					    "route: bad prefix length: %d\n"),
2054*3f33f4f7Sjl138328 					    rcip->ri_masklen);
2055*3f33f4f7Sjl138328 					return (B_FALSE);
20567c478bd9Sstevel@tonic-gate 				}
20577c478bd9Sstevel@tonic-gate 				break;
20587c478bd9Sstevel@tonic-gate 			}
2059*3f33f4f7Sjl138328 			rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2060*3f33f4f7Sjl138328 			rcip->ri_addrs |= RTA_NETMASK;
2061*3f33f4f7Sjl138328 			return (B_TRUE);
20627c478bd9Sstevel@tonic-gate 		case RTA_GATEWAY:
20637c478bd9Sstevel@tonic-gate 		case RTA_IFA:
20647c478bd9Sstevel@tonic-gate 		case RTA_SRC:
2065*3f33f4f7Sjl138328 			return (in6_getaddr(s, &su->sin6, NULL, hpp));
20667c478bd9Sstevel@tonic-gate 		case RTA_NETMASK:
2067*3f33f4f7Sjl138328 			syntax_error(
20687c478bd9Sstevel@tonic-gate 			    gettext("route: -netmask not supported for IPv6: "
2069*3f33f4f7Sjl138328 			    "use <prefix>/<prefix-length> instead\n"));
2070*3f33f4f7Sjl138328 			return (B_FALSE);
20717c478bd9Sstevel@tonic-gate 		default:
20727c478bd9Sstevel@tonic-gate 			quit(gettext("Internal Error"), EINVAL);
20737c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
20747c478bd9Sstevel@tonic-gate 		}
20757c478bd9Sstevel@tonic-gate 	case AF_INET:
20767c478bd9Sstevel@tonic-gate 		switch (which) {
20777c478bd9Sstevel@tonic-gate 		case RTA_DST:
20787c478bd9Sstevel@tonic-gate 			if (s[0] == '/') {
2079*3f33f4f7Sjl138328 				syntax_error(gettext(
2080*3f33f4f7Sjl138328 				    "route: %s: unexpected '/'\n"), s);
2081*3f33f4f7Sjl138328 				return (B_FALSE);
20827c478bd9Sstevel@tonic-gate 			}
2083*3f33f4f7Sjl138328 			ret = in_getaddr(s, &su->sin, &rcip->ri_masklen, which,
2084*3f33f4f7Sjl138328 			    hpp, atype, rcip);
2085*3f33f4f7Sjl138328 			if (ret == B_FALSE) {
2086*3f33f4f7Sjl138328 				return (B_FALSE);
2087*3f33f4f7Sjl138328 			}
2088*3f33f4f7Sjl138328 			switch (rcip->ri_masklen) {
20897c478bd9Sstevel@tonic-gate 			case NO_PREFIX:
20907c478bd9Sstevel@tonic-gate 				/* Nothing there - ok */
2091*3f33f4f7Sjl138328 				return (B_TRUE);
20927c478bd9Sstevel@tonic-gate 			case BAD_ADDR:
2093*3f33f4f7Sjl138328 				syntax_error(gettext(
2094*3f33f4f7Sjl138328 				    "route: bad prefix length in %s\n"), s);
2095*3f33f4f7Sjl138328 				return (B_FALSE);
20967c478bd9Sstevel@tonic-gate 			default:
2097*3f33f4f7Sjl138328 				(void) memset(&rcip->ri_mask.sin.sin_addr, 0,
2098*3f33f4f7Sjl138328 				    sizeof (rcip->ri_mask.sin.sin_addr));
2099*3f33f4f7Sjl138328 				if (!in_prefixlentomask(rcip->ri_masklen,
2100*3f33f4f7Sjl138328 				    IP_ABITS,
2101*3f33f4f7Sjl138328 				    (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
2102*3f33f4f7Sjl138328 					syntax_error(gettext(
2103*3f33f4f7Sjl138328 					    "route: bad prefix length: %d\n"),
2104*3f33f4f7Sjl138328 					    rcip->ri_masklen);
2105*3f33f4f7Sjl138328 					return (B_FALSE);
21067c478bd9Sstevel@tonic-gate 				}
21077c478bd9Sstevel@tonic-gate 				break;
21087c478bd9Sstevel@tonic-gate 			}
2109*3f33f4f7Sjl138328 			rcip->ri_mask.sin.sin_family = rcip->ri_af;
2110*3f33f4f7Sjl138328 			rcip->ri_addrs |= RTA_NETMASK;
2111*3f33f4f7Sjl138328 			return (B_TRUE);
21127c478bd9Sstevel@tonic-gate 		case RTA_GATEWAY:
21137c478bd9Sstevel@tonic-gate 		case RTA_IFA:
21147c478bd9Sstevel@tonic-gate 		case RTA_NETMASK:
21157c478bd9Sstevel@tonic-gate 		case RTA_SRC:
2116*3f33f4f7Sjl138328 			return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2117*3f33f4f7Sjl138328 			    rcip));
21187c478bd9Sstevel@tonic-gate 		default:
21197c478bd9Sstevel@tonic-gate 			quit(gettext("Internal Error"), EINVAL);
21207c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
21217c478bd9Sstevel@tonic-gate 		}
21227c478bd9Sstevel@tonic-gate 	default:
21237c478bd9Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
21247c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
21257c478bd9Sstevel@tonic-gate 	}
2126*3f33f4f7Sjl138328 	return (B_TRUE);
21277c478bd9Sstevel@tonic-gate }
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate /*
21307c478bd9Sstevel@tonic-gate  * Interpret an argument as an IPv4 network address of some kind,
2131*3f33f4f7Sjl138328  * returning B_TRUE on success or B_FALSE on failure.
2132*3f33f4f7Sjl138328  * This function will cause an exit() on failure if exit_on_failure is set.
2133*3f33f4f7Sjl138328  *
21347c478bd9Sstevel@tonic-gate  * Note that this *always* tries host interpretation before network
21357c478bd9Sstevel@tonic-gate  * interpretation.
21367c478bd9Sstevel@tonic-gate  *
21377c478bd9Sstevel@tonic-gate  * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
21387c478bd9Sstevel@tonic-gate  * pass out <n> in *plenp.
21397c478bd9Sstevel@tonic-gate  * If <n> doesn't parse return BAD_ADDR as *plenp.
21407c478bd9Sstevel@tonic-gate  * If no /<n> is present return NO_PREFIX as *plenp.
21417c478bd9Sstevel@tonic-gate  */
21427c478bd9Sstevel@tonic-gate static boolean_t
21437c478bd9Sstevel@tonic-gate in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
2144*3f33f4f7Sjl138328     struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
21457c478bd9Sstevel@tonic-gate {
21467c478bd9Sstevel@tonic-gate 	struct hostent *hp;
21477c478bd9Sstevel@tonic-gate 	struct netent *np;
21487c478bd9Sstevel@tonic-gate 	in_addr_t val;
21497c478bd9Sstevel@tonic-gate 	char str[BUFSIZ];
21507c478bd9Sstevel@tonic-gate 
21517c478bd9Sstevel@tonic-gate 	(void) strncpy(str, s, sizeof (str));
21527c478bd9Sstevel@tonic-gate 
21537c478bd9Sstevel@tonic-gate 	/*
21547c478bd9Sstevel@tonic-gate 	 * Look for '/'<n> is plenp
21557c478bd9Sstevel@tonic-gate 	 */
21567c478bd9Sstevel@tonic-gate 	if (plenp != NULL) {
21577c478bd9Sstevel@tonic-gate 		char *cp;
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 		*plenp = in_getprefixlen(str, IP_ABITS);
21607c478bd9Sstevel@tonic-gate 		if (*plenp == BAD_ADDR)
21617c478bd9Sstevel@tonic-gate 			return (B_FALSE);
21627c478bd9Sstevel@tonic-gate 		cp = strchr(str, '/');
21637c478bd9Sstevel@tonic-gate 		if (cp != NULL)
21647c478bd9Sstevel@tonic-gate 			*cp = '\0';
21657c478bd9Sstevel@tonic-gate 	} else if (strchr(str, '/') != NULL) {
2166*3f33f4f7Sjl138328 		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2167*3f33f4f7Sjl138328 		return (B_FALSE);
21687c478bd9Sstevel@tonic-gate 	}
21697c478bd9Sstevel@tonic-gate 
21707c478bd9Sstevel@tonic-gate 	(void) memset(sin, 0, sizeof (*sin));
21717c478bd9Sstevel@tonic-gate 	sin->sin_family = AF_INET;
21727c478bd9Sstevel@tonic-gate 
21737c478bd9Sstevel@tonic-gate 	/*
21747c478bd9Sstevel@tonic-gate 	 * Note: only the route destination can be a network, so we treat
21757c478bd9Sstevel@tonic-gate 	 * all other addresses as though "-net" was not specified.
21767c478bd9Sstevel@tonic-gate 	 */
21777c478bd9Sstevel@tonic-gate 	if ((((val = inet_addr(str)) != (in_addr_t)-1) ||
21787c478bd9Sstevel@tonic-gate 	    strcmp(str, "255.255.255.255") == 0) &&
2179*3f33f4f7Sjl138328 	    (which != RTA_DST || atype != ADDR_TYPE_NET)) {
21807c478bd9Sstevel@tonic-gate 		sin->sin_addr.s_addr = val;
21817c478bd9Sstevel@tonic-gate 		if (inet_lnaof(sin->sin_addr) != INADDR_ANY ||
2182*3f33f4f7Sjl138328 		    atype == ADDR_TYPE_HOST) {
2183*3f33f4f7Sjl138328 			if (rcip->ri_masklen == NO_PREFIX) {
2184*3f33f4f7Sjl138328 				rcip->ri_masklen = 32;
2185*3f33f4f7Sjl138328 			}
21867c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2187*3f33f4f7Sjl138328 		}
21887c478bd9Sstevel@tonic-gate 		val = ntohl(val);
21897c478bd9Sstevel@tonic-gate 		if (which == RTA_DST)
2190*3f33f4f7Sjl138328 			inet_makenetandmask(rcip, val, sin);
2191*3f33f4f7Sjl138328 		return (B_TRUE);
21927c478bd9Sstevel@tonic-gate 	}
2193*3f33f4f7Sjl138328 	if (atype != ADDR_TYPE_HOST && (val = inet_network(str)) !=
2194*3f33f4f7Sjl138328 	    (in_addr_t)-1) {
21957c478bd9Sstevel@tonic-gate 		if (which == RTA_DST)
2196*3f33f4f7Sjl138328 			inet_makenetandmask(rcip, val, sin);
2197*3f33f4f7Sjl138328 		return (B_TRUE);
21987c478bd9Sstevel@tonic-gate 	}
2199*3f33f4f7Sjl138328 	if ((which != RTA_DST || atype != ADDR_TYPE_NET) &&
22007c478bd9Sstevel@tonic-gate 	    (hp = gethostbyname(str)) != NULL) {
22017c478bd9Sstevel@tonic-gate 		*hpp = hp;
22027c478bd9Sstevel@tonic-gate 		(void) memmove(&sin->sin_addr, hp->h_addr,
22037c478bd9Sstevel@tonic-gate 		    hp->h_length);
2204*3f33f4f7Sjl138328 		if (rcip->ri_masklen == NO_PREFIX) {
2205*3f33f4f7Sjl138328 			rcip->ri_masklen = 32;
2206*3f33f4f7Sjl138328 		}
22077c478bd9Sstevel@tonic-gate 		return (B_TRUE);
22087c478bd9Sstevel@tonic-gate 	}
2209*3f33f4f7Sjl138328 	if (atype != ADDR_TYPE_HOST && (np = getnetbyname(str)) != NULL &&
22107c478bd9Sstevel@tonic-gate 	    (val = np->n_net) != 0) {
22117c478bd9Sstevel@tonic-gate 		if (which == RTA_DST)
2212*3f33f4f7Sjl138328 			inet_makenetandmask(rcip, val, sin);
2213*3f33f4f7Sjl138328 		return (B_TRUE);
22147c478bd9Sstevel@tonic-gate 	}
2215*3f33f4f7Sjl138328 	syntax_error(gettext("%s: bad value\n"), s);
2216*3f33f4f7Sjl138328 	return (B_FALSE);
22177c478bd9Sstevel@tonic-gate }
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate /*
22207c478bd9Sstevel@tonic-gate  * Interpret an argument as an IPv6 network address of some kind,
2221*3f33f4f7Sjl138328  * returning B_TRUE on success or B_FALSE on failure.
2222*3f33f4f7Sjl138328  * This function will cause an exit() on failure if exit_on_failure is set.
22237c478bd9Sstevel@tonic-gate  *
22247c478bd9Sstevel@tonic-gate  * If the last argument is non-NULL allow a <addr>/<n> syntax and
22257c478bd9Sstevel@tonic-gate  * pass out <n> in *plenp.
22267c478bd9Sstevel@tonic-gate  * If <n> doesn't parse return BAD_ADDR as *plenp.
22277c478bd9Sstevel@tonic-gate  * If no /<n> is present return NO_PREFIX as *plenp.
22287c478bd9Sstevel@tonic-gate  */
22297c478bd9Sstevel@tonic-gate static boolean_t
22307c478bd9Sstevel@tonic-gate in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
22317c478bd9Sstevel@tonic-gate     struct hostent **hpp)
22327c478bd9Sstevel@tonic-gate {
22337c478bd9Sstevel@tonic-gate 	struct hostent *hp;
22347c478bd9Sstevel@tonic-gate 	char str[BUFSIZ];
22357c478bd9Sstevel@tonic-gate 	int error_num;
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 	(void) strncpy(str, s, sizeof (str));
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 	/*
22407c478bd9Sstevel@tonic-gate 	 * Look for '/'<n> is plenp
22417c478bd9Sstevel@tonic-gate 	 */
22427c478bd9Sstevel@tonic-gate 	if (plenp != NULL) {
22437c478bd9Sstevel@tonic-gate 		char *cp;
22447c478bd9Sstevel@tonic-gate 
22457c478bd9Sstevel@tonic-gate 		*plenp = in_getprefixlen(str, IPV6_ABITS);
22467c478bd9Sstevel@tonic-gate 		if (*plenp == BAD_ADDR)
22477c478bd9Sstevel@tonic-gate 			return (B_FALSE);
22487c478bd9Sstevel@tonic-gate 		cp = strchr(str, '/');
22497c478bd9Sstevel@tonic-gate 		if (cp != NULL)
22507c478bd9Sstevel@tonic-gate 			*cp = '\0';
22517c478bd9Sstevel@tonic-gate 	} else if (strchr(str, '/') != NULL) {
2252*3f33f4f7Sjl138328 		syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2253*3f33f4f7Sjl138328 		return (B_FALSE);
22547c478bd9Sstevel@tonic-gate 	}
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate 	(void) memset(sin6, 0, sizeof (struct sockaddr_in6));
22577c478bd9Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
22587c478bd9Sstevel@tonic-gate 
22597c478bd9Sstevel@tonic-gate 	hp = getipnodebyname(str, AF_INET6, 0, &error_num);
22607c478bd9Sstevel@tonic-gate 	if (hp != NULL) {
22617c478bd9Sstevel@tonic-gate 		*hpp = hp;
22627c478bd9Sstevel@tonic-gate 		(void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
22637c478bd9Sstevel@tonic-gate 		return (B_TRUE);
22647c478bd9Sstevel@tonic-gate 	}
22657c478bd9Sstevel@tonic-gate 	if (error_num == TRY_AGAIN) {
2266*3f33f4f7Sjl138328 		/*
2267*3f33f4f7Sjl138328 		 * This isn't a problem if we aren't going to use the address
2268*3f33f4f7Sjl138328 		 * right away.
2269*3f33f4f7Sjl138328 		 */
2270*3f33f4f7Sjl138328 		if (!exit_on_error) {
2271*3f33f4f7Sjl138328 			return (B_TRUE);
22727c478bd9Sstevel@tonic-gate 		}
2273*3f33f4f7Sjl138328 		syntax_error(gettext("route: %s: bad address (try "
2274*3f33f4f7Sjl138328 		    "again later)\n"), s);
2275*3f33f4f7Sjl138328 		return (B_FALSE);
2276*3f33f4f7Sjl138328 	}
2277*3f33f4f7Sjl138328 	syntax_error(gettext("route: %s: bad address\n"), s);
2278*3f33f4f7Sjl138328 	return (B_FALSE);
22797c478bd9Sstevel@tonic-gate }
22807c478bd9Sstevel@tonic-gate 
22817c478bd9Sstevel@tonic-gate /*
22827c478bd9Sstevel@tonic-gate  * If "slash" is zero this parses the whole string as
22837c478bd9Sstevel@tonic-gate  * an integer. With "slash" non zero it parses the tail part as an integer.
22847c478bd9Sstevel@tonic-gate  *
22857c478bd9Sstevel@tonic-gate  * If it is not a valid integer this returns BAD_ADDR.
22867c478bd9Sstevel@tonic-gate  * If there is /<n> present this returns NO_PREFIX.
22877c478bd9Sstevel@tonic-gate  */
22887c478bd9Sstevel@tonic-gate int
22897c478bd9Sstevel@tonic-gate in_getprefixlen(char *addr, int max_plen)
22907c478bd9Sstevel@tonic-gate {
22917c478bd9Sstevel@tonic-gate 	int prefixlen;
22927c478bd9Sstevel@tonic-gate 	char *str, *end;
22937c478bd9Sstevel@tonic-gate 
22947c478bd9Sstevel@tonic-gate 	str = strchr(addr, '/');
22957c478bd9Sstevel@tonic-gate 	if (str == NULL)
22967c478bd9Sstevel@tonic-gate 		return (NO_PREFIX);
22977c478bd9Sstevel@tonic-gate 	str++;
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 	prefixlen = strtol(str, &end, 10);
23007c478bd9Sstevel@tonic-gate 	if (prefixlen < 0)
23017c478bd9Sstevel@tonic-gate 		return (BAD_ADDR);
23027c478bd9Sstevel@tonic-gate 	if (str == end)
23037c478bd9Sstevel@tonic-gate 		return (BAD_ADDR);
23047c478bd9Sstevel@tonic-gate 	if (max_plen != 0 && max_plen < prefixlen)
23057c478bd9Sstevel@tonic-gate 		return (BAD_ADDR);
23067c478bd9Sstevel@tonic-gate 	else
23077c478bd9Sstevel@tonic-gate 		return (prefixlen);
23087c478bd9Sstevel@tonic-gate }
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate /*
23117c478bd9Sstevel@tonic-gate  * Convert a prefix length to a mask.
23127c478bd9Sstevel@tonic-gate  * Returns B_TRUE if ok. B_FALSE otherwise.
23137c478bd9Sstevel@tonic-gate  * Assumes the mask array is zeroed by the caller.
23147c478bd9Sstevel@tonic-gate  */
23157c478bd9Sstevel@tonic-gate boolean_t
23167c478bd9Sstevel@tonic-gate in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
23177c478bd9Sstevel@tonic-gate {
23187c478bd9Sstevel@tonic-gate 	if (prefixlen < 0 || prefixlen > maxlen)
23197c478bd9Sstevel@tonic-gate 		return (B_FALSE);
23207c478bd9Sstevel@tonic-gate 
23217c478bd9Sstevel@tonic-gate 	while (prefixlen > 0) {
23227c478bd9Sstevel@tonic-gate 		if (prefixlen >= 8) {
23237c478bd9Sstevel@tonic-gate 			*mask++ = 0xFF;
23247c478bd9Sstevel@tonic-gate 			prefixlen -= 8;
23257c478bd9Sstevel@tonic-gate 			continue;
23267c478bd9Sstevel@tonic-gate 		}
23277c478bd9Sstevel@tonic-gate 		*mask |= 1 << (8 - prefixlen);
23287c478bd9Sstevel@tonic-gate 		prefixlen--;
23297c478bd9Sstevel@tonic-gate 	}
23307c478bd9Sstevel@tonic-gate 	return (B_TRUE);
23317c478bd9Sstevel@tonic-gate }
23327c478bd9Sstevel@tonic-gate 
23337c478bd9Sstevel@tonic-gate void
23347c478bd9Sstevel@tonic-gate rtmonitor(int argc, char *argv[])
23357c478bd9Sstevel@tonic-gate {
23367c478bd9Sstevel@tonic-gate 	int n;
23377c478bd9Sstevel@tonic-gate 	intmax_t msg[2048 / sizeof (intmax_t)];
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 	if (tflag)
23407c478bd9Sstevel@tonic-gate 		exit(0);
23417c478bd9Sstevel@tonic-gate 	verbose = B_TRUE;
23427c478bd9Sstevel@tonic-gate 	if (argc > 1) {
23437c478bd9Sstevel@tonic-gate 		argv++;
23447c478bd9Sstevel@tonic-gate 		if (argc == 2 && **argv == '-') {
23457c478bd9Sstevel@tonic-gate 			switch (keyword(*argv + 1)) {
23467c478bd9Sstevel@tonic-gate 			case K_INET:
23477c478bd9Sstevel@tonic-gate 				af = AF_INET;
23487c478bd9Sstevel@tonic-gate 				break;
23497c478bd9Sstevel@tonic-gate 			case K_LINK:
23507c478bd9Sstevel@tonic-gate 				af = AF_LINK;
23517c478bd9Sstevel@tonic-gate 				break;
23527c478bd9Sstevel@tonic-gate 			case K_INET6:
23537c478bd9Sstevel@tonic-gate 				af = AF_INET6;
23547c478bd9Sstevel@tonic-gate 				break;
23557c478bd9Sstevel@tonic-gate 			default:
23567c478bd9Sstevel@tonic-gate 				usage(*argv);
23577c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
23587c478bd9Sstevel@tonic-gate 			}
23597c478bd9Sstevel@tonic-gate 		} else {
23607c478bd9Sstevel@tonic-gate 			usage(*argv);
23617c478bd9Sstevel@tonic-gate 		}
23627c478bd9Sstevel@tonic-gate 		(void) close(s);
23637c478bd9Sstevel@tonic-gate 		s = socket(PF_ROUTE, SOCK_RAW, af);
23647c478bd9Sstevel@tonic-gate 		if (s < 0)
23657c478bd9Sstevel@tonic-gate 			quit("socket", errno);
23667c478bd9Sstevel@tonic-gate 	}
23677c478bd9Sstevel@tonic-gate 	for (;;) {
23687c478bd9Sstevel@tonic-gate 		n = read(s, msg, sizeof (msg));
23697c478bd9Sstevel@tonic-gate 		if (n <= 0)
23707c478bd9Sstevel@tonic-gate 			quit("read", errno);
23717c478bd9Sstevel@tonic-gate 		(void) printf("got message of size %d\n", n);
23727c478bd9Sstevel@tonic-gate 		print_rtmsg((struct rt_msghdr *)msg, n);
23737c478bd9Sstevel@tonic-gate 	}
23747c478bd9Sstevel@tonic-gate }
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate int
2377*3f33f4f7Sjl138328 rtmsg(rtcmd_irep_t *newrt)
23787c478bd9Sstevel@tonic-gate {
23797c478bd9Sstevel@tonic-gate 	static int seq;
23807c478bd9Sstevel@tonic-gate 	int rlen;
23817c478bd9Sstevel@tonic-gate 	char *cp = m_rtmsg.m_space;
23827c478bd9Sstevel@tonic-gate 	int l;
23837c478bd9Sstevel@tonic-gate 
23847c478bd9Sstevel@tonic-gate 	errno = 0;
23857c478bd9Sstevel@tonic-gate 	(void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
2386*3f33f4f7Sjl138328 
2387*3f33f4f7Sjl138328 	if (newrt->ri_cmd == RTM_GET) {
2388*3f33f4f7Sjl138328 		newrt->ri_ifp.sa.sa_family = AF_LINK;
2389*3f33f4f7Sjl138328 		newrt->ri_addrs |= RTA_IFP;
23907c478bd9Sstevel@tonic-gate 	}
2391*3f33f4f7Sjl138328 
23927c478bd9Sstevel@tonic-gate #define	rtm m_rtmsg.m_rtm
2393*3f33f4f7Sjl138328 	rtm.rtm_type = newrt->ri_cmd;
2394*3f33f4f7Sjl138328 	rtm.rtm_flags = newrt->ri_flags;
23957c478bd9Sstevel@tonic-gate 	rtm.rtm_version = RTM_VERSION;
23967c478bd9Sstevel@tonic-gate 	rtm.rtm_seq = ++seq;
2397*3f33f4f7Sjl138328 	rtm.rtm_addrs = newrt->ri_addrs;
2398*3f33f4f7Sjl138328 	rtm.rtm_rmx = newrt->ri_metrics;
2399*3f33f4f7Sjl138328 	rtm.rtm_inits = newrt->ri_inits;
24007c478bd9Sstevel@tonic-gate 
24017c478bd9Sstevel@tonic-gate #define	NEXTADDR(w, u) \
2402*3f33f4f7Sjl138328 	if (newrt->ri_addrs & (w)) { \
24037c478bd9Sstevel@tonic-gate 		l = ROUNDUP_LONG(salen(&u.sa)); \
24047c478bd9Sstevel@tonic-gate 		(void) memmove(cp, &(u), l); \
24057c478bd9Sstevel@tonic-gate 		cp += l; \
24067c478bd9Sstevel@tonic-gate 		if (verbose) \
24077c478bd9Sstevel@tonic-gate 			sodump(&(u), #u); \
24087c478bd9Sstevel@tonic-gate 	}
2409*3f33f4f7Sjl138328 	NEXTADDR(RTA_DST, newrt->ri_dst);
2410*3f33f4f7Sjl138328 	NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
2411*3f33f4f7Sjl138328 	NEXTADDR(RTA_NETMASK, newrt->ri_mask);
2412*3f33f4f7Sjl138328 	NEXTADDR(RTA_IFP, newrt->ri_ifp);
2413*3f33f4f7Sjl138328 	NEXTADDR(RTA_IFA, newrt->ri_ifa);
24147c478bd9Sstevel@tonic-gate 	/*
24157c478bd9Sstevel@tonic-gate 	 * RTA_SRC has overloaded meaning. It can represent the
24167c478bd9Sstevel@tonic-gate 	 * src address of incoming or outgoing packets.
24177c478bd9Sstevel@tonic-gate 	 */
2418*3f33f4f7Sjl138328 	NEXTADDR(RTA_SRC, newrt->ri_src);
24197c478bd9Sstevel@tonic-gate #undef	NEXTADDR
24207c478bd9Sstevel@tonic-gate 	rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
24217c478bd9Sstevel@tonic-gate 	if (verbose)
24227c478bd9Sstevel@tonic-gate 		print_rtmsg(&rtm, l);
24237c478bd9Sstevel@tonic-gate 	if (debugonly)
24247c478bd9Sstevel@tonic-gate 		return (0);
24257c478bd9Sstevel@tonic-gate 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
24267c478bd9Sstevel@tonic-gate 		switch (errno) {
24277c478bd9Sstevel@tonic-gate 		case ESRCH:
24287c478bd9Sstevel@tonic-gate 		case EBUSY:
24297c478bd9Sstevel@tonic-gate 		case ENOBUFS:
24307c478bd9Sstevel@tonic-gate 		case EEXIST:
24317c478bd9Sstevel@tonic-gate 		case ENETUNREACH:
24327c478bd9Sstevel@tonic-gate 		case EHOSTUNREACH:
24337c478bd9Sstevel@tonic-gate 		case EPERM:
24347c478bd9Sstevel@tonic-gate 			break;
24357c478bd9Sstevel@tonic-gate 		default:
24367c478bd9Sstevel@tonic-gate 			perror(gettext("writing to routing socket"));
24377c478bd9Sstevel@tonic-gate 			break;
24387c478bd9Sstevel@tonic-gate 		}
24397c478bd9Sstevel@tonic-gate 		return (-1);
24407c478bd9Sstevel@tonic-gate 	} else if (rlen < (int)rtm.rtm_msglen) {
24417c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
24427c478bd9Sstevel@tonic-gate 		    gettext("route: write to routing socket got only %d for "
24437c478bd9Sstevel@tonic-gate 		    "len\n"), rlen);
24447c478bd9Sstevel@tonic-gate 		return (-1);
24457c478bd9Sstevel@tonic-gate 	}
2446*3f33f4f7Sjl138328 	if (newrt->ri_cmd == RTM_GET) {
24477c478bd9Sstevel@tonic-gate 		do {
24487c478bd9Sstevel@tonic-gate 			l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
24497c478bd9Sstevel@tonic-gate 		} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
24507c478bd9Sstevel@tonic-gate 		if (l < 0) {
24517c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
24527c478bd9Sstevel@tonic-gate 			    gettext("route: read from routing socket: %s\n"),
24537c478bd9Sstevel@tonic-gate 			    strerror(errno));
24547c478bd9Sstevel@tonic-gate 		} else {
2455*3f33f4f7Sjl138328 			print_getmsg(newrt, &rtm, l);
24567c478bd9Sstevel@tonic-gate 		}
24577c478bd9Sstevel@tonic-gate 	}
24587c478bd9Sstevel@tonic-gate #undef rtm
24597c478bd9Sstevel@tonic-gate 	return (0);
24607c478bd9Sstevel@tonic-gate }
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate static char *msgtypes[] = {
24637c478bd9Sstevel@tonic-gate 	"",
24647c478bd9Sstevel@tonic-gate 	"RTM_ADD: Add Route",
24657c478bd9Sstevel@tonic-gate 	"RTM_DELETE: Delete Route",
24667c478bd9Sstevel@tonic-gate 	"RTM_CHANGE: Change Metrics or flags",
24677c478bd9Sstevel@tonic-gate 	"RTM_GET: Report Metrics",
24687c478bd9Sstevel@tonic-gate 	"RTM_LOSING: Kernel Suspects Partitioning",
24697c478bd9Sstevel@tonic-gate 	"RTM_REDIRECT: Told to use different route",
24707c478bd9Sstevel@tonic-gate 	"RTM_MISS: Lookup failed on this address",
24717c478bd9Sstevel@tonic-gate 	"RTM_LOCK: fix specified metrics",
24727c478bd9Sstevel@tonic-gate 	"RTM_OLDADD: caused by SIOCADDRT",
24737c478bd9Sstevel@tonic-gate 	"RTM_OLDDEL: caused by SIOCDELRT",
24747c478bd9Sstevel@tonic-gate 	"RTM_RESOLVE: Route created by cloning",
24757c478bd9Sstevel@tonic-gate 	"RTM_NEWADDR: address being added to iface",
24767c478bd9Sstevel@tonic-gate 	"RTM_DELADDR: address being removed from iface",
24777c478bd9Sstevel@tonic-gate 	"RTM_IFINFO: iface status change",
24787c478bd9Sstevel@tonic-gate 	0,
24797c478bd9Sstevel@tonic-gate };
24807c478bd9Sstevel@tonic-gate 
24817c478bd9Sstevel@tonic-gate #define	NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
24827c478bd9Sstevel@tonic-gate 
24837c478bd9Sstevel@tonic-gate static char metricnames[] =
24847c478bd9Sstevel@tonic-gate "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
24857c478bd9Sstevel@tonic-gate 	"\1mtu";
24867c478bd9Sstevel@tonic-gate static char routeflags[] =
24877c478bd9Sstevel@tonic-gate "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
24887c478bd9Sstevel@tonic-gate 	"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
24897c478bd9Sstevel@tonic-gate 	"\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC";
24907c478bd9Sstevel@tonic-gate static char ifnetflags[] =
24917c478bd9Sstevel@tonic-gate "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
24927c478bd9Sstevel@tonic-gate 	"\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
24937c478bd9Sstevel@tonic-gate 	"\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
24947c478bd9Sstevel@tonic-gate 	"\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
24957c478bd9Sstevel@tonic-gate 	"\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
24967c478bd9Sstevel@tonic-gate 	"\033MIP\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
24977c478bd9Sstevel@tonic-gate 	"\041XRESOLV\042COS\043PREFERRED\044TEMPORARY";
24987c478bd9Sstevel@tonic-gate static char addrnames[] =
24997c478bd9Sstevel@tonic-gate "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
25007c478bd9Sstevel@tonic-gate 
25017c478bd9Sstevel@tonic-gate void
25027c478bd9Sstevel@tonic-gate print_rtmsg(struct rt_msghdr *rtm, int msglen)
25037c478bd9Sstevel@tonic-gate {
25047c478bd9Sstevel@tonic-gate 	struct if_msghdr *ifm;
25057c478bd9Sstevel@tonic-gate 	struct ifa_msghdr *ifam;
25067c478bd9Sstevel@tonic-gate 
25077c478bd9Sstevel@tonic-gate 	if (!verbose)
25087c478bd9Sstevel@tonic-gate 		return;
25097c478bd9Sstevel@tonic-gate 	if (rtm->rtm_version != RTM_VERSION) {
25107c478bd9Sstevel@tonic-gate 		(void) printf("routing message version %d not understood\n",
25117c478bd9Sstevel@tonic-gate 		    rtm->rtm_version);
25127c478bd9Sstevel@tonic-gate 		return;
25137c478bd9Sstevel@tonic-gate 	}
25147c478bd9Sstevel@tonic-gate 	if (rtm->rtm_msglen > (ushort_t)msglen) {
25157c478bd9Sstevel@tonic-gate 		(void) printf("message length mismatch, in packet %d, "
25167c478bd9Sstevel@tonic-gate 		    "returned %d\n",
25177c478bd9Sstevel@tonic-gate 		    rtm->rtm_msglen, msglen);
25187c478bd9Sstevel@tonic-gate 	}
25197c478bd9Sstevel@tonic-gate 	/*
25207c478bd9Sstevel@tonic-gate 	 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
25217c478bd9Sstevel@tonic-gate 	 * and the upper-bound of (NMSGTYPES - 1).
25227c478bd9Sstevel@tonic-gate 	 */
25237c478bd9Sstevel@tonic-gate 	if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
25247c478bd9Sstevel@tonic-gate 		(void) printf("routing message type %d not understood\n",
25257c478bd9Sstevel@tonic-gate 		    rtm->rtm_type);
25267c478bd9Sstevel@tonic-gate 		return;
25277c478bd9Sstevel@tonic-gate 	}
25287c478bd9Sstevel@tonic-gate 	(void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen);
25297c478bd9Sstevel@tonic-gate 	switch (rtm->rtm_type) {
25307c478bd9Sstevel@tonic-gate 	case RTM_IFINFO:
25317c478bd9Sstevel@tonic-gate 		ifm = (struct if_msghdr *)rtm;
25327c478bd9Sstevel@tonic-gate 		(void) printf("if# %d, flags:", ifm->ifm_index);
25337c478bd9Sstevel@tonic-gate 		bprintf(stdout, ifm->ifm_flags, ifnetflags);
25347c478bd9Sstevel@tonic-gate 		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
25357c478bd9Sstevel@tonic-gate 		break;
25367c478bd9Sstevel@tonic-gate 	case RTM_NEWADDR:
25377c478bd9Sstevel@tonic-gate 	case RTM_DELADDR:
25387c478bd9Sstevel@tonic-gate 		ifam = (struct ifa_msghdr *)rtm;
25397c478bd9Sstevel@tonic-gate 		(void) printf("metric %d, flags:", ifam->ifam_metric);
25407c478bd9Sstevel@tonic-gate 		bprintf(stdout, ifam->ifam_flags, routeflags);
25417c478bd9Sstevel@tonic-gate 		pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
25427c478bd9Sstevel@tonic-gate 		break;
25437c478bd9Sstevel@tonic-gate 	default:
25447c478bd9Sstevel@tonic-gate 		(void) printf("pid: %ld, seq %d, errno %d, flags:",
25457c478bd9Sstevel@tonic-gate 			rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
25467c478bd9Sstevel@tonic-gate 		bprintf(stdout, rtm->rtm_flags, routeflags);
25477c478bd9Sstevel@tonic-gate 		pmsg_common(rtm);
25487c478bd9Sstevel@tonic-gate 	}
25497c478bd9Sstevel@tonic-gate }
25507c478bd9Sstevel@tonic-gate 
25517c478bd9Sstevel@tonic-gate void
2552*3f33f4f7Sjl138328 print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
25537c478bd9Sstevel@tonic-gate {
25547c478bd9Sstevel@tonic-gate 	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
25557c478bd9Sstevel@tonic-gate 	struct sockaddr_dl *ifp = NULL;
25567c478bd9Sstevel@tonic-gate 	struct sockaddr *sa;
25577c478bd9Sstevel@tonic-gate 	char *cp;
25587c478bd9Sstevel@tonic-gate 	int i;
25597c478bd9Sstevel@tonic-gate 
2560*3f33f4f7Sjl138328 	(void) printf("   route to: %s\n", routename(&req_rt->ri_dst.sa));
25617c478bd9Sstevel@tonic-gate 	if (rtm->rtm_version != RTM_VERSION) {
25627c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
25637c478bd9Sstevel@tonic-gate 		    gettext("routing message version %d not understood\n"),
25647c478bd9Sstevel@tonic-gate 		    rtm->rtm_version);
25657c478bd9Sstevel@tonic-gate 		return;
25667c478bd9Sstevel@tonic-gate 	}
25677c478bd9Sstevel@tonic-gate 	if (rtm->rtm_msglen > (ushort_t)msglen) {
25687c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
25697c478bd9Sstevel@tonic-gate 		    gettext("message length mismatch, in packet %d, "
25707c478bd9Sstevel@tonic-gate 			"returned %d\n"), rtm->rtm_msglen, msglen);
25717c478bd9Sstevel@tonic-gate 	}
25727c478bd9Sstevel@tonic-gate 	if (rtm->rtm_errno)  {
25737c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
25747c478bd9Sstevel@tonic-gate 		    strerror(rtm->rtm_errno), rtm->rtm_errno);
25757c478bd9Sstevel@tonic-gate 		return;
25767c478bd9Sstevel@tonic-gate 	}
25777c478bd9Sstevel@tonic-gate 	cp = ((char *)(rtm + 1));
25787c478bd9Sstevel@tonic-gate 	if (rtm->rtm_addrs != 0) {
25797c478bd9Sstevel@tonic-gate 		for (i = 1; i != 0; i <<= 1) {
25807c478bd9Sstevel@tonic-gate 			if (i & rtm->rtm_addrs) {
25817c478bd9Sstevel@tonic-gate 				/* LINTED */
25827c478bd9Sstevel@tonic-gate 				sa = (struct sockaddr *)cp;
25837c478bd9Sstevel@tonic-gate 				switch (i) {
25847c478bd9Sstevel@tonic-gate 				case RTA_DST:
25857c478bd9Sstevel@tonic-gate 					dst = sa;
25867c478bd9Sstevel@tonic-gate 					break;
25877c478bd9Sstevel@tonic-gate 				case RTA_GATEWAY:
25887c478bd9Sstevel@tonic-gate 					gate = sa;
25897c478bd9Sstevel@tonic-gate 					break;
25907c478bd9Sstevel@tonic-gate 				case RTA_NETMASK:
25917c478bd9Sstevel@tonic-gate 					mask = sa;
25927c478bd9Sstevel@tonic-gate 					break;
25937c478bd9Sstevel@tonic-gate 				case RTA_IFP:
25947c478bd9Sstevel@tonic-gate 					if (sa->sa_family == AF_LINK &&
25957c478bd9Sstevel@tonic-gate 					    ((struct sockaddr_dl *)sa)->
25967c478bd9Sstevel@tonic-gate 						sdl_nlen != 0)
25977c478bd9Sstevel@tonic-gate 						ifp = (struct sockaddr_dl *)sa;
25987c478bd9Sstevel@tonic-gate 					break;
25997c478bd9Sstevel@tonic-gate 				case RTA_SRC:
26007c478bd9Sstevel@tonic-gate 					src = sa;
26017c478bd9Sstevel@tonic-gate 					break;
26027c478bd9Sstevel@tonic-gate 				}
26037c478bd9Sstevel@tonic-gate 				ADVANCE(cp, sa);
26047c478bd9Sstevel@tonic-gate 			}
26057c478bd9Sstevel@tonic-gate 		}
26067c478bd9Sstevel@tonic-gate 	}
26077c478bd9Sstevel@tonic-gate 	if (dst != NULL && mask != NULL)
26087c478bd9Sstevel@tonic-gate 		mask->sa_family = dst->sa_family;	/* XXX */
26097c478bd9Sstevel@tonic-gate 	if (dst != NULL)
26107c478bd9Sstevel@tonic-gate 		(void) printf("destination: %s\n", routename(dst));
26117c478bd9Sstevel@tonic-gate 	if (mask != NULL) {
26127c478bd9Sstevel@tonic-gate 		boolean_t savenflag = nflag;
26137c478bd9Sstevel@tonic-gate 
26147c478bd9Sstevel@tonic-gate 		nflag = B_TRUE;
26157c478bd9Sstevel@tonic-gate 		(void) printf("       mask: %s\n", routename(mask));
26167c478bd9Sstevel@tonic-gate 		nflag = savenflag;
26177c478bd9Sstevel@tonic-gate 	}
26187c478bd9Sstevel@tonic-gate 	if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
26197c478bd9Sstevel@tonic-gate 		(void) printf("    gateway: %s\n", routename(gate));
26207c478bd9Sstevel@tonic-gate 	if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
26217c478bd9Sstevel@tonic-gate 		(void) printf("     setsrc: %s\n", routename(src));
26227c478bd9Sstevel@tonic-gate 	if (ifp != NULL) {
26237c478bd9Sstevel@tonic-gate 		if (verbose) {
26247c478bd9Sstevel@tonic-gate 			int i;
26257c478bd9Sstevel@tonic-gate 
26267c478bd9Sstevel@tonic-gate 			(void) printf("  interface: %.*s index %d address ",
26277c478bd9Sstevel@tonic-gate 			    ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
26287c478bd9Sstevel@tonic-gate 			for (i = ifp->sdl_nlen;
26297c478bd9Sstevel@tonic-gate 			    i < ifp->sdl_nlen + ifp->sdl_alen;
26307c478bd9Sstevel@tonic-gate 			    i++) {
26317c478bd9Sstevel@tonic-gate 				(void) printf("%02x ",
26327c478bd9Sstevel@tonic-gate 				    ifp->sdl_data[i] & 0xFF);
26337c478bd9Sstevel@tonic-gate 			}
26347c478bd9Sstevel@tonic-gate 			(void) printf("\n");
26357c478bd9Sstevel@tonic-gate 		} else {
26367c478bd9Sstevel@tonic-gate 			(void) printf("  interface: %.*s\n",
26377c478bd9Sstevel@tonic-gate 			    ifp->sdl_nlen, ifp->sdl_data);
26387c478bd9Sstevel@tonic-gate 		}
26397c478bd9Sstevel@tonic-gate 	}
26407c478bd9Sstevel@tonic-gate 	(void) printf("      flags: ");
26417c478bd9Sstevel@tonic-gate 	bprintf(stdout, rtm->rtm_flags, routeflags);
26427c478bd9Sstevel@tonic-gate 
26437c478bd9Sstevel@tonic-gate #define	lock(f)	((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
26447c478bd9Sstevel@tonic-gate #define	msec(u)	(((u) + 500) / 1000)		/* usec to msec */
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	(void) printf("\n%s\n", " recvpipe  sendpipe  ssthresh    rtt,ms "
26477c478bd9Sstevel@tonic-gate 	    "rttvar,ms  hopcount      mtu     expire");
26487c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
26497c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
26507c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
26517c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
26527c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
26537c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
26547c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
26557c478bd9Sstevel@tonic-gate 	if (rtm->rtm_rmx.rmx_expire)
26567c478bd9Sstevel@tonic-gate 		rtm->rtm_rmx.rmx_expire -= time(0);
26577c478bd9Sstevel@tonic-gate 	(void) printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
26587c478bd9Sstevel@tonic-gate #undef lock
26597c478bd9Sstevel@tonic-gate #undef msec
26607c478bd9Sstevel@tonic-gate #define	RTA_IGN	\
26617c478bd9Sstevel@tonic-gate 	(RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
26627c478bd9Sstevel@tonic-gate 	if (verbose) {
26637c478bd9Sstevel@tonic-gate 		pmsg_common(rtm);
26647c478bd9Sstevel@tonic-gate 	} else if (rtm->rtm_addrs &~ RTA_IGN) {
26657c478bd9Sstevel@tonic-gate 		(void) printf("sockaddrs: ");
26667c478bd9Sstevel@tonic-gate 		bprintf(stdout, rtm->rtm_addrs, addrnames);
26677c478bd9Sstevel@tonic-gate 		(void) putchar('\n');
26687c478bd9Sstevel@tonic-gate 	}
26697c478bd9Sstevel@tonic-gate #undef	RTA_IGN
26707c478bd9Sstevel@tonic-gate }
26717c478bd9Sstevel@tonic-gate 
26727c478bd9Sstevel@tonic-gate void
26737c478bd9Sstevel@tonic-gate pmsg_common(struct rt_msghdr *rtm)
26747c478bd9Sstevel@tonic-gate {
26757c478bd9Sstevel@tonic-gate 	(void) printf("\nlocks: ");
26767c478bd9Sstevel@tonic-gate 	bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
26777c478bd9Sstevel@tonic-gate 	(void) printf(" inits: ");
26787c478bd9Sstevel@tonic-gate 	bprintf(stdout, (int)rtm->rtm_inits, metricnames);
26797c478bd9Sstevel@tonic-gate 	pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
26807c478bd9Sstevel@tonic-gate }
26817c478bd9Sstevel@tonic-gate 
26827c478bd9Sstevel@tonic-gate void
26837c478bd9Sstevel@tonic-gate pmsg_addrs(char *cp, int addrs)
26847c478bd9Sstevel@tonic-gate {
26857c478bd9Sstevel@tonic-gate 	struct sockaddr *sa;
26867c478bd9Sstevel@tonic-gate 	int i;
26877c478bd9Sstevel@tonic-gate 
26887c478bd9Sstevel@tonic-gate 	if (addrs == 0)
26897c478bd9Sstevel@tonic-gate 		return;
26907c478bd9Sstevel@tonic-gate 	(void) printf("\nsockaddrs: ");
26917c478bd9Sstevel@tonic-gate 	bprintf(stdout, addrs, addrnames);
26927c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
26937c478bd9Sstevel@tonic-gate 	for (i = 1; i != 0; i <<= 1) {
26947c478bd9Sstevel@tonic-gate 		if (i & addrs) {
26957c478bd9Sstevel@tonic-gate 			/* LINTED */
26967c478bd9Sstevel@tonic-gate 			sa = (struct sockaddr *)cp;
26977c478bd9Sstevel@tonic-gate 			(void) printf(" %s", routename(sa));
26987c478bd9Sstevel@tonic-gate 			ADVANCE(cp, sa);
26997c478bd9Sstevel@tonic-gate 		}
27007c478bd9Sstevel@tonic-gate 	}
27017c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
27027c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
27037c478bd9Sstevel@tonic-gate }
27047c478bd9Sstevel@tonic-gate 
27057c478bd9Sstevel@tonic-gate void
27067c478bd9Sstevel@tonic-gate bprintf(FILE *fp, int b, char *s)
27077c478bd9Sstevel@tonic-gate {
27087c478bd9Sstevel@tonic-gate 	int i;
27097c478bd9Sstevel@tonic-gate 	boolean_t gotsome = B_FALSE;
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 	if (b == 0)
27127c478bd9Sstevel@tonic-gate 		return;
27137c478bd9Sstevel@tonic-gate 	while ((i = *s++) != 0) {
27147c478bd9Sstevel@tonic-gate 		if (b & (1 << (i - 1))) {
27157c478bd9Sstevel@tonic-gate 			if (!gotsome)
27167c478bd9Sstevel@tonic-gate 				i = '<';
27177c478bd9Sstevel@tonic-gate 			else
27187c478bd9Sstevel@tonic-gate 				i = ',';
27197c478bd9Sstevel@tonic-gate 			(void) putc(i, fp);
27207c478bd9Sstevel@tonic-gate 			gotsome = B_TRUE;
27217c478bd9Sstevel@tonic-gate 			for (; (i = *s) > ' '; s++)
27227c478bd9Sstevel@tonic-gate 				(void) putc(i, fp);
27237c478bd9Sstevel@tonic-gate 		} else {
27247c478bd9Sstevel@tonic-gate 			while (*s > ' ')
27257c478bd9Sstevel@tonic-gate 				s++;
27267c478bd9Sstevel@tonic-gate 		}
27277c478bd9Sstevel@tonic-gate 	}
27287c478bd9Sstevel@tonic-gate 	if (gotsome)
27297c478bd9Sstevel@tonic-gate 		(void) putc('>', fp);
27307c478bd9Sstevel@tonic-gate }
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate int
27337c478bd9Sstevel@tonic-gate keyword(char *cp)
27347c478bd9Sstevel@tonic-gate {
27357c478bd9Sstevel@tonic-gate 	struct keytab *kt = keywords;
27367c478bd9Sstevel@tonic-gate 
27377c478bd9Sstevel@tonic-gate 	while (kt->kt_cp && strcmp(kt->kt_cp, cp))
27387c478bd9Sstevel@tonic-gate 		kt++;
27397c478bd9Sstevel@tonic-gate 	return (kt->kt_i);
27407c478bd9Sstevel@tonic-gate }
27417c478bd9Sstevel@tonic-gate 
27427c478bd9Sstevel@tonic-gate void
2743*3f33f4f7Sjl138328 sodump(su_t *su, char *which)
27447c478bd9Sstevel@tonic-gate {
27457c478bd9Sstevel@tonic-gate 	static char obuf[INET6_ADDRSTRLEN];
27467c478bd9Sstevel@tonic-gate 
27477c478bd9Sstevel@tonic-gate 	switch (su->sa.sa_family) {
27487c478bd9Sstevel@tonic-gate 	case AF_LINK:
27497c478bd9Sstevel@tonic-gate 		(void) printf("%s: link %s; ",
27507c478bd9Sstevel@tonic-gate 		    which, link_ntoa(&su->sdl));
27517c478bd9Sstevel@tonic-gate 		break;
27527c478bd9Sstevel@tonic-gate 	case AF_INET:
27537c478bd9Sstevel@tonic-gate 		(void) printf("%s: inet %s; ",
27547c478bd9Sstevel@tonic-gate 		    which, inet_ntoa(su->sin.sin_addr));
27557c478bd9Sstevel@tonic-gate 		break;
27567c478bd9Sstevel@tonic-gate 	case AF_INET6:
27577c478bd9Sstevel@tonic-gate 		if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
27587c478bd9Sstevel@tonic-gate 		    INET6_ADDRSTRLEN) != NULL) {
27597c478bd9Sstevel@tonic-gate 			(void) printf("%s: inet6 %s; ", which, obuf);
27607c478bd9Sstevel@tonic-gate 			break;
27617c478bd9Sstevel@tonic-gate 		}
27627c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
27637c478bd9Sstevel@tonic-gate 	default:
27647c478bd9Sstevel@tonic-gate 		quit(gettext("Internal Error"), EINVAL);
27657c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
27667c478bd9Sstevel@tonic-gate 	}
27677c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
27687c478bd9Sstevel@tonic-gate }
27697c478bd9Sstevel@tonic-gate 
27707c478bd9Sstevel@tonic-gate /* States */
27717c478bd9Sstevel@tonic-gate #define	VIRGIN	0
27727c478bd9Sstevel@tonic-gate #define	GOTONE	1
27737c478bd9Sstevel@tonic-gate #define	GOTTWO	2
27747c478bd9Sstevel@tonic-gate #define	RESET	3
27757c478bd9Sstevel@tonic-gate /* Inputs */
27767c478bd9Sstevel@tonic-gate #define	DIGIT	(4*0)
27777c478bd9Sstevel@tonic-gate #define	END	(4*1)
27787c478bd9Sstevel@tonic-gate #define	DELIM	(4*2)
27797c478bd9Sstevel@tonic-gate #define	LETTER	(4*3)
27807c478bd9Sstevel@tonic-gate 
27817c478bd9Sstevel@tonic-gate void
27827c478bd9Sstevel@tonic-gate sockaddr(char *addr, struct sockaddr *sa)
27837c478bd9Sstevel@tonic-gate {
27847c478bd9Sstevel@tonic-gate 	char *cp = (char *)sa;
27857c478bd9Sstevel@tonic-gate 	int size = salen(sa);
27867c478bd9Sstevel@tonic-gate 	char *cplim = cp + size;
27877c478bd9Sstevel@tonic-gate 	int byte = 0, state = VIRGIN, new;
27887c478bd9Sstevel@tonic-gate 
27897c478bd9Sstevel@tonic-gate 	(void) memset(cp, 0, size);
27907c478bd9Sstevel@tonic-gate 	cp++;
27917c478bd9Sstevel@tonic-gate 	do {
27927c478bd9Sstevel@tonic-gate 		if ((*addr >= '0') && (*addr <= '9')) {
27937c478bd9Sstevel@tonic-gate 			new = *addr - '0';
27947c478bd9Sstevel@tonic-gate 		} else if ((*addr >= 'a') && (*addr <= 'f')) {
27957c478bd9Sstevel@tonic-gate 			new = *addr - 'a' + 10;
27967c478bd9Sstevel@tonic-gate 		} else if ((*addr >= 'A') && (*addr <= 'F')) {
27977c478bd9Sstevel@tonic-gate 			new = *addr - 'A' + 10;
27987c478bd9Sstevel@tonic-gate 		} else if (*addr == 0) {
27997c478bd9Sstevel@tonic-gate 			state |= END;
28007c478bd9Sstevel@tonic-gate 		} else {
28017c478bd9Sstevel@tonic-gate 			state |= DELIM;
28027c478bd9Sstevel@tonic-gate 		}
28037c478bd9Sstevel@tonic-gate 		addr++;
28047c478bd9Sstevel@tonic-gate 		switch (state /* | INPUT */) {
28057c478bd9Sstevel@tonic-gate 		case GOTTWO | DIGIT:
28067c478bd9Sstevel@tonic-gate 			*cp++ = byte;
28077c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
28087c478bd9Sstevel@tonic-gate 		case VIRGIN | DIGIT:
28097c478bd9Sstevel@tonic-gate 			state = GOTONE; byte = new; continue;
28107c478bd9Sstevel@tonic-gate 		case GOTONE | DIGIT:
28117c478bd9Sstevel@tonic-gate 			state = GOTTWO; byte = new + (byte << 4); continue;
28127c478bd9Sstevel@tonic-gate 		default: /* | DELIM */
28137c478bd9Sstevel@tonic-gate 			state = VIRGIN; *cp++ = byte; byte = 0; continue;
28147c478bd9Sstevel@tonic-gate 		case GOTONE | END:
28157c478bd9Sstevel@tonic-gate 		case GOTTWO | END:
28167c478bd9Sstevel@tonic-gate 			*cp++ = byte;
28177c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
28187c478bd9Sstevel@tonic-gate 		case VIRGIN | END:
28197c478bd9Sstevel@tonic-gate 			break;
28207c478bd9Sstevel@tonic-gate 		}
28217c478bd9Sstevel@tonic-gate 		break;
28227c478bd9Sstevel@tonic-gate 	} while (cp < cplim);
28237c478bd9Sstevel@tonic-gate }
28247c478bd9Sstevel@tonic-gate 
28257c478bd9Sstevel@tonic-gate int
28267c478bd9Sstevel@tonic-gate salen(struct sockaddr *sa)
28277c478bd9Sstevel@tonic-gate {
28287c478bd9Sstevel@tonic-gate 	switch (sa->sa_family) {
28297c478bd9Sstevel@tonic-gate 	case AF_INET:
28307c478bd9Sstevel@tonic-gate 		return (sizeof (struct sockaddr_in));
28317c478bd9Sstevel@tonic-gate 	case AF_LINK:
28327c478bd9Sstevel@tonic-gate 		return (sizeof (struct sockaddr_dl));
28337c478bd9Sstevel@tonic-gate 	case AF_INET6:
28347c478bd9Sstevel@tonic-gate 		return (sizeof (struct sockaddr_in6));
28357c478bd9Sstevel@tonic-gate 	default:
28367c478bd9Sstevel@tonic-gate 		return (sizeof (struct sockaddr));
28377c478bd9Sstevel@tonic-gate 	}
28387c478bd9Sstevel@tonic-gate }
28397c478bd9Sstevel@tonic-gate 
28407c478bd9Sstevel@tonic-gate void
28417c478bd9Sstevel@tonic-gate link_addr(const char *addr, struct sockaddr_dl *sdl)
28427c478bd9Sstevel@tonic-gate {
28437c478bd9Sstevel@tonic-gate 	char *cp = sdl->sdl_data;
28447c478bd9Sstevel@tonic-gate 	char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
28457c478bd9Sstevel@tonic-gate 	int byte = 0, state = VIRGIN, new;
28467c478bd9Sstevel@tonic-gate 
28477c478bd9Sstevel@tonic-gate 	(void) memset(sdl, 0, sizeof (struct sockaddr_dl));
28487c478bd9Sstevel@tonic-gate 	sdl->sdl_family = AF_LINK;
28497c478bd9Sstevel@tonic-gate 	do {
28507c478bd9Sstevel@tonic-gate 		state &= ~LETTER;
28517c478bd9Sstevel@tonic-gate 		if ((*addr >= '0') && (*addr <= '9')) {
28527c478bd9Sstevel@tonic-gate 			new = *addr - '0';
28537c478bd9Sstevel@tonic-gate 		} else if ((*addr >= 'a') && (*addr <= 'f')) {
28547c478bd9Sstevel@tonic-gate 			new = *addr - 'a' + 10;
28557c478bd9Sstevel@tonic-gate 		} else if ((*addr >= 'A') && (*addr <= 'F')) {
28567c478bd9Sstevel@tonic-gate 			new = *addr - 'A' + 10;
28577c478bd9Sstevel@tonic-gate 		} else if (*addr == 0) {
28587c478bd9Sstevel@tonic-gate 			state |= END;
28597c478bd9Sstevel@tonic-gate 		} else if (state == VIRGIN &&
28607c478bd9Sstevel@tonic-gate 		    (((*addr >= 'A') && (*addr <= 'Z')) ||
28617c478bd9Sstevel@tonic-gate 		    ((*addr >= 'a') && (*addr <= 'z')))) {
28627c478bd9Sstevel@tonic-gate 			state |= LETTER;
28637c478bd9Sstevel@tonic-gate 		} else {
28647c478bd9Sstevel@tonic-gate 			state |= DELIM;
28657c478bd9Sstevel@tonic-gate 		}
28667c478bd9Sstevel@tonic-gate 		addr++;
28677c478bd9Sstevel@tonic-gate 		switch (state /* | INPUT */) {
28687c478bd9Sstevel@tonic-gate 		case VIRGIN | DIGIT:
28697c478bd9Sstevel@tonic-gate 		case VIRGIN | LETTER:
28707c478bd9Sstevel@tonic-gate 			*cp++ = addr[-1];
28717c478bd9Sstevel@tonic-gate 			continue;
28727c478bd9Sstevel@tonic-gate 		case VIRGIN | DELIM:
28737c478bd9Sstevel@tonic-gate 			state = RESET;
28747c478bd9Sstevel@tonic-gate 			sdl->sdl_nlen = cp - sdl->sdl_data;
28757c478bd9Sstevel@tonic-gate 			continue;
28767c478bd9Sstevel@tonic-gate 		case GOTTWO | DIGIT:
28777c478bd9Sstevel@tonic-gate 			*cp++ = byte;
28787c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
28797c478bd9Sstevel@tonic-gate 		case RESET | DIGIT:
28807c478bd9Sstevel@tonic-gate 			state = GOTONE;
28817c478bd9Sstevel@tonic-gate 			byte = new;
28827c478bd9Sstevel@tonic-gate 			continue;
28837c478bd9Sstevel@tonic-gate 		case GOTONE | DIGIT:
28847c478bd9Sstevel@tonic-gate 			state = GOTTWO;
28857c478bd9Sstevel@tonic-gate 			byte = new + (byte << 4);
28867c478bd9Sstevel@tonic-gate 			continue;
28877c478bd9Sstevel@tonic-gate 		default: /* | DELIM */
28887c478bd9Sstevel@tonic-gate 			state = RESET;
28897c478bd9Sstevel@tonic-gate 			*cp++ = byte;
28907c478bd9Sstevel@tonic-gate 			byte = 0;
28917c478bd9Sstevel@tonic-gate 			continue;
28927c478bd9Sstevel@tonic-gate 		case GOTONE | END:
28937c478bd9Sstevel@tonic-gate 		case GOTTWO | END:
28947c478bd9Sstevel@tonic-gate 			*cp++ = byte;
28957c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
28967c478bd9Sstevel@tonic-gate 		case RESET | END:
28977c478bd9Sstevel@tonic-gate 			break;
28987c478bd9Sstevel@tonic-gate 		}
28997c478bd9Sstevel@tonic-gate 		break;
29007c478bd9Sstevel@tonic-gate 	} while (cp < cplim);
29017c478bd9Sstevel@tonic-gate 	sdl->sdl_alen = cp - LLADDR(sdl);
29027c478bd9Sstevel@tonic-gate }
29037c478bd9Sstevel@tonic-gate 
29047c478bd9Sstevel@tonic-gate static char hexlist[] = "0123456789abcdef";
29057c478bd9Sstevel@tonic-gate 
29067c478bd9Sstevel@tonic-gate char *
29077c478bd9Sstevel@tonic-gate link_ntoa(const struct sockaddr_dl *sdl)
29087c478bd9Sstevel@tonic-gate {
29097c478bd9Sstevel@tonic-gate 	static char obuf[64];
29107c478bd9Sstevel@tonic-gate 	char *out = obuf;
29117c478bd9Sstevel@tonic-gate 	int i;
29127c478bd9Sstevel@tonic-gate 	uchar_t *in = (uchar_t *)LLADDR(sdl);
29137c478bd9Sstevel@tonic-gate 	uchar_t *inlim = in + sdl->sdl_alen;
29147c478bd9Sstevel@tonic-gate 	boolean_t firsttime = B_TRUE;
29157c478bd9Sstevel@tonic-gate 
29167c478bd9Sstevel@tonic-gate 	if (sdl->sdl_nlen) {
29177c478bd9Sstevel@tonic-gate 		(void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
29187c478bd9Sstevel@tonic-gate 		out += sdl->sdl_nlen;
29197c478bd9Sstevel@tonic-gate 		if (sdl->sdl_alen)
29207c478bd9Sstevel@tonic-gate 			*out++ = ':';
29217c478bd9Sstevel@tonic-gate 	}
29227c478bd9Sstevel@tonic-gate 	while (in < inlim) {
29237c478bd9Sstevel@tonic-gate 		if (firsttime)
29247c478bd9Sstevel@tonic-gate 			firsttime = B_FALSE;
29257c478bd9Sstevel@tonic-gate 		else
29267c478bd9Sstevel@tonic-gate 			*out++ = '.';
29277c478bd9Sstevel@tonic-gate 		i = *in++;
29287c478bd9Sstevel@tonic-gate 		if (i > 0xf) {
29297c478bd9Sstevel@tonic-gate 			out[1] = hexlist[i & 0xf];
29307c478bd9Sstevel@tonic-gate 			i >>= 4;
29317c478bd9Sstevel@tonic-gate 			out[0] = hexlist[i];
29327c478bd9Sstevel@tonic-gate 			out += 2;
29337c478bd9Sstevel@tonic-gate 		} else {
29347c478bd9Sstevel@tonic-gate 			*out++ = hexlist[i];
29357c478bd9Sstevel@tonic-gate 		}
29367c478bd9Sstevel@tonic-gate 	}
29377c478bd9Sstevel@tonic-gate 	*out = 0;
29387c478bd9Sstevel@tonic-gate 	return (obuf);
29397c478bd9Sstevel@tonic-gate }
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate static mib_item_t *
29427c478bd9Sstevel@tonic-gate mibget(int sd)
29437c478bd9Sstevel@tonic-gate {
29447c478bd9Sstevel@tonic-gate 	intmax_t		buf[512 / sizeof (intmax_t)];
29457c478bd9Sstevel@tonic-gate 	int			flags;
29467c478bd9Sstevel@tonic-gate 	int			i, j, getcode;
29477c478bd9Sstevel@tonic-gate 	struct strbuf		ctlbuf, databuf;
29487c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req	*tor = (struct T_optmgmt_req *)buf;
29497c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack	*toa = (struct T_optmgmt_ack *)buf;
29507c478bd9Sstevel@tonic-gate 	struct T_error_ack	*tea = (struct T_error_ack *)buf;
29517c478bd9Sstevel@tonic-gate 	struct opthdr		*req;
29527c478bd9Sstevel@tonic-gate 	mib_item_t		*first_item = NULL;
29537c478bd9Sstevel@tonic-gate 	mib_item_t		*last_item  = NULL;
29547c478bd9Sstevel@tonic-gate 	mib_item_t		*temp;
29557c478bd9Sstevel@tonic-gate 
29567c478bd9Sstevel@tonic-gate 	tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
29577c478bd9Sstevel@tonic-gate 	tor->OPT_offset = sizeof (struct T_optmgmt_req);
29587c478bd9Sstevel@tonic-gate 	tor->OPT_length = sizeof (struct opthdr);
29597c478bd9Sstevel@tonic-gate 	tor->MGMT_flags = T_CURRENT;
29607c478bd9Sstevel@tonic-gate 	req = (struct opthdr *)&tor[1];
29617c478bd9Sstevel@tonic-gate 	req->level = MIB2_IP;		/* any MIB2_xxx value ok here */
29627c478bd9Sstevel@tonic-gate 	req->name  = 0;
29637c478bd9Sstevel@tonic-gate 	req->len   = 0;
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 	ctlbuf.buf = (char *)buf;
29667c478bd9Sstevel@tonic-gate 	ctlbuf.len = tor->OPT_length + tor->OPT_offset;
29677c478bd9Sstevel@tonic-gate 	flags = 0;
29687c478bd9Sstevel@tonic-gate 	if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
29697c478bd9Sstevel@tonic-gate 		perror("mibget: putmsg (ctl)");
29707c478bd9Sstevel@tonic-gate 		return (NULL);
29717c478bd9Sstevel@tonic-gate 	}
29727c478bd9Sstevel@tonic-gate 	/*
29737c478bd9Sstevel@tonic-gate 	 * each reply consists of a ctl part for one fixed structure
29747c478bd9Sstevel@tonic-gate 	 * or table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK,
29757c478bd9Sstevel@tonic-gate 	 * containing an opthdr structure.  level/name identify the entry,
29767c478bd9Sstevel@tonic-gate 	 * len is the size of the data part of the message.
29777c478bd9Sstevel@tonic-gate 	 */
29787c478bd9Sstevel@tonic-gate 	req = (struct opthdr *)&toa[1];
29797c478bd9Sstevel@tonic-gate 	ctlbuf.maxlen = sizeof (buf);
29807c478bd9Sstevel@tonic-gate 	for (j = 1; ; j++) {
29817c478bd9Sstevel@tonic-gate 		flags = 0;
29827c478bd9Sstevel@tonic-gate 		getcode = getmsg(sd, &ctlbuf, NULL, &flags);
29837c478bd9Sstevel@tonic-gate 		if (getcode < 0) {
29847c478bd9Sstevel@tonic-gate 			perror("mibget: getmsg (ctl)");
29857c478bd9Sstevel@tonic-gate 			if (verbose) {
29867c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
29877c478bd9Sstevel@tonic-gate 				    "#   level   name    len\n");
29887c478bd9Sstevel@tonic-gate 				i = 0;
29897c478bd9Sstevel@tonic-gate 				for (last_item = first_item; last_item != NULL;
29907c478bd9Sstevel@tonic-gate 				    last_item = last_item->next_item) {
29917c478bd9Sstevel@tonic-gate 					(void) printf("%d  %4ld   %5ld   %ld\n",
29927c478bd9Sstevel@tonic-gate 					    ++i, last_item->group,
29937c478bd9Sstevel@tonic-gate 					    last_item->mib_id,
29947c478bd9Sstevel@tonic-gate 					    last_item->length);
29957c478bd9Sstevel@tonic-gate 				}
29967c478bd9Sstevel@tonic-gate 			}
29977c478bd9Sstevel@tonic-gate 			break;
29987c478bd9Sstevel@tonic-gate 		}
29997c478bd9Sstevel@tonic-gate 		if (getcode == 0 &&
30007c478bd9Sstevel@tonic-gate 		    ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
30017c478bd9Sstevel@tonic-gate 		    toa->PRIM_type == T_OPTMGMT_ACK &&
30027c478bd9Sstevel@tonic-gate 		    toa->MGMT_flags == T_SUCCESS &&
30037c478bd9Sstevel@tonic-gate 		    req->len == 0) {
30047c478bd9Sstevel@tonic-gate 			if (verbose) {
30057c478bd9Sstevel@tonic-gate 				(void) printf("mibget getmsg() %d returned EOD "
30067c478bd9Sstevel@tonic-gate 				    "(level %lu, name %lu)\n", j, req->level,
30077c478bd9Sstevel@tonic-gate 				    req->name);
30087c478bd9Sstevel@tonic-gate 			}
30097c478bd9Sstevel@tonic-gate 			return (first_item);		/* this is EOD msg */
30107c478bd9Sstevel@tonic-gate 		}
30117c478bd9Sstevel@tonic-gate 
30127c478bd9Sstevel@tonic-gate 		if (ctlbuf.len >= sizeof (struct T_error_ack) &&
30137c478bd9Sstevel@tonic-gate 		    tea->PRIM_type == T_ERROR_ACK) {
30147c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("mibget %d gives "
30157c478bd9Sstevel@tonic-gate 			    "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
30167c478bd9Sstevel@tonic-gate 			    "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
30177c478bd9Sstevel@tonic-gate 			errno = (tea->TLI_error == TSYSERR)
30187c478bd9Sstevel@tonic-gate 				? tea->UNIX_error : EPROTO;
30197c478bd9Sstevel@tonic-gate 			break;
30207c478bd9Sstevel@tonic-gate 		}
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate 		if (getcode != MOREDATA ||
30237c478bd9Sstevel@tonic-gate 		    ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
30247c478bd9Sstevel@tonic-gate 		    toa->PRIM_type != T_OPTMGMT_ACK ||
30257c478bd9Sstevel@tonic-gate 		    toa->MGMT_flags != T_SUCCESS) {
30267c478bd9Sstevel@tonic-gate 			(void) printf("mibget getmsg(ctl) %d returned %d, "
30277c478bd9Sstevel@tonic-gate 			    "ctlbuf.len = %d, PRIM_type = %ld\n",
30287c478bd9Sstevel@tonic-gate 			    j, getcode, ctlbuf.len, toa->PRIM_type);
30297c478bd9Sstevel@tonic-gate 			if (toa->PRIM_type == T_OPTMGMT_ACK) {
30307c478bd9Sstevel@tonic-gate 				(void) printf("T_OPTMGMT_ACK: "
30317c478bd9Sstevel@tonic-gate 				    "MGMT_flags = 0x%lx, req->len = %ld\n",
30327c478bd9Sstevel@tonic-gate 				    toa->MGMT_flags, req->len);
30337c478bd9Sstevel@tonic-gate 			}
30347c478bd9Sstevel@tonic-gate 			errno = ENOMSG;
30357c478bd9Sstevel@tonic-gate 			break;
30367c478bd9Sstevel@tonic-gate 		}
30377c478bd9Sstevel@tonic-gate 
30387c478bd9Sstevel@tonic-gate 		temp = malloc(sizeof (mib_item_t));
30397c478bd9Sstevel@tonic-gate 		if (temp == NULL) {
30407c478bd9Sstevel@tonic-gate 			perror("mibget: malloc");
30417c478bd9Sstevel@tonic-gate 			break;
30427c478bd9Sstevel@tonic-gate 		}
30437c478bd9Sstevel@tonic-gate 		if (last_item != NULL)
30447c478bd9Sstevel@tonic-gate 			last_item->next_item = temp;
30457c478bd9Sstevel@tonic-gate 		else
30467c478bd9Sstevel@tonic-gate 			first_item = temp;
30477c478bd9Sstevel@tonic-gate 		last_item = temp;
30487c478bd9Sstevel@tonic-gate 		last_item->next_item = NULL;
30497c478bd9Sstevel@tonic-gate 		last_item->group = req->level;
30507c478bd9Sstevel@tonic-gate 		last_item->mib_id = req->name;
30517c478bd9Sstevel@tonic-gate 		last_item->length = req->len;
30527c478bd9Sstevel@tonic-gate 		last_item->valp = malloc(req->len);
30537c478bd9Sstevel@tonic-gate 		if (verbose) {
30547c478bd9Sstevel@tonic-gate 			(void) printf("msg %d:  group = %4ld   mib_id = %5ld   "
30557c478bd9Sstevel@tonic-gate 			    "length = %ld\n",
30567c478bd9Sstevel@tonic-gate 			    j, last_item->group, last_item->mib_id,
30577c478bd9Sstevel@tonic-gate 			    last_item->length);
30587c478bd9Sstevel@tonic-gate 		}
30597c478bd9Sstevel@tonic-gate 
30607c478bd9Sstevel@tonic-gate 		databuf.maxlen = last_item->length;
30617c478bd9Sstevel@tonic-gate 		databuf.buf    = (char *)last_item->valp;
30627c478bd9Sstevel@tonic-gate 		databuf.len    = 0;
30637c478bd9Sstevel@tonic-gate 		flags = 0;
30647c478bd9Sstevel@tonic-gate 		getcode = getmsg(sd, NULL, &databuf, &flags);
30657c478bd9Sstevel@tonic-gate 		if (getcode < 0) {
30667c478bd9Sstevel@tonic-gate 			perror("mibget: getmsg (data)");
30677c478bd9Sstevel@tonic-gate 			break;
30687c478bd9Sstevel@tonic-gate 		} else if (getcode != 0) {
30697c478bd9Sstevel@tonic-gate 			(void) printf("mibget getmsg(data) returned %d, "
30707c478bd9Sstevel@tonic-gate 			    "databuf.maxlen = %d, databuf.len = %d\n",
30717c478bd9Sstevel@tonic-gate 			    getcode, databuf.maxlen, databuf.len);
30727c478bd9Sstevel@tonic-gate 			break;
30737c478bd9Sstevel@tonic-gate 		}
30747c478bd9Sstevel@tonic-gate 	}
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate 	/*
30777c478bd9Sstevel@tonic-gate 	 * On error, free all the allocated mib_item_t objects.
30787c478bd9Sstevel@tonic-gate 	 */
30797c478bd9Sstevel@tonic-gate 	while (first_item != NULL) {
30807c478bd9Sstevel@tonic-gate 		last_item = first_item;
30817c478bd9Sstevel@tonic-gate 		first_item = first_item->next_item;
30827c478bd9Sstevel@tonic-gate 		free(last_item);
30837c478bd9Sstevel@tonic-gate 	}
30847c478bd9Sstevel@tonic-gate 	return (NULL);
30857c478bd9Sstevel@tonic-gate }
3086