2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
5 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
6 /* All Rights Reserved */
8 /* Copyright (c) 1990 Mentat Inc. */
12 * Copyright (c) 1983, 1989, 1991, 1993
13 * The Regents of the University of California. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. All advertising materials mentioning features or use of this software
24 * must display the following acknowledgement:
25 * This product includes software developed by the University of
26 * California, Berkeley and its contributors.
27 * 4. Neither the name of the University nor the names of its contributors
28 * may be used to endorse or promote products derived from this software
29 * without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * @(#)route.c 8.6 (Berkeley) 4/28/95
44 * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93
47 #include <sys/param.h>
49 #include <sys/socket.h>
50 #include <sys/ioctl.h>
52 #include <sys/stream.h>
53 #include <sys/sysmacros.h>
54 #include <sys/tihdr.h>
55 #include <sys/types.h>
56 #include <sys/ccompile.h>
59 #include <net/route.h>
60 #include <net/if_dl.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
64 #include <inet/mib2.h>
83 #include <tsol/label.h>
85 static struct keytab
{
92 {"blackhole", K_BLACKHOLE
},
96 {"cloning", K_CLONING
},
102 {"expire", K_EXPIRE
},
106 {"gateway", K_GATEWAY
},
109 #define K_HOPCOUNT 12
110 {"hopcount", K_HOPCOUNT
},
123 #define K_INTERFACE 19
124 {"interface", K_INTERFACE
},
129 #define K_LOCKREST 22
130 {"lockrest", K_LOCKREST
},
134 {"monitor", K_MONITOR
},
140 {"netmask", K_NETMASK
},
141 #define K_NOSTATIC 28
142 {"nostatic", K_NOSTATIC
},
144 {"private", K_PRIVATE
},
146 {"proto1", K_PROTO1
},
148 {"proto2", K_PROTO2
},
149 #define K_RECVPIPE 32
150 {"recvpipe", K_RECVPIPE
},
152 {"reject", K_REJECT
},
156 {"rttvar", K_RTTVAR
},
159 #define K_SENDPIPE 37
160 {"sendpipe", K_SENDPIPE
},
161 #define K_SSTHRESH 38
162 {"ssthresh", K_SSTHRESH
},
164 {"static", K_STATIC
},
165 #define K_XRESOLVE 40
166 {"xresolve", K_XRESOLVE
},
168 {"multirt", K_MULTIRT
},
170 {"setsrc", K_SETSRC
},
174 {"secattr", K_SECATTR
},
175 #define K_INDIRECT 44
176 {"indirect", K_INDIRECT
},
181 * Size of buffers used to hold command lines from the saved route file as
182 * well as error strings.
184 #define BUF_SIZE 2048
186 typedef union sockunion
{
188 struct sockaddr_in sin
;
189 struct sockaddr_dl sdl
;
190 struct sockaddr_in6 sin6
;
194 * This structure represents the digested information from parsing arguments
195 * to route add, change, delete, and get.
198 typedef struct rtcmd_irep
{
203 struct rt_metrics ri_metrics
;
209 struct hostent
*ri_gate_hp
;
215 int ri_rtsa_cnt
; /* number of gateway security attributes */
216 struct rtsa_s ri_rtsa
; /* enough space for one attribute */
219 typedef struct mib_item_s
{
220 struct mib_item_s
*next_item
;
239 static boolean_t
args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
,
241 static void bprintf(FILE *fp
, int b
, char *s
);
242 static boolean_t
compare_rtcmd(rtcmd_irep_t
*srch_rt
,
243 rtcmd_irep_t
*file_rt
);
244 static void delRouteEntry(mib2_ipRouteEntry_t
*rp
,
245 mib2_ipv6RouteEntry_t
*rp6
, int seqno
);
246 static void del_rtcmd_irep(rtcmd_irep_t
*rcip
);
247 static void flushroutes(int argc
, char *argv
[]);
248 static boolean_t
getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
,
250 static boolean_t
in6_getaddr(char *s
, struct sockaddr_in6
*sin6
,
251 int *plenp
, struct hostent
**hpp
);
252 static boolean_t
in_getaddr(char *s
, struct sockaddr_in
*sin
,
253 int *plenp
, int which
, struct hostent
**hpp
, addr_type_t atype
,
255 static int in_getprefixlen(char *addr
, int max_plen
);
256 static boolean_t
in_prefixlentomask(int prefixlen
, int maxlen
,
258 static void inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
,
259 struct sockaddr_in
*sin
);
260 static in_addr_t
inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
);
261 static int keyword(const char *cp
);
262 static void link_addr(const char *addr
, struct sockaddr_dl
*sdl
);
263 static char *link_ntoa(const struct sockaddr_dl
*sdl
);
264 static mib_item_t
*mibget(int sd
);
265 static char *netname(struct sockaddr
*sa
);
266 static int newroute(char **argv
);
267 static rtcmd_irep_t
*new_rtcmd_irep(void);
268 static void pmsg_addrs(const char *cp
, size_t len
, uint_t addrs
);
269 static void pmsg_common(const struct rt_msghdr
*rtm
, size_t len
);
270 static void print_getmsg(rtcmd_irep_t
*req_rt
,
271 struct rt_msghdr
*rtm
, int msglen
);
272 static void print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
,
273 boolean_t gw_good
, boolean_t to_saved
);
274 static void print_rtmsg(struct rt_msghdr
*rtm
, int msglen
);
275 static void quit(char *s
, int err
) __NORETURN
;
276 static char *routename(const struct sockaddr
*sa
);
277 static void rtmonitor(int argc
, char *argv
[]);
278 static int rtmsg(rtcmd_irep_t
*rcip
);
279 static int salen(const struct sockaddr
*sa
);
280 static void save_route(int argc
, char **argv
, int do_flush
);
281 static void save_string(char **dst
, char *src
);
282 static int search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
,
284 static void set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
,
286 static int show_saved_routes(int argc
);
287 static void sockaddr(char *addr
, struct sockaddr
*sa
);
288 static void sodump(su_t
*su
, char *which
);
289 static void syntax_arg_missing(char *keyword
);
290 static void syntax_bad_keyword(char *keyword
);
291 static void syntax_error(char *err
, ...);
292 static void usage(char *cp
);
293 static void write_to_rtfile(FILE *fp
, int argc
, char **argv
);
294 static void pmsg_secattr(const char *, size_t, const char *);
298 static boolean_t nflag
;
299 static int af
= AF_INET
;
300 static boolean_t qflag
, tflag
;
301 static boolean_t verbose
;
302 static boolean_t debugonly
;
303 static boolean_t fflag
;
304 static boolean_t update_table
;
305 static boolean_t perm_flag
;
306 static boolean_t early_v6_keyword
;
307 static char perm_file_sfx
[] = "/etc/inet/static_routes";
308 static char *perm_file
;
309 static char temp_file_sfx
[] = "/etc/inet/static_routes.tmp";
310 static char *temp_file
;
311 static struct in6_addr in6_host_mask
= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
312 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
315 * This next variable indicates whether certain functions exit when an error
316 * is detected in the user input. Currently, exit_on_error is only set false
317 * in search_rtfile(), when argument are being parsed. Only those functions
318 * used by search_rtfile() to parse its arguments are designed to work in
319 * both modes. Take particular care in setting this false to ensure that any
320 * functions you call that might act on this flag properly return errors when
321 * exit_on_error is false.
323 static int exit_on_error
= B_TRUE
;
326 struct rt_msghdr m_rtm
;
327 char m_space
[BUF_SIZE
];
331 * Sizes of data structures extracted from the base mib.
332 * This allows the size of the tables entries to grow while preserving
333 * binary compatibility.
335 static int ipRouteEntrySize
;
336 static int ipv6RouteEntrySize
;
338 #define ROUNDUP_LONG(a) \
339 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
340 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
341 #define C(x) ((x) & 0xff)
344 * return values from in_getprefixlen()
346 #define BAD_ADDR -1 /* prefix is invalid */
347 #define NO_PREFIX -2 /* no prefix was found */
353 (void) fprintf(stderr
, gettext("route: botched keyword: %s\n"),
356 (void) fprintf(stderr
, gettext("usage: route [ -fnpqv ] "
357 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
364 syntax_error(char *err
, ...)
370 (void) vfprintf(stderr
, err
, args
);
378 syntax_bad_keyword(char *keyword
)
380 syntax_error(gettext("route: botched keyword: %s\n"), keyword
);
384 syntax_arg_missing(char *keyword
)
386 syntax_error(gettext("route: argument required following keyword %s\n"),
391 quit(char *s
, int sverrno
)
393 (void) fprintf(stderr
, "route: ");
395 (void) fprintf(stderr
, "%s: ", s
);
396 (void) fprintf(stderr
, "%s\n", strerror(sverrno
));
402 main(int argc
, char **argv
)
409 const char *root_dir
= NULL
;
411 (void) setlocale(LC_ALL
, "");
413 #if !defined(TEXT_DOMAIN)
414 #define TEXT_DOMAIN "SYS_TEST"
416 (void) textdomain(TEXT_DOMAIN
);
421 while ((ch
= getopt(argc
, argv
, "R:nqdtvfp")) != EOF
) {
458 s
= open("/dev/null", O_WRONLY
);
460 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
462 quit("socket", errno
);
465 * Handle the -p and -R flags. The -R flag only applies
466 * when the -p flag is set.
468 if (root_dir
== NULL
) {
469 perm_file
= perm_file_sfx
;
470 temp_file
= temp_file_sfx
;
472 size
= strlen(root_dir
) + sizeof (perm_file_sfx
);
473 perm_file
= malloc(size
);
474 if (perm_file
== NULL
)
475 quit("malloc", errno
);
476 (void) snprintf(perm_file
, size
, "%s%s", root_dir
,
478 size
= strlen(root_dir
) + sizeof (temp_file_sfx
);
479 temp_file
= malloc(size
);
480 if (temp_file
== NULL
)
481 quit("malloc", errno
);
482 (void) snprintf(temp_file
, size
, "%s%s", root_dir
,
486 * Whether or not to act on the routing table. The only time the
487 * routing table is not modified is when both -p and -R are present.
489 update_table
= (!perm_flag
|| root_dir
== NULL
);
495 * Accept an address family keyword after the -f. Since the
496 * default address family is AF_INET, reassign af only for the
497 * other valid address families.
500 switch (keyword(*argv
)) {
503 early_v6_keyword
= B_TRUE
;
506 /* Skip over the address family parameter. */
512 flushroutes(0, NULL
);
516 switch (keyword(*argv
)) {
523 rval
= newroute(argv
);
525 if (perm_flag
&& (rval
== 0 || rval
== EEXIST
||
527 save_route(argc
, argv
, B_FALSE
);
533 return (show_saved_routes(argc
));
535 syntax_error(gettext(
536 "route: show command requires -p\n"));
540 rtmonitor(argc
, argv
);
544 flushroutes(argc
, argv
);
554 * Purge all entries in the routing tables not
555 * associated with network interfaces.
558 flushroutes(int argc
, char *argv
[])
561 int sd
; /* mib stream */
563 mib2_ipRouteEntry_t
*rp
;
564 mib2_ipv6RouteEntry_t
*rp6
;
571 if (argc
== 2 && **argv
== '-') {
573 * The address family (preceded by a dash) may be used
574 * to flush the routes of that particular family.
576 switch (keyword(*argv
+ 1)) {
595 /* This flushes the persistent route file */
596 save_route(0, NULL
, B_TRUE
);
602 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&off
,
604 quit("setsockopt", errno
);
606 sd
= open("/dev/ip", O_RDWR
);
611 (void) fprintf(stderr
,
612 gettext("route: flush: insufficient privileges\n"));
616 quit(gettext("can't open mib stream"), oerrno
);
620 if ((item
= mibget(sd
)) == NULL
)
621 quit("mibget", errno
);
623 (void) printf("Examining routing table from "
624 "T_SVR4_OPTMGMT_REQ\n");
629 /* Extract ipRouteEntrySize */
630 for (; item
!= NULL
; item
= item
->next_item
) {
631 if (item
->mib_id
!= 0)
633 if (item
->group
== MIB2_IP
) {
635 ((mib2_ip_t
*)item
->valp
)->ipRouteEntrySize
;
636 assert(IS_P2ALIGNED(ipRouteEntrySize
,
637 sizeof (mib2_ipRouteEntry_t
*)));
641 if (ipRouteEntrySize
== 0) {
642 (void) fprintf(stderr
,
643 gettext("ipRouteEntrySize can't be determined.\n"));
646 for (; item
!= NULL
; item
= item
->next_item
) {
648 * skip all the other trash that comes up the mib stream
650 if (item
->group
!= MIB2_IP
||
651 item
->mib_id
!= MIB2_IP_ROUTE
)
653 for (rp
= (mib2_ipRouteEntry_t
*)item
->valp
;
654 (char *)rp
< (char *)item
->valp
+ item
->length
;
656 rp
= (mib2_ipRouteEntry_t
*)
657 ((char *)rp
+ ipRouteEntrySize
)) {
658 delRouteEntry(rp
, NULL
, seqno
);
665 /* Extract ipv6RouteEntrySize */
666 for (; item
!= NULL
; item
= item
->next_item
) {
667 if (item
->mib_id
!= 0)
669 if (item
->group
== MIB2_IP6
) {
671 ((mib2_ipv6IfStatsEntry_t
*)item
->valp
)->
673 assert(IS_P2ALIGNED(ipv6RouteEntrySize
,
674 sizeof (mib2_ipv6RouteEntry_t
*)));
678 if (ipv6RouteEntrySize
== 0) {
679 (void) fprintf(stderr
, gettext(
680 "ipv6RouteEntrySize cannot be determined.\n"));
683 for (; item
!= NULL
; item
= item
->next_item
) {
685 * skip all the other trash that comes up the mib stream
687 if (item
->group
!= MIB2_IP6
||
688 item
->mib_id
!= MIB2_IP6_ROUTE
)
690 for (rp6
= (mib2_ipv6RouteEntry_t
*)item
->valp
;
691 (char *)rp6
< (char *)item
->valp
+ item
->length
;
693 rp6
= (mib2_ipv6RouteEntry_t
*)
694 ((char *)rp6
+ ipv6RouteEntrySize
)) {
695 delRouteEntry(NULL
, rp6
, seqno
);
703 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&on
,
705 quit("setsockopt", errno
);
709 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
710 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
711 * order to facilitate the flushing of RTF_GATEWAY routes.
714 delRouteEntry(mib2_ipRouteEntry_t
*rp
, mib2_ipv6RouteEntry_t
*rp6
, int seqno
)
719 struct rt_msghdr
*rtm
;
720 struct sockaddr_in sin
;
721 struct sockaddr_in6 sin6
;
725 ire_type
= rp
->ipRouteInfo
.re_ire_type
;
727 ire_type
= rp6
->ipv6RouteInfo
.re_ire_type
;
728 if (ire_type
!= IRE_DEFAULT
&&
729 ire_type
!= IRE_PREFIX
&&
730 ire_type
!= IRE_HOST
&&
731 ire_type
!= IRE_HOST_REDIRECT
)
734 rtm
= &m_rtmsg
.m_rtm
;
735 (void) memset(rtm
, 0, sizeof (m_rtmsg
));
736 rtm
->rtm_type
= RTM_DELETE
;
737 rtm
->rtm_seq
= seqno
;
738 rtm
->rtm_flags
|= RTF_GATEWAY
;
739 rtm
->rtm_version
= RTM_VERSION
;
740 rtm
->rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
741 cp
= m_rtmsg
.m_space
;
743 slen
= sizeof (struct sockaddr_in
);
744 if (rp
->ipRouteMask
== IP_HOST_MASK
)
745 rtm
->rtm_flags
|= RTF_HOST
;
746 (void) memset(&sin
, 0, slen
);
747 sin
.sin_family
= AF_INET
;
748 sin
.sin_addr
.s_addr
= rp
->ipRouteDest
;
749 (void) memmove(cp
, &sin
, slen
);
751 sin
.sin_addr
.s_addr
= rp
->ipRouteNextHop
;
752 (void) memmove(cp
, &sin
, slen
);
754 sin
.sin_addr
.s_addr
= rp
->ipRouteMask
;
755 (void) memmove(cp
, &sin
, slen
);
758 slen
= sizeof (struct sockaddr_in6
);
759 if (rp6
->ipv6RoutePfxLength
== IPV6_ABITS
)
760 rtm
->rtm_flags
|= RTF_HOST
;
761 (void) memset(&sin6
, 0, slen
);
762 sin6
.sin6_family
= AF_INET6
;
763 sin6
.sin6_addr
= rp6
->ipv6RouteDest
;
764 (void) memmove(cp
, &sin6
, slen
);
766 sin6
.sin6_addr
= rp6
->ipv6RouteNextHop
;
767 (void) memmove(cp
, &sin6
, slen
);
769 (void) memset(&sin6
.sin6_addr
, 0, sizeof (sin6
.sin6_addr
));
770 (void) in_prefixlentomask(rp6
->ipv6RoutePfxLength
, IPV6_ABITS
,
771 (uchar_t
*)&sin6
.sin6_addr
.s6_addr
);
772 (void) memmove(cp
, &sin6
, slen
);
775 rtm
->rtm_msglen
= cp
- (char *)&m_rtmsg
;
778 * In debugonly mode, the routing socket message to delete the
779 * current entry is not actually sent. However if verbose is
780 * also set, the routing socket message that would have been
784 print_rtmsg(rtm
, rtm
->rtm_msglen
);
788 rlen
= write(s
, (char *)&m_rtmsg
, rtm
->rtm_msglen
);
789 if (rlen
< (int)rtm
->rtm_msglen
) {
791 (void) fprintf(stderr
,
792 gettext("route: write to routing socket: %s\n"),
795 (void) fprintf(stderr
, gettext("route: write to "
796 "routing socket got only %d for rlen\n"), rlen
);
802 * In quiet mode, nothing is printed at all (unless the write()
808 print_rtmsg(rtm
, rlen
);
810 struct sockaddr
*sa
= (struct sockaddr
*)(rtm
+ 1);
812 (void) printf("%-20.20s ",
813 rtm
->rtm_flags
& RTF_HOST
? routename(sa
) :
816 sa
= (struct sockaddr
*)(salen(sa
) + (char *)sa
);
817 (void) printf("%-20.20s ", routename(sa
));
818 (void) printf("done\n");
823 * Return the name of the host whose address is given.
826 routename(const struct sockaddr
*sa
)
829 static char line
[MAXHOSTNAMELEN
+ 1];
830 struct hostent
*hp
= NULL
;
831 static char domain
[MAXHOSTNAMELEN
+ 1];
832 static boolean_t first
= B_TRUE
;
841 if (gethostname(domain
, MAXHOSTNAMELEN
) == 0 &&
842 (cp
= strchr(domain
, '.')))
843 (void) strcpy(domain
, cp
+ 1);
848 if (salen(sa
) == 0) {
849 (void) strcpy(line
, "default");
852 switch (sa
->sa_family
) {
856 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
859 if (in
.s_addr
== INADDR_ANY
)
861 if (cp
== NULL
&& !nflag
) {
862 hp
= gethostbyaddr((char *)&in
, sizeof (struct in_addr
),
865 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
866 (strcmp(cp
+ 1, domain
) == 0))
872 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
873 line
[MAXHOSTNAMELEN
] = '\0';
875 in
.s_addr
= ntohl(in
.s_addr
);
876 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
877 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
883 return (link_ntoa((struct sockaddr_dl
*)sa
));
887 in6
= ((struct sockaddr_in6
*)sa
)->sin6_addr
;
890 if (IN6_IS_ADDR_UNSPECIFIED(&in6
))
892 if (cp
== NULL
&& !nflag
) {
893 hp
= getipnodebyaddr((char *)&in6
,
894 sizeof (struct in6_addr
), AF_INET6
, &error_num
);
896 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
897 (strcmp(cp
+ 1, domain
) == 0))
903 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
904 line
[MAXHOSTNAMELEN
] = '\0';
906 (void) inet_ntop(AF_INET6
, (void *)&in6
, line
,
917 slim
= s
+ ((salen(sa
) + 1) >> 1);
918 cp
= line
+ sprintf(line
, "(%d)", sa
->sa_family
);
920 while (++s
< slim
) /* start with sa->sa_data */
921 cp
+= sprintf(cp
, " %x", *s
);
928 * Return the name of the network whose address is given.
929 * The address is assumed to be that of a net or subnet, not a host.
932 netname(struct sockaddr
*sa
)
935 static char line
[MAXHOSTNAMELEN
+ 1];
943 switch (sa
->sa_family
) {
947 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
949 in
.s_addr
= ntohl(in
.s_addr
);
950 if (in
.s_addr
== INADDR_ANY
) {
953 if (IN_CLASSA(in
.s_addr
)) {
954 mask
= IN_CLASSA_NET
;
956 } else if (IN_CLASSB(in
.s_addr
)) {
957 mask
= IN_CLASSB_NET
;
960 mask
= IN_CLASSC_NET
;
964 * If there are more bits than the standard mask
965 * would suggest, subnets must be in use.
966 * Guess at the subnet mask, assuming reasonable
967 * width subnet fields.
969 while (in
.s_addr
&~ mask
)
970 mask
= (long)mask
>> subnetshift
;
971 net
= in
.s_addr
& mask
;
972 while ((mask
& 1) == 0)
973 mask
>>= 1, net
>>= 1;
974 np
= getnetbyaddr(net
, AF_INET
);
979 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
980 line
[MAXHOSTNAMELEN
] = '\0';
981 } else if ((in
.s_addr
& 0xffffff) == 0) {
982 (void) sprintf(line
, "%u", C(in
.s_addr
>> 24));
983 } else if ((in
.s_addr
& 0xffff) == 0) {
984 (void) sprintf(line
, "%u.%u", C(in
.s_addr
>> 24),
986 } else if ((in
.s_addr
& 0xff) == 0) {
987 (void) sprintf(line
, "%u.%u.%u", C(in
.s_addr
>> 24),
988 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8));
990 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
991 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
997 return (link_ntoa((struct sockaddr_dl
*)sa
));
1000 return (routename(sa
));
1004 s
= (ushort_t
*)sa
->sa_data
;
1006 slim
= s
+ ((salen(sa
) + 1) >> 1);
1007 cp
= line
+ sprintf(line
, "af %d:", sa
->sa_family
);
1010 cp
+= sprintf(cp
, " %x", *s
++);
1017 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1018 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1019 * or point to dynamically allocated memory.
1022 new_rtcmd_irep(void)
1026 rcip
= calloc(1, sizeof (rtcmd_irep_t
));
1028 quit("calloc", errno
);
1031 rcip
->ri_flags
= RTF_STATIC
;
1036 del_rtcmd_irep(rtcmd_irep_t
*rcip
)
1038 free(rcip
->ri_dest_str
);
1039 free(rcip
->ri_gate_str
);
1040 free(rcip
->ri_ifp_str
);
1042 * IPv6 host entries come from getipnodebyname, which dynamically
1043 * allocates memory. IPv4 host entries come from gethostbyname, which
1044 * returns static memory and cannot be freed with freehostent.
1046 if (rcip
->ri_gate_hp
!= NULL
&&
1047 rcip
->ri_gate_hp
->h_addrtype
== AF_INET6
)
1048 freehostent(rcip
->ri_gate_hp
);
1053 save_string(char **dst
, char *src
)
1058 quit("malloc", errno
);
1063 * Print the short form summary of a route command.
1064 * Eg. "add net default: gateway 10.0.0.1"
1065 * The final newline is not added, allowing the caller to append additional
1069 print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
, boolean_t gw_good
,
1073 char obuf
[INET6_ADDRSTRLEN
];
1075 switch (rcip
->ri_cmd
) {
1092 (void) fprintf(to
, "%s%s %s %s", cmd
,
1093 (to_saved
) ? " persistent" : "",
1094 (rcip
->ri_flags
& RTF_HOST
) ? "host" : "net",
1095 (rcip
->ri_dest_str
== NULL
) ? "NULL" : rcip
->ri_dest_str
);
1097 if (rcip
->ri_gate_str
!= NULL
) {
1098 switch (rcip
->ri_af
) {
1101 (void) fprintf(to
, ": gateway %s",
1102 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1103 } else if (gw_good
&&
1104 rcip
->ri_gate_hp
!= NULL
&&
1105 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1107 * Print the actual address used in the case
1108 * where there was more than one address
1109 * available for the name, and one was used
1112 (void) fprintf(to
, ": gateway %s (%s)",
1114 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1116 (void) fprintf(to
, ": gateway %s",
1121 if (inet_ntop(AF_INET6
,
1122 &rcip
->ri_gate
.sin6
.sin6_addr
, obuf
,
1123 INET6_ADDRSTRLEN
) != NULL
) {
1125 (void) fprintf(to
, ": gateway %s",
1130 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1131 (void) fprintf(to
, ": gateway %s (%s)",
1132 rcip
->ri_gate_str
, obuf
);
1138 (void) fprintf(to
, ": gateway %s",
1146 set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
, boolean_t lock
)
1149 uint_t noval
, *valp
= &noval
;
1152 #define caseof(x, y, z) \
1153 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1155 caseof(K_MTU
, RTV_MTU
, rmx_mtu
);
1156 caseof(K_HOPCOUNT
, RTV_HOPCOUNT
, rmx_hopcount
);
1157 caseof(K_EXPIRE
, RTV_EXPIRE
, rmx_expire
);
1158 caseof(K_RECVPIPE
, RTV_RPIPE
, rmx_recvpipe
);
1159 caseof(K_SENDPIPE
, RTV_SPIPE
, rmx_sendpipe
);
1160 caseof(K_SSTHRESH
, RTV_SSTHRESH
, rmx_ssthresh
);
1161 caseof(K_RTT
, RTV_RTT
, rmx_rtt
);
1162 caseof(K_RTTVAR
, RTV_RTTVAR
, rmx_rttvar
);
1165 rcip
->ri_inits
|= flag
;
1167 rcip
->ri_metrics
.rmx_locks
|= flag
;
1168 *valp
= atoi(value
);
1172 * Parse the options give in argv[], filling in rcip with the results.
1173 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1174 * tokenized to produce the command line. Cmd_string is tokenized using
1175 * strtok, which will overwrite whitespace in the string with nulls.
1177 * Returns B_TRUE on success and B_FALSE on failure.
1180 args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
, char *cmd_string
)
1182 const char *ws
= "\f\n\r\t\v ";
1183 char *tok
= cmd_string
;
1185 addr_type_t atype
= ADDR_TYPE_ANY
;
1186 boolean_t iflag
= B_FALSE
;
1187 boolean_t locknext
= B_FALSE
;
1188 boolean_t lockrest
= B_FALSE
;
1189 boolean_t dash_keyword
;
1193 if (cmd_string
== NULL
) {
1196 tok
= strtok(cmd_string
, ws
);
1200 * The command keywords are already fully checked by main() or
1205 rcip
->ri_cmd
= RTM_ADD
;
1208 rcip
->ri_cmd
= RTM_CHANGE
;
1211 rcip
->ri_cmd
= RTM_DELETE
;
1214 rcip
->ri_cmd
= RTM_GET
;
1218 quit(gettext("Internal Error"), EINVAL
);
1223 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1228 dash_keyword
= B_TRUE
;
1229 key
= keyword(tok
+ 1);
1231 dash_keyword
= B_FALSE
;
1233 if (key
!= K_HOST
&& key
!= K_NET
) {
1234 /* All others must be preceded by '-' */
1240 if (atype
== ADDR_TYPE_NET
) {
1241 syntax_error(gettext("route: -host and -net "
1242 "are mutually exclusive\n"));
1245 atype
= ADDR_TYPE_HOST
;
1248 if (atype
== ADDR_TYPE_HOST
) {
1249 syntax_error(gettext("route: -host and -net "
1250 "are mutually exclusive\n"));
1253 atype
= ADDR_TYPE_NET
;
1256 rcip
->ri_af
= AF_LINK
;
1259 rcip
->ri_af
= AF_INET
;
1262 rcip
->ri_af
= PF_ROUTE
;
1265 rcip
->ri_af
= AF_INET6
;
1272 rcip
->ri_flags
&= ~RTF_STATIC
;
1281 rcip
->ri_flags
|= RTF_REJECT
;
1284 rcip
->ri_flags
|= RTF_BLACKHOLE
;
1287 rcip
->ri_flags
|= RTF_PROTO1
;
1290 rcip
->ri_flags
|= RTF_PROTO2
;
1293 rcip
->ri_flags
|= RTF_CLONING
;
1296 rcip
->ri_flags
|= RTF_XRESOLVE
;
1299 rcip
->ri_flags
|= RTF_STATIC
;
1303 syntax_arg_missing(keyword_str
);
1306 if (!getaddr(rcip
, RTA_IFA
, tok
, atype
)) {
1312 syntax_arg_missing(keyword_str
);
1315 if (!getaddr(rcip
, RTA_IFP
, tok
, atype
)) {
1321 syntax_arg_missing(keyword_str
);
1324 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1330 syntax_arg_missing(keyword_str
);
1333 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1339 syntax_arg_missing(keyword_str
);
1342 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1345 atype
= ADDR_TYPE_NET
;
1356 syntax_arg_missing(keyword_str
);
1359 set_metric(rcip
, tok
, key
, locknext
|| lockrest
);
1363 rcip
->ri_flags
|= RTF_PRIVATE
;
1366 rcip
->ri_flags
|= RTF_MULTIRT
;
1370 syntax_arg_missing(keyword_str
);
1373 if (!getaddr(rcip
, RTA_SRC
, tok
, atype
)) {
1376 rcip
->ri_flags
|= RTF_SETSRC
;
1380 syntax_arg_missing(keyword_str
);
1383 if (is_system_labeled()) {
1386 if (rcip
->ri_rtsa_cnt
>= 1) {
1387 syntax_error(gettext("route: can't "
1388 "specify more than one security "
1392 if (!rtsa_keyword(tok
, &rcip
->ri_rtsa
, &err
,
1394 syntax_error(gettext("route: "
1395 "bad security attribute: %s\n"),
1396 tsol_strerror(err
, errno
));
1399 rcip
->ri_rtsa_cnt
++;
1401 syntax_error(gettext("route: "
1402 "system is not labeled; cannot specify "
1403 "security attributes.\n"));
1408 rcip
->ri_flags
|= RTF_INDIRECT
;
1412 syntax_bad_keyword(tok
+ 1);
1415 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1416 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1419 } else if ((rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1421 * For the gateway parameter, retrieve the
1422 * pointer to the struct hostent so that all
1423 * possible addresses can be tried until one
1426 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1432 * Assume that a regular number is a metric.
1433 * Needed for compatibility with old route
1437 metric
= strtoul(tok
, &err
, 10);
1438 if (errno
== 0 && *err
== '\0' &&
1439 metric
< 0x80000000ul
) {
1440 iflag
= (metric
== 0);
1442 (void) printf("old usage of "
1443 "trailing number, assuming "
1444 "route %s\n", iflag
?
1445 "to if" : "via gateway");
1449 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1457 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1458 syntax_error(gettext("route: destination required\n"));
1460 } else if ((rcip
->ri_cmd
== RTM_ADD
|| rcip
->ri_cmd
== RTM_DELETE
) &&
1461 (rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1462 syntax_error(gettext(
1463 "route: gateway required for add or delete command\n"));
1468 rcip
->ri_flags
|= RTF_GATEWAY
;
1471 if (atype
!= ADDR_TYPE_NET
) {
1472 if (rcip
->ri_addrs
& RTA_NETMASK
) {
1474 * We know the netmask, so we can set the host flag
1475 * based on whether the netmask is the host netmask.
1477 if (rcip
->ri_af
== AF_INET
&&
1478 rcip
->ri_mask
.sin
.sin_addr
.s_addr
==
1480 rcip
->ri_flags
|= RTF_HOST
;
1482 if (rcip
->ri_af
== AF_INET6
&&
1483 memcmp(&rcip
->ri_mask
.sin6
.sin6_addr
,
1485 sizeof (struct in6_addr
)) == 0) {
1486 rcip
->ri_flags
|= RTF_HOST
;
1490 * If no prefix mask has been saved at this point, it
1491 * only makes sense to treat the destination address
1492 * as a host address.
1494 rcip
->ri_flags
|= RTF_HOST
;
1501 * This command always seeks to the end of the file prior to writing.
1504 write_to_rtfile(FILE *fp
, int argc
, char **argv
)
1506 char file_line
[BUF_SIZE
];
1511 if (early_v6_keyword
) {
1513 * This flag is set when "inet6" was seen as an
1514 * argument to the -f flag. Normally, when writing
1515 * routes to the persistent route file, everything on
1516 * the command line after "add" is saved verbatim.
1517 * In this case, the arguments after "add" may not be
1518 * sufficient, as the ipv6 keyword came before "add",
1519 * yet must be present in the persistent route file.
1521 len
+= snprintf(file_line
, BUF_SIZE
, "-inet6 ");
1523 for (i
= 0; argc
> 0 && len
< BUF_SIZE
; i
++, argc
--) {
1524 len
+= snprintf(&file_line
[len
], BUF_SIZE
- len
, "%s ",
1527 if (len
>= BUF_SIZE
)
1528 quit(gettext("Internal Error"), EINVAL
);
1529 file_line
[len
- 1] = '\n';
1530 if (fseek(fp
, 0, SEEK_END
) != 0 ||
1531 fputs(file_line
, fp
) == EOF
) {
1532 quit(gettext("failed to write to route file"),
1538 compare_rtcmd(rtcmd_irep_t
*srch_rt
, rtcmd_irep_t
*file_rt
)
1540 if (strcmp(srch_rt
->ri_dest_str
, file_rt
->ri_dest_str
) != 0 ||
1541 memcmp(&srch_rt
->ri_mask
, &file_rt
->ri_mask
, sizeof (su_t
)) != 0) {
1544 return (srch_rt
->ri_gate_str
== NULL
||
1545 strcmp(srch_rt
->ri_gate_str
, file_rt
->ri_gate_str
) == 0);
1549 * Search the route file for routes matching the supplied route. There are 3
1550 * modes of operation:
1551 * SEARCH_MODE_RET - no side effects.
1552 * SEARCH_MODE_PRINT - prints each matching line.
1553 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1555 * In all cases, the number of matches is returned. If rt is NULL, all routes
1556 * matching the global af value are considered matching.
1559 search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
, search_mode_t mode
)
1564 char file_line
[BUF_SIZE
+ 4] = "add ";
1565 rtcmd_irep_t
*thisrt
;
1570 * Leave space at the beginning of file_line for "add ".
1572 while (fgets(file_line
+ 4, BUF_SIZE
, fp
) != NULL
) {
1574 if (file_line
[4] == '#' || file_line
[4] == '\n') {
1575 /* Handle comments and blank lines */
1576 if (mode
== SEARCH_MODE_DEL
&&
1577 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1579 "route: failed to write to temp file"),
1584 thisrt
= new_rtcmd_irep();
1586 * thisrt->ri_af defaults to whatever address family happens
1587 * to be set in the global af, but routes in the persistent
1588 * route file must be treated as AF_INET by default.
1590 thisrt
->ri_af
= AF_INET
;
1592 exit_on_error
= B_FALSE
;
1593 tmp_buf
= strdup(file_line
);
1594 /* args_to_rtcmd() will mangle the string passed. */
1595 if (!args_to_rtcmd(thisrt
, NULL
, tmp_buf
)) {
1596 /* There was an error in args_to_rtcmd() or helpers */
1597 del_rtcmd_irep(thisrt
);
1601 exit_on_error
= B_TRUE
;
1604 if (thisrt
->ri_gate_str
== NULL
) {
1605 del_rtcmd_irep(thisrt
);
1608 match
= (rt
== NULL
) ? (thisrt
->ri_af
== af
) :
1609 compare_rtcmd(rt
, thisrt
);
1611 if (match
) match_cnt
++;
1612 if (match
&& mode
== SEARCH_MODE_PRINT
) {
1613 (void) printf("persistent: route %s", file_line
);
1615 if (match
&& mode
== SEARCH_MODE_DEL
) {
1616 thisrt
->ri_cmd
= RTM_DELETE
;
1617 print_rtcmd_short(stdout
, thisrt
, B_FALSE
, B_TRUE
);
1618 (void) printf("\n");
1620 del_rtcmd_irep(thisrt
);
1622 if (!match
&& mode
== SEARCH_MODE_DEL
&&
1623 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1624 quit(gettext("failed to write to temp file"),
1632 * Perform the route operation given in argv on the persistent route file.
1633 * If do_flush is set, the persistent route file is flushed of all routes
1634 * matching the global family, and the arguments are ignored.
1637 save_route(int argc
, char **argv
, int do_flush
)
1643 mode_t fmode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1646 const char commentstr
[] =
1647 "# File generated by route(1M) - do not edit.\n";
1649 perm_fd
= open(perm_file
, O_RDWR
| O_CREAT
, fmode
);
1650 if (perm_fd
== -1 || fstat(perm_fd
, &st
) == -1)
1651 quit("failed to open route file", errno
);
1653 lock
.l_type
= F_WRLCK
;
1654 lock
.l_whence
= SEEK_SET
;
1657 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1658 quit(gettext("failed to lock route file"), errno
);
1661 if (st
.st_size
== 0 &&
1662 write(perm_fd
, commentstr
, sizeof (commentstr
) - 1) !=
1663 sizeof (commentstr
) - 1)
1664 quit(gettext("failed to open route file"), errno
);
1666 if ((perm_fp
= fdopen(perm_fd
, "r+")) == NULL
) {
1667 quit(gettext("failed to open route file"), errno
);
1672 rt
= new_rtcmd_irep();
1673 (void) args_to_rtcmd(rt
, argv
, NULL
);
1675 if (do_flush
|| rt
->ri_cmd
== RTM_DELETE
) {
1676 if ((temp_fp
= fopen(temp_file
, "w")) == NULL
) {
1677 quit(gettext("failed to open temp file"), errno
);
1682 (void) search_rtfile(perm_fp
, temp_fp
, NULL
, SEARCH_MODE_DEL
);
1683 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1684 quit(gettext("failed to update route file"), errno
);
1687 (void) fclose(perm_fp
);
1691 switch (rt
->ri_cmd
) {
1693 if (search_rtfile(perm_fp
, NULL
, rt
, SEARCH_MODE_NULL
) > 0) {
1694 /* Route is already in the file */
1695 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1696 (void) fprintf(stderr
, ": entry exists\n");
1699 write_to_rtfile(perm_fp
, argc
- 1, argv
+ 1);
1700 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1701 (void) printf("\n");
1706 gettext("route: change command not supported with -p\n"));
1710 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_DEL
) <= 0) {
1711 /* Route not found */
1712 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1713 (void) fprintf(stderr
, gettext(": not in file\n"));
1716 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1717 quit(gettext("failed to update route file"), errno
);
1723 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_PRINT
) <=
1725 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1726 (void) printf(gettext(": not in file\n"));
1731 quit(gettext("Internal Error"), EINVAL
);
1736 * Closing the file unlocks it.
1738 (void) fclose(perm_fp
);
1742 show_saved_routes(int argc
)
1750 syntax_error(gettext("route: invalid arguments for show\n"));
1753 perm_fd
= open(perm_file
, O_RDONLY
, 0);
1755 if (perm_fd
== -1) {
1756 if (errno
== ENOENT
) {
1757 (void) printf("No persistent routes are defined\n");
1760 quit(gettext("failed to open route file"), errno
);
1763 lock
.l_type
= F_RDLCK
;
1764 lock
.l_whence
= SEEK_SET
;
1767 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1768 quit(gettext("failed to lock route file"),
1772 if ((perm_fp
= fdopen(perm_fd
, "r")) == NULL
) {
1773 quit(gettext("failed to open route file"), errno
);
1776 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1777 (void) fseek(perm_fp
, 0, SEEK_SET
);
1779 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1782 (void) printf("No persistent routes are defined\n");
1784 (void) fclose(perm_fp
);
1789 newroute(char **argv
)
1791 rtcmd_irep_t
*newrt
;
1792 int ret
, attempts
, oerrno
;
1794 char obuf
[INET6_ADDRSTRLEN
];
1795 #define hp (newrt->ri_gate_hp)
1797 newrt
= new_rtcmd_irep();
1798 (void) args_to_rtcmd(newrt
, argv
, NULL
);
1800 if (newrt
->ri_cmd
!= RTM_GET
&& !tflag
) {
1801 /* Don't want to read back our messages */
1802 (void) shutdown(s
, 0);
1804 if (newrt
->ri_addrs
& RTA_IFP
) {
1805 newrt
->ri_ifp
.sdl
.sdl_index
= if_nametoindex(newrt
->ri_ifp_str
);
1806 if (newrt
->ri_ifp
.sdl
.sdl_index
== 0) {
1807 if (errno
!= ENXIO
) {
1808 quit("if_nametoindex", errno
);
1810 (void) fprintf(stderr
,
1811 gettext("route: %s: no such interface\n"),
1816 newrt
->ri_ifp
.sdl
.sdl_family
= AF_LINK
;
1818 for (attempts
= 1; ; attempts
++) {
1820 if ((ret
= rtmsg(newrt
)) == 0)
1822 if (errno
!= ENETUNREACH
&& errno
!= ESRCH
)
1824 if ((newrt
->ri_addrs
& RTA_GATEWAY
) && hp
!= NULL
&&
1825 hp
->h_addr_list
[attempts
] != NULL
) {
1828 (void) memmove(&newrt
->ri_gate
.sin
.sin_addr
,
1829 hp
->h_addr_list
[attempts
], hp
->h_length
);
1832 (void) memmove(&newrt
->ri_gate
.sin6
.sin6_addr
,
1833 hp
->h_addr_list
[attempts
], hp
->h_length
);
1841 if (newrt
->ri_cmd
!= RTM_GET
) {
1842 print_rtcmd_short(stdout
, newrt
, (ret
== 0), B_FALSE
);
1844 (void) printf("\n");
1845 } else if (ret
!= 0) {
1847 * Note: there is nothing additional to print for get
1851 switch (newrt
->ri_af
) {
1853 (void) printf(" %s",
1854 inet_ntoa(newrt
->ri_dst
.sin
.sin_addr
));
1857 if (inet_ntop(AF_INET6
,
1858 (void *)&newrt
->ri_dst
.sin6
.sin6_addr
,
1859 obuf
, INET6_ADDRSTRLEN
) != NULL
) {
1860 (void) printf(" %s", obuf
);
1865 (void) printf("%s", newrt
->ri_dest_str
);
1869 (void) printf("%s", newrt
->ri_dest_str
);
1876 err
= "not in table";
1879 err
= "entry in use";
1882 err
= "routing table overflow";
1885 err
= "entry exists";
1888 err
= "insufficient privileges";
1891 err
= strerror(oerrno
);
1894 (void) printf(": %s\n", err
);
1897 del_rtcmd_irep(newrt
);
1905 * Convert a network number to the corresponding IP address.
1906 * If the RTA_NETMASK hasn't been specified yet set it based
1907 * on the class of address.
1910 inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
, struct sockaddr_in
*sin
)
1912 in_addr_t addr
, mask
;
1916 } else if (net
< 128) {
1917 addr
= net
<< IN_CLASSA_NSHIFT
;
1918 mask
= IN_CLASSA_NET
;
1919 } else if (net
< 65536) {
1920 addr
= net
<< IN_CLASSB_NSHIFT
;
1921 mask
= IN_CLASSB_NET
;
1922 } else if (net
< 16777216L) {
1923 addr
= net
<< IN_CLASSC_NSHIFT
;
1924 mask
= IN_CLASSC_NET
;
1927 if ((addr
& IN_CLASSA_HOST
) == 0)
1928 mask
= IN_CLASSA_NET
;
1929 else if ((addr
& IN_CLASSB_HOST
) == 0)
1930 mask
= IN_CLASSB_NET
;
1931 else if ((addr
& IN_CLASSC_HOST
) == 0)
1932 mask
= IN_CLASSC_NET
;
1934 if (IN_CLASSA(addr
))
1935 mask
= IN_CLASSA_NET
;
1936 else if (IN_CLASSB(addr
))
1937 mask
= IN_CLASSB_NET
;
1938 else if (IN_CLASSC(addr
))
1939 mask
= IN_CLASSC_NET
;
1941 mask
= IP_HOST_MASK
;
1942 mask
= inet_makesubnetmask(addr
, mask
);
1945 sin
->sin_addr
.s_addr
= htonl(addr
);
1947 /* Class E default mask is 32 */
1948 if (IN_CLASSE(addr
))
1949 mask
= IN_CLASSE_NET
;
1951 if (!(rcip
->ri_addrs
& RTA_NETMASK
)) {
1952 rcip
->ri_addrs
|= RTA_NETMASK
;
1953 sin
= &rcip
->ri_mask
.sin
;
1954 sin
->sin_addr
.s_addr
= htonl(mask
);
1955 sin
->sin_family
= AF_INET
;
1960 inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
)
1966 struct sockaddr_in
*sin
;
1971 in_addr_t if_addr
, if_mask
;
1972 in_addr_t if_subnetmask
= 0;
1977 if ((iosoc
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1978 quit("socket", errno
);
1979 if (ioctl(iosoc
, SIOCGIFNUM
, (char *)&numifs
) < 0)
1980 quit("ioctl", errno
);
1981 bufsize
= numifs
* sizeof (struct ifreq
);
1982 buf
= malloc(bufsize
);
1984 quit("malloc", errno
);
1985 (void) memset(&ifc
, 0, sizeof (ifc
));
1986 ifc
.ifc_len
= bufsize
;
1988 if (ioctl(iosoc
, SIOCGIFCONF
, (char *)&ifc
) < 0)
1989 quit("ioctl (get interface configuration)", errno
);
1990 /* Let's check to see if this is maybe a local subnet route. */
1992 for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
1995 sin
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1996 if_addr
= ntohl(sin
->sin_addr
.s_addr
);
1998 if (ioctl(iosoc
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0)
1999 quit("ioctl (get interface flags)", errno
);
2000 if ((ifreq
.ifr_flags
& IFF_UP
) == 0)
2002 if_flags
= ifreq
.ifr_flags
;
2004 if (ioctl(iosoc
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0)
2005 quit("ioctl (get netmask)", errno
);
2007 sin
= (struct sockaddr_in
*)&ifreq
.ifr_addr
;
2008 if_mask
= ntohl(sin
->sin_addr
.s_addr
);
2009 if ((if_addr
& mask
) == (addr
& mask
)) {
2011 * Don't trust pt-pt interfaces if there are
2014 if (if_flags
& IFF_POINTOPOINT
) {
2015 if_subnetmask
= if_mask
;
2019 * Fine. Just assume the same net mask as the
2020 * directly attached subnet interface is using.
2025 if (if_subnetmask
!= 0)
2026 return (if_subnetmask
);
2031 * Interpret an argument as a network address of some kind.
2033 * If the address family is one looked up in getaddr() using one of the
2034 * getipnodebyX() functions (currently only AF_INET6), then callers should
2035 * freehostent() the returned "struct hostent" pointer if one was passed in.
2037 * If exit_on_error is true, this function will cause route to exit on error by
2038 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
2042 getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
, addr_type_t atype
)
2045 struct hostent
**hpp
;
2049 if (which
== RTA_GATEWAY
) {
2050 hpp
= &(rcip
->ri_gate_hp
);
2056 rcip
->ri_addrs
|= which
;
2059 save_string(&rcip
->ri_dest_str
, s
);
2061 su
->sa
.sa_family
= rcip
->ri_af
;
2064 save_string(&rcip
->ri_gate_str
, s
);
2065 su
= &rcip
->ri_gate
;
2066 su
->sa
.sa_family
= rcip
->ri_af
;
2069 su
= &rcip
->ri_mask
;
2070 su
->sa
.sa_family
= rcip
->ri_af
;
2073 save_string(&rcip
->ri_ifp_str
, s
);
2076 * RTA_SRC has overloaded meaning. It can represent the
2077 * src address of incoming or outgoing packets.
2081 su
->sa
.sa_family
= rcip
->ri_af
;
2085 su
->sa
.sa_family
= rcip
->ri_af
;
2089 quit(gettext("Internal Error"), EINVAL
);
2092 if (strcmp(s
, "default") == 0) {
2093 if (which
== RTA_DST
) {
2094 return (getaddr(rcip
, RTA_NETMASK
, s
, ADDR_TYPE_NET
));
2096 if (which
== RTA_SRC
) {
2101 switch (rcip
->ri_af
) {
2103 link_addr(s
, &su
->sdl
);
2106 sockaddr(s
, &su
->sa
);
2111 if (!in6_getaddr(s
, &su
->sin6
, &masklen
, hpp
)) {
2114 if (masklen
!= NO_PREFIX
) {
2115 (void) memset(&rcip
->ri_mask
.sin6
.sin6_addr
, 0,
2116 sizeof (rcip
->ri_mask
.sin6
.sin6_addr
));
2117 if (!in_prefixlentomask(masklen
, IPV6_ABITS
,
2118 (uchar_t
*)&rcip
->ri_mask
.sin6
.sin6_addr
)) {
2119 syntax_error(gettext(
2120 "route: bad prefix length: %d\n"),
2124 rcip
->ri_mask
.sin6
.sin6_family
= rcip
->ri_af
;
2125 rcip
->ri_addrs
|= RTA_NETMASK
;
2131 return (in6_getaddr(s
, &su
->sin6
, NULL
, hpp
));
2134 gettext("route: -netmask not supported for IPv6: "
2135 "use <prefix>/<prefix-length> instead\n"));
2138 quit(gettext("Internal Error"), EINVAL
);
2144 if (!in_getaddr(s
, &su
->sin
, &masklen
, which
, hpp
,
2148 if (masklen
!= NO_PREFIX
) {
2149 (void) memset(&rcip
->ri_mask
.sin
.sin_addr
, 0,
2150 sizeof (rcip
->ri_mask
.sin
.sin_addr
));
2151 if (!in_prefixlentomask(masklen
, IP_ABITS
,
2152 (uchar_t
*)&rcip
->ri_mask
.sin
.sin_addr
)) {
2153 syntax_error(gettext(
2154 "route: bad prefix length: %d\n"),
2158 rcip
->ri_mask
.sin
.sin_family
= rcip
->ri_af
;
2159 rcip
->ri_addrs
|= RTA_NETMASK
;
2166 return (in_getaddr(s
, &su
->sin
, NULL
, which
, hpp
, atype
,
2169 quit(gettext("Internal Error"), EINVAL
);
2173 quit(gettext("Internal Error"), EINVAL
);
2180 * Interpret an argument as an IPv4 network address of some kind,
2181 * returning B_TRUE on success or B_FALSE on failure.
2182 * This function will cause an exit() on failure if exit_on_failure is set.
2184 * Note that this tries host interpretation before network interpretation,
2185 * except when -net has been given and the destination address is being parsed.
2187 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2188 * pass out <n> in *plenp.
2189 * If <n> doesn't parse return BAD_ADDR as *plenp.
2190 * If no /<n> is present return NO_PREFIX as *plenp.
2193 in_getaddr(char *s
, struct sockaddr_in
*sin
, int *plenp
, int which
,
2194 struct hostent
**hpp
, addr_type_t atype
, rtcmd_irep_t
*rcip
)
2201 (void) strlcpy(str
, s
, sizeof (str
));
2204 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2206 if (plenp
!= NULL
) {
2209 *plenp
= in_getprefixlen(str
, IP_ABITS
);
2210 if (*plenp
== BAD_ADDR
)
2212 cp
= strchr(str
, '/');
2215 } else if (strchr(str
, '/') != NULL
) {
2216 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2220 (void) memset(sin
, 0, sizeof (*sin
));
2221 sin
->sin_family
= AF_INET
;
2224 * Handle 255.255.255.255 as a special case first.
2226 if (strcmp(str
, "255.255.255.255") == 0) {
2227 sin
->sin_addr
.s_addr
= INADDR_BROADCAST
;
2231 val
= inet_addr(str
);
2232 if (val
!= (in_addr_t
)-1) {
2233 /* Numeric address */
2234 sin
->sin_addr
.s_addr
= val
;
2235 if (which
== RTA_DST
) {
2236 if (atype
== ADDR_TYPE_NET
||
2237 (atype
== ADDR_TYPE_ANY
&&
2238 inet_lnaof(sin
->sin_addr
) == INADDR_ANY
)) {
2239 /* This looks like a network address. */
2240 inet_makenetandmask(rcip
, ntohl(val
),
2246 /* Host or net name */
2247 if (which
!= RTA_DST
|| atype
!= ADDR_TYPE_NET
) {
2248 /* A host name is allowed. */
2249 if ((hp
= gethostbyname(str
)) != NULL
) {
2251 (void) memmove(&sin
->sin_addr
, hp
->h_addr
,
2256 if (atype
!= ADDR_TYPE_HOST
) {
2257 /* A network name is allowed */
2258 if ((np
= getnetbyname(str
)) != NULL
&&
2259 (val
= np
->n_net
) != 0) {
2260 if (which
== RTA_DST
) {
2261 inet_makenetandmask(rcip
, val
, sin
);
2266 syntax_error(gettext("%s: bad value\n"), s
);
2271 * Interpret an argument as an IPv6 network address of some kind,
2272 * returning B_TRUE on success or B_FALSE on failure.
2273 * This function will cause an exit() on failure if exit_on_failure is set.
2275 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2276 * pass out <n> in *plenp.
2277 * If <n> doesn't parse return BAD_ADDR as *plenp.
2278 * If no /<n> is present return NO_PREFIX as *plenp.
2281 in6_getaddr(char *s
, struct sockaddr_in6
*sin6
, int *plenp
,
2282 struct hostent
**hpp
)
2288 (void) strlcpy(str
, s
, sizeof (str
));
2291 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2293 if (plenp
!= NULL
) {
2296 *plenp
= in_getprefixlen(str
, IPV6_ABITS
);
2297 if (*plenp
== BAD_ADDR
)
2299 cp
= strchr(str
, '/');
2302 } else if (strchr(str
, '/') != NULL
) {
2303 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2307 (void) memset(sin6
, 0, sizeof (struct sockaddr_in6
));
2308 sin6
->sin6_family
= AF_INET6
;
2310 hp
= getipnodebyname(str
, AF_INET6
, 0, &error_num
);
2313 (void) memmove(&sin6
->sin6_addr
, hp
->h_addr
, hp
->h_length
);
2316 if (error_num
== TRY_AGAIN
) {
2318 * This isn't a problem if we aren't going to use the address
2321 if (!exit_on_error
) {
2324 syntax_error(gettext("route: %s: bad address (try "
2325 "again later)\n"), s
);
2328 syntax_error(gettext("route: %s: bad address\n"), s
);
2333 * Parse <addr>/<n> syntax and return the integer n.
2334 * If <addr> is missing or <n> is not a valid integer, this function calls
2335 * syntax_error() and returns BAD_ADDR.
2336 * if n is not between 0 and max_plen inclusive, this functions calls
2337 * syntax_error() and returns BAD_ADDR.
2338 * If /<n> is not present, this function returns NO_PREFIX.
2339 * The string addr is not modified.
2342 in_getprefixlen(char *addr
, int max_plen
)
2347 str
= strchr(addr
, '/');
2349 syntax_error(gettext("route: %s: unexpected '/'\n"), addr
);
2357 prefixlen
= strtoul(str
, &end
, 10);
2358 if (errno
!= 0 || str
== end
) {
2359 syntax_error(gettext("route: bad prefix length %s\n"), str
);
2362 if (prefixlen
> max_plen
) {
2363 syntax_error(gettext("route: prefix length %s out of range\n"),
2371 * Convert a prefix length to a mask.
2372 * Returns B_TRUE if ok. B_FALSE otherwise.
2373 * Assumes the mask array is zeroed by the caller.
2376 in_prefixlentomask(int prefixlen
, int maxlen
, uchar_t
*mask
)
2378 if (prefixlen
< 0 || prefixlen
> maxlen
)
2381 while (prefixlen
> 0) {
2382 if (prefixlen
>= 8) {
2387 *mask
|= 1 << (8 - prefixlen
);
2394 rtmonitor(int argc
, char *argv
[])
2397 intmax_t msg
[2048 / sizeof (intmax_t)];
2404 if (argc
== 2 && **argv
== '-') {
2405 switch (keyword(*argv
+ 1)) {
2423 s
= socket(PF_ROUTE
, SOCK_RAW
, af
);
2425 quit("socket", errno
);
2428 n
= read(s
, msg
, sizeof (msg
));
2430 quit("read", errno
);
2431 (void) printf("got message of size %d\n", n
);
2432 print_rtmsg((struct rt_msghdr
*)msg
, n
);
2437 rtmsg(rtcmd_irep_t
*newrt
)
2441 char *cp
= m_rtmsg
.m_space
;
2445 (void) memset(&m_rtmsg
, 0, sizeof (m_rtmsg
));
2447 if (newrt
->ri_cmd
== RTM_GET
) {
2448 newrt
->ri_ifp
.sa
.sa_family
= AF_LINK
;
2449 newrt
->ri_addrs
|= RTA_IFP
;
2452 #define rtm m_rtmsg.m_rtm
2453 rtm
.rtm_type
= newrt
->ri_cmd
;
2454 rtm
.rtm_flags
= newrt
->ri_flags
;
2455 rtm
.rtm_version
= RTM_VERSION
;
2456 rtm
.rtm_seq
= ++seq
;
2457 rtm
.rtm_addrs
= newrt
->ri_addrs
;
2458 rtm
.rtm_rmx
= newrt
->ri_metrics
;
2459 rtm
.rtm_inits
= newrt
->ri_inits
;
2461 #define NEXTADDR(w, u) \
2462 if (newrt->ri_addrs & (w)) { \
2463 l = ROUNDUP_LONG(salen(&u.sa)); \
2464 (void) memmove(cp, &(u), l); \
2469 NEXTADDR(RTA_DST
, newrt
->ri_dst
);
2470 NEXTADDR(RTA_GATEWAY
, newrt
->ri_gate
);
2471 NEXTADDR(RTA_NETMASK
, newrt
->ri_mask
);
2472 NEXTADDR(RTA_IFP
, newrt
->ri_ifp
);
2473 NEXTADDR(RTA_IFA
, newrt
->ri_ifa
);
2475 * RTA_SRC has overloaded meaning. It can represent the
2476 * src address of incoming or outgoing packets.
2478 NEXTADDR(RTA_SRC
, newrt
->ri_src
);
2481 if (newrt
->ri_rtsa_cnt
> 0) {
2482 /* LINTED: aligned */
2483 rtm_ext_t
*rtm_ext
= (rtm_ext_t
*)cp
;
2484 tsol_rtsecattr_t
*rtsecattr
;
2486 rtm_ext
->rtmex_type
= RTMEX_GATEWAY_SECATTR
;
2487 rtm_ext
->rtmex_len
= TSOL_RTSECATTR_SIZE(1);
2489 rtsecattr
= (tsol_rtsecattr_t
*)(rtm_ext
+ 1);
2490 rtsecattr
->rtsa_cnt
= 1;
2492 bcopy(&newrt
->ri_rtsa
, rtsecattr
->rtsa_attr
,
2493 sizeof (newrt
->ri_rtsa
));
2494 cp
= (char *)(rtsecattr
->rtsa_attr
+ 1);
2497 rtm
.rtm_msglen
= l
= cp
- (char *)&m_rtmsg
;
2500 print_rtmsg(&rtm
, l
);
2503 if ((rlen
= write(s
, (char *)&m_rtmsg
, l
)) < 0) {
2514 perror(gettext("writing to routing socket"));
2518 } else if (rlen
< (int)rtm
.rtm_msglen
) {
2519 (void) fprintf(stderr
,
2520 gettext("route: write to routing socket got only %d for "
2524 if (newrt
->ri_cmd
== RTM_GET
) {
2526 l
= read(s
, (char *)&m_rtmsg
, sizeof (m_rtmsg
));
2527 } while (l
> 0 && (rtm
.rtm_seq
!= seq
|| rtm
.rtm_pid
!= pid
));
2529 (void) fprintf(stderr
,
2530 gettext("route: read from routing socket: %s\n"),
2533 print_getmsg(newrt
, &rtm
, l
);
2540 static char *msgtypes
[] = {
2542 "RTM_ADD: Add Route",
2543 "RTM_DELETE: Delete Route",
2544 "RTM_CHANGE: Change Metrics or flags",
2545 "RTM_GET: Report Metrics",
2546 "RTM_LOSING: Kernel Suspects Partitioning",
2547 "RTM_REDIRECT: Told to use different route",
2548 "RTM_MISS: Lookup failed on this address",
2549 "RTM_LOCK: fix specified metrics",
2550 "RTM_OLDADD: caused by SIOCADDRT",
2551 "RTM_OLDDEL: caused by SIOCDELRT",
2552 "RTM_RESOLVE: Route created by cloning",
2553 "RTM_NEWADDR: address being brought up on iface",
2554 "RTM_DELADDR: address being brought down on iface",
2555 "RTM_IFINFO: iface status change",
2556 "RTM_CHGADDR: address being changed on iface",
2557 "RTM_FREEADDR: address being removed from iface",
2561 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2563 static char metricnames
[] =
2564 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2566 static char routeflags
[] =
2567 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2568 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2569 "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
2570 "\024KERNEL\025ZONE";
2571 static char ifnetflags
[] =
2572 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2573 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2574 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2575 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2576 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2577 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2578 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2580 static char addrnames
[] =
2581 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2584 print_rtmsg(struct rt_msghdr
*rtm
, int msglen
)
2586 struct if_msghdr
*ifm
;
2587 struct ifa_msghdr
*ifam
;
2591 if (rtm
->rtm_version
!= RTM_VERSION
) {
2592 (void) printf("routing message version %d not understood\n",
2596 if (rtm
->rtm_msglen
!= msglen
) {
2597 (void) printf("message length mismatch, in packet %d, "
2599 rtm
->rtm_msglen
, msglen
);
2600 if (msglen
> rtm
->rtm_msglen
)
2601 msglen
= rtm
->rtm_msglen
;
2604 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2605 * and the upper-bound of (NMSGTYPES - 1).
2607 if (rtm
->rtm_type
== 0 || rtm
->rtm_type
>= (NMSGTYPES
- 1)) {
2608 (void) printf("routing message type %d not understood\n",
2612 (void) printf("%s: len %d, ", msgtypes
[rtm
->rtm_type
], msglen
);
2613 switch (rtm
->rtm_type
) {
2615 ifm
= (struct if_msghdr
*)rtm
;
2616 (void) printf("if# %d, flags:", ifm
->ifm_index
);
2617 bprintf(stdout
, ifm
->ifm_flags
, ifnetflags
);
2618 pmsg_addrs((const char *)(ifm
+ 1), msglen
- sizeof (*ifm
),
2625 ifam
= (struct ifa_msghdr
*)rtm
;
2626 (void) printf("metric %d, flags:", ifam
->ifam_metric
);
2627 bprintf(stdout
, ifam
->ifam_flags
, routeflags
);
2628 pmsg_addrs((const char *)(ifam
+ 1), msglen
- sizeof (*ifam
),
2632 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2633 rtm
->rtm_pid
, rtm
->rtm_seq
, rtm
->rtm_errno
);
2634 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2635 pmsg_common(rtm
, msglen
);
2641 print_getmsg(rtcmd_irep_t
*req_rt
, struct rt_msghdr
*rtm
, int msglen
)
2643 struct sockaddr
*dst
= NULL
, *gate
= NULL
, *mask
= NULL
, *src
= NULL
;
2644 struct sockaddr_dl
*ifp
= NULL
;
2645 struct sockaddr
*sa
;
2649 (void) printf(" route to: %s\n", routename(&req_rt
->ri_dst
.sa
));
2650 if (rtm
->rtm_version
!= RTM_VERSION
) {
2651 (void) fprintf(stderr
,
2652 gettext("routing message version %d not understood\n"),
2656 if (rtm
->rtm_msglen
> (ushort_t
)msglen
) {
2657 (void) fprintf(stderr
,
2658 gettext("message length mismatch, in packet %d, "
2659 "returned %d\n"), rtm
->rtm_msglen
, msglen
);
2661 if (rtm
->rtm_errno
) {
2662 (void) fprintf(stderr
, "RTM_GET: %s (errno %d)\n",
2663 strerror(rtm
->rtm_errno
), rtm
->rtm_errno
);
2666 cp
= ((char *)(rtm
+ 1));
2667 if (rtm
->rtm_addrs
!= 0) {
2668 for (i
= 1; i
!= 0; i
<<= 1) {
2669 if (i
& rtm
->rtm_addrs
) {
2671 sa
= (struct sockaddr
*)cp
;
2683 if (sa
->sa_family
== AF_LINK
&&
2684 ((struct sockaddr_dl
*)sa
)->
2686 ifp
= (struct sockaddr_dl
*)sa
;
2696 if (dst
!= NULL
&& mask
!= NULL
)
2697 mask
->sa_family
= dst
->sa_family
; /* XXX */
2699 (void) printf("destination: %s\n", routename(dst
));
2701 boolean_t savenflag
= nflag
;
2704 (void) printf(" mask: %s\n", routename(mask
));
2707 if (gate
!= NULL
&& rtm
->rtm_flags
& RTF_GATEWAY
)
2708 (void) printf(" gateway: %s\n", routename(gate
));
2709 if (src
!= NULL
&& rtm
->rtm_flags
& RTF_SETSRC
)
2710 (void) printf(" setsrc: %s\n", routename(src
));
2715 (void) printf(" interface: %.*s index %d address ",
2716 ifp
->sdl_nlen
, ifp
->sdl_data
, ifp
->sdl_index
);
2717 for (i
= ifp
->sdl_nlen
;
2718 i
< ifp
->sdl_nlen
+ ifp
->sdl_alen
;
2720 (void) printf("%02x ",
2721 ifp
->sdl_data
[i
] & 0xFF);
2723 (void) printf("\n");
2725 (void) printf(" interface: %.*s\n",
2726 ifp
->sdl_nlen
, ifp
->sdl_data
);
2729 (void) printf(" flags: ");
2730 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2732 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2733 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2735 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2736 "rttvar,ms hopcount mtu expire");
2737 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_recvpipe
, lock(RPIPE
));
2738 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_sendpipe
, lock(SPIPE
));
2739 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_ssthresh
, lock(SSTHRESH
));
2740 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rtt
), lock(RTT
));
2741 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rttvar
), lock(RTTVAR
));
2742 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_hopcount
, lock(HOPCOUNT
));
2743 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_mtu
, lock(MTU
));
2744 if (rtm
->rtm_rmx
.rmx_expire
)
2745 rtm
->rtm_rmx
.rmx_expire
-= time(0);
2746 (void) printf("%8d%c", rtm
->rtm_rmx
.rmx_expire
, lock(EXPIRE
));
2750 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2752 pmsg_common(rtm
, msglen
);
2754 const char *sptr
, *endptr
;
2755 const struct sockaddr
*sa
;
2758 /* Not verbose; just print out the exceptional cases */
2759 if (rtm
->rtm_addrs
&~ RTA_IGN
) {
2760 (void) printf("\nsockaddrs: ");
2761 bprintf(stdout
, rtm
->rtm_addrs
, addrnames
);
2763 sptr
= (const char *)(rtm
+ 1);
2764 endptr
= (const char *)rtm
+ msglen
;
2765 addrs
= rtm
->rtm_addrs
;
2766 while (addrs
!= 0 && sptr
+ sizeof (*sa
) <= endptr
) {
2769 sa
= (const struct sockaddr
*)sptr
;
2773 pmsg_secattr(sptr
, endptr
- sptr
, " secattr: ");
2774 (void) putchar('\n');
2780 pmsg_common(const struct rt_msghdr
*rtm
, size_t msglen
)
2782 (void) printf("\nlocks: ");
2783 bprintf(stdout
, (int)rtm
->rtm_rmx
.rmx_locks
, metricnames
);
2784 (void) printf(" inits: ");
2785 bprintf(stdout
, (int)rtm
->rtm_inits
, metricnames
);
2786 pmsg_addrs((const char *)(rtm
+ 1), msglen
- sizeof (*rtm
),
2791 pmsg_addrs(const char *cp
, size_t msglen
, uint_t addrs
)
2793 const struct sockaddr
*sa
;
2798 (void) printf("\nsockaddrs: ");
2799 bprintf(stdout
, addrs
, addrnames
);
2800 (void) putchar('\n');
2801 maxptr
= cp
+ msglen
;
2802 for (i
= 1; i
!= 0 && cp
+ sizeof (*sa
) <= maxptr
; i
<<= 1) {
2805 sa
= (const struct sockaddr
*)cp
;
2806 (void) printf(" %s", routename(sa
));
2813 msglen
= maxptr
- cp
;
2815 pmsg_secattr(cp
, msglen
, "secattr: ");
2816 (void) putchar('\n');
2817 (void) fflush(stdout
);
2821 bprintf(FILE *fp
, int b
, char *s
)
2824 boolean_t gotsome
= B_FALSE
;
2828 while ((i
= *s
++) != 0) {
2829 if (b
& (1 << (i
- 1))) {
2836 for (; (i
= *s
) > ' '; s
++)
2844 (void) putc('>', fp
);
2848 keyword(const char *cp
)
2850 struct keytab
*kt
= keywords
;
2852 while (kt
->kt_cp
&& strcmp(kt
->kt_cp
, cp
))
2858 sodump(su_t
*su
, char *which
)
2860 static char obuf
[INET6_ADDRSTRLEN
];
2862 switch (su
->sa
.sa_family
) {
2864 (void) printf("%s: link %s; ",
2865 which
, link_ntoa(&su
->sdl
));
2868 (void) printf("%s: inet %s; ",
2869 which
, inet_ntoa(su
->sin
.sin_addr
));
2872 if (inet_ntop(AF_INET6
, (void *)&su
->sin6
.sin6_addr
, obuf
,
2873 INET6_ADDRSTRLEN
) != NULL
) {
2874 (void) printf("%s: inet6 %s; ", which
, obuf
);
2879 quit(gettext("Internal Error"), EINVAL
);
2882 (void) fflush(stdout
);
2894 #define LETTER (4*3)
2897 sockaddr(char *addr
, struct sockaddr
*sa
)
2899 char *cp
= (char *)sa
;
2900 int size
= salen(sa
);
2901 char *cplim
= cp
+ size
;
2902 int byte
= 0, state
= VIRGIN
, new;
2904 (void) memset(cp
, 0, size
);
2907 if ((*addr
>= '0') && (*addr
<= '9')) {
2909 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2910 new = *addr
- 'a' + 10;
2911 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2912 new = *addr
- 'A' + 10;
2913 } else if (*addr
== 0) {
2919 switch (state
/* | INPUT */) {
2920 case GOTTWO
| DIGIT
:
2923 case VIRGIN
| DIGIT
:
2924 state
= GOTONE
; byte
= new; continue;
2925 case GOTONE
| DIGIT
:
2926 state
= GOTTWO
; byte
= new + (byte
<< 4); continue;
2927 default: /* | DELIM */
2928 state
= VIRGIN
; *cp
++ = byte
; byte
= 0; continue;
2937 } while (cp
< cplim
);
2941 salen(const struct sockaddr
*sa
)
2943 switch (sa
->sa_family
) {
2945 return (sizeof (struct sockaddr_in
));
2947 return (sizeof (struct sockaddr_dl
));
2949 return (sizeof (struct sockaddr_in6
));
2951 return (sizeof (struct sockaddr
));
2956 link_addr(const char *addr
, struct sockaddr_dl
*sdl
)
2958 char *cp
= sdl
->sdl_data
;
2959 char *cplim
= sizeof (struct sockaddr_dl
) + (char *)sdl
;
2960 int byte
= 0, state
= VIRGIN
, new;
2962 (void) memset(sdl
, 0, sizeof (struct sockaddr_dl
));
2963 sdl
->sdl_family
= AF_LINK
;
2966 if ((*addr
>= '0') && (*addr
<= '9')) {
2968 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2969 new = *addr
- 'a' + 10;
2970 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2971 new = *addr
- 'A' + 10;
2972 } else if (*addr
== 0) {
2974 } else if (state
== VIRGIN
&&
2975 (((*addr
>= 'A') && (*addr
<= 'Z')) ||
2976 ((*addr
>= 'a') && (*addr
<= 'z')))) {
2982 switch (state
/* | INPUT */) {
2983 case VIRGIN
| DIGIT
:
2984 case VIRGIN
| LETTER
:
2987 case VIRGIN
| DELIM
:
2989 sdl
->sdl_nlen
= cp
- sdl
->sdl_data
;
2991 case GOTTWO
| DIGIT
:
2998 case GOTONE
| DIGIT
:
3000 byte
= new + (byte
<< 4);
3002 default: /* | DELIM */
3015 } while (cp
< cplim
);
3016 sdl
->sdl_alen
= cp
- LLADDR(sdl
);
3019 static char hexlist
[] = "0123456789abcdef";
3022 link_ntoa(const struct sockaddr_dl
*sdl
)
3024 static char obuf
[64];
3027 uchar_t
*in
= (uchar_t
*)LLADDR(sdl
);
3028 uchar_t
*inlim
= in
+ sdl
->sdl_alen
;
3029 boolean_t firsttime
= B_TRUE
;
3031 if (sdl
->sdl_nlen
) {
3032 (void) memcpy(obuf
, sdl
->sdl_data
, sdl
->sdl_nlen
);
3033 out
+= sdl
->sdl_nlen
;
3037 while (in
< inlim
) {
3039 firsttime
= B_FALSE
;
3044 out
[1] = hexlist
[i
& 0xf];
3046 out
[0] = hexlist
[i
];
3049 *out
++ = hexlist
[i
];
3059 intmax_t buf
[512 / sizeof (intmax_t)];
3062 struct strbuf ctlbuf
, databuf
;
3063 struct T_optmgmt_req
*tor
= (struct T_optmgmt_req
*)buf
;
3064 struct T_optmgmt_ack
*toa
= (struct T_optmgmt_ack
*)buf
;
3065 struct T_error_ack
*tea
= (struct T_error_ack
*)buf
;
3067 mib_item_t
*first_item
= NULL
;
3068 mib_item_t
*last_item
= NULL
;
3071 tor
->PRIM_type
= T_SVR4_OPTMGMT_REQ
;
3072 tor
->OPT_offset
= sizeof (struct T_optmgmt_req
);
3073 tor
->OPT_length
= sizeof (struct opthdr
);
3074 tor
->MGMT_flags
= T_CURRENT
;
3075 req
= (struct opthdr
*)&tor
[1];
3076 req
->level
= MIB2_IP
; /* any MIB2_xxx value ok here */
3080 ctlbuf
.buf
= (char *)buf
;
3081 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
3083 if (putmsg(sd
, &ctlbuf
, NULL
, flags
) < 0) {
3084 perror("mibget: putmsg (ctl)");
3088 * each reply consists of a ctl part for one fixed structure
3089 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3090 * containing an opthdr structure. level/name identify the entry,
3091 * len is the size of the data part of the message.
3093 req
= (struct opthdr
*)&toa
[1];
3094 ctlbuf
.maxlen
= sizeof (buf
);
3095 for (j
= 1; ; j
++) {
3097 getcode
= getmsg(sd
, &ctlbuf
, NULL
, &flags
);
3099 perror("mibget: getmsg (ctl)");
3101 (void) fprintf(stderr
,
3102 "# level name len\n");
3104 for (last_item
= first_item
; last_item
!= NULL
;
3105 last_item
= last_item
->next_item
) {
3106 (void) printf("%d %4ld %5ld %ld\n",
3107 ++i
, last_item
->group
,
3115 ctlbuf
.len
>= sizeof (struct T_optmgmt_ack
) &&
3116 toa
->PRIM_type
== T_OPTMGMT_ACK
&&
3117 toa
->MGMT_flags
== T_SUCCESS
&&
3120 (void) printf("mibget getmsg() %d returned EOD "
3121 "(level %lu, name %lu)\n", j
, req
->level
,
3124 return (first_item
); /* this is EOD msg */
3127 if (ctlbuf
.len
>= sizeof (struct T_error_ack
) &&
3128 tea
->PRIM_type
== T_ERROR_ACK
) {
3129 (void) fprintf(stderr
, gettext("mibget %d gives "
3130 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3131 "0x%lx\n"), j
, tea
->TLI_error
, tea
->UNIX_error
);
3132 errno
= (tea
->TLI_error
== TSYSERR
) ?
3133 tea
->UNIX_error
: EPROTO
;
3137 if (getcode
!= MOREDATA
||
3138 ctlbuf
.len
< sizeof (struct T_optmgmt_ack
) ||
3139 toa
->PRIM_type
!= T_OPTMGMT_ACK
||
3140 toa
->MGMT_flags
!= T_SUCCESS
) {
3141 (void) printf("mibget getmsg(ctl) %d returned %d, "
3142 "ctlbuf.len = %d, PRIM_type = %ld\n",
3143 j
, getcode
, ctlbuf
.len
, toa
->PRIM_type
);
3144 if (toa
->PRIM_type
== T_OPTMGMT_ACK
) {
3145 (void) printf("T_OPTMGMT_ACK: "
3146 "MGMT_flags = 0x%lx, req->len = %ld\n",
3147 toa
->MGMT_flags
, req
->len
);
3153 temp
= malloc(sizeof (mib_item_t
));
3155 perror("mibget: malloc");
3158 if (last_item
!= NULL
)
3159 last_item
->next_item
= temp
;
3163 last_item
->next_item
= NULL
;
3164 last_item
->group
= req
->level
;
3165 last_item
->mib_id
= req
->name
;
3166 last_item
->length
= req
->len
;
3167 last_item
->valp
= malloc(req
->len
);
3169 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3171 j
, last_item
->group
, last_item
->mib_id
,
3175 databuf
.maxlen
= last_item
->length
;
3176 databuf
.buf
= (char *)last_item
->valp
;
3179 getcode
= getmsg(sd
, NULL
, &databuf
, &flags
);
3181 perror("mibget: getmsg (data)");
3183 } else if (getcode
!= 0) {
3184 (void) printf("mibget getmsg(data) returned %d, "
3185 "databuf.maxlen = %d, databuf.len = %d\n",
3186 getcode
, databuf
.maxlen
, databuf
.len
);
3192 * On error, free all the allocated mib_item_t objects.
3194 while (first_item
!= NULL
) {
3195 last_item
= first_item
;
3196 first_item
= first_item
->next_item
;
3203 * print label security attributes for gateways.
3206 pmsg_secattr(const char *sptr
, size_t msglen
, const char *labelstr
)
3209 tsol_rtsecattr_t sp
;
3210 struct rtsa_s
*rtsa
= &sp
.rtsa_attr
[0];
3215 if (!is_system_labeled())
3218 endptr
= sptr
+ msglen
;
3221 if (sptr
+ sizeof (rtm_ext_t
) + sizeof (sp
) > endptr
)
3224 bcopy(sptr
, &rtm_ext
, sizeof (rtm_ext
));
3225 sptr
+= sizeof (rtm_ext
);
3226 if (rtm_ext
.rtmex_type
== RTMEX_GATEWAY_SECATTR
)
3228 sptr
+= rtm_ext
.rtmex_len
;
3231 /* bail if this entry is corrupt or overruns buffer length */
3232 if (rtm_ext
.rtmex_len
< sizeof (sp
) ||
3233 sptr
+ rtm_ext
.rtmex_len
> endptr
)
3236 /* run up just to the end of this extension */
3237 endptr
= sptr
+ rtm_ext
.rtmex_len
;
3239 bcopy(sptr
, &sp
, sizeof (sp
));
3240 sptr
+= sizeof (sp
);
3242 if (sptr
+ (sp
.rtsa_cnt
- 1) * sizeof (*rtsa
) != endptr
)
3245 for (i
= 0; i
< sp
.rtsa_cnt
; i
++) {
3247 /* first element is part of sp initalized above */
3248 bcopy(sptr
, rtsa
, sizeof (*rtsa
));
3249 sptr
+= sizeof (*rtsa
);
3251 (void) printf("\n%s%s", labelstr
, rtsa_to_str(rtsa
, buf
,