2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
9 /* Copyright (c) 1990 Mentat Inc. */
13 * Copyright (c) 1983, 1989, 1991, 1993
14 * The Regents of the University of California. All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * @(#)route.c 8.6 (Berkeley) 4/28/95
45 * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93
48 #pragma ident "%Z%%M% %I% %E% SMI"
50 #include <sys/param.h>
52 #include <sys/socket.h>
53 #include <sys/ioctl.h>
55 #include <sys/stream.h>
56 #include <sys/sysmacros.h>
57 #include <sys/tihdr.h>
58 #include <sys/types.h>
59 #include <sys/ccompile.h>
62 #include <net/route.h>
63 #include <net/if_dl.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
67 #include <inet/mib2.h>
86 #include <tsol/label.h>
88 static struct keytab
{
95 {"blackhole", K_BLACKHOLE
},
99 {"cloning", K_CLONING
},
101 {"delete", K_DELETE
},
105 {"expire", K_EXPIRE
},
109 {"gateway", K_GATEWAY
},
112 #define K_HOPCOUNT 12
113 {"hopcount", K_HOPCOUNT
},
126 #define K_INTERFACE 19
127 {"interface", K_INTERFACE
},
132 #define K_LOCKREST 22
133 {"lockrest", K_LOCKREST
},
137 {"monitor", K_MONITOR
},
143 {"netmask", K_NETMASK
},
144 #define K_NOSTATIC 28
145 {"nostatic", K_NOSTATIC
},
147 {"private", K_PRIVATE
},
149 {"proto1", K_PROTO1
},
151 {"proto2", K_PROTO2
},
152 #define K_RECVPIPE 32
153 {"recvpipe", K_RECVPIPE
},
155 {"reject", K_REJECT
},
159 {"rttvar", K_RTTVAR
},
162 #define K_SENDPIPE 37
163 {"sendpipe", K_SENDPIPE
},
164 #define K_SSTHRESH 38
165 {"ssthresh", K_SSTHRESH
},
167 {"static", K_STATIC
},
168 #define K_XRESOLVE 40
169 {"xresolve", K_XRESOLVE
},
171 {"multirt", K_MULTIRT
},
173 {"setsrc", K_SETSRC
},
177 {"secattr", K_SECATTR
},
182 * Size of buffers used to hold command lines from the saved route file as
183 * well as error strings.
185 #define BUF_SIZE 2048
187 typedef union sockunion
{
189 struct sockaddr_in sin
;
190 struct sockaddr_dl sdl
;
191 struct sockaddr_in6 sin6
;
195 * This structure represents the digested information from parsing arguments
196 * to route add, change, delete, and get.
199 typedef struct rtcmd_irep
{
204 struct rt_metrics ri_metrics
;
210 struct hostent
*ri_gate_hp
;
216 int ri_rtsa_cnt
; /* number of gateway security attributes */
217 struct rtsa_s ri_rtsa
; /* enough space for one attribute */
220 typedef struct mib_item_s
{
221 struct mib_item_s
*next_item
;
240 static boolean_t
args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
,
242 static void bprintf(FILE *fp
, int b
, char *s
);
243 static boolean_t
compare_rtcmd(rtcmd_irep_t
*srch_rt
,
244 rtcmd_irep_t
*file_rt
);
245 static void delRouteEntry(mib2_ipRouteEntry_t
*rp
,
246 mib2_ipv6RouteEntry_t
*rp6
, int seqno
);
247 static void del_rtcmd_irep(rtcmd_irep_t
*rcip
);
248 static void flushroutes(int argc
, char *argv
[]);
249 static boolean_t
getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
,
251 static boolean_t
in6_getaddr(char *s
, struct sockaddr_in6
*sin6
,
252 int *plenp
, struct hostent
**hpp
);
253 static boolean_t
in_getaddr(char *s
, struct sockaddr_in
*sin
,
254 int *plenp
, int which
, struct hostent
**hpp
, addr_type_t atype
,
256 static int in_getprefixlen(char *addr
, int max_plen
);
257 static boolean_t
in_prefixlentomask(int prefixlen
, int maxlen
,
259 static void inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
,
260 struct sockaddr_in
*sin
);
261 static in_addr_t
inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
);
262 static int keyword(const char *cp
);
263 static void link_addr(const char *addr
, struct sockaddr_dl
*sdl
);
264 static char *link_ntoa(const struct sockaddr_dl
*sdl
);
265 static mib_item_t
*mibget(int sd
);
266 static char *netname(struct sockaddr
*sa
);
267 static int newroute(char **argv
);
268 static rtcmd_irep_t
*new_rtcmd_irep(void);
269 static void pmsg_addrs(const char *cp
, size_t len
, uint_t addrs
);
270 static void pmsg_common(const struct rt_msghdr
*rtm
, size_t len
);
271 static void print_getmsg(rtcmd_irep_t
*req_rt
,
272 struct rt_msghdr
*rtm
, int msglen
);
273 static void print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
,
274 boolean_t gw_good
, boolean_t to_saved
);
275 static void print_rtmsg(struct rt_msghdr
*rtm
, int msglen
);
276 static void quit(char *s
, int err
) __NORETURN
;
277 static char *routename(const struct sockaddr
*sa
);
278 static void rtmonitor(int argc
, char *argv
[]);
279 static int rtmsg(rtcmd_irep_t
*rcip
);
280 static int salen(const struct sockaddr
*sa
);
281 static void save_route(int argc
, char **argv
, int do_flush
);
282 static void save_string(char **dst
, char *src
);
283 static int search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
,
285 static void set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
,
287 static int show_saved_routes(int argc
);
288 static void sockaddr(char *addr
, struct sockaddr
*sa
);
289 static void sodump(su_t
*su
, char *which
);
290 static void syntax_arg_missing(char *keyword
);
291 static void syntax_bad_keyword(char *keyword
);
292 static void syntax_error(char *err
, ...);
293 static void usage(char *cp
);
294 static void write_to_rtfile(FILE *fp
, int argc
, char **argv
);
295 static void pmsg_secattr(const char *, size_t, const char *);
299 static boolean_t nflag
;
300 static int af
= AF_INET
;
301 static boolean_t qflag
, tflag
;
302 static boolean_t verbose
;
303 static boolean_t debugonly
;
304 static boolean_t fflag
;
305 static boolean_t update_table
;
306 static boolean_t perm_flag
;
307 static boolean_t early_v6_keyword
;
308 static char perm_file_sfx
[] = "/etc/inet/static_routes";
309 static char *perm_file
;
310 static char temp_file_sfx
[] = "/etc/inet/static_routes.tmp";
311 static char *temp_file
;
312 static struct in6_addr in6_host_mask
= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
313 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
316 * This next variable indicates whether certain functions exit when an error
317 * is detected in the user input. Currently, exit_on_error is only set false
318 * in search_rtfile(), when argument are being parsed. Only those functions
319 * used by search_rtfile() to parse its arguments are designed to work in
320 * both modes. Take particular care in setting this false to ensure that any
321 * functions you call that might act on this flag properly return errors when
322 * exit_on_error is false.
324 static int exit_on_error
= B_TRUE
;
327 struct rt_msghdr m_rtm
;
328 char m_space
[BUF_SIZE
];
332 * Sizes of data structures extracted from the base mib.
333 * This allows the size of the tables entries to grow while preserving
334 * binary compatibility.
336 static int ipRouteEntrySize
;
337 static int ipv6RouteEntrySize
;
339 #define ROUNDUP_LONG(a) \
340 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
341 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
342 #define C(x) ((x) & 0xff)
345 * return values from in_getprefixlen()
347 #define BAD_ADDR -1 /* prefix is invalid */
348 #define NO_PREFIX -2 /* no prefix was found */
354 (void) fprintf(stderr
, gettext("route: botched keyword: %s\n"),
357 (void) fprintf(stderr
, gettext("usage: route [ -fnpqv ] "
358 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
365 syntax_error(char *err
, ...)
371 (void) vfprintf(stderr
, err
, args
);
379 syntax_bad_keyword(char *keyword
)
381 syntax_error(gettext("route: botched keyword: %s\n"), keyword
);
385 syntax_arg_missing(char *keyword
)
387 syntax_error(gettext("route: argument required following keyword %s\n"),
392 quit(char *s
, int sverrno
)
394 (void) fprintf(stderr
, "route: ");
396 (void) fprintf(stderr
, "%s: ", s
);
397 (void) fprintf(stderr
, "%s\n", strerror(sverrno
));
403 main(int argc
, char **argv
)
410 const char *root_dir
= NULL
;
412 (void) setlocale(LC_ALL
, "");
414 #if !defined(TEXT_DOMAIN)
415 #define TEXT_DOMAIN "SYS_TEST"
417 (void) textdomain(TEXT_DOMAIN
);
422 while ((ch
= getopt(argc
, argv
, "R:nqdtvfp")) != EOF
) {
459 s
= open("/dev/null", O_WRONLY
);
461 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
463 quit("socket", errno
);
466 * Handle the -p and -R flags. The -R flag only applies
467 * when the -p flag is set.
469 if (root_dir
== NULL
) {
470 perm_file
= perm_file_sfx
;
471 temp_file
= temp_file_sfx
;
473 size
= strlen(root_dir
) + sizeof (perm_file_sfx
);
474 perm_file
= malloc(size
);
475 if (perm_file
== NULL
)
476 quit("malloc", errno
);
477 (void) snprintf(perm_file
, size
, "%s%s", root_dir
,
479 size
= strlen(root_dir
) + sizeof (temp_file_sfx
);
480 temp_file
= malloc(size
);
481 if (temp_file
== NULL
)
482 quit("malloc", errno
);
483 (void) snprintf(temp_file
, size
, "%s%s", root_dir
,
487 * Whether or not to act on the routing table. The only time the
488 * routing table is not modified is when both -p and -R are present.
490 update_table
= (!perm_flag
|| root_dir
== NULL
);
496 * Accept an address family keyword after the -f. Since the
497 * default address family is AF_INET, reassign af only for the
498 * other valid address families.
501 switch (keyword(*argv
)) {
504 early_v6_keyword
= B_TRUE
;
507 /* Skip over the address family parameter. */
513 flushroutes(0, NULL
);
517 switch (keyword(*argv
)) {
524 rval
= newroute(argv
);
526 if (perm_flag
&& (rval
== 0 || rval
== EEXIST
||
528 save_route(argc
, argv
, B_FALSE
);
534 return (show_saved_routes(argc
));
536 syntax_error(gettext(
537 "route: show command requires -p\n"));
541 rtmonitor(argc
, argv
);
545 flushroutes(argc
, argv
);
555 * Purge all entries in the routing tables not
556 * associated with network interfaces.
559 flushroutes(int argc
, char *argv
[])
562 int sd
; /* mib stream */
564 mib2_ipRouteEntry_t
*rp
;
565 mib2_ipv6RouteEntry_t
*rp6
;
572 if (argc
== 2 && **argv
== '-') {
574 * The address family (preceded by a dash) may be used
575 * to flush the routes of that particular family.
577 switch (keyword(*argv
+ 1)) {
596 /* This flushes the persistent route file */
597 save_route(0, NULL
, B_TRUE
);
603 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&off
,
605 quit("setsockopt", errno
);
607 sd
= open("/dev/ip", O_RDWR
);
612 (void) fprintf(stderr
,
613 gettext("route: flush: insufficient privileges\n"));
617 quit(gettext("can't open mib stream"), oerrno
);
621 if ((item
= mibget(sd
)) == NULL
)
622 quit("mibget", errno
);
624 (void) printf("Examining routing table from "
625 "T_SVR4_OPTMGMT_REQ\n");
630 /* Extract ipRouteEntrySize */
631 for (; item
!= NULL
; item
= item
->next_item
) {
632 if (item
->mib_id
!= 0)
634 if (item
->group
== MIB2_IP
) {
636 ((mib2_ip_t
*)item
->valp
)->ipRouteEntrySize
;
637 assert(IS_P2ALIGNED(ipRouteEntrySize
,
638 sizeof (mib2_ipRouteEntry_t
*)));
642 if (ipRouteEntrySize
== 0) {
643 (void) fprintf(stderr
,
644 gettext("ipRouteEntrySize can't be determined.\n"));
647 for (; item
!= NULL
; item
= item
->next_item
) {
649 * skip all the other trash that comes up the mib stream
651 if (item
->group
!= MIB2_IP
||
652 item
->mib_id
!= MIB2_IP_ROUTE
)
654 for (rp
= (mib2_ipRouteEntry_t
*)item
->valp
;
655 (char *)rp
< (char *)item
->valp
+ item
->length
;
657 rp
= (mib2_ipRouteEntry_t
*)
658 ((char *)rp
+ ipRouteEntrySize
)) {
659 delRouteEntry(rp
, NULL
, seqno
);
666 /* Extract ipv6RouteEntrySize */
667 for (; item
!= NULL
; item
= item
->next_item
) {
668 if (item
->mib_id
!= 0)
670 if (item
->group
== MIB2_IP6
) {
672 ((mib2_ipv6IfStatsEntry_t
*)item
->valp
)->
674 assert(IS_P2ALIGNED(ipv6RouteEntrySize
,
675 sizeof (mib2_ipv6RouteEntry_t
*)));
679 if (ipv6RouteEntrySize
== 0) {
680 (void) fprintf(stderr
, gettext(
681 "ipv6RouteEntrySize cannot be determined.\n"));
684 for (; item
!= NULL
; item
= item
->next_item
) {
686 * skip all the other trash that comes up the mib stream
688 if (item
->group
!= MIB2_IP6
||
689 item
->mib_id
!= MIB2_IP6_ROUTE
)
691 for (rp6
= (mib2_ipv6RouteEntry_t
*)item
->valp
;
692 (char *)rp6
< (char *)item
->valp
+ item
->length
;
694 rp6
= (mib2_ipv6RouteEntry_t
*)
695 ((char *)rp6
+ ipv6RouteEntrySize
)) {
696 delRouteEntry(NULL
, rp6
, seqno
);
704 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&on
,
706 quit("setsockopt", errno
);
710 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
711 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
712 * order to facilitate the flushing of RTF_GATEWAY routes.
715 delRouteEntry(mib2_ipRouteEntry_t
*rp
, mib2_ipv6RouteEntry_t
*rp6
, int seqno
)
720 struct rt_msghdr
*rtm
;
721 struct sockaddr_in sin
;
722 struct sockaddr_in6 sin6
;
726 ire_type
= rp
->ipRouteInfo
.re_ire_type
;
728 ire_type
= rp6
->ipv6RouteInfo
.re_ire_type
;
729 if (ire_type
!= IRE_DEFAULT
&&
730 ire_type
!= IRE_PREFIX
&&
731 ire_type
!= IRE_HOST
&&
732 ire_type
!= IRE_HOST_REDIRECT
)
735 rtm
= &m_rtmsg
.m_rtm
;
736 (void) memset(rtm
, 0, sizeof (m_rtmsg
));
737 rtm
->rtm_type
= RTM_DELETE
;
738 rtm
->rtm_seq
= seqno
;
739 rtm
->rtm_flags
|= RTF_GATEWAY
;
740 rtm
->rtm_version
= RTM_VERSION
;
741 rtm
->rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
742 cp
= m_rtmsg
.m_space
;
744 slen
= sizeof (struct sockaddr_in
);
745 if (rp
->ipRouteMask
== IP_HOST_MASK
)
746 rtm
->rtm_flags
|= RTF_HOST
;
747 (void) memset(&sin
, 0, slen
);
748 sin
.sin_family
= AF_INET
;
749 sin
.sin_addr
.s_addr
= rp
->ipRouteDest
;
750 (void) memmove(cp
, &sin
, slen
);
752 sin
.sin_addr
.s_addr
= rp
->ipRouteNextHop
;
753 (void) memmove(cp
, &sin
, slen
);
755 sin
.sin_addr
.s_addr
= rp
->ipRouteMask
;
756 (void) memmove(cp
, &sin
, slen
);
759 slen
= sizeof (struct sockaddr_in6
);
760 if (rp6
->ipv6RoutePfxLength
== IPV6_ABITS
)
761 rtm
->rtm_flags
|= RTF_HOST
;
762 (void) memset(&sin6
, 0, slen
);
763 sin6
.sin6_family
= AF_INET6
;
764 sin6
.sin6_addr
= rp6
->ipv6RouteDest
;
765 (void) memmove(cp
, &sin6
, slen
);
767 sin6
.sin6_addr
= rp6
->ipv6RouteNextHop
;
768 (void) memmove(cp
, &sin6
, slen
);
770 (void) memset(&sin6
.sin6_addr
, 0, sizeof (sin6
.sin6_addr
));
771 (void) in_prefixlentomask(rp6
->ipv6RoutePfxLength
, IPV6_ABITS
,
772 (uchar_t
*)&sin6
.sin6_addr
.s6_addr
);
773 (void) memmove(cp
, &sin6
, slen
);
776 rtm
->rtm_msglen
= cp
- (char *)&m_rtmsg
;
779 * In debugonly mode, the routing socket message to delete the
780 * current entry is not actually sent. However if verbose is
781 * also set, the routing socket message that would have been
785 print_rtmsg(rtm
, rtm
->rtm_msglen
);
789 rlen
= write(s
, (char *)&m_rtmsg
, rtm
->rtm_msglen
);
790 if (rlen
< (int)rtm
->rtm_msglen
) {
792 (void) fprintf(stderr
,
793 gettext("route: write to routing socket: %s\n"),
796 (void) fprintf(stderr
, gettext("route: write to "
797 "routing socket got only %d for rlen\n"), rlen
);
803 * In quiet mode, nothing is printed at all (unless the write()
809 print_rtmsg(rtm
, rlen
);
811 struct sockaddr
*sa
= (struct sockaddr
*)(rtm
+ 1);
813 (void) printf("%-20.20s ",
814 rtm
->rtm_flags
& RTF_HOST
? routename(sa
) :
817 sa
= (struct sockaddr
*)(salen(sa
) + (char *)sa
);
818 (void) printf("%-20.20s ", routename(sa
));
819 (void) printf("done\n");
824 * Return the name of the host whose address is given.
827 routename(const struct sockaddr
*sa
)
830 static char line
[MAXHOSTNAMELEN
+ 1];
831 struct hostent
*hp
= NULL
;
832 static char domain
[MAXHOSTNAMELEN
+ 1];
833 static boolean_t first
= B_TRUE
;
842 if (gethostname(domain
, MAXHOSTNAMELEN
) == 0 &&
843 (cp
= strchr(domain
, '.')))
844 (void) strcpy(domain
, cp
+ 1);
849 if (salen(sa
) == 0) {
850 (void) strcpy(line
, "default");
853 switch (sa
->sa_family
) {
857 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
860 if (in
.s_addr
== INADDR_ANY
)
862 if (cp
== NULL
&& !nflag
) {
863 hp
= gethostbyaddr((char *)&in
, sizeof (struct in_addr
),
866 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
867 (strcmp(cp
+ 1, domain
) == 0))
873 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
874 line
[MAXHOSTNAMELEN
] = '\0';
876 in
.s_addr
= ntohl(in
.s_addr
);
877 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
878 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
884 return (link_ntoa((struct sockaddr_dl
*)sa
));
888 in6
= ((struct sockaddr_in6
*)sa
)->sin6_addr
;
891 if (IN6_IS_ADDR_UNSPECIFIED(&in6
))
893 if (cp
== NULL
&& !nflag
) {
894 hp
= getipnodebyaddr((char *)&in6
,
895 sizeof (struct in6_addr
), AF_INET6
, &error_num
);
897 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
898 (strcmp(cp
+ 1, domain
) == 0))
904 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
905 line
[MAXHOSTNAMELEN
] = '\0';
907 (void) inet_ntop(AF_INET6
, (void *)&in6
, line
,
918 slim
= s
+ ((salen(sa
) + 1) >> 1);
919 cp
= line
+ sprintf(line
, "(%d)", sa
->sa_family
);
921 while (++s
< slim
) /* start with sa->sa_data */
922 cp
+= sprintf(cp
, " %x", *s
);
929 * Return the name of the network whose address is given.
930 * The address is assumed to be that of a net or subnet, not a host.
933 netname(struct sockaddr
*sa
)
936 static char line
[MAXHOSTNAMELEN
+ 1];
944 switch (sa
->sa_family
) {
948 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
950 in
.s_addr
= ntohl(in
.s_addr
);
951 if (in
.s_addr
== INADDR_ANY
) {
954 if (IN_CLASSA(in
.s_addr
)) {
955 mask
= IN_CLASSA_NET
;
957 } else if (IN_CLASSB(in
.s_addr
)) {
958 mask
= IN_CLASSB_NET
;
961 mask
= IN_CLASSC_NET
;
965 * If there are more bits than the standard mask
966 * would suggest, subnets must be in use.
967 * Guess at the subnet mask, assuming reasonable
968 * width subnet fields.
970 while (in
.s_addr
&~ mask
)
971 mask
= (long)mask
>> subnetshift
;
972 net
= in
.s_addr
& mask
;
973 while ((mask
& 1) == 0)
974 mask
>>= 1, net
>>= 1;
975 np
= getnetbyaddr(net
, AF_INET
);
980 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
981 line
[MAXHOSTNAMELEN
] = '\0';
982 } else if ((in
.s_addr
& 0xffffff) == 0) {
983 (void) sprintf(line
, "%u", C(in
.s_addr
>> 24));
984 } else if ((in
.s_addr
& 0xffff) == 0) {
985 (void) sprintf(line
, "%u.%u", C(in
.s_addr
>> 24),
987 } else if ((in
.s_addr
& 0xff) == 0) {
988 (void) sprintf(line
, "%u.%u.%u", C(in
.s_addr
>> 24),
989 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8));
991 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
992 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
998 return (link_ntoa((struct sockaddr_dl
*)sa
));
1001 return (routename(sa
));
1005 s
= (ushort_t
*)sa
->sa_data
;
1007 slim
= s
+ ((salen(sa
) + 1) >> 1);
1008 cp
= line
+ sprintf(line
, "af %d:", sa
->sa_family
);
1011 cp
+= sprintf(cp
, " %x", *s
++);
1018 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1019 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1020 * or point to dynamically allocated memory.
1023 new_rtcmd_irep(void)
1027 rcip
= calloc(1, sizeof (rtcmd_irep_t
));
1029 quit("calloc", errno
);
1032 rcip
->ri_flags
= RTF_STATIC
;
1037 del_rtcmd_irep(rtcmd_irep_t
*rcip
)
1039 free(rcip
->ri_dest_str
);
1040 free(rcip
->ri_gate_str
);
1041 free(rcip
->ri_ifp_str
);
1043 * IPv6 host entries come from getipnodebyname, which dynamically
1044 * allocates memory. IPv4 host entries come from gethostbyname, which
1045 * returns static memory and cannot be freed with freehostent.
1047 if (rcip
->ri_gate_hp
!= NULL
&&
1048 rcip
->ri_gate_hp
->h_addrtype
== AF_INET6
)
1049 freehostent(rcip
->ri_gate_hp
);
1054 save_string(char **dst
, char *src
)
1059 quit("malloc", errno
);
1064 * Print the short form summary of a route command.
1065 * Eg. "add net default: gateway 10.0.0.1"
1066 * The final newline is not added, allowing the caller to append additional
1070 print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
, boolean_t gw_good
,
1074 char obuf
[INET6_ADDRSTRLEN
];
1076 switch (rcip
->ri_cmd
) {
1093 (void) fprintf(to
, "%s%s %s %s", cmd
,
1094 (to_saved
) ? " persistent" : "",
1095 (rcip
->ri_flags
& RTF_HOST
) ? "host" : "net",
1096 (rcip
->ri_dest_str
== NULL
) ? "NULL" : rcip
->ri_dest_str
);
1098 if (rcip
->ri_gate_str
!= NULL
) {
1099 switch (rcip
->ri_af
) {
1102 (void) fprintf(to
, ": gateway %s",
1103 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1104 } else if (gw_good
&&
1105 rcip
->ri_gate_hp
!= NULL
&&
1106 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1108 * Print the actual address used in the case
1109 * where there was more than one address
1110 * available for the name, and one was used
1113 (void) fprintf(to
, ": gateway %s (%s)",
1115 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1117 (void) fprintf(to
, ": gateway %s",
1122 if (inet_ntop(AF_INET6
,
1123 &rcip
->ri_gate
.sin6
.sin6_addr
, obuf
,
1124 INET6_ADDRSTRLEN
) != NULL
) {
1126 (void) fprintf(to
, ": gateway %s",
1131 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1132 (void) fprintf(to
, ": gateway %s (%s)",
1133 rcip
->ri_gate_str
, obuf
);
1139 (void) fprintf(to
, ": gateway %s",
1147 set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
, boolean_t lock
)
1150 uint_t noval
, *valp
= &noval
;
1153 #define caseof(x, y, z) \
1154 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1156 caseof(K_MTU
, RTV_MTU
, rmx_mtu
);
1157 caseof(K_HOPCOUNT
, RTV_HOPCOUNT
, rmx_hopcount
);
1158 caseof(K_EXPIRE
, RTV_EXPIRE
, rmx_expire
);
1159 caseof(K_RECVPIPE
, RTV_RPIPE
, rmx_recvpipe
);
1160 caseof(K_SENDPIPE
, RTV_SPIPE
, rmx_sendpipe
);
1161 caseof(K_SSTHRESH
, RTV_SSTHRESH
, rmx_ssthresh
);
1162 caseof(K_RTT
, RTV_RTT
, rmx_rtt
);
1163 caseof(K_RTTVAR
, RTV_RTTVAR
, rmx_rttvar
);
1166 rcip
->ri_inits
|= flag
;
1168 rcip
->ri_metrics
.rmx_locks
|= flag
;
1169 *valp
= atoi(value
);
1173 * Parse the options give in argv[], filling in rcip with the results.
1174 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1175 * tokenized to produce the command line. Cmd_string is tokenized using
1176 * strtok, which will overwrite whitespace in the string with nulls.
1178 * Returns B_TRUE on success and B_FALSE on failure.
1181 args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
, char *cmd_string
)
1183 const char *ws
= "\f\n\r\t\v ";
1184 char *tok
= cmd_string
;
1186 addr_type_t atype
= ADDR_TYPE_ANY
;
1187 boolean_t iflag
= B_FALSE
;
1188 boolean_t locknext
= B_FALSE
;
1189 boolean_t lockrest
= B_FALSE
;
1190 boolean_t dash_keyword
;
1194 if (cmd_string
== NULL
) {
1197 tok
= strtok(cmd_string
, ws
);
1201 * The command keywords are already fully checked by main() or
1206 rcip
->ri_cmd
= RTM_ADD
;
1209 rcip
->ri_cmd
= RTM_CHANGE
;
1212 rcip
->ri_cmd
= RTM_DELETE
;
1215 rcip
->ri_cmd
= RTM_GET
;
1219 quit(gettext("Internal Error"), EINVAL
);
1224 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1229 dash_keyword
= B_TRUE
;
1230 key
= keyword(tok
+ 1);
1232 dash_keyword
= B_FALSE
;
1234 if (key
!= K_HOST
&& key
!= K_NET
) {
1235 /* All others must be preceded by '-' */
1241 if (atype
== ADDR_TYPE_NET
) {
1242 syntax_error(gettext("route: -host and -net "
1243 "are mutually exclusive\n"));
1246 atype
= ADDR_TYPE_HOST
;
1249 if (atype
== ADDR_TYPE_HOST
) {
1250 syntax_error(gettext("route: -host and -net "
1251 "are mutually exclusive\n"));
1254 atype
= ADDR_TYPE_NET
;
1257 rcip
->ri_af
= AF_LINK
;
1260 rcip
->ri_af
= AF_INET
;
1263 rcip
->ri_af
= PF_ROUTE
;
1266 rcip
->ri_af
= AF_INET6
;
1273 rcip
->ri_flags
&= ~RTF_STATIC
;
1282 rcip
->ri_flags
|= RTF_REJECT
;
1285 rcip
->ri_flags
|= RTF_BLACKHOLE
;
1288 rcip
->ri_flags
|= RTF_PROTO1
;
1291 rcip
->ri_flags
|= RTF_PROTO2
;
1294 rcip
->ri_flags
|= RTF_CLONING
;
1297 rcip
->ri_flags
|= RTF_XRESOLVE
;
1300 rcip
->ri_flags
|= RTF_STATIC
;
1304 syntax_arg_missing(keyword_str
);
1307 if (!getaddr(rcip
, RTA_IFA
, tok
, atype
)) {
1313 syntax_arg_missing(keyword_str
);
1316 if (!getaddr(rcip
, RTA_IFP
, tok
, atype
)) {
1322 syntax_arg_missing(keyword_str
);
1325 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1331 syntax_arg_missing(keyword_str
);
1334 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1340 syntax_arg_missing(keyword_str
);
1343 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1346 atype
= ADDR_TYPE_NET
;
1357 syntax_arg_missing(keyword_str
);
1360 set_metric(rcip
, tok
, key
, locknext
|| lockrest
);
1364 rcip
->ri_flags
|= RTF_PRIVATE
;
1367 rcip
->ri_flags
|= RTF_MULTIRT
;
1371 syntax_arg_missing(keyword_str
);
1374 if (!getaddr(rcip
, RTA_SRC
, tok
, atype
)) {
1377 rcip
->ri_flags
|= RTF_SETSRC
;
1381 syntax_arg_missing(keyword_str
);
1384 if (is_system_labeled()) {
1387 if (rcip
->ri_rtsa_cnt
>= 1) {
1388 syntax_error(gettext("route: can't "
1389 "specify more than one security "
1393 if (!rtsa_keyword(tok
, &rcip
->ri_rtsa
, &err
,
1395 syntax_error(gettext("route: "
1396 "bad security attribute: %s\n"),
1397 tsol_strerror(err
, errno
));
1400 rcip
->ri_rtsa_cnt
++;
1402 syntax_error(gettext("route: "
1403 "system is not labeled; cannot specify "
1404 "security attributes.\n"));
1410 syntax_bad_keyword(tok
+ 1);
1413 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1414 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1417 } else if ((rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1419 * For the gateway parameter, retrieve the
1420 * pointer to the struct hostent so that all
1421 * possible addresses can be tried until one
1424 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1430 * Assume that a regular number is a metric.
1431 * Needed for compatibility with old route
1435 metric
= strtoul(tok
, &err
, 10);
1436 if (errno
== 0 && *err
== '\0' &&
1437 metric
< 0x80000000ul
) {
1438 iflag
= (metric
== 0);
1440 (void) printf("old usage of "
1441 "trailing number, assuming "
1442 "route %s\n", iflag
?
1443 "to if" : "via gateway");
1447 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1455 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1456 syntax_error(gettext("route: destination required\n"));
1458 } else if ((rcip
->ri_cmd
== RTM_ADD
|| rcip
->ri_cmd
== RTM_DELETE
) &&
1459 (rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1460 syntax_error(gettext(
1461 "route: gateway required for add or delete command\n"));
1466 rcip
->ri_flags
|= RTF_GATEWAY
;
1469 if (atype
!= ADDR_TYPE_NET
) {
1470 if (rcip
->ri_addrs
& RTA_NETMASK
) {
1472 * We know the netmask, so we can set the host flag
1473 * based on whether the netmask is the host netmask.
1475 if (rcip
->ri_af
== AF_INET
&&
1476 rcip
->ri_mask
.sin
.sin_addr
.s_addr
==
1478 rcip
->ri_flags
|= RTF_HOST
;
1480 if (rcip
->ri_af
== AF_INET6
&&
1481 memcmp(&rcip
->ri_mask
.sin6
.sin6_addr
,
1483 sizeof (struct in6_addr
)) == 0) {
1484 rcip
->ri_flags
|= RTF_HOST
;
1488 * If no prefix mask has been saved at this point, it
1489 * only makes sense to treat the destination address
1490 * as a host address.
1492 rcip
->ri_flags
|= RTF_HOST
;
1499 * This command always seeks to the end of the file prior to writing.
1502 write_to_rtfile(FILE *fp
, int argc
, char **argv
)
1504 char file_line
[BUF_SIZE
];
1509 if (early_v6_keyword
) {
1511 * This flag is set when "inet6" was seen as an
1512 * argument to the -f flag. Normally, when writing
1513 * routes to the persistent route file, everything on
1514 * the command line after "add" is saved verbatim.
1515 * In this case, the arguments after "add" may not be
1516 * sufficient, as the ipv6 keyword came before "add",
1517 * yet must be present in the persistent route file.
1519 len
+= snprintf(file_line
, BUF_SIZE
, "-inet6 ");
1521 for (i
= 0; argc
> 0 && len
< BUF_SIZE
; i
++, argc
--) {
1522 len
+= snprintf(&file_line
[len
], BUF_SIZE
- len
, "%s ",
1525 if (len
>= BUF_SIZE
)
1526 quit(gettext("Internal Error"), EINVAL
);
1527 file_line
[len
- 1] = '\n';
1528 if (fseek(fp
, 0, SEEK_END
) != 0 ||
1529 fputs(file_line
, fp
) == EOF
) {
1530 quit(gettext("failed to write to route file"),
1536 compare_rtcmd(rtcmd_irep_t
*srch_rt
, rtcmd_irep_t
*file_rt
)
1538 if (strcmp(srch_rt
->ri_dest_str
, file_rt
->ri_dest_str
) != 0 ||
1539 memcmp(&srch_rt
->ri_mask
, &file_rt
->ri_mask
, sizeof (su_t
)) != 0) {
1542 return (srch_rt
->ri_gate_str
== NULL
||
1543 strcmp(srch_rt
->ri_gate_str
, file_rt
->ri_gate_str
) == 0);
1547 * Search the route file for routes matching the supplied route. There are 3
1548 * modes of operation:
1549 * SEARCH_MODE_RET - no side effects.
1550 * SEARCH_MODE_PRINT - prints each matching line.
1551 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1553 * In all cases, the number of matches is returned. If rt is NULL, all routes
1554 * matching the global af value are considered matching.
1557 search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
, search_mode_t mode
)
1562 char file_line
[BUF_SIZE
+ 4] = "add ";
1563 rtcmd_irep_t
*thisrt
;
1568 * Leave space at the beginning of file_line for "add ".
1570 while (fgets(file_line
+ 4, BUF_SIZE
, fp
) != NULL
) {
1572 if (file_line
[4] == '#' || file_line
[4] == '\n') {
1573 /* Handle comments and blank lines */
1574 if (mode
== SEARCH_MODE_DEL
&&
1575 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1577 "route: failed to write to temp file"),
1582 thisrt
= new_rtcmd_irep();
1584 * thisrt->ri_af defaults to whatever address family happens
1585 * to be set in the global af, but routes in the persistent
1586 * route file must be treated as AF_INET by default.
1588 thisrt
->ri_af
= AF_INET
;
1590 exit_on_error
= B_FALSE
;
1591 tmp_buf
= strdup(file_line
);
1592 /* args_to_rtcmd() will mangle the string passed. */
1593 if (!args_to_rtcmd(thisrt
, NULL
, tmp_buf
)) {
1594 /* There was an error in args_to_rtcmd() or helpers */
1595 del_rtcmd_irep(thisrt
);
1599 exit_on_error
= B_TRUE
;
1602 if (thisrt
->ri_gate_str
== NULL
) {
1603 del_rtcmd_irep(thisrt
);
1606 match
= (rt
== NULL
) ? (thisrt
->ri_af
== af
) :
1607 compare_rtcmd(rt
, thisrt
);
1609 if (match
) match_cnt
++;
1610 if (match
&& mode
== SEARCH_MODE_PRINT
) {
1611 (void) printf("persistent: route %s", file_line
);
1613 if (match
&& mode
== SEARCH_MODE_DEL
) {
1614 thisrt
->ri_cmd
= RTM_DELETE
;
1615 print_rtcmd_short(stdout
, thisrt
, B_FALSE
, B_TRUE
);
1616 (void) printf("\n");
1618 del_rtcmd_irep(thisrt
);
1620 if (!match
&& mode
== SEARCH_MODE_DEL
&&
1621 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1622 quit(gettext("failed to write to temp file"),
1630 * Perform the route operation given in argv on the persistent route file.
1631 * If do_flush is set, the persistent route file is flushed of all routes
1632 * matching the global family, and the arguments are ignored.
1635 save_route(int argc
, char **argv
, int do_flush
)
1641 mode_t fmode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1644 const char commentstr
[] =
1645 "# File generated by route(1M) - do not edit.\n";
1647 perm_fd
= open(perm_file
, O_RDWR
| O_CREAT
, fmode
);
1648 if (perm_fd
== -1 || fstat(perm_fd
, &st
) == -1)
1649 quit("failed to open route file", errno
);
1651 lock
.l_type
= F_WRLCK
;
1652 lock
.l_whence
= SEEK_SET
;
1655 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1656 quit(gettext("failed to lock route file"), errno
);
1659 if (st
.st_size
== 0 &&
1660 write(perm_fd
, commentstr
, sizeof (commentstr
) - 1) !=
1661 sizeof (commentstr
) - 1)
1662 quit(gettext("failed to open route file"), errno
);
1664 if ((perm_fp
= fdopen(perm_fd
, "r+")) == NULL
) {
1665 quit(gettext("failed to open route file"), errno
);
1670 rt
= new_rtcmd_irep();
1671 (void) args_to_rtcmd(rt
, argv
, NULL
);
1673 if (do_flush
|| rt
->ri_cmd
== RTM_DELETE
) {
1674 if ((temp_fp
= fopen(temp_file
, "w")) == NULL
) {
1675 quit(gettext("failed to open temp file"), errno
);
1680 (void) search_rtfile(perm_fp
, temp_fp
, NULL
, SEARCH_MODE_DEL
);
1681 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1682 quit(gettext("failed to update route file"), errno
);
1685 (void) fclose(perm_fp
);
1689 switch (rt
->ri_cmd
) {
1691 if (search_rtfile(perm_fp
, NULL
, rt
, SEARCH_MODE_NULL
) > 0) {
1692 /* Route is already in the file */
1693 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1694 (void) fprintf(stderr
, ": entry exists\n");
1697 write_to_rtfile(perm_fp
, argc
- 1, argv
+ 1);
1698 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1699 (void) printf("\n");
1704 gettext("route: change command not supported with -p\n"));
1708 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_DEL
) <= 0) {
1709 /* Route not found */
1710 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1711 (void) fprintf(stderr
, gettext(": not in file\n"));
1714 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1715 quit(gettext("failed to update route file"), errno
);
1721 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_PRINT
) <=
1723 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1724 (void) printf(gettext(": not in file\n"));
1729 quit(gettext("Internal Error"), EINVAL
);
1734 * Closing the file unlocks it.
1736 (void) fclose(perm_fp
);
1740 show_saved_routes(int argc
)
1748 syntax_error(gettext("route: invalid arguments for show\n"));
1751 perm_fd
= open(perm_file
, O_RDONLY
, 0);
1753 if (perm_fd
== -1) {
1754 if (errno
== ENOENT
) {
1755 (void) printf("No persistent routes are defined\n");
1758 quit(gettext("failed to open route file"), errno
);
1761 lock
.l_type
= F_RDLCK
;
1762 lock
.l_whence
= SEEK_SET
;
1765 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1766 quit(gettext("failed to lock route file"),
1770 if ((perm_fp
= fdopen(perm_fd
, "r")) == NULL
) {
1771 quit(gettext("failed to open route file"), errno
);
1774 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1775 (void) fseek(perm_fp
, 0, SEEK_SET
);
1777 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1780 (void) printf("No persistent routes are defined\n");
1782 (void) fclose(perm_fp
);
1787 newroute(char **argv
)
1789 rtcmd_irep_t
*newrt
;
1790 int ret
, attempts
, oerrno
;
1792 char obuf
[INET6_ADDRSTRLEN
];
1793 #define hp (newrt->ri_gate_hp)
1795 newrt
= new_rtcmd_irep();
1796 (void) args_to_rtcmd(newrt
, argv
, NULL
);
1798 if (newrt
->ri_cmd
!= RTM_GET
&& !tflag
) {
1799 /* Don't want to read back our messages */
1800 (void) shutdown(s
, 0);
1802 if (newrt
->ri_addrs
& RTA_IFP
) {
1803 newrt
->ri_ifp
.sdl
.sdl_index
= if_nametoindex(newrt
->ri_ifp_str
);
1804 if (newrt
->ri_ifp
.sdl
.sdl_index
== 0) {
1805 if (errno
!= ENXIO
) {
1806 quit("if_nametoindex", errno
);
1808 (void) fprintf(stderr
,
1809 gettext("route: %s: no such interface\n"),
1814 newrt
->ri_ifp
.sdl
.sdl_family
= AF_LINK
;
1816 for (attempts
= 1; ; attempts
++) {
1818 if ((ret
= rtmsg(newrt
)) == 0)
1820 if (errno
!= ENETUNREACH
&& errno
!= ESRCH
)
1822 if ((newrt
->ri_addrs
& RTA_GATEWAY
) && hp
!= NULL
&&
1823 hp
->h_addr_list
[attempts
] != NULL
) {
1826 (void) memmove(&newrt
->ri_gate
.sin
.sin_addr
,
1827 hp
->h_addr_list
[attempts
], hp
->h_length
);
1830 (void) memmove(&newrt
->ri_gate
.sin6
.sin6_addr
,
1831 hp
->h_addr_list
[attempts
], hp
->h_length
);
1839 if (newrt
->ri_cmd
!= RTM_GET
) {
1840 print_rtcmd_short(stdout
, newrt
, (ret
== 0), B_FALSE
);
1842 (void) printf("\n");
1843 } else if (ret
!= 0) {
1845 * Note: there is nothing additional to print for get
1849 switch (newrt
->ri_af
) {
1851 (void) printf(" %s",
1852 inet_ntoa(newrt
->ri_dst
.sin
.sin_addr
));
1855 if (inet_ntop(AF_INET6
,
1856 (void *)&newrt
->ri_dst
.sin6
.sin6_addr
,
1857 obuf
, INET6_ADDRSTRLEN
) != NULL
) {
1858 (void) printf(" %s", obuf
);
1863 (void) printf("%s", newrt
->ri_dest_str
);
1867 (void) printf("%s", newrt
->ri_dest_str
);
1874 err
= "not in table";
1877 err
= "entry in use";
1880 err
= "routing table overflow";
1883 err
= "entry exists";
1886 err
= "insufficient privileges";
1889 err
= strerror(oerrno
);
1892 (void) printf(": %s\n", err
);
1895 del_rtcmd_irep(newrt
);
1903 * Convert a network number to the corresponding IP address.
1904 * If the RTA_NETMASK hasn't been specified yet set it based
1905 * on the class of address.
1908 inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
, struct sockaddr_in
*sin
)
1910 in_addr_t addr
, mask
;
1914 } else if (net
< 128) {
1915 addr
= net
<< IN_CLASSA_NSHIFT
;
1916 mask
= IN_CLASSA_NET
;
1917 } else if (net
< 65536) {
1918 addr
= net
<< IN_CLASSB_NSHIFT
;
1919 mask
= IN_CLASSB_NET
;
1920 } else if (net
< 16777216L) {
1921 addr
= net
<< IN_CLASSC_NSHIFT
;
1922 mask
= IN_CLASSC_NET
;
1925 if ((addr
& IN_CLASSA_HOST
) == 0)
1926 mask
= IN_CLASSA_NET
;
1927 else if ((addr
& IN_CLASSB_HOST
) == 0)
1928 mask
= IN_CLASSB_NET
;
1929 else if ((addr
& IN_CLASSC_HOST
) == 0)
1930 mask
= IN_CLASSC_NET
;
1932 if (IN_CLASSA(addr
))
1933 mask
= IN_CLASSA_NET
;
1934 else if (IN_CLASSB(addr
))
1935 mask
= IN_CLASSB_NET
;
1936 else if (IN_CLASSC(addr
))
1937 mask
= IN_CLASSC_NET
;
1939 mask
= IP_HOST_MASK
;
1940 mask
= inet_makesubnetmask(addr
, mask
);
1943 sin
->sin_addr
.s_addr
= htonl(addr
);
1945 /* Class E default mask is 32 */
1946 if (IN_CLASSE(addr
))
1947 mask
= IN_CLASSE_NET
;
1949 if (!(rcip
->ri_addrs
& RTA_NETMASK
)) {
1950 rcip
->ri_addrs
|= RTA_NETMASK
;
1951 sin
= &rcip
->ri_mask
.sin
;
1952 sin
->sin_addr
.s_addr
= htonl(mask
);
1953 sin
->sin_family
= AF_INET
;
1958 inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
)
1964 struct sockaddr_in
*sin
;
1969 in_addr_t if_addr
, if_mask
;
1970 in_addr_t if_subnetmask
= 0;
1975 if ((iosoc
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1976 quit("socket", errno
);
1977 if (ioctl(iosoc
, SIOCGIFNUM
, (char *)&numifs
) < 0)
1978 quit("ioctl", errno
);
1979 bufsize
= numifs
* sizeof (struct ifreq
);
1980 buf
= malloc(bufsize
);
1982 quit("malloc", errno
);
1983 (void) memset(&ifc
, 0, sizeof (ifc
));
1984 ifc
.ifc_len
= bufsize
;
1986 if (ioctl(iosoc
, SIOCGIFCONF
, (char *)&ifc
) < 0)
1987 quit("ioctl (get interface configuration)", errno
);
1988 /* Let's check to see if this is maybe a local subnet route. */
1990 for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
1993 sin
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1994 if_addr
= ntohl(sin
->sin_addr
.s_addr
);
1996 if (ioctl(iosoc
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0)
1997 quit("ioctl (get interface flags)", errno
);
1998 if ((ifreq
.ifr_flags
& IFF_UP
) == 0)
2000 if_flags
= ifreq
.ifr_flags
;
2002 if (ioctl(iosoc
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0)
2003 quit("ioctl (get netmask)", errno
);
2005 sin
= (struct sockaddr_in
*)&ifreq
.ifr_addr
;
2006 if_mask
= ntohl(sin
->sin_addr
.s_addr
);
2007 if ((if_addr
& mask
) == (addr
& mask
)) {
2009 * Don't trust pt-pt interfaces if there are
2012 if (if_flags
& IFF_POINTOPOINT
) {
2013 if_subnetmask
= if_mask
;
2017 * Fine. Just assume the same net mask as the
2018 * directly attached subnet interface is using.
2023 if (if_subnetmask
!= 0)
2024 return (if_subnetmask
);
2029 * Interpret an argument as a network address of some kind.
2031 * If the address family is one looked up in getaddr() using one of the
2032 * getipnodebyX() functions (currently only AF_INET6), then callers should
2033 * freehostent() the returned "struct hostent" pointer if one was passed in.
2035 * If exit_on_error is true, this function will cause route to exit on error by
2036 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
2040 getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
, addr_type_t atype
)
2043 struct hostent
**hpp
;
2047 if (which
== RTA_GATEWAY
) {
2048 hpp
= &(rcip
->ri_gate_hp
);
2054 rcip
->ri_addrs
|= which
;
2057 save_string(&rcip
->ri_dest_str
, s
);
2059 su
->sa
.sa_family
= rcip
->ri_af
;
2062 save_string(&rcip
->ri_gate_str
, s
);
2063 su
= &rcip
->ri_gate
;
2064 su
->sa
.sa_family
= rcip
->ri_af
;
2067 su
= &rcip
->ri_mask
;
2068 su
->sa
.sa_family
= rcip
->ri_af
;
2071 save_string(&rcip
->ri_ifp_str
, s
);
2074 * RTA_SRC has overloaded meaning. It can represent the
2075 * src address of incoming or outgoing packets.
2079 su
->sa
.sa_family
= rcip
->ri_af
;
2083 su
->sa
.sa_family
= rcip
->ri_af
;
2087 quit(gettext("Internal Error"), EINVAL
);
2090 if (strcmp(s
, "default") == 0) {
2091 if (which
== RTA_DST
) {
2092 return (getaddr(rcip
, RTA_NETMASK
, s
, ADDR_TYPE_NET
));
2094 if (which
== RTA_SRC
) {
2099 switch (rcip
->ri_af
) {
2101 link_addr(s
, &su
->sdl
);
2104 sockaddr(s
, &su
->sa
);
2109 if (!in6_getaddr(s
, &su
->sin6
, &masklen
, hpp
)) {
2112 if (masklen
!= NO_PREFIX
) {
2113 (void) memset(&rcip
->ri_mask
.sin6
.sin6_addr
, 0,
2114 sizeof (rcip
->ri_mask
.sin6
.sin6_addr
));
2115 if (!in_prefixlentomask(masklen
, IPV6_ABITS
,
2116 (uchar_t
*)&rcip
->ri_mask
.sin6
.sin6_addr
)) {
2117 syntax_error(gettext(
2118 "route: bad prefix length: %d\n"),
2122 rcip
->ri_mask
.sin6
.sin6_family
= rcip
->ri_af
;
2123 rcip
->ri_addrs
|= RTA_NETMASK
;
2129 return (in6_getaddr(s
, &su
->sin6
, NULL
, hpp
));
2132 gettext("route: -netmask not supported for IPv6: "
2133 "use <prefix>/<prefix-length> instead\n"));
2136 quit(gettext("Internal Error"), EINVAL
);
2142 if (!in_getaddr(s
, &su
->sin
, &masklen
, which
, hpp
,
2146 if (masklen
!= NO_PREFIX
) {
2147 (void) memset(&rcip
->ri_mask
.sin
.sin_addr
, 0,
2148 sizeof (rcip
->ri_mask
.sin
.sin_addr
));
2149 if (!in_prefixlentomask(masklen
, IP_ABITS
,
2150 (uchar_t
*)&rcip
->ri_mask
.sin
.sin_addr
)) {
2151 syntax_error(gettext(
2152 "route: bad prefix length: %d\n"),
2156 rcip
->ri_mask
.sin
.sin_family
= rcip
->ri_af
;
2157 rcip
->ri_addrs
|= RTA_NETMASK
;
2164 return (in_getaddr(s
, &su
->sin
, NULL
, which
, hpp
, atype
,
2167 quit(gettext("Internal Error"), EINVAL
);
2171 quit(gettext("Internal Error"), EINVAL
);
2178 * Interpret an argument as an IPv4 network address of some kind,
2179 * returning B_TRUE on success or B_FALSE on failure.
2180 * This function will cause an exit() on failure if exit_on_failure is set.
2182 * Note that this tries host interpretation before network interpretation,
2183 * except when -net has been given and the destination address is being parsed.
2185 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2186 * pass out <n> in *plenp.
2187 * If <n> doesn't parse return BAD_ADDR as *plenp.
2188 * If no /<n> is present return NO_PREFIX as *plenp.
2191 in_getaddr(char *s
, struct sockaddr_in
*sin
, int *plenp
, int which
,
2192 struct hostent
**hpp
, addr_type_t atype
, rtcmd_irep_t
*rcip
)
2199 (void) strlcpy(str
, s
, sizeof (str
));
2202 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2204 if (plenp
!= NULL
) {
2207 *plenp
= in_getprefixlen(str
, IP_ABITS
);
2208 if (*plenp
== BAD_ADDR
)
2210 cp
= strchr(str
, '/');
2213 } else if (strchr(str
, '/') != NULL
) {
2214 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2218 (void) memset(sin
, 0, sizeof (*sin
));
2219 sin
->sin_family
= AF_INET
;
2222 * Handle 255.255.255.255 as a special case first.
2224 if (strcmp(str
, "255.255.255.255") == 0) {
2225 sin
->sin_addr
.s_addr
= INADDR_BROADCAST
;
2229 val
= inet_addr(str
);
2230 if (val
!= (in_addr_t
)-1) {
2231 /* Numeric address */
2232 sin
->sin_addr
.s_addr
= val
;
2233 if (which
== RTA_DST
) {
2234 if (atype
== ADDR_TYPE_NET
||
2235 (atype
== ADDR_TYPE_ANY
&&
2236 inet_lnaof(sin
->sin_addr
) == INADDR_ANY
)) {
2237 /* This looks like a network address. */
2238 inet_makenetandmask(rcip
, ntohl(val
),
2244 /* Host or net name */
2245 if (which
!= RTA_DST
|| atype
!= ADDR_TYPE_NET
) {
2246 /* A host name is allowed. */
2247 if ((hp
= gethostbyname(str
)) != NULL
) {
2249 (void) memmove(&sin
->sin_addr
, hp
->h_addr
,
2254 if (atype
!= ADDR_TYPE_HOST
) {
2255 /* A network name is allowed */
2256 if ((np
= getnetbyname(str
)) != NULL
&&
2257 (val
= np
->n_net
) != 0) {
2258 if (which
== RTA_DST
) {
2259 inet_makenetandmask(rcip
, val
, sin
);
2264 syntax_error(gettext("%s: bad value\n"), s
);
2269 * Interpret an argument as an IPv6 network address of some kind,
2270 * returning B_TRUE on success or B_FALSE on failure.
2271 * This function will cause an exit() on failure if exit_on_failure is set.
2273 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2274 * pass out <n> in *plenp.
2275 * If <n> doesn't parse return BAD_ADDR as *plenp.
2276 * If no /<n> is present return NO_PREFIX as *plenp.
2279 in6_getaddr(char *s
, struct sockaddr_in6
*sin6
, int *plenp
,
2280 struct hostent
**hpp
)
2286 (void) strlcpy(str
, s
, sizeof (str
));
2289 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2291 if (plenp
!= NULL
) {
2294 *plenp
= in_getprefixlen(str
, IPV6_ABITS
);
2295 if (*plenp
== BAD_ADDR
)
2297 cp
= strchr(str
, '/');
2300 } else if (strchr(str
, '/') != NULL
) {
2301 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2305 (void) memset(sin6
, 0, sizeof (struct sockaddr_in6
));
2306 sin6
->sin6_family
= AF_INET6
;
2308 hp
= getipnodebyname(str
, AF_INET6
, 0, &error_num
);
2311 (void) memmove(&sin6
->sin6_addr
, hp
->h_addr
, hp
->h_length
);
2314 if (error_num
== TRY_AGAIN
) {
2316 * This isn't a problem if we aren't going to use the address
2319 if (!exit_on_error
) {
2322 syntax_error(gettext("route: %s: bad address (try "
2323 "again later)\n"), s
);
2326 syntax_error(gettext("route: %s: bad address\n"), s
);
2331 * Parse <addr>/<n> syntax and return the integer n.
2332 * If <addr> is missing or <n> is not a valid integer, this function calls
2333 * syntax_error() and returns BAD_ADDR.
2334 * if n is not between 0 and max_plen inclusive, this functions calls
2335 * syntax_error() and returns BAD_ADDR.
2336 * If /<n> is not present, this function returns NO_PREFIX.
2337 * The string addr is not modified.
2340 in_getprefixlen(char *addr
, int max_plen
)
2345 str
= strchr(addr
, '/');
2347 syntax_error(gettext("route: %s: unexpected '/'\n"), addr
);
2355 prefixlen
= strtoul(str
, &end
, 10);
2356 if (errno
!= 0 || str
== end
) {
2357 syntax_error(gettext("route: bad prefix length %s\n"), str
);
2360 if (prefixlen
> max_plen
) {
2361 syntax_error(gettext("route: prefix length %s out of range\n"),
2369 * Convert a prefix length to a mask.
2370 * Returns B_TRUE if ok. B_FALSE otherwise.
2371 * Assumes the mask array is zeroed by the caller.
2374 in_prefixlentomask(int prefixlen
, int maxlen
, uchar_t
*mask
)
2376 if (prefixlen
< 0 || prefixlen
> maxlen
)
2379 while (prefixlen
> 0) {
2380 if (prefixlen
>= 8) {
2385 *mask
|= 1 << (8 - prefixlen
);
2392 rtmonitor(int argc
, char *argv
[])
2395 intmax_t msg
[2048 / sizeof (intmax_t)];
2402 if (argc
== 2 && **argv
== '-') {
2403 switch (keyword(*argv
+ 1)) {
2421 s
= socket(PF_ROUTE
, SOCK_RAW
, af
);
2423 quit("socket", errno
);
2426 n
= read(s
, msg
, sizeof (msg
));
2428 quit("read", errno
);
2429 (void) printf("got message of size %d\n", n
);
2430 print_rtmsg((struct rt_msghdr
*)msg
, n
);
2435 rtmsg(rtcmd_irep_t
*newrt
)
2439 char *cp
= m_rtmsg
.m_space
;
2443 (void) memset(&m_rtmsg
, 0, sizeof (m_rtmsg
));
2445 if (newrt
->ri_cmd
== RTM_GET
) {
2446 newrt
->ri_ifp
.sa
.sa_family
= AF_LINK
;
2447 newrt
->ri_addrs
|= RTA_IFP
;
2450 #define rtm m_rtmsg.m_rtm
2451 rtm
.rtm_type
= newrt
->ri_cmd
;
2452 rtm
.rtm_flags
= newrt
->ri_flags
;
2453 rtm
.rtm_version
= RTM_VERSION
;
2454 rtm
.rtm_seq
= ++seq
;
2455 rtm
.rtm_addrs
= newrt
->ri_addrs
;
2456 rtm
.rtm_rmx
= newrt
->ri_metrics
;
2457 rtm
.rtm_inits
= newrt
->ri_inits
;
2459 #define NEXTADDR(w, u) \
2460 if (newrt->ri_addrs & (w)) { \
2461 l = ROUNDUP_LONG(salen(&u.sa)); \
2462 (void) memmove(cp, &(u), l); \
2467 NEXTADDR(RTA_DST
, newrt
->ri_dst
);
2468 NEXTADDR(RTA_GATEWAY
, newrt
->ri_gate
);
2469 NEXTADDR(RTA_NETMASK
, newrt
->ri_mask
);
2470 NEXTADDR(RTA_IFP
, newrt
->ri_ifp
);
2471 NEXTADDR(RTA_IFA
, newrt
->ri_ifa
);
2473 * RTA_SRC has overloaded meaning. It can represent the
2474 * src address of incoming or outgoing packets.
2476 NEXTADDR(RTA_SRC
, newrt
->ri_src
);
2479 if (newrt
->ri_rtsa_cnt
> 0) {
2480 /* LINTED: aligned */
2481 rtm_ext_t
*rtm_ext
= (rtm_ext_t
*)cp
;
2482 tsol_rtsecattr_t
*rtsecattr
;
2484 rtm_ext
->rtmex_type
= RTMEX_GATEWAY_SECATTR
;
2485 rtm_ext
->rtmex_len
= TSOL_RTSECATTR_SIZE(1);
2487 rtsecattr
= (tsol_rtsecattr_t
*)(rtm_ext
+ 1);
2488 rtsecattr
->rtsa_cnt
= 1;
2490 bcopy(&newrt
->ri_rtsa
, rtsecattr
->rtsa_attr
,
2491 sizeof (newrt
->ri_rtsa
));
2492 cp
= (char *)(rtsecattr
->rtsa_attr
+ 1);
2495 rtm
.rtm_msglen
= l
= cp
- (char *)&m_rtmsg
;
2498 print_rtmsg(&rtm
, l
);
2501 if ((rlen
= write(s
, (char *)&m_rtmsg
, l
)) < 0) {
2512 perror(gettext("writing to routing socket"));
2516 } else if (rlen
< (int)rtm
.rtm_msglen
) {
2517 (void) fprintf(stderr
,
2518 gettext("route: write to routing socket got only %d for "
2522 if (newrt
->ri_cmd
== RTM_GET
) {
2524 l
= read(s
, (char *)&m_rtmsg
, sizeof (m_rtmsg
));
2525 } while (l
> 0 && (rtm
.rtm_seq
!= seq
|| rtm
.rtm_pid
!= pid
));
2527 (void) fprintf(stderr
,
2528 gettext("route: read from routing socket: %s\n"),
2531 print_getmsg(newrt
, &rtm
, l
);
2538 static char *msgtypes
[] = {
2540 "RTM_ADD: Add Route",
2541 "RTM_DELETE: Delete Route",
2542 "RTM_CHANGE: Change Metrics or flags",
2543 "RTM_GET: Report Metrics",
2544 "RTM_LOSING: Kernel Suspects Partitioning",
2545 "RTM_REDIRECT: Told to use different route",
2546 "RTM_MISS: Lookup failed on this address",
2547 "RTM_LOCK: fix specified metrics",
2548 "RTM_OLDADD: caused by SIOCADDRT",
2549 "RTM_OLDDEL: caused by SIOCDELRT",
2550 "RTM_RESOLVE: Route created by cloning",
2551 "RTM_NEWADDR: address being added to iface",
2552 "RTM_DELADDR: address being removed from iface",
2553 "RTM_IFINFO: iface status change",
2557 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2559 static char metricnames
[] =
2560 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2562 static char routeflags
[] =
2563 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2564 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2565 "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC";
2566 static char ifnetflags
[] =
2567 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2568 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2569 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2570 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2571 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2572 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2573 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2575 static char addrnames
[] =
2576 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2579 print_rtmsg(struct rt_msghdr
*rtm
, int msglen
)
2581 struct if_msghdr
*ifm
;
2582 struct ifa_msghdr
*ifam
;
2586 if (rtm
->rtm_version
!= RTM_VERSION
) {
2587 (void) printf("routing message version %d not understood\n",
2591 if (rtm
->rtm_msglen
!= msglen
) {
2592 (void) printf("message length mismatch, in packet %d, "
2594 rtm
->rtm_msglen
, msglen
);
2595 if (msglen
> rtm
->rtm_msglen
)
2596 msglen
= rtm
->rtm_msglen
;
2599 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2600 * and the upper-bound of (NMSGTYPES - 1).
2602 if (rtm
->rtm_type
== 0 || rtm
->rtm_type
>= (NMSGTYPES
- 1)) {
2603 (void) printf("routing message type %d not understood\n",
2607 (void) printf("%s: len %d, ", msgtypes
[rtm
->rtm_type
], msglen
);
2608 switch (rtm
->rtm_type
) {
2610 ifm
= (struct if_msghdr
*)rtm
;
2611 (void) printf("if# %d, flags:", ifm
->ifm_index
);
2612 bprintf(stdout
, ifm
->ifm_flags
, ifnetflags
);
2613 pmsg_addrs((const char *)(ifm
+ 1), msglen
- sizeof (*ifm
),
2618 ifam
= (struct ifa_msghdr
*)rtm
;
2619 (void) printf("metric %d, flags:", ifam
->ifam_metric
);
2620 bprintf(stdout
, ifam
->ifam_flags
, routeflags
);
2621 pmsg_addrs((const char *)(ifam
+ 1), msglen
- sizeof (*ifam
),
2625 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2626 rtm
->rtm_pid
, rtm
->rtm_seq
, rtm
->rtm_errno
);
2627 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2628 pmsg_common(rtm
, msglen
);
2634 print_getmsg(rtcmd_irep_t
*req_rt
, struct rt_msghdr
*rtm
, int msglen
)
2636 struct sockaddr
*dst
= NULL
, *gate
= NULL
, *mask
= NULL
, *src
= NULL
;
2637 struct sockaddr_dl
*ifp
= NULL
;
2638 struct sockaddr
*sa
;
2642 (void) printf(" route to: %s\n", routename(&req_rt
->ri_dst
.sa
));
2643 if (rtm
->rtm_version
!= RTM_VERSION
) {
2644 (void) fprintf(stderr
,
2645 gettext("routing message version %d not understood\n"),
2649 if (rtm
->rtm_msglen
> (ushort_t
)msglen
) {
2650 (void) fprintf(stderr
,
2651 gettext("message length mismatch, in packet %d, "
2652 "returned %d\n"), rtm
->rtm_msglen
, msglen
);
2654 if (rtm
->rtm_errno
) {
2655 (void) fprintf(stderr
, "RTM_GET: %s (errno %d)\n",
2656 strerror(rtm
->rtm_errno
), rtm
->rtm_errno
);
2659 cp
= ((char *)(rtm
+ 1));
2660 if (rtm
->rtm_addrs
!= 0) {
2661 for (i
= 1; i
!= 0; i
<<= 1) {
2662 if (i
& rtm
->rtm_addrs
) {
2664 sa
= (struct sockaddr
*)cp
;
2676 if (sa
->sa_family
== AF_LINK
&&
2677 ((struct sockaddr_dl
*)sa
)->
2679 ifp
= (struct sockaddr_dl
*)sa
;
2689 if (dst
!= NULL
&& mask
!= NULL
)
2690 mask
->sa_family
= dst
->sa_family
; /* XXX */
2692 (void) printf("destination: %s\n", routename(dst
));
2694 boolean_t savenflag
= nflag
;
2697 (void) printf(" mask: %s\n", routename(mask
));
2700 if (gate
!= NULL
&& rtm
->rtm_flags
& RTF_GATEWAY
)
2701 (void) printf(" gateway: %s\n", routename(gate
));
2702 if (src
!= NULL
&& rtm
->rtm_flags
& RTF_SETSRC
)
2703 (void) printf(" setsrc: %s\n", routename(src
));
2708 (void) printf(" interface: %.*s index %d address ",
2709 ifp
->sdl_nlen
, ifp
->sdl_data
, ifp
->sdl_index
);
2710 for (i
= ifp
->sdl_nlen
;
2711 i
< ifp
->sdl_nlen
+ ifp
->sdl_alen
;
2713 (void) printf("%02x ",
2714 ifp
->sdl_data
[i
] & 0xFF);
2716 (void) printf("\n");
2718 (void) printf(" interface: %.*s\n",
2719 ifp
->sdl_nlen
, ifp
->sdl_data
);
2722 (void) printf(" flags: ");
2723 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2725 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2726 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2728 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2729 "rttvar,ms hopcount mtu expire");
2730 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_recvpipe
, lock(RPIPE
));
2731 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_sendpipe
, lock(SPIPE
));
2732 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_ssthresh
, lock(SSTHRESH
));
2733 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rtt
), lock(RTT
));
2734 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rttvar
), lock(RTTVAR
));
2735 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_hopcount
, lock(HOPCOUNT
));
2736 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_mtu
, lock(MTU
));
2737 if (rtm
->rtm_rmx
.rmx_expire
)
2738 rtm
->rtm_rmx
.rmx_expire
-= time(0);
2739 (void) printf("%8d%c", rtm
->rtm_rmx
.rmx_expire
, lock(EXPIRE
));
2743 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2745 pmsg_common(rtm
, msglen
);
2747 const char *sptr
, *endptr
;
2748 const struct sockaddr
*sa
;
2751 /* Not verbose; just print out the exceptional cases */
2752 if (rtm
->rtm_addrs
&~ RTA_IGN
) {
2753 (void) printf("\nsockaddrs: ");
2754 bprintf(stdout
, rtm
->rtm_addrs
, addrnames
);
2756 sptr
= (const char *)(rtm
+ 1);
2757 endptr
= (const char *)rtm
+ msglen
;
2758 addrs
= rtm
->rtm_addrs
;
2759 while (addrs
!= 0 && sptr
+ sizeof (*sa
) <= endptr
) {
2762 sa
= (const struct sockaddr
*)sptr
;
2766 pmsg_secattr(sptr
, endptr
- sptr
, " secattr: ");
2767 (void) putchar('\n');
2773 pmsg_common(const struct rt_msghdr
*rtm
, size_t msglen
)
2775 (void) printf("\nlocks: ");
2776 bprintf(stdout
, (int)rtm
->rtm_rmx
.rmx_locks
, metricnames
);
2777 (void) printf(" inits: ");
2778 bprintf(stdout
, (int)rtm
->rtm_inits
, metricnames
);
2779 pmsg_addrs((const char *)(rtm
+ 1), msglen
- sizeof (*rtm
),
2784 pmsg_addrs(const char *cp
, size_t msglen
, uint_t addrs
)
2786 const struct sockaddr
*sa
;
2791 (void) printf("\nsockaddrs: ");
2792 bprintf(stdout
, addrs
, addrnames
);
2793 (void) putchar('\n');
2794 maxptr
= cp
+ msglen
;
2795 for (i
= 1; i
!= 0 && cp
+ sizeof (*sa
) <= maxptr
; i
<<= 1) {
2798 sa
= (const struct sockaddr
*)cp
;
2799 (void) printf(" %s", routename(sa
));
2806 msglen
= maxptr
- cp
;
2808 pmsg_secattr(cp
, msglen
, "secattr: ");
2809 (void) putchar('\n');
2810 (void) fflush(stdout
);
2814 bprintf(FILE *fp
, int b
, char *s
)
2817 boolean_t gotsome
= B_FALSE
;
2821 while ((i
= *s
++) != 0) {
2822 if (b
& (1 << (i
- 1))) {
2829 for (; (i
= *s
) > ' '; s
++)
2837 (void) putc('>', fp
);
2841 keyword(const char *cp
)
2843 struct keytab
*kt
= keywords
;
2845 while (kt
->kt_cp
&& strcmp(kt
->kt_cp
, cp
))
2851 sodump(su_t
*su
, char *which
)
2853 static char obuf
[INET6_ADDRSTRLEN
];
2855 switch (su
->sa
.sa_family
) {
2857 (void) printf("%s: link %s; ",
2858 which
, link_ntoa(&su
->sdl
));
2861 (void) printf("%s: inet %s; ",
2862 which
, inet_ntoa(su
->sin
.sin_addr
));
2865 if (inet_ntop(AF_INET6
, (void *)&su
->sin6
.sin6_addr
, obuf
,
2866 INET6_ADDRSTRLEN
) != NULL
) {
2867 (void) printf("%s: inet6 %s; ", which
, obuf
);
2872 quit(gettext("Internal Error"), EINVAL
);
2875 (void) fflush(stdout
);
2887 #define LETTER (4*3)
2890 sockaddr(char *addr
, struct sockaddr
*sa
)
2892 char *cp
= (char *)sa
;
2893 int size
= salen(sa
);
2894 char *cplim
= cp
+ size
;
2895 int byte
= 0, state
= VIRGIN
, new;
2897 (void) memset(cp
, 0, size
);
2900 if ((*addr
>= '0') && (*addr
<= '9')) {
2902 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2903 new = *addr
- 'a' + 10;
2904 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2905 new = *addr
- 'A' + 10;
2906 } else if (*addr
== 0) {
2912 switch (state
/* | INPUT */) {
2913 case GOTTWO
| DIGIT
:
2916 case VIRGIN
| DIGIT
:
2917 state
= GOTONE
; byte
= new; continue;
2918 case GOTONE
| DIGIT
:
2919 state
= GOTTWO
; byte
= new + (byte
<< 4); continue;
2920 default: /* | DELIM */
2921 state
= VIRGIN
; *cp
++ = byte
; byte
= 0; continue;
2930 } while (cp
< cplim
);
2934 salen(const struct sockaddr
*sa
)
2936 switch (sa
->sa_family
) {
2938 return (sizeof (struct sockaddr_in
));
2940 return (sizeof (struct sockaddr_dl
));
2942 return (sizeof (struct sockaddr_in6
));
2944 return (sizeof (struct sockaddr
));
2949 link_addr(const char *addr
, struct sockaddr_dl
*sdl
)
2951 char *cp
= sdl
->sdl_data
;
2952 char *cplim
= sizeof (struct sockaddr_dl
) + (char *)sdl
;
2953 int byte
= 0, state
= VIRGIN
, new;
2955 (void) memset(sdl
, 0, sizeof (struct sockaddr_dl
));
2956 sdl
->sdl_family
= AF_LINK
;
2959 if ((*addr
>= '0') && (*addr
<= '9')) {
2961 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2962 new = *addr
- 'a' + 10;
2963 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2964 new = *addr
- 'A' + 10;
2965 } else if (*addr
== 0) {
2967 } else if (state
== VIRGIN
&&
2968 (((*addr
>= 'A') && (*addr
<= 'Z')) ||
2969 ((*addr
>= 'a') && (*addr
<= 'z')))) {
2975 switch (state
/* | INPUT */) {
2976 case VIRGIN
| DIGIT
:
2977 case VIRGIN
| LETTER
:
2980 case VIRGIN
| DELIM
:
2982 sdl
->sdl_nlen
= cp
- sdl
->sdl_data
;
2984 case GOTTWO
| DIGIT
:
2991 case GOTONE
| DIGIT
:
2993 byte
= new + (byte
<< 4);
2995 default: /* | DELIM */
3008 } while (cp
< cplim
);
3009 sdl
->sdl_alen
= cp
- LLADDR(sdl
);
3012 static char hexlist
[] = "0123456789abcdef";
3015 link_ntoa(const struct sockaddr_dl
*sdl
)
3017 static char obuf
[64];
3020 uchar_t
*in
= (uchar_t
*)LLADDR(sdl
);
3021 uchar_t
*inlim
= in
+ sdl
->sdl_alen
;
3022 boolean_t firsttime
= B_TRUE
;
3024 if (sdl
->sdl_nlen
) {
3025 (void) memcpy(obuf
, sdl
->sdl_data
, sdl
->sdl_nlen
);
3026 out
+= sdl
->sdl_nlen
;
3030 while (in
< inlim
) {
3032 firsttime
= B_FALSE
;
3037 out
[1] = hexlist
[i
& 0xf];
3039 out
[0] = hexlist
[i
];
3042 *out
++ = hexlist
[i
];
3052 intmax_t buf
[512 / sizeof (intmax_t)];
3055 struct strbuf ctlbuf
, databuf
;
3056 struct T_optmgmt_req
*tor
= (struct T_optmgmt_req
*)buf
;
3057 struct T_optmgmt_ack
*toa
= (struct T_optmgmt_ack
*)buf
;
3058 struct T_error_ack
*tea
= (struct T_error_ack
*)buf
;
3060 mib_item_t
*first_item
= NULL
;
3061 mib_item_t
*last_item
= NULL
;
3064 tor
->PRIM_type
= T_SVR4_OPTMGMT_REQ
;
3065 tor
->OPT_offset
= sizeof (struct T_optmgmt_req
);
3066 tor
->OPT_length
= sizeof (struct opthdr
);
3067 tor
->MGMT_flags
= T_CURRENT
;
3068 req
= (struct opthdr
*)&tor
[1];
3069 req
->level
= MIB2_IP
; /* any MIB2_xxx value ok here */
3073 ctlbuf
.buf
= (char *)buf
;
3074 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
3076 if (putmsg(sd
, &ctlbuf
, NULL
, flags
) < 0) {
3077 perror("mibget: putmsg (ctl)");
3081 * each reply consists of a ctl part for one fixed structure
3082 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3083 * containing an opthdr structure. level/name identify the entry,
3084 * len is the size of the data part of the message.
3086 req
= (struct opthdr
*)&toa
[1];
3087 ctlbuf
.maxlen
= sizeof (buf
);
3088 for (j
= 1; ; j
++) {
3090 getcode
= getmsg(sd
, &ctlbuf
, NULL
, &flags
);
3092 perror("mibget: getmsg (ctl)");
3094 (void) fprintf(stderr
,
3095 "# level name len\n");
3097 for (last_item
= first_item
; last_item
!= NULL
;
3098 last_item
= last_item
->next_item
) {
3099 (void) printf("%d %4ld %5ld %ld\n",
3100 ++i
, last_item
->group
,
3108 ctlbuf
.len
>= sizeof (struct T_optmgmt_ack
) &&
3109 toa
->PRIM_type
== T_OPTMGMT_ACK
&&
3110 toa
->MGMT_flags
== T_SUCCESS
&&
3113 (void) printf("mibget getmsg() %d returned EOD "
3114 "(level %lu, name %lu)\n", j
, req
->level
,
3117 return (first_item
); /* this is EOD msg */
3120 if (ctlbuf
.len
>= sizeof (struct T_error_ack
) &&
3121 tea
->PRIM_type
== T_ERROR_ACK
) {
3122 (void) fprintf(stderr
, gettext("mibget %d gives "
3123 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3124 "0x%lx\n"), j
, tea
->TLI_error
, tea
->UNIX_error
);
3125 errno
= (tea
->TLI_error
== TSYSERR
)
3126 ? tea
->UNIX_error
: EPROTO
;
3130 if (getcode
!= MOREDATA
||
3131 ctlbuf
.len
< sizeof (struct T_optmgmt_ack
) ||
3132 toa
->PRIM_type
!= T_OPTMGMT_ACK
||
3133 toa
->MGMT_flags
!= T_SUCCESS
) {
3134 (void) printf("mibget getmsg(ctl) %d returned %d, "
3135 "ctlbuf.len = %d, PRIM_type = %ld\n",
3136 j
, getcode
, ctlbuf
.len
, toa
->PRIM_type
);
3137 if (toa
->PRIM_type
== T_OPTMGMT_ACK
) {
3138 (void) printf("T_OPTMGMT_ACK: "
3139 "MGMT_flags = 0x%lx, req->len = %ld\n",
3140 toa
->MGMT_flags
, req
->len
);
3146 temp
= malloc(sizeof (mib_item_t
));
3148 perror("mibget: malloc");
3151 if (last_item
!= NULL
)
3152 last_item
->next_item
= temp
;
3156 last_item
->next_item
= NULL
;
3157 last_item
->group
= req
->level
;
3158 last_item
->mib_id
= req
->name
;
3159 last_item
->length
= req
->len
;
3160 last_item
->valp
= malloc(req
->len
);
3162 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3164 j
, last_item
->group
, last_item
->mib_id
,
3168 databuf
.maxlen
= last_item
->length
;
3169 databuf
.buf
= (char *)last_item
->valp
;
3172 getcode
= getmsg(sd
, NULL
, &databuf
, &flags
);
3174 perror("mibget: getmsg (data)");
3176 } else if (getcode
!= 0) {
3177 (void) printf("mibget getmsg(data) returned %d, "
3178 "databuf.maxlen = %d, databuf.len = %d\n",
3179 getcode
, databuf
.maxlen
, databuf
.len
);
3185 * On error, free all the allocated mib_item_t objects.
3187 while (first_item
!= NULL
) {
3188 last_item
= first_item
;
3189 first_item
= first_item
->next_item
;
3196 * print label security attributes for gateways.
3199 pmsg_secattr(const char *sptr
, size_t msglen
, const char *labelstr
)
3202 tsol_rtsecattr_t sp
;
3203 struct rtsa_s
*rtsa
= &sp
.rtsa_attr
[0];
3208 if (!is_system_labeled())
3211 endptr
= sptr
+ msglen
;
3214 if (sptr
+ sizeof (rtm_ext_t
) + sizeof (sp
) > endptr
)
3217 bcopy(sptr
, &rtm_ext
, sizeof (rtm_ext
));
3218 sptr
+= sizeof (rtm_ext
);
3219 if (rtm_ext
.rtmex_type
== RTMEX_GATEWAY_SECATTR
)
3221 sptr
+= rtm_ext
.rtmex_len
;
3224 /* bail if this entry is corrupt or overruns buffer length */
3225 if (rtm_ext
.rtmex_len
< sizeof (sp
) ||
3226 sptr
+ rtm_ext
.rtmex_len
> endptr
)
3229 /* run up just to the end of this extension */
3230 endptr
= sptr
+ rtm_ext
.rtmex_len
;
3232 bcopy(sptr
, &sp
, sizeof (sp
));
3233 sptr
+= sizeof (sp
);
3235 if (sptr
+ (sp
.rtsa_cnt
- 1) * sizeof (*rtsa
) != endptr
)
3238 for (i
= 0; i
< sp
.rtsa_cnt
; i
++) {
3240 /* first element is part of sp initalized above */
3241 bcopy(sptr
, rtsa
, sizeof (*rtsa
));
3242 sptr
+= sizeof (*rtsa
);
3244 (void) printf("\n%s%s", labelstr
, rtsa_to_str(rtsa
, buf
,