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>
82 static struct keytab
{
89 {"blackhole", K_BLACKHOLE
},
93 {"cloning", K_CLONING
},
103 {"gateway", K_GATEWAY
},
106 #define K_HOPCOUNT 12
107 {"hopcount", K_HOPCOUNT
},
120 #define K_INTERFACE 19
121 {"interface", K_INTERFACE
},
126 #define K_LOCKREST 22
127 {"lockrest", K_LOCKREST
},
131 {"monitor", K_MONITOR
},
137 {"netmask", K_NETMASK
},
138 #define K_NOSTATIC 28
139 {"nostatic", K_NOSTATIC
},
141 {"private", K_PRIVATE
},
143 {"proto1", K_PROTO1
},
145 {"proto2", K_PROTO2
},
146 #define K_RECVPIPE 32
147 {"recvpipe", K_RECVPIPE
},
149 {"reject", K_REJECT
},
153 {"rttvar", K_RTTVAR
},
156 #define K_SENDPIPE 37
157 {"sendpipe", K_SENDPIPE
},
158 #define K_SSTHRESH 38
159 {"ssthresh", K_SSTHRESH
},
161 {"static", K_STATIC
},
162 #define K_XRESOLVE 40
163 {"xresolve", K_XRESOLVE
},
165 {"setsrc", K_SETSRC
},
168 #define K_INDIRECT 44
169 {"indirect", K_INDIRECT
},
174 * Size of buffers used to hold command lines from the saved route file as
175 * well as error strings.
177 #define BUF_SIZE 2048
179 typedef union sockunion
{
181 struct sockaddr_in sin
;
182 struct sockaddr_dl sdl
;
183 struct sockaddr_in6 sin6
;
187 * This structure represents the digested information from parsing arguments
188 * to route add, change, delete, and get.
191 typedef struct rtcmd_irep
{
196 struct rt_metrics ri_metrics
;
202 struct hostent
*ri_gate_hp
;
210 typedef struct mib_item_s
{
211 struct mib_item_s
*next_item
;
230 static boolean_t
args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
,
232 static void bprintf(FILE *fp
, int b
, char *s
);
233 static boolean_t
compare_rtcmd(rtcmd_irep_t
*srch_rt
,
234 rtcmd_irep_t
*file_rt
);
235 static void delRouteEntry(mib2_ipRouteEntry_t
*rp
,
236 mib2_ipv6RouteEntry_t
*rp6
, int seqno
);
237 static void del_rtcmd_irep(rtcmd_irep_t
*rcip
);
238 static void flushroutes(int argc
, char *argv
[]);
239 static boolean_t
getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
,
241 static boolean_t
in6_getaddr(char *s
, struct sockaddr_in6
*sin6
,
242 int *plenp
, struct hostent
**hpp
);
243 static boolean_t
in_getaddr(char *s
, struct sockaddr_in
*sin
,
244 int *plenp
, int which
, struct hostent
**hpp
, addr_type_t atype
,
246 static int in_getprefixlen(char *addr
, int max_plen
);
247 static boolean_t
in_prefixlentomask(int prefixlen
, int maxlen
,
249 static void inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
,
250 struct sockaddr_in
*sin
);
251 static in_addr_t
inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
);
252 static int keyword(const char *cp
);
253 static void link_addr(const char *addr
, struct sockaddr_dl
*sdl
);
254 static char *link_ntoa(const struct sockaddr_dl
*sdl
);
255 static mib_item_t
*mibget(int sd
);
256 static char *netname(struct sockaddr
*sa
);
257 static int newroute(char **argv
);
258 static rtcmd_irep_t
*new_rtcmd_irep(void);
259 static void pmsg_addrs(const char *cp
, size_t len
, uint_t addrs
);
260 static void pmsg_common(const struct rt_msghdr
*rtm
, size_t len
);
261 static void print_getmsg(rtcmd_irep_t
*req_rt
,
262 struct rt_msghdr
*rtm
, int msglen
);
263 static void print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
,
264 boolean_t gw_good
, boolean_t to_saved
);
265 static void print_rtmsg(struct rt_msghdr
*rtm
, int msglen
);
266 static void quit(char *s
, int err
) __NORETURN
;
267 static char *routename(const struct sockaddr
*sa
);
268 static void rtmonitor(int argc
, char *argv
[]);
269 static int rtmsg(rtcmd_irep_t
*rcip
);
270 static int salen(const struct sockaddr
*sa
);
271 static void save_route(int argc
, char **argv
, int do_flush
);
272 static void save_string(char **dst
, char *src
);
273 static int search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
,
275 static void set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
,
277 static int show_saved_routes(int argc
);
278 static void sockaddr(char *addr
, struct sockaddr
*sa
);
279 static void sodump(su_t
*su
, char *which
);
280 static void syntax_arg_missing(char *keyword
);
281 static void syntax_bad_keyword(char *keyword
);
282 static void syntax_error(char *err
, ...);
283 static void usage(char *cp
);
284 static void write_to_rtfile(FILE *fp
, int argc
, char **argv
);
288 static boolean_t nflag
;
289 static int af
= AF_INET
;
290 static boolean_t qflag
, tflag
;
291 static boolean_t verbose
;
292 static boolean_t debugonly
;
293 static boolean_t fflag
;
294 static boolean_t update_table
;
295 static boolean_t perm_flag
;
296 static boolean_t early_v6_keyword
;
297 static char perm_file_sfx
[] = "/etc/inet/static_routes";
298 static char *perm_file
;
299 static char temp_file_sfx
[] = "/etc/inet/static_routes.tmp";
300 static char *temp_file
;
301 static struct in6_addr in6_host_mask
= { { { 0xff, 0xff, 0xff, 0xff, 0xff,
302 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } };
305 * This next variable indicates whether certain functions exit when an error
306 * is detected in the user input. Currently, exit_on_error is only set false
307 * in search_rtfile(), when argument are being parsed. Only those functions
308 * used by search_rtfile() to parse its arguments are designed to work in
309 * both modes. Take particular care in setting this false to ensure that any
310 * functions you call that might act on this flag properly return errors when
311 * exit_on_error is false.
313 static int exit_on_error
= B_TRUE
;
316 struct rt_msghdr m_rtm
;
317 char m_space
[BUF_SIZE
];
321 * Sizes of data structures extracted from the base mib.
322 * This allows the size of the tables entries to grow while preserving
323 * binary compatibility.
325 static int ipRouteEntrySize
;
326 static int ipv6RouteEntrySize
;
328 #define ROUNDUP_LONG(a) \
329 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
330 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
331 #define C(x) ((x) & 0xff)
334 * return values from in_getprefixlen()
336 #define BAD_ADDR -1 /* prefix is invalid */
337 #define NO_PREFIX -2 /* no prefix was found */
343 (void) fprintf(stderr
, gettext("route: botched keyword: %s\n"),
346 (void) fprintf(stderr
, gettext("usage: route [ -fnpqv ] "
347 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
354 syntax_error(char *err
, ...)
360 (void) vfprintf(stderr
, err
, args
);
368 syntax_bad_keyword(char *keyword
)
370 syntax_error(gettext("route: botched keyword: %s\n"), keyword
);
374 syntax_arg_missing(char *keyword
)
376 syntax_error(gettext("route: argument required following keyword %s\n"),
381 quit(char *s
, int sverrno
)
383 (void) fprintf(stderr
, "route: ");
385 (void) fprintf(stderr
, "%s: ", s
);
386 (void) fprintf(stderr
, "%s\n", strerror(sverrno
));
392 main(int argc
, char **argv
)
399 const char *root_dir
= NULL
;
401 (void) setlocale(LC_ALL
, "");
403 #if !defined(TEXT_DOMAIN)
404 #define TEXT_DOMAIN "SYS_TEST"
406 (void) textdomain(TEXT_DOMAIN
);
411 while ((ch
= getopt(argc
, argv
, "R:nqdtvfp")) != EOF
) {
448 s
= open("/dev/null", O_WRONLY
);
450 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
452 quit("socket", errno
);
455 * Handle the -p and -R flags. The -R flag only applies
456 * when the -p flag is set.
458 if (root_dir
== NULL
) {
459 perm_file
= perm_file_sfx
;
460 temp_file
= temp_file_sfx
;
462 size
= strlen(root_dir
) + sizeof (perm_file_sfx
);
463 perm_file
= malloc(size
);
464 if (perm_file
== NULL
)
465 quit("malloc", errno
);
466 (void) snprintf(perm_file
, size
, "%s%s", root_dir
,
468 size
= strlen(root_dir
) + sizeof (temp_file_sfx
);
469 temp_file
= malloc(size
);
470 if (temp_file
== NULL
)
471 quit("malloc", errno
);
472 (void) snprintf(temp_file
, size
, "%s%s", root_dir
,
476 * Whether or not to act on the routing table. The only time the
477 * routing table is not modified is when both -p and -R are present.
479 update_table
= (!perm_flag
|| root_dir
== NULL
);
485 * Accept an address family keyword after the -f. Since the
486 * default address family is AF_INET, reassign af only for the
487 * other valid address families.
490 switch (keyword(*argv
)) {
493 early_v6_keyword
= B_TRUE
;
496 /* Skip over the address family parameter. */
502 flushroutes(0, NULL
);
506 switch (keyword(*argv
)) {
513 rval
= newroute(argv
);
515 if (perm_flag
&& (rval
== 0 || rval
== EEXIST
||
517 save_route(argc
, argv
, B_FALSE
);
523 return (show_saved_routes(argc
));
525 syntax_error(gettext(
526 "route: show command requires -p\n"));
530 rtmonitor(argc
, argv
);
534 flushroutes(argc
, argv
);
544 * Purge all entries in the routing tables not
545 * associated with network interfaces.
548 flushroutes(int argc
, char *argv
[])
551 int sd
; /* mib stream */
553 mib2_ipRouteEntry_t
*rp
;
554 mib2_ipv6RouteEntry_t
*rp6
;
561 if (argc
== 2 && **argv
== '-') {
563 * The address family (preceded by a dash) may be used
564 * to flush the routes of that particular family.
566 switch (keyword(*argv
+ 1)) {
585 /* This flushes the persistent route file */
586 save_route(0, NULL
, B_TRUE
);
592 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&off
,
594 quit("setsockopt", errno
);
596 sd
= open("/dev/ip", O_RDWR
);
601 (void) fprintf(stderr
,
602 gettext("route: flush: insufficient privileges\n"));
606 quit(gettext("can't open mib stream"), oerrno
);
610 if ((item
= mibget(sd
)) == NULL
)
611 quit("mibget", errno
);
613 (void) printf("Examining routing table from "
614 "T_SVR4_OPTMGMT_REQ\n");
619 /* Extract ipRouteEntrySize */
620 for (; item
!= NULL
; item
= item
->next_item
) {
621 if (item
->mib_id
!= 0)
623 if (item
->group
== MIB2_IP
) {
625 ((mib2_ip_t
*)item
->valp
)->ipRouteEntrySize
;
626 assert(IS_P2ALIGNED(ipRouteEntrySize
,
627 sizeof (mib2_ipRouteEntry_t
*)));
631 if (ipRouteEntrySize
== 0) {
632 (void) fprintf(stderr
,
633 gettext("ipRouteEntrySize can't be determined.\n"));
636 for (; item
!= NULL
; item
= item
->next_item
) {
638 * skip all the other trash that comes up the mib stream
640 if (item
->group
!= MIB2_IP
||
641 item
->mib_id
!= MIB2_IP_ROUTE
)
643 for (rp
= (mib2_ipRouteEntry_t
*)item
->valp
;
644 (char *)rp
< (char *)item
->valp
+ item
->length
;
646 rp
= (mib2_ipRouteEntry_t
*)
647 ((char *)rp
+ ipRouteEntrySize
)) {
648 delRouteEntry(rp
, NULL
, seqno
);
655 /* Extract ipv6RouteEntrySize */
656 for (; item
!= NULL
; item
= item
->next_item
) {
657 if (item
->mib_id
!= 0)
659 if (item
->group
== MIB2_IP6
) {
661 ((mib2_ipv6IfStatsEntry_t
*)item
->valp
)->
663 assert(IS_P2ALIGNED(ipv6RouteEntrySize
,
664 sizeof (mib2_ipv6RouteEntry_t
*)));
668 if (ipv6RouteEntrySize
== 0) {
669 (void) fprintf(stderr
, gettext(
670 "ipv6RouteEntrySize cannot be determined.\n"));
673 for (; item
!= NULL
; item
= item
->next_item
) {
675 * skip all the other trash that comes up the mib stream
677 if (item
->group
!= MIB2_IP6
||
678 item
->mib_id
!= MIB2_IP6_ROUTE
)
680 for (rp6
= (mib2_ipv6RouteEntry_t
*)item
->valp
;
681 (char *)rp6
< (char *)item
->valp
+ item
->length
;
683 rp6
= (mib2_ipv6RouteEntry_t
*)
684 ((char *)rp6
+ ipv6RouteEntrySize
)) {
685 delRouteEntry(NULL
, rp6
, seqno
);
693 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&on
,
695 quit("setsockopt", errno
);
699 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
700 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
701 * order to facilitate the flushing of RTF_GATEWAY routes.
704 delRouteEntry(mib2_ipRouteEntry_t
*rp
, mib2_ipv6RouteEntry_t
*rp6
, int seqno
)
709 struct rt_msghdr
*rtm
;
710 struct sockaddr_in sin
;
711 struct sockaddr_in6 sin6
;
715 ire_type
= rp
->ipRouteInfo
.re_ire_type
;
717 ire_type
= rp6
->ipv6RouteInfo
.re_ire_type
;
718 if (ire_type
!= IRE_DEFAULT
&&
719 ire_type
!= IRE_PREFIX
&&
720 ire_type
!= IRE_HOST
&&
721 ire_type
!= IRE_HOST_REDIRECT
)
724 rtm
= &m_rtmsg
.m_rtm
;
725 (void) memset(rtm
, 0, sizeof (m_rtmsg
));
726 rtm
->rtm_type
= RTM_DELETE
;
727 rtm
->rtm_seq
= seqno
;
728 rtm
->rtm_flags
|= RTF_GATEWAY
;
729 rtm
->rtm_version
= RTM_VERSION
;
730 rtm
->rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
731 cp
= m_rtmsg
.m_space
;
733 slen
= sizeof (struct sockaddr_in
);
734 if (rp
->ipRouteMask
== IP_HOST_MASK
)
735 rtm
->rtm_flags
|= RTF_HOST
;
736 (void) memset(&sin
, 0, slen
);
737 sin
.sin_family
= AF_INET
;
738 sin
.sin_addr
.s_addr
= rp
->ipRouteDest
;
739 (void) memmove(cp
, &sin
, slen
);
741 sin
.sin_addr
.s_addr
= rp
->ipRouteNextHop
;
742 (void) memmove(cp
, &sin
, slen
);
744 sin
.sin_addr
.s_addr
= rp
->ipRouteMask
;
745 (void) memmove(cp
, &sin
, slen
);
748 slen
= sizeof (struct sockaddr_in6
);
749 if (rp6
->ipv6RoutePfxLength
== IPV6_ABITS
)
750 rtm
->rtm_flags
|= RTF_HOST
;
751 (void) memset(&sin6
, 0, slen
);
752 sin6
.sin6_family
= AF_INET6
;
753 sin6
.sin6_addr
= rp6
->ipv6RouteDest
;
754 (void) memmove(cp
, &sin6
, slen
);
756 sin6
.sin6_addr
= rp6
->ipv6RouteNextHop
;
757 (void) memmove(cp
, &sin6
, slen
);
759 (void) memset(&sin6
.sin6_addr
, 0, sizeof (sin6
.sin6_addr
));
760 (void) in_prefixlentomask(rp6
->ipv6RoutePfxLength
, IPV6_ABITS
,
761 (uchar_t
*)&sin6
.sin6_addr
.s6_addr
);
762 (void) memmove(cp
, &sin6
, slen
);
765 rtm
->rtm_msglen
= cp
- (char *)&m_rtmsg
;
768 * In debugonly mode, the routing socket message to delete the
769 * current entry is not actually sent. However if verbose is
770 * also set, the routing socket message that would have been
774 print_rtmsg(rtm
, rtm
->rtm_msglen
);
778 rlen
= write(s
, (char *)&m_rtmsg
, rtm
->rtm_msglen
);
779 if (rlen
< (int)rtm
->rtm_msglen
) {
781 (void) fprintf(stderr
,
782 gettext("route: write to routing socket: %s\n"),
785 (void) fprintf(stderr
, gettext("route: write to "
786 "routing socket got only %d for rlen\n"), rlen
);
792 * In quiet mode, nothing is printed at all (unless the write()
798 print_rtmsg(rtm
, rlen
);
800 struct sockaddr
*sa
= (struct sockaddr
*)(rtm
+ 1);
802 (void) printf("%-20.20s ",
803 rtm
->rtm_flags
& RTF_HOST
? routename(sa
) :
806 sa
= (struct sockaddr
*)(salen(sa
) + (char *)sa
);
807 (void) printf("%-20.20s ", routename(sa
));
808 (void) printf("done\n");
813 * Return the name of the host whose address is given.
816 routename(const struct sockaddr
*sa
)
819 static char line
[MAXHOSTNAMELEN
+ 1];
820 struct hostent
*hp
= NULL
;
821 static char domain
[MAXHOSTNAMELEN
+ 1];
822 static boolean_t first
= B_TRUE
;
831 if (gethostname(domain
, MAXHOSTNAMELEN
) == 0 &&
832 (cp
= strchr(domain
, '.')))
833 (void) strcpy(domain
, cp
+ 1);
838 if (salen(sa
) == 0) {
839 (void) strcpy(line
, "default");
842 switch (sa
->sa_family
) {
846 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
849 if (in
.s_addr
== INADDR_ANY
)
851 if (cp
== NULL
&& !nflag
) {
852 hp
= gethostbyaddr((char *)&in
, sizeof (struct in_addr
),
855 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
856 (strcmp(cp
+ 1, domain
) == 0))
862 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
863 line
[MAXHOSTNAMELEN
] = '\0';
865 in
.s_addr
= ntohl(in
.s_addr
);
866 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
867 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
873 return (link_ntoa((struct sockaddr_dl
*)sa
));
877 in6
= ((struct sockaddr_in6
*)sa
)->sin6_addr
;
880 if (IN6_IS_ADDR_UNSPECIFIED(&in6
))
882 if (cp
== NULL
&& !nflag
) {
883 hp
= getipnodebyaddr((char *)&in6
,
884 sizeof (struct in6_addr
), AF_INET6
, &error_num
);
886 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
887 (strcmp(cp
+ 1, domain
) == 0))
893 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
894 line
[MAXHOSTNAMELEN
] = '\0';
896 (void) inet_ntop(AF_INET6
, (void *)&in6
, line
,
907 slim
= s
+ ((salen(sa
) + 1) >> 1);
908 cp
= line
+ sprintf(line
, "(%d)", sa
->sa_family
);
910 while (++s
< slim
) /* start with sa->sa_data */
911 cp
+= sprintf(cp
, " %x", *s
);
918 * Return the name of the network whose address is given.
919 * The address is assumed to be that of a net or subnet, not a host.
922 netname(struct sockaddr
*sa
)
925 static char line
[MAXHOSTNAMELEN
+ 1];
933 switch (sa
->sa_family
) {
937 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
939 in
.s_addr
= ntohl(in
.s_addr
);
940 if (in
.s_addr
== INADDR_ANY
) {
943 if (IN_CLASSA(in
.s_addr
)) {
944 mask
= IN_CLASSA_NET
;
946 } else if (IN_CLASSB(in
.s_addr
)) {
947 mask
= IN_CLASSB_NET
;
950 mask
= IN_CLASSC_NET
;
954 * If there are more bits than the standard mask
955 * would suggest, subnets must be in use.
956 * Guess at the subnet mask, assuming reasonable
957 * width subnet fields.
959 while (in
.s_addr
&~ mask
)
960 mask
= (long)mask
>> subnetshift
;
961 net
= in
.s_addr
& mask
;
962 while ((mask
& 1) == 0)
963 mask
>>= 1, net
>>= 1;
964 np
= getnetbyaddr(net
, AF_INET
);
969 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
970 line
[MAXHOSTNAMELEN
] = '\0';
971 } else if ((in
.s_addr
& 0xffffff) == 0) {
972 (void) sprintf(line
, "%u", C(in
.s_addr
>> 24));
973 } else if ((in
.s_addr
& 0xffff) == 0) {
974 (void) sprintf(line
, "%u.%u", C(in
.s_addr
>> 24),
976 } else if ((in
.s_addr
& 0xff) == 0) {
977 (void) sprintf(line
, "%u.%u.%u", C(in
.s_addr
>> 24),
978 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8));
980 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
981 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
987 return (link_ntoa((struct sockaddr_dl
*)sa
));
990 return (routename(sa
));
994 s
= (ushort_t
*)sa
->sa_data
;
996 slim
= s
+ ((salen(sa
) + 1) >> 1);
997 cp
= line
+ sprintf(line
, "af %d:", sa
->sa_family
);
1000 cp
+= sprintf(cp
, " %x", *s
++);
1007 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1008 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1009 * or point to dynamically allocated memory.
1012 new_rtcmd_irep(void)
1016 rcip
= calloc(1, sizeof (rtcmd_irep_t
));
1018 quit("calloc", errno
);
1021 rcip
->ri_flags
= RTF_STATIC
;
1026 del_rtcmd_irep(rtcmd_irep_t
*rcip
)
1028 free(rcip
->ri_dest_str
);
1029 free(rcip
->ri_gate_str
);
1030 free(rcip
->ri_ifp_str
);
1032 * IPv6 host entries come from getipnodebyname, which dynamically
1033 * allocates memory. IPv4 host entries come from gethostbyname, which
1034 * returns static memory and cannot be freed with freehostent.
1036 if (rcip
->ri_gate_hp
!= NULL
&&
1037 rcip
->ri_gate_hp
->h_addrtype
== AF_INET6
)
1038 freehostent(rcip
->ri_gate_hp
);
1043 save_string(char **dst
, char *src
)
1048 quit("malloc", errno
);
1053 * Print the short form summary of a route command.
1054 * Eg. "add net default: gateway 10.0.0.1"
1055 * The final newline is not added, allowing the caller to append additional
1059 print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
, boolean_t gw_good
,
1063 char obuf
[INET6_ADDRSTRLEN
];
1065 switch (rcip
->ri_cmd
) {
1082 (void) fprintf(to
, "%s%s %s %s", cmd
,
1083 (to_saved
) ? " persistent" : "",
1084 (rcip
->ri_flags
& RTF_HOST
) ? "host" : "net",
1085 (rcip
->ri_dest_str
== NULL
) ? "NULL" : rcip
->ri_dest_str
);
1087 if (rcip
->ri_gate_str
!= NULL
) {
1088 switch (rcip
->ri_af
) {
1091 (void) fprintf(to
, ": gateway %s",
1092 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1093 } else if (gw_good
&&
1094 rcip
->ri_gate_hp
!= NULL
&&
1095 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1097 * Print the actual address used in the case
1098 * where there was more than one address
1099 * available for the name, and one was used
1102 (void) fprintf(to
, ": gateway %s (%s)",
1104 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1106 (void) fprintf(to
, ": gateway %s",
1111 if (inet_ntop(AF_INET6
,
1112 &rcip
->ri_gate
.sin6
.sin6_addr
, obuf
,
1113 INET6_ADDRSTRLEN
) != NULL
) {
1115 (void) fprintf(to
, ": gateway %s",
1120 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1121 (void) fprintf(to
, ": gateway %s (%s)",
1122 rcip
->ri_gate_str
, obuf
);
1128 (void) fprintf(to
, ": gateway %s",
1136 set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
, boolean_t lock
)
1139 uint_t noval
, *valp
= &noval
;
1142 #define caseof(x, y, z) \
1143 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1145 caseof(K_MTU
, RTV_MTU
, rmx_mtu
);
1146 caseof(K_HOPCOUNT
, RTV_HOPCOUNT
, rmx_hopcount
);
1147 caseof(K_EXPIRE
, RTV_EXPIRE
, rmx_expire
);
1148 caseof(K_RECVPIPE
, RTV_RPIPE
, rmx_recvpipe
);
1149 caseof(K_SENDPIPE
, RTV_SPIPE
, rmx_sendpipe
);
1150 caseof(K_SSTHRESH
, RTV_SSTHRESH
, rmx_ssthresh
);
1151 caseof(K_RTT
, RTV_RTT
, rmx_rtt
);
1152 caseof(K_RTTVAR
, RTV_RTTVAR
, rmx_rttvar
);
1155 rcip
->ri_inits
|= flag
;
1157 rcip
->ri_metrics
.rmx_locks
|= flag
;
1158 *valp
= atoi(value
);
1162 * Parse the options give in argv[], filling in rcip with the results.
1163 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1164 * tokenized to produce the command line. Cmd_string is tokenized using
1165 * strtok, which will overwrite whitespace in the string with nulls.
1167 * Returns B_TRUE on success and B_FALSE on failure.
1170 args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
, char *cmd_string
)
1172 const char *ws
= "\f\n\r\t\v ";
1173 char *tok
= cmd_string
;
1175 addr_type_t atype
= ADDR_TYPE_ANY
;
1176 boolean_t iflag
= B_FALSE
;
1177 boolean_t locknext
= B_FALSE
;
1178 boolean_t lockrest
= B_FALSE
;
1179 boolean_t dash_keyword
;
1183 if (cmd_string
== NULL
) {
1186 tok
= strtok(cmd_string
, ws
);
1190 * The command keywords are already fully checked by main() or
1195 rcip
->ri_cmd
= RTM_ADD
;
1198 rcip
->ri_cmd
= RTM_CHANGE
;
1201 rcip
->ri_cmd
= RTM_DELETE
;
1204 rcip
->ri_cmd
= RTM_GET
;
1208 quit(gettext("Internal Error"), EINVAL
);
1213 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1218 dash_keyword
= B_TRUE
;
1219 key
= keyword(tok
+ 1);
1221 dash_keyword
= B_FALSE
;
1223 if (key
!= K_HOST
&& key
!= K_NET
) {
1224 /* All others must be preceded by '-' */
1230 if (atype
== ADDR_TYPE_NET
) {
1231 syntax_error(gettext("route: -host and -net "
1232 "are mutually exclusive\n"));
1235 atype
= ADDR_TYPE_HOST
;
1238 if (atype
== ADDR_TYPE_HOST
) {
1239 syntax_error(gettext("route: -host and -net "
1240 "are mutually exclusive\n"));
1243 atype
= ADDR_TYPE_NET
;
1246 rcip
->ri_af
= AF_LINK
;
1249 rcip
->ri_af
= AF_INET
;
1252 rcip
->ri_af
= PF_ROUTE
;
1255 rcip
->ri_af
= AF_INET6
;
1262 rcip
->ri_flags
&= ~RTF_STATIC
;
1271 rcip
->ri_flags
|= RTF_REJECT
;
1274 rcip
->ri_flags
|= RTF_BLACKHOLE
;
1277 rcip
->ri_flags
|= RTF_PROTO1
;
1280 rcip
->ri_flags
|= RTF_PROTO2
;
1283 rcip
->ri_flags
|= RTF_CLONING
;
1286 rcip
->ri_flags
|= RTF_XRESOLVE
;
1289 rcip
->ri_flags
|= RTF_STATIC
;
1293 syntax_arg_missing(keyword_str
);
1296 if (!getaddr(rcip
, RTA_IFA
, tok
, atype
)) {
1302 syntax_arg_missing(keyword_str
);
1305 if (!getaddr(rcip
, RTA_IFP
, tok
, atype
)) {
1311 syntax_arg_missing(keyword_str
);
1314 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1320 syntax_arg_missing(keyword_str
);
1323 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1329 syntax_arg_missing(keyword_str
);
1332 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1335 atype
= ADDR_TYPE_NET
;
1346 syntax_arg_missing(keyword_str
);
1349 set_metric(rcip
, tok
, key
, locknext
|| lockrest
);
1353 rcip
->ri_flags
|= RTF_PRIVATE
;
1357 syntax_arg_missing(keyword_str
);
1360 if (!getaddr(rcip
, RTA_SRC
, tok
, atype
)) {
1363 rcip
->ri_flags
|= RTF_SETSRC
;
1366 rcip
->ri_flags
|= RTF_INDIRECT
;
1370 syntax_bad_keyword(tok
+ 1);
1373 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1374 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1377 } else if ((rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1379 * For the gateway parameter, retrieve the
1380 * pointer to the struct hostent so that all
1381 * possible addresses can be tried until one
1384 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1390 * Assume that a regular number is a metric.
1391 * Needed for compatibility with old route
1395 metric
= strtoul(tok
, &err
, 10);
1396 if (errno
== 0 && *err
== '\0' &&
1397 metric
< 0x80000000ul
) {
1398 iflag
= (metric
== 0);
1400 (void) printf("old usage of "
1401 "trailing number, assuming "
1402 "route %s\n", iflag
?
1403 "to if" : "via gateway");
1407 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1415 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1416 syntax_error(gettext("route: destination required\n"));
1418 } else if ((rcip
->ri_cmd
== RTM_ADD
|| rcip
->ri_cmd
== RTM_DELETE
) &&
1419 (rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1420 syntax_error(gettext(
1421 "route: gateway required for add or delete command\n"));
1426 rcip
->ri_flags
|= RTF_GATEWAY
;
1429 if (atype
!= ADDR_TYPE_NET
) {
1430 if (rcip
->ri_addrs
& RTA_NETMASK
) {
1432 * We know the netmask, so we can set the host flag
1433 * based on whether the netmask is the host netmask.
1435 if (rcip
->ri_af
== AF_INET
&&
1436 rcip
->ri_mask
.sin
.sin_addr
.s_addr
==
1438 rcip
->ri_flags
|= RTF_HOST
;
1440 if (rcip
->ri_af
== AF_INET6
&&
1441 memcmp(&rcip
->ri_mask
.sin6
.sin6_addr
,
1443 sizeof (struct in6_addr
)) == 0) {
1444 rcip
->ri_flags
|= RTF_HOST
;
1448 * If no prefix mask has been saved at this point, it
1449 * only makes sense to treat the destination address
1450 * as a host address.
1452 rcip
->ri_flags
|= RTF_HOST
;
1459 * This command always seeks to the end of the file prior to writing.
1462 write_to_rtfile(FILE *fp
, int argc
, char **argv
)
1464 char file_line
[BUF_SIZE
];
1469 if (early_v6_keyword
) {
1471 * This flag is set when "inet6" was seen as an
1472 * argument to the -f flag. Normally, when writing
1473 * routes to the persistent route file, everything on
1474 * the command line after "add" is saved verbatim.
1475 * In this case, the arguments after "add" may not be
1476 * sufficient, as the ipv6 keyword came before "add",
1477 * yet must be present in the persistent route file.
1479 len
+= snprintf(file_line
, BUF_SIZE
, "-inet6 ");
1481 for (i
= 0; argc
> 0 && len
< BUF_SIZE
; i
++, argc
--) {
1482 len
+= snprintf(&file_line
[len
], BUF_SIZE
- len
, "%s ",
1485 if (len
>= BUF_SIZE
)
1486 quit(gettext("Internal Error"), EINVAL
);
1487 file_line
[len
- 1] = '\n';
1488 if (fseek(fp
, 0, SEEK_END
) != 0 ||
1489 fputs(file_line
, fp
) == EOF
) {
1490 quit(gettext("failed to write to route file"),
1496 compare_rtcmd(rtcmd_irep_t
*srch_rt
, rtcmd_irep_t
*file_rt
)
1498 if (strcmp(srch_rt
->ri_dest_str
, file_rt
->ri_dest_str
) != 0 ||
1499 memcmp(&srch_rt
->ri_mask
, &file_rt
->ri_mask
, sizeof (su_t
)) != 0) {
1502 return (srch_rt
->ri_gate_str
== NULL
||
1503 strcmp(srch_rt
->ri_gate_str
, file_rt
->ri_gate_str
) == 0);
1507 * Search the route file for routes matching the supplied route. There are 3
1508 * modes of operation:
1509 * SEARCH_MODE_RET - no side effects.
1510 * SEARCH_MODE_PRINT - prints each matching line.
1511 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1513 * In all cases, the number of matches is returned. If rt is NULL, all routes
1514 * matching the global af value are considered matching.
1517 search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
, search_mode_t mode
)
1522 char file_line
[BUF_SIZE
+ 4] = "add ";
1523 rtcmd_irep_t
*thisrt
;
1528 * Leave space at the beginning of file_line for "add ".
1530 while (fgets(file_line
+ 4, BUF_SIZE
, fp
) != NULL
) {
1532 if (file_line
[4] == '#' || file_line
[4] == '\n') {
1533 /* Handle comments and blank lines */
1534 if (mode
== SEARCH_MODE_DEL
&&
1535 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1537 "route: failed to write to temp file"),
1542 thisrt
= new_rtcmd_irep();
1544 * thisrt->ri_af defaults to whatever address family happens
1545 * to be set in the global af, but routes in the persistent
1546 * route file must be treated as AF_INET by default.
1548 thisrt
->ri_af
= AF_INET
;
1550 exit_on_error
= B_FALSE
;
1551 tmp_buf
= strdup(file_line
);
1552 /* args_to_rtcmd() will mangle the string passed. */
1553 if (!args_to_rtcmd(thisrt
, NULL
, tmp_buf
)) {
1554 /* There was an error in args_to_rtcmd() or helpers */
1555 del_rtcmd_irep(thisrt
);
1559 exit_on_error
= B_TRUE
;
1562 if (thisrt
->ri_gate_str
== NULL
) {
1563 del_rtcmd_irep(thisrt
);
1566 match
= (rt
== NULL
) ? (thisrt
->ri_af
== af
) :
1567 compare_rtcmd(rt
, thisrt
);
1569 if (match
) match_cnt
++;
1570 if (match
&& mode
== SEARCH_MODE_PRINT
) {
1571 (void) printf("persistent: route %s", file_line
);
1573 if (match
&& mode
== SEARCH_MODE_DEL
) {
1574 thisrt
->ri_cmd
= RTM_DELETE
;
1575 print_rtcmd_short(stdout
, thisrt
, B_FALSE
, B_TRUE
);
1576 (void) printf("\n");
1578 del_rtcmd_irep(thisrt
);
1580 if (!match
&& mode
== SEARCH_MODE_DEL
&&
1581 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1582 quit(gettext("failed to write to temp file"),
1590 * Perform the route operation given in argv on the persistent route file.
1591 * If do_flush is set, the persistent route file is flushed of all routes
1592 * matching the global family, and the arguments are ignored.
1595 save_route(int argc
, char **argv
, int do_flush
)
1601 mode_t fmode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1604 const char commentstr
[] =
1605 "# File generated by route(1M) - do not edit.\n";
1607 perm_fd
= open(perm_file
, O_RDWR
| O_CREAT
, fmode
);
1608 if (perm_fd
== -1 || fstat(perm_fd
, &st
) == -1)
1609 quit("failed to open route file", errno
);
1611 lock
.l_type
= F_WRLCK
;
1612 lock
.l_whence
= SEEK_SET
;
1615 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1616 quit(gettext("failed to lock route file"), errno
);
1619 if (st
.st_size
== 0 &&
1620 write(perm_fd
, commentstr
, sizeof (commentstr
) - 1) !=
1621 sizeof (commentstr
) - 1)
1622 quit(gettext("failed to open route file"), errno
);
1624 if ((perm_fp
= fdopen(perm_fd
, "r+")) == NULL
) {
1625 quit(gettext("failed to open route file"), errno
);
1630 rt
= new_rtcmd_irep();
1631 (void) args_to_rtcmd(rt
, argv
, NULL
);
1633 if (do_flush
|| rt
->ri_cmd
== RTM_DELETE
) {
1634 if ((temp_fp
= fopen(temp_file
, "w")) == NULL
) {
1635 quit(gettext("failed to open temp file"), errno
);
1640 (void) search_rtfile(perm_fp
, temp_fp
, NULL
, SEARCH_MODE_DEL
);
1641 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1642 quit(gettext("failed to update route file"), errno
);
1645 (void) fclose(perm_fp
);
1649 switch (rt
->ri_cmd
) {
1651 if (search_rtfile(perm_fp
, NULL
, rt
, SEARCH_MODE_NULL
) > 0) {
1652 /* Route is already in the file */
1653 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1654 (void) fprintf(stderr
, ": entry exists\n");
1657 write_to_rtfile(perm_fp
, argc
- 1, argv
+ 1);
1658 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1659 (void) printf("\n");
1664 gettext("route: change command not supported with -p\n"));
1668 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_DEL
) <= 0) {
1669 /* Route not found */
1670 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1671 (void) fprintf(stderr
, gettext(": not in file\n"));
1674 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1675 quit(gettext("failed to update route file"), errno
);
1681 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_PRINT
) <=
1683 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1684 (void) printf(gettext(": not in file\n"));
1689 quit(gettext("Internal Error"), EINVAL
);
1694 * Closing the file unlocks it.
1696 (void) fclose(perm_fp
);
1700 show_saved_routes(int argc
)
1708 syntax_error(gettext("route: invalid arguments for show\n"));
1711 perm_fd
= open(perm_file
, O_RDONLY
, 0);
1713 if (perm_fd
== -1) {
1714 if (errno
== ENOENT
) {
1715 (void) printf("No persistent routes are defined\n");
1718 quit(gettext("failed to open route file"), errno
);
1721 lock
.l_type
= F_RDLCK
;
1722 lock
.l_whence
= SEEK_SET
;
1725 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1726 quit(gettext("failed to lock route file"),
1730 if ((perm_fp
= fdopen(perm_fd
, "r")) == NULL
) {
1731 quit(gettext("failed to open route file"), errno
);
1734 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1735 (void) fseek(perm_fp
, 0, SEEK_SET
);
1737 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1740 (void) printf("No persistent routes are defined\n");
1742 (void) fclose(perm_fp
);
1747 newroute(char **argv
)
1749 rtcmd_irep_t
*newrt
;
1750 int ret
, attempts
, oerrno
;
1752 char obuf
[INET6_ADDRSTRLEN
];
1753 #define hp (newrt->ri_gate_hp)
1755 newrt
= new_rtcmd_irep();
1756 (void) args_to_rtcmd(newrt
, argv
, NULL
);
1758 if (newrt
->ri_cmd
!= RTM_GET
&& !tflag
) {
1759 /* Don't want to read back our messages */
1760 (void) shutdown(s
, 0);
1762 if (newrt
->ri_addrs
& RTA_IFP
) {
1763 newrt
->ri_ifp
.sdl
.sdl_index
= if_nametoindex(newrt
->ri_ifp_str
);
1764 if (newrt
->ri_ifp
.sdl
.sdl_index
== 0) {
1765 if (errno
!= ENXIO
) {
1766 quit("if_nametoindex", errno
);
1768 (void) fprintf(stderr
,
1769 gettext("route: %s: no such interface\n"),
1774 newrt
->ri_ifp
.sdl
.sdl_family
= AF_LINK
;
1776 for (attempts
= 1; ; attempts
++) {
1778 if ((ret
= rtmsg(newrt
)) == 0)
1780 if (errno
!= ENETUNREACH
&& errno
!= ESRCH
)
1782 if ((newrt
->ri_addrs
& RTA_GATEWAY
) && hp
!= NULL
&&
1783 hp
->h_addr_list
[attempts
] != NULL
) {
1786 (void) memmove(&newrt
->ri_gate
.sin
.sin_addr
,
1787 hp
->h_addr_list
[attempts
], hp
->h_length
);
1790 (void) memmove(&newrt
->ri_gate
.sin6
.sin6_addr
,
1791 hp
->h_addr_list
[attempts
], hp
->h_length
);
1799 if (newrt
->ri_cmd
!= RTM_GET
) {
1800 print_rtcmd_short(stdout
, newrt
, (ret
== 0), B_FALSE
);
1802 (void) printf("\n");
1803 } else if (ret
!= 0) {
1805 * Note: there is nothing additional to print for get
1809 switch (newrt
->ri_af
) {
1811 (void) printf(" %s",
1812 inet_ntoa(newrt
->ri_dst
.sin
.sin_addr
));
1815 if (inet_ntop(AF_INET6
,
1816 (void *)&newrt
->ri_dst
.sin6
.sin6_addr
,
1817 obuf
, INET6_ADDRSTRLEN
) != NULL
) {
1818 (void) printf(" %s", obuf
);
1823 (void) printf("%s", newrt
->ri_dest_str
);
1827 (void) printf("%s", newrt
->ri_dest_str
);
1834 err
= "not in table";
1837 err
= "entry in use";
1840 err
= "routing table overflow";
1843 err
= "entry exists";
1846 err
= "insufficient privileges";
1849 err
= strerror(oerrno
);
1852 (void) printf(": %s\n", err
);
1855 del_rtcmd_irep(newrt
);
1863 * Convert a network number to the corresponding IP address.
1864 * If the RTA_NETMASK hasn't been specified yet set it based
1865 * on the class of address.
1868 inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
, struct sockaddr_in
*sin
)
1870 in_addr_t addr
, mask
;
1874 } else if (net
< 128) {
1875 addr
= net
<< IN_CLASSA_NSHIFT
;
1876 mask
= IN_CLASSA_NET
;
1877 } else if (net
< 65536) {
1878 addr
= net
<< IN_CLASSB_NSHIFT
;
1879 mask
= IN_CLASSB_NET
;
1880 } else if (net
< 16777216L) {
1881 addr
= net
<< IN_CLASSC_NSHIFT
;
1882 mask
= IN_CLASSC_NET
;
1885 if ((addr
& IN_CLASSA_HOST
) == 0)
1886 mask
= IN_CLASSA_NET
;
1887 else if ((addr
& IN_CLASSB_HOST
) == 0)
1888 mask
= IN_CLASSB_NET
;
1889 else if ((addr
& IN_CLASSC_HOST
) == 0)
1890 mask
= IN_CLASSC_NET
;
1892 if (IN_CLASSA(addr
))
1893 mask
= IN_CLASSA_NET
;
1894 else if (IN_CLASSB(addr
))
1895 mask
= IN_CLASSB_NET
;
1896 else if (IN_CLASSC(addr
))
1897 mask
= IN_CLASSC_NET
;
1899 mask
= IP_HOST_MASK
;
1900 mask
= inet_makesubnetmask(addr
, mask
);
1903 sin
->sin_addr
.s_addr
= htonl(addr
);
1905 /* Class E default mask is 32 */
1906 if (IN_CLASSE(addr
))
1907 mask
= IN_CLASSE_NET
;
1909 if (!(rcip
->ri_addrs
& RTA_NETMASK
)) {
1910 rcip
->ri_addrs
|= RTA_NETMASK
;
1911 sin
= &rcip
->ri_mask
.sin
;
1912 sin
->sin_addr
.s_addr
= htonl(mask
);
1913 sin
->sin_family
= AF_INET
;
1918 inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
)
1924 struct sockaddr_in
*sin
;
1929 in_addr_t if_addr
, if_mask
;
1930 in_addr_t if_subnetmask
= 0;
1935 if ((iosoc
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1936 quit("socket", errno
);
1937 if (ioctl(iosoc
, SIOCGIFNUM
, (char *)&numifs
) < 0)
1938 quit("ioctl", errno
);
1939 bufsize
= numifs
* sizeof (struct ifreq
);
1940 buf
= malloc(bufsize
);
1942 quit("malloc", errno
);
1943 (void) memset(&ifc
, 0, sizeof (ifc
));
1944 ifc
.ifc_len
= bufsize
;
1946 if (ioctl(iosoc
, SIOCGIFCONF
, (char *)&ifc
) < 0)
1947 quit("ioctl (get interface configuration)", errno
);
1948 /* Let's check to see if this is maybe a local subnet route. */
1950 for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
1953 sin
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1954 if_addr
= ntohl(sin
->sin_addr
.s_addr
);
1956 if (ioctl(iosoc
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0)
1957 quit("ioctl (get interface flags)", errno
);
1958 if ((ifreq
.ifr_flags
& IFF_UP
) == 0)
1960 if_flags
= ifreq
.ifr_flags
;
1962 if (ioctl(iosoc
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0)
1963 quit("ioctl (get netmask)", errno
);
1965 sin
= (struct sockaddr_in
*)&ifreq
.ifr_addr
;
1966 if_mask
= ntohl(sin
->sin_addr
.s_addr
);
1967 if ((if_addr
& mask
) == (addr
& mask
)) {
1969 * Don't trust pt-pt interfaces if there are
1972 if (if_flags
& IFF_POINTOPOINT
) {
1973 if_subnetmask
= if_mask
;
1977 * Fine. Just assume the same net mask as the
1978 * directly attached subnet interface is using.
1983 if (if_subnetmask
!= 0)
1984 return (if_subnetmask
);
1989 * Interpret an argument as a network address of some kind.
1991 * If the address family is one looked up in getaddr() using one of the
1992 * getipnodebyX() functions (currently only AF_INET6), then callers should
1993 * freehostent() the returned "struct hostent" pointer if one was passed in.
1995 * If exit_on_error is true, this function will cause route to exit on error by
1996 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
2000 getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
, addr_type_t atype
)
2003 struct hostent
**hpp
;
2007 if (which
== RTA_GATEWAY
) {
2008 hpp
= &(rcip
->ri_gate_hp
);
2014 rcip
->ri_addrs
|= which
;
2017 save_string(&rcip
->ri_dest_str
, s
);
2019 su
->sa
.sa_family
= rcip
->ri_af
;
2022 save_string(&rcip
->ri_gate_str
, s
);
2023 su
= &rcip
->ri_gate
;
2024 su
->sa
.sa_family
= rcip
->ri_af
;
2027 su
= &rcip
->ri_mask
;
2028 su
->sa
.sa_family
= rcip
->ri_af
;
2031 save_string(&rcip
->ri_ifp_str
, s
);
2034 * RTA_SRC has overloaded meaning. It can represent the
2035 * src address of incoming or outgoing packets.
2039 su
->sa
.sa_family
= rcip
->ri_af
;
2043 su
->sa
.sa_family
= rcip
->ri_af
;
2047 quit(gettext("Internal Error"), EINVAL
);
2050 if (strcmp(s
, "default") == 0) {
2051 if (which
== RTA_DST
) {
2052 return (getaddr(rcip
, RTA_NETMASK
, s
, ADDR_TYPE_NET
));
2054 if (which
== RTA_SRC
) {
2059 switch (rcip
->ri_af
) {
2061 link_addr(s
, &su
->sdl
);
2064 sockaddr(s
, &su
->sa
);
2069 if (!in6_getaddr(s
, &su
->sin6
, &masklen
, hpp
)) {
2072 if (masklen
!= NO_PREFIX
) {
2073 (void) memset(&rcip
->ri_mask
.sin6
.sin6_addr
, 0,
2074 sizeof (rcip
->ri_mask
.sin6
.sin6_addr
));
2075 if (!in_prefixlentomask(masklen
, IPV6_ABITS
,
2076 (uchar_t
*)&rcip
->ri_mask
.sin6
.sin6_addr
)) {
2077 syntax_error(gettext(
2078 "route: bad prefix length: %d\n"),
2082 rcip
->ri_mask
.sin6
.sin6_family
= rcip
->ri_af
;
2083 rcip
->ri_addrs
|= RTA_NETMASK
;
2089 return (in6_getaddr(s
, &su
->sin6
, NULL
, hpp
));
2092 gettext("route: -netmask not supported for IPv6: "
2093 "use <prefix>/<prefix-length> instead\n"));
2096 quit(gettext("Internal Error"), EINVAL
);
2102 if (!in_getaddr(s
, &su
->sin
, &masklen
, which
, hpp
,
2106 if (masklen
!= NO_PREFIX
) {
2107 (void) memset(&rcip
->ri_mask
.sin
.sin_addr
, 0,
2108 sizeof (rcip
->ri_mask
.sin
.sin_addr
));
2109 if (!in_prefixlentomask(masklen
, IP_ABITS
,
2110 (uchar_t
*)&rcip
->ri_mask
.sin
.sin_addr
)) {
2111 syntax_error(gettext(
2112 "route: bad prefix length: %d\n"),
2116 rcip
->ri_mask
.sin
.sin_family
= rcip
->ri_af
;
2117 rcip
->ri_addrs
|= RTA_NETMASK
;
2124 return (in_getaddr(s
, &su
->sin
, NULL
, which
, hpp
, atype
,
2127 quit(gettext("Internal Error"), EINVAL
);
2131 quit(gettext("Internal Error"), EINVAL
);
2138 * Interpret an argument as an IPv4 network address of some kind,
2139 * returning B_TRUE on success or B_FALSE on failure.
2140 * This function will cause an exit() on failure if exit_on_failure is set.
2142 * Note that this tries host interpretation before network interpretation,
2143 * except when -net has been given and the destination address is being parsed.
2145 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2146 * pass out <n> in *plenp.
2147 * If <n> doesn't parse return BAD_ADDR as *plenp.
2148 * If no /<n> is present return NO_PREFIX as *plenp.
2151 in_getaddr(char *s
, struct sockaddr_in
*sin
, int *plenp
, int which
,
2152 struct hostent
**hpp
, addr_type_t atype
, rtcmd_irep_t
*rcip
)
2159 (void) strlcpy(str
, s
, sizeof (str
));
2162 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2164 if (plenp
!= NULL
) {
2167 *plenp
= in_getprefixlen(str
, IP_ABITS
);
2168 if (*plenp
== BAD_ADDR
)
2170 cp
= strchr(str
, '/');
2173 } else if (strchr(str
, '/') != NULL
) {
2174 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2178 (void) memset(sin
, 0, sizeof (*sin
));
2179 sin
->sin_family
= AF_INET
;
2182 * Handle 255.255.255.255 as a special case first.
2184 if (strcmp(str
, "255.255.255.255") == 0) {
2185 sin
->sin_addr
.s_addr
= INADDR_BROADCAST
;
2189 val
= inet_addr(str
);
2190 if (val
!= (in_addr_t
)-1) {
2191 /* Numeric address */
2192 sin
->sin_addr
.s_addr
= val
;
2193 if (which
== RTA_DST
) {
2194 if (atype
== ADDR_TYPE_NET
||
2195 (atype
== ADDR_TYPE_ANY
&&
2196 inet_lnaof(sin
->sin_addr
) == INADDR_ANY
)) {
2197 /* This looks like a network address. */
2198 inet_makenetandmask(rcip
, ntohl(val
),
2204 /* Host or net name */
2205 if (which
!= RTA_DST
|| atype
!= ADDR_TYPE_NET
) {
2206 /* A host name is allowed. */
2207 if ((hp
= gethostbyname(str
)) != NULL
) {
2209 (void) memmove(&sin
->sin_addr
, hp
->h_addr
,
2214 if (atype
!= ADDR_TYPE_HOST
) {
2215 /* A network name is allowed */
2216 if ((np
= getnetbyname(str
)) != NULL
&&
2217 (val
= np
->n_net
) != 0) {
2218 if (which
== RTA_DST
) {
2219 inet_makenetandmask(rcip
, val
, sin
);
2224 syntax_error(gettext("%s: bad value\n"), s
);
2229 * Interpret an argument as an IPv6 network address of some kind,
2230 * returning B_TRUE on success or B_FALSE on failure.
2231 * This function will cause an exit() on failure if exit_on_failure is set.
2233 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2234 * pass out <n> in *plenp.
2235 * If <n> doesn't parse return BAD_ADDR as *plenp.
2236 * If no /<n> is present return NO_PREFIX as *plenp.
2239 in6_getaddr(char *s
, struct sockaddr_in6
*sin6
, int *plenp
,
2240 struct hostent
**hpp
)
2246 (void) strlcpy(str
, s
, sizeof (str
));
2249 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2251 if (plenp
!= NULL
) {
2254 *plenp
= in_getprefixlen(str
, IPV6_ABITS
);
2255 if (*plenp
== BAD_ADDR
)
2257 cp
= strchr(str
, '/');
2260 } else if (strchr(str
, '/') != NULL
) {
2261 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2265 (void) memset(sin6
, 0, sizeof (struct sockaddr_in6
));
2266 sin6
->sin6_family
= AF_INET6
;
2268 hp
= getipnodebyname(str
, AF_INET6
, 0, &error_num
);
2271 (void) memmove(&sin6
->sin6_addr
, hp
->h_addr
, hp
->h_length
);
2274 if (error_num
== TRY_AGAIN
) {
2276 * This isn't a problem if we aren't going to use the address
2279 if (!exit_on_error
) {
2282 syntax_error(gettext("route: %s: bad address (try "
2283 "again later)\n"), s
);
2286 syntax_error(gettext("route: %s: bad address\n"), s
);
2291 * Parse <addr>/<n> syntax and return the integer n.
2292 * If <addr> is missing or <n> is not a valid integer, this function calls
2293 * syntax_error() and returns BAD_ADDR.
2294 * if n is not between 0 and max_plen inclusive, this functions calls
2295 * syntax_error() and returns BAD_ADDR.
2296 * If /<n> is not present, this function returns NO_PREFIX.
2297 * The string addr is not modified.
2300 in_getprefixlen(char *addr
, int max_plen
)
2305 str
= strchr(addr
, '/');
2307 syntax_error(gettext("route: %s: unexpected '/'\n"), addr
);
2315 prefixlen
= strtoul(str
, &end
, 10);
2316 if (errno
!= 0 || str
== end
) {
2317 syntax_error(gettext("route: bad prefix length %s\n"), str
);
2320 if (prefixlen
> max_plen
) {
2321 syntax_error(gettext("route: prefix length %s out of range\n"),
2329 * Convert a prefix length to a mask.
2330 * Returns B_TRUE if ok. B_FALSE otherwise.
2331 * Assumes the mask array is zeroed by the caller.
2334 in_prefixlentomask(int prefixlen
, int maxlen
, uchar_t
*mask
)
2336 if (prefixlen
< 0 || prefixlen
> maxlen
)
2339 while (prefixlen
> 0) {
2340 if (prefixlen
>= 8) {
2345 *mask
|= 1 << (8 - prefixlen
);
2352 rtmonitor(int argc
, char *argv
[])
2355 intmax_t msg
[2048 / sizeof (intmax_t)];
2362 if (argc
== 2 && **argv
== '-') {
2363 switch (keyword(*argv
+ 1)) {
2381 s
= socket(PF_ROUTE
, SOCK_RAW
, af
);
2383 quit("socket", errno
);
2386 n
= read(s
, msg
, sizeof (msg
));
2388 quit("read", errno
);
2389 (void) printf("got message of size %d\n", n
);
2390 print_rtmsg((struct rt_msghdr
*)msg
, n
);
2395 rtmsg(rtcmd_irep_t
*newrt
)
2399 char *cp
= m_rtmsg
.m_space
;
2403 (void) memset(&m_rtmsg
, 0, sizeof (m_rtmsg
));
2405 if (newrt
->ri_cmd
== RTM_GET
) {
2406 newrt
->ri_ifp
.sa
.sa_family
= AF_LINK
;
2407 newrt
->ri_addrs
|= RTA_IFP
;
2410 #define rtm m_rtmsg.m_rtm
2411 rtm
.rtm_type
= newrt
->ri_cmd
;
2412 rtm
.rtm_flags
= newrt
->ri_flags
;
2413 rtm
.rtm_version
= RTM_VERSION
;
2414 rtm
.rtm_seq
= ++seq
;
2415 rtm
.rtm_addrs
= newrt
->ri_addrs
;
2416 rtm
.rtm_rmx
= newrt
->ri_metrics
;
2417 rtm
.rtm_inits
= newrt
->ri_inits
;
2419 #define NEXTADDR(w, u) \
2420 if (newrt->ri_addrs & (w)) { \
2421 l = ROUNDUP_LONG(salen(&u.sa)); \
2422 (void) memmove(cp, &(u), l); \
2427 NEXTADDR(RTA_DST
, newrt
->ri_dst
);
2428 NEXTADDR(RTA_GATEWAY
, newrt
->ri_gate
);
2429 NEXTADDR(RTA_NETMASK
, newrt
->ri_mask
);
2430 NEXTADDR(RTA_IFP
, newrt
->ri_ifp
);
2431 NEXTADDR(RTA_IFA
, newrt
->ri_ifa
);
2433 * RTA_SRC has overloaded meaning. It can represent the
2434 * src address of incoming or outgoing packets.
2436 NEXTADDR(RTA_SRC
, newrt
->ri_src
);
2439 rtm
.rtm_msglen
= l
= cp
- (char *)&m_rtmsg
;
2442 print_rtmsg(&rtm
, l
);
2445 if ((rlen
= write(s
, (char *)&m_rtmsg
, l
)) < 0) {
2456 perror(gettext("writing to routing socket"));
2460 } else if (rlen
< (int)rtm
.rtm_msglen
) {
2461 (void) fprintf(stderr
,
2462 gettext("route: write to routing socket got only %d for "
2466 if (newrt
->ri_cmd
== RTM_GET
) {
2468 l
= read(s
, (char *)&m_rtmsg
, sizeof (m_rtmsg
));
2469 } while (l
> 0 && (rtm
.rtm_seq
!= seq
|| rtm
.rtm_pid
!= pid
));
2471 (void) fprintf(stderr
,
2472 gettext("route: read from routing socket: %s\n"),
2475 print_getmsg(newrt
, &rtm
, l
);
2482 static char *msgtypes
[] = {
2484 "RTM_ADD: Add Route",
2485 "RTM_DELETE: Delete Route",
2486 "RTM_CHANGE: Change Metrics or flags",
2487 "RTM_GET: Report Metrics",
2488 "RTM_LOSING: Kernel Suspects Partitioning",
2489 "RTM_REDIRECT: Told to use different route",
2490 "RTM_MISS: Lookup failed on this address",
2491 "RTM_LOCK: fix specified metrics",
2492 "RTM_OLDADD: caused by SIOCADDRT",
2493 "RTM_OLDDEL: caused by SIOCDELRT",
2494 "RTM_RESOLVE: Route created by cloning",
2495 "RTM_NEWADDR: address being brought up on iface",
2496 "RTM_DELADDR: address being brought down on iface",
2497 "RTM_IFINFO: iface status change",
2498 "RTM_CHGADDR: address being changed on iface",
2499 "RTM_FREEADDR: address being removed from iface",
2503 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2505 static char metricnames
[] =
2506 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2508 static char routeflags
[] =
2509 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2510 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2511 "\016PRIVATE\017PROTO2\020PROTO1\022SETSRC\023INDIRECT"
2512 "\024KERNEL\025ZONE";
2513 static char ifnetflags
[] =
2514 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2515 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2516 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2517 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2518 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2519 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2520 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2522 static char addrnames
[] =
2523 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2526 print_rtmsg(struct rt_msghdr
*rtm
, int msglen
)
2528 struct if_msghdr
*ifm
;
2529 struct ifa_msghdr
*ifam
;
2533 if (rtm
->rtm_version
!= RTM_VERSION
) {
2534 (void) printf("routing message version %d not understood\n",
2538 if (rtm
->rtm_msglen
!= msglen
) {
2539 (void) printf("message length mismatch, in packet %d, "
2541 rtm
->rtm_msglen
, msglen
);
2542 if (msglen
> rtm
->rtm_msglen
)
2543 msglen
= rtm
->rtm_msglen
;
2546 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2547 * and the upper-bound of (NMSGTYPES - 1).
2549 if (rtm
->rtm_type
== 0 || rtm
->rtm_type
>= (NMSGTYPES
- 1)) {
2550 (void) printf("routing message type %d not understood\n",
2554 (void) printf("%s: len %d, ", msgtypes
[rtm
->rtm_type
], msglen
);
2555 switch (rtm
->rtm_type
) {
2557 ifm
= (struct if_msghdr
*)rtm
;
2558 (void) printf("if# %d, flags:", ifm
->ifm_index
);
2559 bprintf(stdout
, ifm
->ifm_flags
, ifnetflags
);
2560 pmsg_addrs((const char *)(ifm
+ 1), msglen
- sizeof (*ifm
),
2567 ifam
= (struct ifa_msghdr
*)rtm
;
2568 (void) printf("metric %d, flags:", ifam
->ifam_metric
);
2569 bprintf(stdout
, ifam
->ifam_flags
, routeflags
);
2570 pmsg_addrs((const char *)(ifam
+ 1), msglen
- sizeof (*ifam
),
2574 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2575 rtm
->rtm_pid
, rtm
->rtm_seq
, rtm
->rtm_errno
);
2576 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2577 pmsg_common(rtm
, msglen
);
2583 print_getmsg(rtcmd_irep_t
*req_rt
, struct rt_msghdr
*rtm
, int msglen
)
2585 struct sockaddr
*dst
= NULL
, *gate
= NULL
, *mask
= NULL
, *src
= NULL
;
2586 struct sockaddr_dl
*ifp
= NULL
;
2587 struct sockaddr
*sa
;
2591 (void) printf(" route to: %s\n", routename(&req_rt
->ri_dst
.sa
));
2592 if (rtm
->rtm_version
!= RTM_VERSION
) {
2593 (void) fprintf(stderr
,
2594 gettext("routing message version %d not understood\n"),
2598 if (rtm
->rtm_msglen
> (ushort_t
)msglen
) {
2599 (void) fprintf(stderr
,
2600 gettext("message length mismatch, in packet %d, "
2601 "returned %d\n"), rtm
->rtm_msglen
, msglen
);
2603 if (rtm
->rtm_errno
) {
2604 (void) fprintf(stderr
, "RTM_GET: %s (errno %d)\n",
2605 strerror(rtm
->rtm_errno
), rtm
->rtm_errno
);
2608 cp
= ((char *)(rtm
+ 1));
2609 if (rtm
->rtm_addrs
!= 0) {
2610 for (i
= 1; i
!= 0; i
<<= 1) {
2611 if (i
& rtm
->rtm_addrs
) {
2613 sa
= (struct sockaddr
*)cp
;
2625 if (sa
->sa_family
== AF_LINK
&&
2626 ((struct sockaddr_dl
*)sa
)->
2628 ifp
= (struct sockaddr_dl
*)sa
;
2638 if (dst
!= NULL
&& mask
!= NULL
)
2639 mask
->sa_family
= dst
->sa_family
; /* XXX */
2641 (void) printf("destination: %s\n", routename(dst
));
2643 boolean_t savenflag
= nflag
;
2646 (void) printf(" mask: %s\n", routename(mask
));
2649 if (gate
!= NULL
&& rtm
->rtm_flags
& RTF_GATEWAY
)
2650 (void) printf(" gateway: %s\n", routename(gate
));
2651 if (src
!= NULL
&& rtm
->rtm_flags
& RTF_SETSRC
)
2652 (void) printf(" setsrc: %s\n", routename(src
));
2657 (void) printf(" interface: %.*s index %d address ",
2658 ifp
->sdl_nlen
, ifp
->sdl_data
, ifp
->sdl_index
);
2659 for (i
= ifp
->sdl_nlen
;
2660 i
< ifp
->sdl_nlen
+ ifp
->sdl_alen
;
2662 (void) printf("%02x ",
2663 ifp
->sdl_data
[i
] & 0xFF);
2665 (void) printf("\n");
2667 (void) printf(" interface: %.*s\n",
2668 ifp
->sdl_nlen
, ifp
->sdl_data
);
2671 (void) printf(" flags: ");
2672 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2674 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2675 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2677 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2678 "rttvar,ms hopcount mtu expire");
2679 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_recvpipe
, lock(RPIPE
));
2680 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_sendpipe
, lock(SPIPE
));
2681 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_ssthresh
, lock(SSTHRESH
));
2682 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rtt
), lock(RTT
));
2683 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rttvar
), lock(RTTVAR
));
2684 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_hopcount
, lock(HOPCOUNT
));
2685 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_mtu
, lock(MTU
));
2686 if (rtm
->rtm_rmx
.rmx_expire
)
2687 rtm
->rtm_rmx
.rmx_expire
-= time(0);
2688 (void) printf("%8d%c", rtm
->rtm_rmx
.rmx_expire
, lock(EXPIRE
));
2692 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2694 pmsg_common(rtm
, msglen
);
2696 const char *sptr
, *endptr
;
2697 const struct sockaddr
*sa
;
2700 /* Not verbose; just print out the exceptional cases */
2701 if (rtm
->rtm_addrs
&~ RTA_IGN
) {
2702 (void) printf("\nsockaddrs: ");
2703 bprintf(stdout
, rtm
->rtm_addrs
, addrnames
);
2705 sptr
= (const char *)(rtm
+ 1);
2706 endptr
= (const char *)rtm
+ msglen
;
2707 addrs
= rtm
->rtm_addrs
;
2708 while (addrs
!= 0 && sptr
+ sizeof (*sa
) <= endptr
) {
2711 sa
= (const struct sockaddr
*)sptr
;
2714 (void) putchar('\n');
2720 pmsg_common(const struct rt_msghdr
*rtm
, size_t msglen
)
2722 (void) printf("\nlocks: ");
2723 bprintf(stdout
, (int)rtm
->rtm_rmx
.rmx_locks
, metricnames
);
2724 (void) printf(" inits: ");
2725 bprintf(stdout
, (int)rtm
->rtm_inits
, metricnames
);
2726 pmsg_addrs((const char *)(rtm
+ 1), msglen
- sizeof (*rtm
),
2731 pmsg_addrs(const char *cp
, size_t msglen
, uint_t addrs
)
2733 const struct sockaddr
*sa
;
2738 (void) printf("\nsockaddrs: ");
2739 bprintf(stdout
, addrs
, addrnames
);
2740 (void) putchar('\n');
2741 maxptr
= cp
+ msglen
;
2742 for (i
= 1; i
!= 0 && cp
+ sizeof (*sa
) <= maxptr
; i
<<= 1) {
2745 sa
= (const struct sockaddr
*)cp
;
2746 (void) printf(" %s", routename(sa
));
2753 msglen
= maxptr
- cp
;
2755 (void) putchar('\n');
2756 (void) fflush(stdout
);
2760 bprintf(FILE *fp
, int b
, char *s
)
2763 boolean_t gotsome
= B_FALSE
;
2767 while ((i
= *s
++) != 0) {
2768 if (b
& (1 << (i
- 1))) {
2775 for (; (i
= *s
) > ' '; s
++)
2783 (void) putc('>', fp
);
2787 keyword(const char *cp
)
2789 struct keytab
*kt
= keywords
;
2791 while (kt
->kt_cp
&& strcmp(kt
->kt_cp
, cp
))
2797 sodump(su_t
*su
, char *which
)
2799 static char obuf
[INET6_ADDRSTRLEN
];
2801 switch (su
->sa
.sa_family
) {
2803 (void) printf("%s: link %s; ",
2804 which
, link_ntoa(&su
->sdl
));
2807 (void) printf("%s: inet %s; ",
2808 which
, inet_ntoa(su
->sin
.sin_addr
));
2811 if (inet_ntop(AF_INET6
, (void *)&su
->sin6
.sin6_addr
, obuf
,
2812 INET6_ADDRSTRLEN
) != NULL
) {
2813 (void) printf("%s: inet6 %s; ", which
, obuf
);
2818 quit(gettext("Internal Error"), EINVAL
);
2821 (void) fflush(stdout
);
2833 #define LETTER (4*3)
2836 sockaddr(char *addr
, struct sockaddr
*sa
)
2838 char *cp
= (char *)sa
;
2839 int size
= salen(sa
);
2840 char *cplim
= cp
+ size
;
2841 int byte
= 0, state
= VIRGIN
, new;
2843 (void) memset(cp
, 0, size
);
2846 if ((*addr
>= '0') && (*addr
<= '9')) {
2848 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2849 new = *addr
- 'a' + 10;
2850 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2851 new = *addr
- 'A' + 10;
2852 } else if (*addr
== 0) {
2858 switch (state
/* | INPUT */) {
2859 case GOTTWO
| DIGIT
:
2862 case VIRGIN
| DIGIT
:
2863 state
= GOTONE
; byte
= new; continue;
2864 case GOTONE
| DIGIT
:
2865 state
= GOTTWO
; byte
= new + (byte
<< 4); continue;
2866 default: /* | DELIM */
2867 state
= VIRGIN
; *cp
++ = byte
; byte
= 0; continue;
2876 } while (cp
< cplim
);
2880 salen(const struct sockaddr
*sa
)
2882 switch (sa
->sa_family
) {
2884 return (sizeof (struct sockaddr_in
));
2886 return (sizeof (struct sockaddr_dl
));
2888 return (sizeof (struct sockaddr_in6
));
2890 return (sizeof (struct sockaddr
));
2895 link_addr(const char *addr
, struct sockaddr_dl
*sdl
)
2897 char *cp
= sdl
->sdl_data
;
2898 char *cplim
= sizeof (struct sockaddr_dl
) + (char *)sdl
;
2899 int byte
= 0, state
= VIRGIN
, new;
2901 (void) memset(sdl
, 0, sizeof (struct sockaddr_dl
));
2902 sdl
->sdl_family
= AF_LINK
;
2905 if ((*addr
>= '0') && (*addr
<= '9')) {
2907 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2908 new = *addr
- 'a' + 10;
2909 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2910 new = *addr
- 'A' + 10;
2911 } else if (*addr
== 0) {
2913 } else if (state
== VIRGIN
&&
2914 (((*addr
>= 'A') && (*addr
<= 'Z')) ||
2915 ((*addr
>= 'a') && (*addr
<= 'z')))) {
2921 switch (state
/* | INPUT */) {
2922 case VIRGIN
| DIGIT
:
2923 case VIRGIN
| LETTER
:
2926 case VIRGIN
| DELIM
:
2928 sdl
->sdl_nlen
= cp
- sdl
->sdl_data
;
2930 case GOTTWO
| DIGIT
:
2937 case GOTONE
| DIGIT
:
2939 byte
= new + (byte
<< 4);
2941 default: /* | DELIM */
2954 } while (cp
< cplim
);
2955 sdl
->sdl_alen
= cp
- LLADDR(sdl
);
2958 static char hexlist
[] = "0123456789abcdef";
2961 link_ntoa(const struct sockaddr_dl
*sdl
)
2963 static char obuf
[64];
2966 uchar_t
*in
= (uchar_t
*)LLADDR(sdl
);
2967 uchar_t
*inlim
= in
+ sdl
->sdl_alen
;
2968 boolean_t firsttime
= B_TRUE
;
2970 if (sdl
->sdl_nlen
) {
2971 (void) memcpy(obuf
, sdl
->sdl_data
, sdl
->sdl_nlen
);
2972 out
+= sdl
->sdl_nlen
;
2976 while (in
< inlim
) {
2978 firsttime
= B_FALSE
;
2983 out
[1] = hexlist
[i
& 0xf];
2985 out
[0] = hexlist
[i
];
2988 *out
++ = hexlist
[i
];
2998 intmax_t buf
[512 / sizeof (intmax_t)];
3001 struct strbuf ctlbuf
, databuf
;
3002 struct T_optmgmt_req
*tor
= (struct T_optmgmt_req
*)buf
;
3003 struct T_optmgmt_ack
*toa
= (struct T_optmgmt_ack
*)buf
;
3004 struct T_error_ack
*tea
= (struct T_error_ack
*)buf
;
3006 mib_item_t
*first_item
= NULL
;
3007 mib_item_t
*last_item
= NULL
;
3010 tor
->PRIM_type
= T_SVR4_OPTMGMT_REQ
;
3011 tor
->OPT_offset
= sizeof (struct T_optmgmt_req
);
3012 tor
->OPT_length
= sizeof (struct opthdr
);
3013 tor
->MGMT_flags
= T_CURRENT
;
3014 req
= (struct opthdr
*)&tor
[1];
3015 req
->level
= MIB2_IP
; /* any MIB2_xxx value ok here */
3019 ctlbuf
.buf
= (char *)buf
;
3020 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
3022 if (putmsg(sd
, &ctlbuf
, NULL
, flags
) < 0) {
3023 perror("mibget: putmsg (ctl)");
3027 * each reply consists of a ctl part for one fixed structure
3028 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3029 * containing an opthdr structure. level/name identify the entry,
3030 * len is the size of the data part of the message.
3032 req
= (struct opthdr
*)&toa
[1];
3033 ctlbuf
.maxlen
= sizeof (buf
);
3034 for (j
= 1; ; j
++) {
3036 getcode
= getmsg(sd
, &ctlbuf
, NULL
, &flags
);
3038 perror("mibget: getmsg (ctl)");
3040 (void) fprintf(stderr
,
3041 "# level name len\n");
3043 for (last_item
= first_item
; last_item
!= NULL
;
3044 last_item
= last_item
->next_item
) {
3045 (void) printf("%d %4ld %5ld %ld\n",
3046 ++i
, last_item
->group
,
3054 ctlbuf
.len
>= sizeof (struct T_optmgmt_ack
) &&
3055 toa
->PRIM_type
== T_OPTMGMT_ACK
&&
3056 toa
->MGMT_flags
== T_SUCCESS
&&
3059 (void) printf("mibget getmsg() %d returned EOD "
3060 "(level %lu, name %lu)\n", j
, req
->level
,
3063 return (first_item
); /* this is EOD msg */
3066 if (ctlbuf
.len
>= sizeof (struct T_error_ack
) &&
3067 tea
->PRIM_type
== T_ERROR_ACK
) {
3068 (void) fprintf(stderr
, gettext("mibget %d gives "
3069 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3070 "0x%lx\n"), j
, tea
->TLI_error
, tea
->UNIX_error
);
3071 errno
= (tea
->TLI_error
== TSYSERR
) ?
3072 tea
->UNIX_error
: EPROTO
;
3076 if (getcode
!= MOREDATA
||
3077 ctlbuf
.len
< sizeof (struct T_optmgmt_ack
) ||
3078 toa
->PRIM_type
!= T_OPTMGMT_ACK
||
3079 toa
->MGMT_flags
!= T_SUCCESS
) {
3080 (void) printf("mibget getmsg(ctl) %d returned %d, "
3081 "ctlbuf.len = %d, PRIM_type = %ld\n",
3082 j
, getcode
, ctlbuf
.len
, toa
->PRIM_type
);
3083 if (toa
->PRIM_type
== T_OPTMGMT_ACK
) {
3084 (void) printf("T_OPTMGMT_ACK: "
3085 "MGMT_flags = 0x%lx, req->len = %ld\n",
3086 toa
->MGMT_flags
, req
->len
);
3092 temp
= malloc(sizeof (mib_item_t
));
3094 perror("mibget: malloc");
3097 if (last_item
!= NULL
)
3098 last_item
->next_item
= temp
;
3102 last_item
->next_item
= NULL
;
3103 last_item
->group
= req
->level
;
3104 last_item
->mib_id
= req
->name
;
3105 last_item
->length
= req
->len
;
3106 last_item
->valp
= malloc(req
->len
);
3108 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3110 j
, last_item
->group
, last_item
->mib_id
,
3114 databuf
.maxlen
= last_item
->length
;
3115 databuf
.buf
= (char *)last_item
->valp
;
3118 getcode
= getmsg(sd
, NULL
, &databuf
, &flags
);
3120 perror("mibget: getmsg (data)");
3122 } else if (getcode
!= 0) {
3123 (void) printf("mibget getmsg(data) returned %d, "
3124 "databuf.maxlen = %d, databuf.len = %d\n",
3125 getcode
, databuf
.maxlen
, databuf
.len
);
3131 * On error, free all the allocated mib_item_t objects.
3133 while (first_item
!= NULL
) {
3134 last_item
= first_item
;
3135 first_item
= first_item
->next_item
;