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 {"multirt", K_MULTIRT
},
167 {"setsrc", K_SETSRC
},
170 #define K_INDIRECT 44
171 {"indirect", K_INDIRECT
},
176 * Size of buffers used to hold command lines from the saved route file as
177 * well as error strings.
179 #define BUF_SIZE 2048
181 typedef union sockunion
{
183 struct sockaddr_in sin
;
184 struct sockaddr_dl sdl
;
185 struct sockaddr_in6 sin6
;
189 * This structure represents the digested information from parsing arguments
190 * to route add, change, delete, and get.
193 typedef struct rtcmd_irep
{
198 struct rt_metrics ri_metrics
;
204 struct hostent
*ri_gate_hp
;
212 typedef struct mib_item_s
{
213 struct mib_item_s
*next_item
;
232 static boolean_t
args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
,
234 static void bprintf(FILE *fp
, int b
, char *s
);
235 static boolean_t
compare_rtcmd(rtcmd_irep_t
*srch_rt
,
236 rtcmd_irep_t
*file_rt
);
237 static void delRouteEntry(mib2_ipRouteEntry_t
*rp
,
238 mib2_ipv6RouteEntry_t
*rp6
, int seqno
);
239 static void del_rtcmd_irep(rtcmd_irep_t
*rcip
);
240 static void flushroutes(int argc
, char *argv
[]);
241 static boolean_t
getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
,
243 static boolean_t
in6_getaddr(char *s
, struct sockaddr_in6
*sin6
,
244 int *plenp
, struct hostent
**hpp
);
245 static boolean_t
in_getaddr(char *s
, struct sockaddr_in
*sin
,
246 int *plenp
, int which
, struct hostent
**hpp
, addr_type_t atype
,
248 static int in_getprefixlen(char *addr
, int max_plen
);
249 static boolean_t
in_prefixlentomask(int prefixlen
, int maxlen
,
251 static void inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
,
252 struct sockaddr_in
*sin
);
253 static in_addr_t
inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
);
254 static int keyword(const char *cp
);
255 static void link_addr(const char *addr
, struct sockaddr_dl
*sdl
);
256 static char *link_ntoa(const struct sockaddr_dl
*sdl
);
257 static mib_item_t
*mibget(int sd
);
258 static char *netname(struct sockaddr
*sa
);
259 static int newroute(char **argv
);
260 static rtcmd_irep_t
*new_rtcmd_irep(void);
261 static void pmsg_addrs(const char *cp
, size_t len
, uint_t addrs
);
262 static void pmsg_common(const struct rt_msghdr
*rtm
, size_t len
);
263 static void print_getmsg(rtcmd_irep_t
*req_rt
,
264 struct rt_msghdr
*rtm
, int msglen
);
265 static void print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
,
266 boolean_t gw_good
, boolean_t to_saved
);
267 static void print_rtmsg(struct rt_msghdr
*rtm
, int msglen
);
268 static void quit(char *s
, int err
) __NORETURN
;
269 static char *routename(const struct sockaddr
*sa
);
270 static void rtmonitor(int argc
, char *argv
[]);
271 static int rtmsg(rtcmd_irep_t
*rcip
);
272 static int salen(const struct sockaddr
*sa
);
273 static void save_route(int argc
, char **argv
, int do_flush
);
274 static void save_string(char **dst
, char *src
);
275 static int search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
,
277 static void set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
,
279 static int show_saved_routes(int argc
);
280 static void sockaddr(char *addr
, struct sockaddr
*sa
);
281 static void sodump(su_t
*su
, char *which
);
282 static void syntax_arg_missing(char *keyword
);
283 static void syntax_bad_keyword(char *keyword
);
284 static void syntax_error(char *err
, ...);
285 static void usage(char *cp
);
286 static void write_to_rtfile(FILE *fp
, int argc
, char **argv
);
290 static boolean_t nflag
;
291 static int af
= AF_INET
;
292 static boolean_t qflag
, tflag
;
293 static boolean_t verbose
;
294 static boolean_t debugonly
;
295 static boolean_t fflag
;
296 static boolean_t update_table
;
297 static boolean_t perm_flag
;
298 static boolean_t early_v6_keyword
;
299 static char perm_file_sfx
[] = "/etc/inet/static_routes";
300 static char *perm_file
;
301 static char temp_file_sfx
[] = "/etc/inet/static_routes.tmp";
302 static char *temp_file
;
303 static struct in6_addr in6_host_mask
= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
304 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
307 * This next variable indicates whether certain functions exit when an error
308 * is detected in the user input. Currently, exit_on_error is only set false
309 * in search_rtfile(), when argument are being parsed. Only those functions
310 * used by search_rtfile() to parse its arguments are designed to work in
311 * both modes. Take particular care in setting this false to ensure that any
312 * functions you call that might act on this flag properly return errors when
313 * exit_on_error is false.
315 static int exit_on_error
= B_TRUE
;
318 struct rt_msghdr m_rtm
;
319 char m_space
[BUF_SIZE
];
323 * Sizes of data structures extracted from the base mib.
324 * This allows the size of the tables entries to grow while preserving
325 * binary compatibility.
327 static int ipRouteEntrySize
;
328 static int ipv6RouteEntrySize
;
330 #define ROUNDUP_LONG(a) \
331 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
332 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
333 #define C(x) ((x) & 0xff)
336 * return values from in_getprefixlen()
338 #define BAD_ADDR -1 /* prefix is invalid */
339 #define NO_PREFIX -2 /* no prefix was found */
345 (void) fprintf(stderr
, gettext("route: botched keyword: %s\n"),
348 (void) fprintf(stderr
, gettext("usage: route [ -fnpqv ] "
349 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
356 syntax_error(char *err
, ...)
362 (void) vfprintf(stderr
, err
, args
);
370 syntax_bad_keyword(char *keyword
)
372 syntax_error(gettext("route: botched keyword: %s\n"), keyword
);
376 syntax_arg_missing(char *keyword
)
378 syntax_error(gettext("route: argument required following keyword %s\n"),
383 quit(char *s
, int sverrno
)
385 (void) fprintf(stderr
, "route: ");
387 (void) fprintf(stderr
, "%s: ", s
);
388 (void) fprintf(stderr
, "%s\n", strerror(sverrno
));
394 main(int argc
, char **argv
)
401 const char *root_dir
= NULL
;
403 (void) setlocale(LC_ALL
, "");
405 #if !defined(TEXT_DOMAIN)
406 #define TEXT_DOMAIN "SYS_TEST"
408 (void) textdomain(TEXT_DOMAIN
);
413 while ((ch
= getopt(argc
, argv
, "R:nqdtvfp")) != EOF
) {
450 s
= open("/dev/null", O_WRONLY
);
452 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
454 quit("socket", errno
);
457 * Handle the -p and -R flags. The -R flag only applies
458 * when the -p flag is set.
460 if (root_dir
== NULL
) {
461 perm_file
= perm_file_sfx
;
462 temp_file
= temp_file_sfx
;
464 size
= strlen(root_dir
) + sizeof (perm_file_sfx
);
465 perm_file
= malloc(size
);
466 if (perm_file
== NULL
)
467 quit("malloc", errno
);
468 (void) snprintf(perm_file
, size
, "%s%s", root_dir
,
470 size
= strlen(root_dir
) + sizeof (temp_file_sfx
);
471 temp_file
= malloc(size
);
472 if (temp_file
== NULL
)
473 quit("malloc", errno
);
474 (void) snprintf(temp_file
, size
, "%s%s", root_dir
,
478 * Whether or not to act on the routing table. The only time the
479 * routing table is not modified is when both -p and -R are present.
481 update_table
= (!perm_flag
|| root_dir
== NULL
);
487 * Accept an address family keyword after the -f. Since the
488 * default address family is AF_INET, reassign af only for the
489 * other valid address families.
492 switch (keyword(*argv
)) {
495 early_v6_keyword
= B_TRUE
;
498 /* Skip over the address family parameter. */
504 flushroutes(0, NULL
);
508 switch (keyword(*argv
)) {
515 rval
= newroute(argv
);
517 if (perm_flag
&& (rval
== 0 || rval
== EEXIST
||
519 save_route(argc
, argv
, B_FALSE
);
525 return (show_saved_routes(argc
));
527 syntax_error(gettext(
528 "route: show command requires -p\n"));
532 rtmonitor(argc
, argv
);
536 flushroutes(argc
, argv
);
546 * Purge all entries in the routing tables not
547 * associated with network interfaces.
550 flushroutes(int argc
, char *argv
[])
553 int sd
; /* mib stream */
555 mib2_ipRouteEntry_t
*rp
;
556 mib2_ipv6RouteEntry_t
*rp6
;
563 if (argc
== 2 && **argv
== '-') {
565 * The address family (preceded by a dash) may be used
566 * to flush the routes of that particular family.
568 switch (keyword(*argv
+ 1)) {
587 /* This flushes the persistent route file */
588 save_route(0, NULL
, B_TRUE
);
594 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&off
,
596 quit("setsockopt", errno
);
598 sd
= open("/dev/ip", O_RDWR
);
603 (void) fprintf(stderr
,
604 gettext("route: flush: insufficient privileges\n"));
608 quit(gettext("can't open mib stream"), oerrno
);
612 if ((item
= mibget(sd
)) == NULL
)
613 quit("mibget", errno
);
615 (void) printf("Examining routing table from "
616 "T_SVR4_OPTMGMT_REQ\n");
621 /* Extract ipRouteEntrySize */
622 for (; item
!= NULL
; item
= item
->next_item
) {
623 if (item
->mib_id
!= 0)
625 if (item
->group
== MIB2_IP
) {
627 ((mib2_ip_t
*)item
->valp
)->ipRouteEntrySize
;
628 assert(IS_P2ALIGNED(ipRouteEntrySize
,
629 sizeof (mib2_ipRouteEntry_t
*)));
633 if (ipRouteEntrySize
== 0) {
634 (void) fprintf(stderr
,
635 gettext("ipRouteEntrySize can't be determined.\n"));
638 for (; item
!= NULL
; item
= item
->next_item
) {
640 * skip all the other trash that comes up the mib stream
642 if (item
->group
!= MIB2_IP
||
643 item
->mib_id
!= MIB2_IP_ROUTE
)
645 for (rp
= (mib2_ipRouteEntry_t
*)item
->valp
;
646 (char *)rp
< (char *)item
->valp
+ item
->length
;
648 rp
= (mib2_ipRouteEntry_t
*)
649 ((char *)rp
+ ipRouteEntrySize
)) {
650 delRouteEntry(rp
, NULL
, seqno
);
657 /* Extract ipv6RouteEntrySize */
658 for (; item
!= NULL
; item
= item
->next_item
) {
659 if (item
->mib_id
!= 0)
661 if (item
->group
== MIB2_IP6
) {
663 ((mib2_ipv6IfStatsEntry_t
*)item
->valp
)->
665 assert(IS_P2ALIGNED(ipv6RouteEntrySize
,
666 sizeof (mib2_ipv6RouteEntry_t
*)));
670 if (ipv6RouteEntrySize
== 0) {
671 (void) fprintf(stderr
, gettext(
672 "ipv6RouteEntrySize cannot be determined.\n"));
675 for (; item
!= NULL
; item
= item
->next_item
) {
677 * skip all the other trash that comes up the mib stream
679 if (item
->group
!= MIB2_IP6
||
680 item
->mib_id
!= MIB2_IP6_ROUTE
)
682 for (rp6
= (mib2_ipv6RouteEntry_t
*)item
->valp
;
683 (char *)rp6
< (char *)item
->valp
+ item
->length
;
685 rp6
= (mib2_ipv6RouteEntry_t
*)
686 ((char *)rp6
+ ipv6RouteEntrySize
)) {
687 delRouteEntry(NULL
, rp6
, seqno
);
695 if (setsockopt(s
, SOL_SOCKET
, SO_USELOOPBACK
, (char *)&on
,
697 quit("setsockopt", errno
);
701 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
702 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
703 * order to facilitate the flushing of RTF_GATEWAY routes.
706 delRouteEntry(mib2_ipRouteEntry_t
*rp
, mib2_ipv6RouteEntry_t
*rp6
, int seqno
)
711 struct rt_msghdr
*rtm
;
712 struct sockaddr_in sin
;
713 struct sockaddr_in6 sin6
;
717 ire_type
= rp
->ipRouteInfo
.re_ire_type
;
719 ire_type
= rp6
->ipv6RouteInfo
.re_ire_type
;
720 if (ire_type
!= IRE_DEFAULT
&&
721 ire_type
!= IRE_PREFIX
&&
722 ire_type
!= IRE_HOST
&&
723 ire_type
!= IRE_HOST_REDIRECT
)
726 rtm
= &m_rtmsg
.m_rtm
;
727 (void) memset(rtm
, 0, sizeof (m_rtmsg
));
728 rtm
->rtm_type
= RTM_DELETE
;
729 rtm
->rtm_seq
= seqno
;
730 rtm
->rtm_flags
|= RTF_GATEWAY
;
731 rtm
->rtm_version
= RTM_VERSION
;
732 rtm
->rtm_addrs
= RTA_DST
| RTA_GATEWAY
| RTA_NETMASK
;
733 cp
= m_rtmsg
.m_space
;
735 slen
= sizeof (struct sockaddr_in
);
736 if (rp
->ipRouteMask
== IP_HOST_MASK
)
737 rtm
->rtm_flags
|= RTF_HOST
;
738 (void) memset(&sin
, 0, slen
);
739 sin
.sin_family
= AF_INET
;
740 sin
.sin_addr
.s_addr
= rp
->ipRouteDest
;
741 (void) memmove(cp
, &sin
, slen
);
743 sin
.sin_addr
.s_addr
= rp
->ipRouteNextHop
;
744 (void) memmove(cp
, &sin
, slen
);
746 sin
.sin_addr
.s_addr
= rp
->ipRouteMask
;
747 (void) memmove(cp
, &sin
, slen
);
750 slen
= sizeof (struct sockaddr_in6
);
751 if (rp6
->ipv6RoutePfxLength
== IPV6_ABITS
)
752 rtm
->rtm_flags
|= RTF_HOST
;
753 (void) memset(&sin6
, 0, slen
);
754 sin6
.sin6_family
= AF_INET6
;
755 sin6
.sin6_addr
= rp6
->ipv6RouteDest
;
756 (void) memmove(cp
, &sin6
, slen
);
758 sin6
.sin6_addr
= rp6
->ipv6RouteNextHop
;
759 (void) memmove(cp
, &sin6
, slen
);
761 (void) memset(&sin6
.sin6_addr
, 0, sizeof (sin6
.sin6_addr
));
762 (void) in_prefixlentomask(rp6
->ipv6RoutePfxLength
, IPV6_ABITS
,
763 (uchar_t
*)&sin6
.sin6_addr
.s6_addr
);
764 (void) memmove(cp
, &sin6
, slen
);
767 rtm
->rtm_msglen
= cp
- (char *)&m_rtmsg
;
770 * In debugonly mode, the routing socket message to delete the
771 * current entry is not actually sent. However if verbose is
772 * also set, the routing socket message that would have been
776 print_rtmsg(rtm
, rtm
->rtm_msglen
);
780 rlen
= write(s
, (char *)&m_rtmsg
, rtm
->rtm_msglen
);
781 if (rlen
< (int)rtm
->rtm_msglen
) {
783 (void) fprintf(stderr
,
784 gettext("route: write to routing socket: %s\n"),
787 (void) fprintf(stderr
, gettext("route: write to "
788 "routing socket got only %d for rlen\n"), rlen
);
794 * In quiet mode, nothing is printed at all (unless the write()
800 print_rtmsg(rtm
, rlen
);
802 struct sockaddr
*sa
= (struct sockaddr
*)(rtm
+ 1);
804 (void) printf("%-20.20s ",
805 rtm
->rtm_flags
& RTF_HOST
? routename(sa
) :
808 sa
= (struct sockaddr
*)(salen(sa
) + (char *)sa
);
809 (void) printf("%-20.20s ", routename(sa
));
810 (void) printf("done\n");
815 * Return the name of the host whose address is given.
818 routename(const struct sockaddr
*sa
)
821 static char line
[MAXHOSTNAMELEN
+ 1];
822 struct hostent
*hp
= NULL
;
823 static char domain
[MAXHOSTNAMELEN
+ 1];
824 static boolean_t first
= B_TRUE
;
833 if (gethostname(domain
, MAXHOSTNAMELEN
) == 0 &&
834 (cp
= strchr(domain
, '.')))
835 (void) strcpy(domain
, cp
+ 1);
840 if (salen(sa
) == 0) {
841 (void) strcpy(line
, "default");
844 switch (sa
->sa_family
) {
848 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
851 if (in
.s_addr
== INADDR_ANY
)
853 if (cp
== NULL
&& !nflag
) {
854 hp
= gethostbyaddr((char *)&in
, sizeof (struct in_addr
),
857 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
858 (strcmp(cp
+ 1, domain
) == 0))
864 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
865 line
[MAXHOSTNAMELEN
] = '\0';
867 in
.s_addr
= ntohl(in
.s_addr
);
868 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
869 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
875 return (link_ntoa((struct sockaddr_dl
*)sa
));
879 in6
= ((struct sockaddr_in6
*)sa
)->sin6_addr
;
882 if (IN6_IS_ADDR_UNSPECIFIED(&in6
))
884 if (cp
== NULL
&& !nflag
) {
885 hp
= getipnodebyaddr((char *)&in6
,
886 sizeof (struct in6_addr
), AF_INET6
, &error_num
);
888 if (((cp
= strchr(hp
->h_name
, '.')) != NULL
) &&
889 (strcmp(cp
+ 1, domain
) == 0))
895 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
896 line
[MAXHOSTNAMELEN
] = '\0';
898 (void) inet_ntop(AF_INET6
, (void *)&in6
, line
,
909 slim
= s
+ ((salen(sa
) + 1) >> 1);
910 cp
= line
+ sprintf(line
, "(%d)", sa
->sa_family
);
912 while (++s
< slim
) /* start with sa->sa_data */
913 cp
+= sprintf(cp
, " %x", *s
);
920 * Return the name of the network whose address is given.
921 * The address is assumed to be that of a net or subnet, not a host.
924 netname(struct sockaddr
*sa
)
927 static char line
[MAXHOSTNAMELEN
+ 1];
935 switch (sa
->sa_family
) {
939 in
= ((struct sockaddr_in
*)sa
)->sin_addr
;
941 in
.s_addr
= ntohl(in
.s_addr
);
942 if (in
.s_addr
== INADDR_ANY
) {
945 if (IN_CLASSA(in
.s_addr
)) {
946 mask
= IN_CLASSA_NET
;
948 } else if (IN_CLASSB(in
.s_addr
)) {
949 mask
= IN_CLASSB_NET
;
952 mask
= IN_CLASSC_NET
;
956 * If there are more bits than the standard mask
957 * would suggest, subnets must be in use.
958 * Guess at the subnet mask, assuming reasonable
959 * width subnet fields.
961 while (in
.s_addr
&~ mask
)
962 mask
= (long)mask
>> subnetshift
;
963 net
= in
.s_addr
& mask
;
964 while ((mask
& 1) == 0)
965 mask
>>= 1, net
>>= 1;
966 np
= getnetbyaddr(net
, AF_INET
);
971 (void) strncpy(line
, cp
, MAXHOSTNAMELEN
);
972 line
[MAXHOSTNAMELEN
] = '\0';
973 } else if ((in
.s_addr
& 0xffffff) == 0) {
974 (void) sprintf(line
, "%u", C(in
.s_addr
>> 24));
975 } else if ((in
.s_addr
& 0xffff) == 0) {
976 (void) sprintf(line
, "%u.%u", C(in
.s_addr
>> 24),
978 } else if ((in
.s_addr
& 0xff) == 0) {
979 (void) sprintf(line
, "%u.%u.%u", C(in
.s_addr
>> 24),
980 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8));
982 (void) sprintf(line
, "%u.%u.%u.%u", C(in
.s_addr
>> 24),
983 C(in
.s_addr
>> 16), C(in
.s_addr
>> 8),
989 return (link_ntoa((struct sockaddr_dl
*)sa
));
992 return (routename(sa
));
996 s
= (ushort_t
*)sa
->sa_data
;
998 slim
= s
+ ((salen(sa
) + 1) >> 1);
999 cp
= line
+ sprintf(line
, "af %d:", sa
->sa_family
);
1002 cp
+= sprintf(cp
, " %x", *s
++);
1009 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1010 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1011 * or point to dynamically allocated memory.
1014 new_rtcmd_irep(void)
1018 rcip
= calloc(1, sizeof (rtcmd_irep_t
));
1020 quit("calloc", errno
);
1023 rcip
->ri_flags
= RTF_STATIC
;
1028 del_rtcmd_irep(rtcmd_irep_t
*rcip
)
1030 free(rcip
->ri_dest_str
);
1031 free(rcip
->ri_gate_str
);
1032 free(rcip
->ri_ifp_str
);
1034 * IPv6 host entries come from getipnodebyname, which dynamically
1035 * allocates memory. IPv4 host entries come from gethostbyname, which
1036 * returns static memory and cannot be freed with freehostent.
1038 if (rcip
->ri_gate_hp
!= NULL
&&
1039 rcip
->ri_gate_hp
->h_addrtype
== AF_INET6
)
1040 freehostent(rcip
->ri_gate_hp
);
1045 save_string(char **dst
, char *src
)
1050 quit("malloc", errno
);
1055 * Print the short form summary of a route command.
1056 * Eg. "add net default: gateway 10.0.0.1"
1057 * The final newline is not added, allowing the caller to append additional
1061 print_rtcmd_short(FILE *to
, rtcmd_irep_t
*rcip
, boolean_t gw_good
,
1065 char obuf
[INET6_ADDRSTRLEN
];
1067 switch (rcip
->ri_cmd
) {
1084 (void) fprintf(to
, "%s%s %s %s", cmd
,
1085 (to_saved
) ? " persistent" : "",
1086 (rcip
->ri_flags
& RTF_HOST
) ? "host" : "net",
1087 (rcip
->ri_dest_str
== NULL
) ? "NULL" : rcip
->ri_dest_str
);
1089 if (rcip
->ri_gate_str
!= NULL
) {
1090 switch (rcip
->ri_af
) {
1093 (void) fprintf(to
, ": gateway %s",
1094 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1095 } else if (gw_good
&&
1096 rcip
->ri_gate_hp
!= NULL
&&
1097 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1099 * Print the actual address used in the case
1100 * where there was more than one address
1101 * available for the name, and one was used
1104 (void) fprintf(to
, ": gateway %s (%s)",
1106 inet_ntoa(rcip
->ri_gate
.sin
.sin_addr
));
1108 (void) fprintf(to
, ": gateway %s",
1113 if (inet_ntop(AF_INET6
,
1114 &rcip
->ri_gate
.sin6
.sin6_addr
, obuf
,
1115 INET6_ADDRSTRLEN
) != NULL
) {
1117 (void) fprintf(to
, ": gateway %s",
1122 rcip
->ri_gate_hp
->h_addr_list
[1] != NULL
) {
1123 (void) fprintf(to
, ": gateway %s (%s)",
1124 rcip
->ri_gate_str
, obuf
);
1130 (void) fprintf(to
, ": gateway %s",
1138 set_metric(rtcmd_irep_t
*rcip
, char *value
, int key
, boolean_t lock
)
1141 uint_t noval
, *valp
= &noval
;
1144 #define caseof(x, y, z) \
1145 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1147 caseof(K_MTU
, RTV_MTU
, rmx_mtu
);
1148 caseof(K_HOPCOUNT
, RTV_HOPCOUNT
, rmx_hopcount
);
1149 caseof(K_EXPIRE
, RTV_EXPIRE
, rmx_expire
);
1150 caseof(K_RECVPIPE
, RTV_RPIPE
, rmx_recvpipe
);
1151 caseof(K_SENDPIPE
, RTV_SPIPE
, rmx_sendpipe
);
1152 caseof(K_SSTHRESH
, RTV_SSTHRESH
, rmx_ssthresh
);
1153 caseof(K_RTT
, RTV_RTT
, rmx_rtt
);
1154 caseof(K_RTTVAR
, RTV_RTTVAR
, rmx_rttvar
);
1157 rcip
->ri_inits
|= flag
;
1159 rcip
->ri_metrics
.rmx_locks
|= flag
;
1160 *valp
= atoi(value
);
1164 * Parse the options give in argv[], filling in rcip with the results.
1165 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1166 * tokenized to produce the command line. Cmd_string is tokenized using
1167 * strtok, which will overwrite whitespace in the string with nulls.
1169 * Returns B_TRUE on success and B_FALSE on failure.
1172 args_to_rtcmd(rtcmd_irep_t
*rcip
, char **argv
, char *cmd_string
)
1174 const char *ws
= "\f\n\r\t\v ";
1175 char *tok
= cmd_string
;
1177 addr_type_t atype
= ADDR_TYPE_ANY
;
1178 boolean_t iflag
= B_FALSE
;
1179 boolean_t locknext
= B_FALSE
;
1180 boolean_t lockrest
= B_FALSE
;
1181 boolean_t dash_keyword
;
1185 if (cmd_string
== NULL
) {
1188 tok
= strtok(cmd_string
, ws
);
1192 * The command keywords are already fully checked by main() or
1197 rcip
->ri_cmd
= RTM_ADD
;
1200 rcip
->ri_cmd
= RTM_CHANGE
;
1203 rcip
->ri_cmd
= RTM_DELETE
;
1206 rcip
->ri_cmd
= RTM_GET
;
1210 quit(gettext("Internal Error"), EINVAL
);
1215 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1220 dash_keyword
= B_TRUE
;
1221 key
= keyword(tok
+ 1);
1223 dash_keyword
= B_FALSE
;
1225 if (key
!= K_HOST
&& key
!= K_NET
) {
1226 /* All others must be preceded by '-' */
1232 if (atype
== ADDR_TYPE_NET
) {
1233 syntax_error(gettext("route: -host and -net "
1234 "are mutually exclusive\n"));
1237 atype
= ADDR_TYPE_HOST
;
1240 if (atype
== ADDR_TYPE_HOST
) {
1241 syntax_error(gettext("route: -host and -net "
1242 "are mutually exclusive\n"));
1245 atype
= ADDR_TYPE_NET
;
1248 rcip
->ri_af
= AF_LINK
;
1251 rcip
->ri_af
= AF_INET
;
1254 rcip
->ri_af
= PF_ROUTE
;
1257 rcip
->ri_af
= AF_INET6
;
1264 rcip
->ri_flags
&= ~RTF_STATIC
;
1273 rcip
->ri_flags
|= RTF_REJECT
;
1276 rcip
->ri_flags
|= RTF_BLACKHOLE
;
1279 rcip
->ri_flags
|= RTF_PROTO1
;
1282 rcip
->ri_flags
|= RTF_PROTO2
;
1285 rcip
->ri_flags
|= RTF_CLONING
;
1288 rcip
->ri_flags
|= RTF_XRESOLVE
;
1291 rcip
->ri_flags
|= RTF_STATIC
;
1295 syntax_arg_missing(keyword_str
);
1298 if (!getaddr(rcip
, RTA_IFA
, tok
, atype
)) {
1304 syntax_arg_missing(keyword_str
);
1307 if (!getaddr(rcip
, RTA_IFP
, tok
, atype
)) {
1313 syntax_arg_missing(keyword_str
);
1316 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1322 syntax_arg_missing(keyword_str
);
1325 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1331 syntax_arg_missing(keyword_str
);
1334 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1337 atype
= ADDR_TYPE_NET
;
1348 syntax_arg_missing(keyword_str
);
1351 set_metric(rcip
, tok
, key
, locknext
|| lockrest
);
1355 rcip
->ri_flags
|= RTF_PRIVATE
;
1358 rcip
->ri_flags
|= RTF_MULTIRT
;
1362 syntax_arg_missing(keyword_str
);
1365 if (!getaddr(rcip
, RTA_SRC
, tok
, atype
)) {
1368 rcip
->ri_flags
|= RTF_SETSRC
;
1371 rcip
->ri_flags
|= RTF_INDIRECT
;
1375 syntax_bad_keyword(tok
+ 1);
1378 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1379 if (!getaddr(rcip
, RTA_DST
, tok
, atype
)) {
1382 } else if ((rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1384 * For the gateway parameter, retrieve the
1385 * pointer to the struct hostent so that all
1386 * possible addresses can be tried until one
1389 if (!getaddr(rcip
, RTA_GATEWAY
, tok
, atype
)) {
1395 * Assume that a regular number is a metric.
1396 * Needed for compatibility with old route
1400 metric
= strtoul(tok
, &err
, 10);
1401 if (errno
== 0 && *err
== '\0' &&
1402 metric
< 0x80000000ul
) {
1403 iflag
= (metric
== 0);
1405 (void) printf("old usage of "
1406 "trailing number, assuming "
1407 "route %s\n", iflag
?
1408 "to if" : "via gateway");
1412 if (!getaddr(rcip
, RTA_NETMASK
, tok
, atype
)) {
1420 if ((rcip
->ri_addrs
& RTA_DST
) == 0) {
1421 syntax_error(gettext("route: destination required\n"));
1423 } else if ((rcip
->ri_cmd
== RTM_ADD
|| rcip
->ri_cmd
== RTM_DELETE
) &&
1424 (rcip
->ri_addrs
& RTA_GATEWAY
) == 0) {
1425 syntax_error(gettext(
1426 "route: gateway required for add or delete command\n"));
1431 rcip
->ri_flags
|= RTF_GATEWAY
;
1434 if (atype
!= ADDR_TYPE_NET
) {
1435 if (rcip
->ri_addrs
& RTA_NETMASK
) {
1437 * We know the netmask, so we can set the host flag
1438 * based on whether the netmask is the host netmask.
1440 if (rcip
->ri_af
== AF_INET
&&
1441 rcip
->ri_mask
.sin
.sin_addr
.s_addr
==
1443 rcip
->ri_flags
|= RTF_HOST
;
1445 if (rcip
->ri_af
== AF_INET6
&&
1446 memcmp(&rcip
->ri_mask
.sin6
.sin6_addr
,
1448 sizeof (struct in6_addr
)) == 0) {
1449 rcip
->ri_flags
|= RTF_HOST
;
1453 * If no prefix mask has been saved at this point, it
1454 * only makes sense to treat the destination address
1455 * as a host address.
1457 rcip
->ri_flags
|= RTF_HOST
;
1464 * This command always seeks to the end of the file prior to writing.
1467 write_to_rtfile(FILE *fp
, int argc
, char **argv
)
1469 char file_line
[BUF_SIZE
];
1474 if (early_v6_keyword
) {
1476 * This flag is set when "inet6" was seen as an
1477 * argument to the -f flag. Normally, when writing
1478 * routes to the persistent route file, everything on
1479 * the command line after "add" is saved verbatim.
1480 * In this case, the arguments after "add" may not be
1481 * sufficient, as the ipv6 keyword came before "add",
1482 * yet must be present in the persistent route file.
1484 len
+= snprintf(file_line
, BUF_SIZE
, "-inet6 ");
1486 for (i
= 0; argc
> 0 && len
< BUF_SIZE
; i
++, argc
--) {
1487 len
+= snprintf(&file_line
[len
], BUF_SIZE
- len
, "%s ",
1490 if (len
>= BUF_SIZE
)
1491 quit(gettext("Internal Error"), EINVAL
);
1492 file_line
[len
- 1] = '\n';
1493 if (fseek(fp
, 0, SEEK_END
) != 0 ||
1494 fputs(file_line
, fp
) == EOF
) {
1495 quit(gettext("failed to write to route file"),
1501 compare_rtcmd(rtcmd_irep_t
*srch_rt
, rtcmd_irep_t
*file_rt
)
1503 if (strcmp(srch_rt
->ri_dest_str
, file_rt
->ri_dest_str
) != 0 ||
1504 memcmp(&srch_rt
->ri_mask
, &file_rt
->ri_mask
, sizeof (su_t
)) != 0) {
1507 return (srch_rt
->ri_gate_str
== NULL
||
1508 strcmp(srch_rt
->ri_gate_str
, file_rt
->ri_gate_str
) == 0);
1512 * Search the route file for routes matching the supplied route. There are 3
1513 * modes of operation:
1514 * SEARCH_MODE_RET - no side effects.
1515 * SEARCH_MODE_PRINT - prints each matching line.
1516 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1518 * In all cases, the number of matches is returned. If rt is NULL, all routes
1519 * matching the global af value are considered matching.
1522 search_rtfile(FILE *fp
, FILE *temp_fp
, rtcmd_irep_t
*rt
, search_mode_t mode
)
1527 char file_line
[BUF_SIZE
+ 4] = "add ";
1528 rtcmd_irep_t
*thisrt
;
1533 * Leave space at the beginning of file_line for "add ".
1535 while (fgets(file_line
+ 4, BUF_SIZE
, fp
) != NULL
) {
1537 if (file_line
[4] == '#' || file_line
[4] == '\n') {
1538 /* Handle comments and blank lines */
1539 if (mode
== SEARCH_MODE_DEL
&&
1540 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1542 "route: failed to write to temp file"),
1547 thisrt
= new_rtcmd_irep();
1549 * thisrt->ri_af defaults to whatever address family happens
1550 * to be set in the global af, but routes in the persistent
1551 * route file must be treated as AF_INET by default.
1553 thisrt
->ri_af
= AF_INET
;
1555 exit_on_error
= B_FALSE
;
1556 tmp_buf
= strdup(file_line
);
1557 /* args_to_rtcmd() will mangle the string passed. */
1558 if (!args_to_rtcmd(thisrt
, NULL
, tmp_buf
)) {
1559 /* There was an error in args_to_rtcmd() or helpers */
1560 del_rtcmd_irep(thisrt
);
1564 exit_on_error
= B_TRUE
;
1567 if (thisrt
->ri_gate_str
== NULL
) {
1568 del_rtcmd_irep(thisrt
);
1571 match
= (rt
== NULL
) ? (thisrt
->ri_af
== af
) :
1572 compare_rtcmd(rt
, thisrt
);
1574 if (match
) match_cnt
++;
1575 if (match
&& mode
== SEARCH_MODE_PRINT
) {
1576 (void) printf("persistent: route %s", file_line
);
1578 if (match
&& mode
== SEARCH_MODE_DEL
) {
1579 thisrt
->ri_cmd
= RTM_DELETE
;
1580 print_rtcmd_short(stdout
, thisrt
, B_FALSE
, B_TRUE
);
1581 (void) printf("\n");
1583 del_rtcmd_irep(thisrt
);
1585 if (!match
&& mode
== SEARCH_MODE_DEL
&&
1586 fputs(file_line
+ 4, temp_fp
) == EOF
) {
1587 quit(gettext("failed to write to temp file"),
1595 * Perform the route operation given in argv on the persistent route file.
1596 * If do_flush is set, the persistent route file is flushed of all routes
1597 * matching the global family, and the arguments are ignored.
1600 save_route(int argc
, char **argv
, int do_flush
)
1606 mode_t fmode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
1609 const char commentstr
[] =
1610 "# File generated by route(1M) - do not edit.\n";
1612 perm_fd
= open(perm_file
, O_RDWR
| O_CREAT
, fmode
);
1613 if (perm_fd
== -1 || fstat(perm_fd
, &st
) == -1)
1614 quit("failed to open route file", errno
);
1616 lock
.l_type
= F_WRLCK
;
1617 lock
.l_whence
= SEEK_SET
;
1620 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1621 quit(gettext("failed to lock route file"), errno
);
1624 if (st
.st_size
== 0 &&
1625 write(perm_fd
, commentstr
, sizeof (commentstr
) - 1) !=
1626 sizeof (commentstr
) - 1)
1627 quit(gettext("failed to open route file"), errno
);
1629 if ((perm_fp
= fdopen(perm_fd
, "r+")) == NULL
) {
1630 quit(gettext("failed to open route file"), errno
);
1635 rt
= new_rtcmd_irep();
1636 (void) args_to_rtcmd(rt
, argv
, NULL
);
1638 if (do_flush
|| rt
->ri_cmd
== RTM_DELETE
) {
1639 if ((temp_fp
= fopen(temp_file
, "w")) == NULL
) {
1640 quit(gettext("failed to open temp file"), errno
);
1645 (void) search_rtfile(perm_fp
, temp_fp
, NULL
, SEARCH_MODE_DEL
);
1646 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1647 quit(gettext("failed to update route file"), errno
);
1650 (void) fclose(perm_fp
);
1654 switch (rt
->ri_cmd
) {
1656 if (search_rtfile(perm_fp
, NULL
, rt
, SEARCH_MODE_NULL
) > 0) {
1657 /* Route is already in the file */
1658 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1659 (void) fprintf(stderr
, ": entry exists\n");
1662 write_to_rtfile(perm_fp
, argc
- 1, argv
+ 1);
1663 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1664 (void) printf("\n");
1669 gettext("route: change command not supported with -p\n"));
1673 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_DEL
) <= 0) {
1674 /* Route not found */
1675 print_rtcmd_short(stderr
, rt
, B_FALSE
, B_TRUE
);
1676 (void) fprintf(stderr
, gettext(": not in file\n"));
1679 if (fclose(temp_fp
) != 0 || rename(temp_file
, perm_file
) != 0) {
1680 quit(gettext("failed to update route file"), errno
);
1686 if (search_rtfile(perm_fp
, temp_fp
, rt
, SEARCH_MODE_PRINT
) <=
1688 print_rtcmd_short(stdout
, rt
, B_FALSE
, B_TRUE
);
1689 (void) printf(gettext(": not in file\n"));
1694 quit(gettext("Internal Error"), EINVAL
);
1699 * Closing the file unlocks it.
1701 (void) fclose(perm_fp
);
1705 show_saved_routes(int argc
)
1713 syntax_error(gettext("route: invalid arguments for show\n"));
1716 perm_fd
= open(perm_file
, O_RDONLY
, 0);
1718 if (perm_fd
== -1) {
1719 if (errno
== ENOENT
) {
1720 (void) printf("No persistent routes are defined\n");
1723 quit(gettext("failed to open route file"), errno
);
1726 lock
.l_type
= F_RDLCK
;
1727 lock
.l_whence
= SEEK_SET
;
1730 if (fcntl(perm_fd
, F_SETLK
, &lock
) != 0) {
1731 quit(gettext("failed to lock route file"),
1735 if ((perm_fp
= fdopen(perm_fd
, "r")) == NULL
) {
1736 quit(gettext("failed to open route file"), errno
);
1739 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1740 (void) fseek(perm_fp
, 0, SEEK_SET
);
1742 count
+= search_rtfile(perm_fp
, NULL
, NULL
, SEARCH_MODE_PRINT
);
1745 (void) printf("No persistent routes are defined\n");
1747 (void) fclose(perm_fp
);
1752 newroute(char **argv
)
1754 rtcmd_irep_t
*newrt
;
1755 int ret
, attempts
, oerrno
;
1757 char obuf
[INET6_ADDRSTRLEN
];
1758 #define hp (newrt->ri_gate_hp)
1760 newrt
= new_rtcmd_irep();
1761 (void) args_to_rtcmd(newrt
, argv
, NULL
);
1763 if (newrt
->ri_cmd
!= RTM_GET
&& !tflag
) {
1764 /* Don't want to read back our messages */
1765 (void) shutdown(s
, 0);
1767 if (newrt
->ri_addrs
& RTA_IFP
) {
1768 newrt
->ri_ifp
.sdl
.sdl_index
= if_nametoindex(newrt
->ri_ifp_str
);
1769 if (newrt
->ri_ifp
.sdl
.sdl_index
== 0) {
1770 if (errno
!= ENXIO
) {
1771 quit("if_nametoindex", errno
);
1773 (void) fprintf(stderr
,
1774 gettext("route: %s: no such interface\n"),
1779 newrt
->ri_ifp
.sdl
.sdl_family
= AF_LINK
;
1781 for (attempts
= 1; ; attempts
++) {
1783 if ((ret
= rtmsg(newrt
)) == 0)
1785 if (errno
!= ENETUNREACH
&& errno
!= ESRCH
)
1787 if ((newrt
->ri_addrs
& RTA_GATEWAY
) && hp
!= NULL
&&
1788 hp
->h_addr_list
[attempts
] != NULL
) {
1791 (void) memmove(&newrt
->ri_gate
.sin
.sin_addr
,
1792 hp
->h_addr_list
[attempts
], hp
->h_length
);
1795 (void) memmove(&newrt
->ri_gate
.sin6
.sin6_addr
,
1796 hp
->h_addr_list
[attempts
], hp
->h_length
);
1804 if (newrt
->ri_cmd
!= RTM_GET
) {
1805 print_rtcmd_short(stdout
, newrt
, (ret
== 0), B_FALSE
);
1807 (void) printf("\n");
1808 } else if (ret
!= 0) {
1810 * Note: there is nothing additional to print for get
1814 switch (newrt
->ri_af
) {
1816 (void) printf(" %s",
1817 inet_ntoa(newrt
->ri_dst
.sin
.sin_addr
));
1820 if (inet_ntop(AF_INET6
,
1821 (void *)&newrt
->ri_dst
.sin6
.sin6_addr
,
1822 obuf
, INET6_ADDRSTRLEN
) != NULL
) {
1823 (void) printf(" %s", obuf
);
1828 (void) printf("%s", newrt
->ri_dest_str
);
1832 (void) printf("%s", newrt
->ri_dest_str
);
1839 err
= "not in table";
1842 err
= "entry in use";
1845 err
= "routing table overflow";
1848 err
= "entry exists";
1851 err
= "insufficient privileges";
1854 err
= strerror(oerrno
);
1857 (void) printf(": %s\n", err
);
1860 del_rtcmd_irep(newrt
);
1868 * Convert a network number to the corresponding IP address.
1869 * If the RTA_NETMASK hasn't been specified yet set it based
1870 * on the class of address.
1873 inet_makenetandmask(rtcmd_irep_t
*rcip
, in_addr_t net
, struct sockaddr_in
*sin
)
1875 in_addr_t addr
, mask
;
1879 } else if (net
< 128) {
1880 addr
= net
<< IN_CLASSA_NSHIFT
;
1881 mask
= IN_CLASSA_NET
;
1882 } else if (net
< 65536) {
1883 addr
= net
<< IN_CLASSB_NSHIFT
;
1884 mask
= IN_CLASSB_NET
;
1885 } else if (net
< 16777216L) {
1886 addr
= net
<< IN_CLASSC_NSHIFT
;
1887 mask
= IN_CLASSC_NET
;
1890 if ((addr
& IN_CLASSA_HOST
) == 0)
1891 mask
= IN_CLASSA_NET
;
1892 else if ((addr
& IN_CLASSB_HOST
) == 0)
1893 mask
= IN_CLASSB_NET
;
1894 else if ((addr
& IN_CLASSC_HOST
) == 0)
1895 mask
= IN_CLASSC_NET
;
1897 if (IN_CLASSA(addr
))
1898 mask
= IN_CLASSA_NET
;
1899 else if (IN_CLASSB(addr
))
1900 mask
= IN_CLASSB_NET
;
1901 else if (IN_CLASSC(addr
))
1902 mask
= IN_CLASSC_NET
;
1904 mask
= IP_HOST_MASK
;
1905 mask
= inet_makesubnetmask(addr
, mask
);
1908 sin
->sin_addr
.s_addr
= htonl(addr
);
1910 /* Class E default mask is 32 */
1911 if (IN_CLASSE(addr
))
1912 mask
= IN_CLASSE_NET
;
1914 if (!(rcip
->ri_addrs
& RTA_NETMASK
)) {
1915 rcip
->ri_addrs
|= RTA_NETMASK
;
1916 sin
= &rcip
->ri_mask
.sin
;
1917 sin
->sin_addr
.s_addr
= htonl(mask
);
1918 sin
->sin_family
= AF_INET
;
1923 inet_makesubnetmask(in_addr_t addr
, in_addr_t mask
)
1929 struct sockaddr_in
*sin
;
1934 in_addr_t if_addr
, if_mask
;
1935 in_addr_t if_subnetmask
= 0;
1940 if ((iosoc
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1941 quit("socket", errno
);
1942 if (ioctl(iosoc
, SIOCGIFNUM
, (char *)&numifs
) < 0)
1943 quit("ioctl", errno
);
1944 bufsize
= numifs
* sizeof (struct ifreq
);
1945 buf
= malloc(bufsize
);
1947 quit("malloc", errno
);
1948 (void) memset(&ifc
, 0, sizeof (ifc
));
1949 ifc
.ifc_len
= bufsize
;
1951 if (ioctl(iosoc
, SIOCGIFCONF
, (char *)&ifc
) < 0)
1952 quit("ioctl (get interface configuration)", errno
);
1953 /* Let's check to see if this is maybe a local subnet route. */
1955 for (n
= ifc
.ifc_len
/ sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
1958 sin
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1959 if_addr
= ntohl(sin
->sin_addr
.s_addr
);
1961 if (ioctl(iosoc
, SIOCGIFFLAGS
, (char *)&ifreq
) < 0)
1962 quit("ioctl (get interface flags)", errno
);
1963 if ((ifreq
.ifr_flags
& IFF_UP
) == 0)
1965 if_flags
= ifreq
.ifr_flags
;
1967 if (ioctl(iosoc
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0)
1968 quit("ioctl (get netmask)", errno
);
1970 sin
= (struct sockaddr_in
*)&ifreq
.ifr_addr
;
1971 if_mask
= ntohl(sin
->sin_addr
.s_addr
);
1972 if ((if_addr
& mask
) == (addr
& mask
)) {
1974 * Don't trust pt-pt interfaces if there are
1977 if (if_flags
& IFF_POINTOPOINT
) {
1978 if_subnetmask
= if_mask
;
1982 * Fine. Just assume the same net mask as the
1983 * directly attached subnet interface is using.
1988 if (if_subnetmask
!= 0)
1989 return (if_subnetmask
);
1994 * Interpret an argument as a network address of some kind.
1996 * If the address family is one looked up in getaddr() using one of the
1997 * getipnodebyX() functions (currently only AF_INET6), then callers should
1998 * freehostent() the returned "struct hostent" pointer if one was passed in.
2000 * If exit_on_error is true, this function will cause route to exit on error by
2001 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
2005 getaddr(rtcmd_irep_t
*rcip
, int which
, char *s
, addr_type_t atype
)
2008 struct hostent
**hpp
;
2012 if (which
== RTA_GATEWAY
) {
2013 hpp
= &(rcip
->ri_gate_hp
);
2019 rcip
->ri_addrs
|= which
;
2022 save_string(&rcip
->ri_dest_str
, s
);
2024 su
->sa
.sa_family
= rcip
->ri_af
;
2027 save_string(&rcip
->ri_gate_str
, s
);
2028 su
= &rcip
->ri_gate
;
2029 su
->sa
.sa_family
= rcip
->ri_af
;
2032 su
= &rcip
->ri_mask
;
2033 su
->sa
.sa_family
= rcip
->ri_af
;
2036 save_string(&rcip
->ri_ifp_str
, s
);
2039 * RTA_SRC has overloaded meaning. It can represent the
2040 * src address of incoming or outgoing packets.
2044 su
->sa
.sa_family
= rcip
->ri_af
;
2048 su
->sa
.sa_family
= rcip
->ri_af
;
2052 quit(gettext("Internal Error"), EINVAL
);
2055 if (strcmp(s
, "default") == 0) {
2056 if (which
== RTA_DST
) {
2057 return (getaddr(rcip
, RTA_NETMASK
, s
, ADDR_TYPE_NET
));
2059 if (which
== RTA_SRC
) {
2064 switch (rcip
->ri_af
) {
2066 link_addr(s
, &su
->sdl
);
2069 sockaddr(s
, &su
->sa
);
2074 if (!in6_getaddr(s
, &su
->sin6
, &masklen
, hpp
)) {
2077 if (masklen
!= NO_PREFIX
) {
2078 (void) memset(&rcip
->ri_mask
.sin6
.sin6_addr
, 0,
2079 sizeof (rcip
->ri_mask
.sin6
.sin6_addr
));
2080 if (!in_prefixlentomask(masklen
, IPV6_ABITS
,
2081 (uchar_t
*)&rcip
->ri_mask
.sin6
.sin6_addr
)) {
2082 syntax_error(gettext(
2083 "route: bad prefix length: %d\n"),
2087 rcip
->ri_mask
.sin6
.sin6_family
= rcip
->ri_af
;
2088 rcip
->ri_addrs
|= RTA_NETMASK
;
2094 return (in6_getaddr(s
, &su
->sin6
, NULL
, hpp
));
2097 gettext("route: -netmask not supported for IPv6: "
2098 "use <prefix>/<prefix-length> instead\n"));
2101 quit(gettext("Internal Error"), EINVAL
);
2107 if (!in_getaddr(s
, &su
->sin
, &masklen
, which
, hpp
,
2111 if (masklen
!= NO_PREFIX
) {
2112 (void) memset(&rcip
->ri_mask
.sin
.sin_addr
, 0,
2113 sizeof (rcip
->ri_mask
.sin
.sin_addr
));
2114 if (!in_prefixlentomask(masklen
, IP_ABITS
,
2115 (uchar_t
*)&rcip
->ri_mask
.sin
.sin_addr
)) {
2116 syntax_error(gettext(
2117 "route: bad prefix length: %d\n"),
2121 rcip
->ri_mask
.sin
.sin_family
= rcip
->ri_af
;
2122 rcip
->ri_addrs
|= RTA_NETMASK
;
2129 return (in_getaddr(s
, &su
->sin
, NULL
, which
, hpp
, atype
,
2132 quit(gettext("Internal Error"), EINVAL
);
2136 quit(gettext("Internal Error"), EINVAL
);
2143 * Interpret an argument as an IPv4 network address of some kind,
2144 * returning B_TRUE on success or B_FALSE on failure.
2145 * This function will cause an exit() on failure if exit_on_failure is set.
2147 * Note that this tries host interpretation before network interpretation,
2148 * except when -net has been given and the destination address is being parsed.
2150 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2151 * pass out <n> in *plenp.
2152 * If <n> doesn't parse return BAD_ADDR as *plenp.
2153 * If no /<n> is present return NO_PREFIX as *plenp.
2156 in_getaddr(char *s
, struct sockaddr_in
*sin
, int *plenp
, int which
,
2157 struct hostent
**hpp
, addr_type_t atype
, rtcmd_irep_t
*rcip
)
2164 (void) strlcpy(str
, s
, sizeof (str
));
2167 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2169 if (plenp
!= NULL
) {
2172 *plenp
= in_getprefixlen(str
, IP_ABITS
);
2173 if (*plenp
== BAD_ADDR
)
2175 cp
= strchr(str
, '/');
2178 } else if (strchr(str
, '/') != NULL
) {
2179 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2183 (void) memset(sin
, 0, sizeof (*sin
));
2184 sin
->sin_family
= AF_INET
;
2187 * Handle 255.255.255.255 as a special case first.
2189 if (strcmp(str
, "255.255.255.255") == 0) {
2190 sin
->sin_addr
.s_addr
= INADDR_BROADCAST
;
2194 val
= inet_addr(str
);
2195 if (val
!= (in_addr_t
)-1) {
2196 /* Numeric address */
2197 sin
->sin_addr
.s_addr
= val
;
2198 if (which
== RTA_DST
) {
2199 if (atype
== ADDR_TYPE_NET
||
2200 (atype
== ADDR_TYPE_ANY
&&
2201 inet_lnaof(sin
->sin_addr
) == INADDR_ANY
)) {
2202 /* This looks like a network address. */
2203 inet_makenetandmask(rcip
, ntohl(val
),
2209 /* Host or net name */
2210 if (which
!= RTA_DST
|| atype
!= ADDR_TYPE_NET
) {
2211 /* A host name is allowed. */
2212 if ((hp
= gethostbyname(str
)) != NULL
) {
2214 (void) memmove(&sin
->sin_addr
, hp
->h_addr
,
2219 if (atype
!= ADDR_TYPE_HOST
) {
2220 /* A network name is allowed */
2221 if ((np
= getnetbyname(str
)) != NULL
&&
2222 (val
= np
->n_net
) != 0) {
2223 if (which
== RTA_DST
) {
2224 inet_makenetandmask(rcip
, val
, sin
);
2229 syntax_error(gettext("%s: bad value\n"), s
);
2234 * Interpret an argument as an IPv6 network address of some kind,
2235 * returning B_TRUE on success or B_FALSE on failure.
2236 * This function will cause an exit() on failure if exit_on_failure is set.
2238 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2239 * pass out <n> in *plenp.
2240 * If <n> doesn't parse return BAD_ADDR as *plenp.
2241 * If no /<n> is present return NO_PREFIX as *plenp.
2244 in6_getaddr(char *s
, struct sockaddr_in6
*sin6
, int *plenp
,
2245 struct hostent
**hpp
)
2251 (void) strlcpy(str
, s
, sizeof (str
));
2254 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2256 if (plenp
!= NULL
) {
2259 *plenp
= in_getprefixlen(str
, IPV6_ABITS
);
2260 if (*plenp
== BAD_ADDR
)
2262 cp
= strchr(str
, '/');
2265 } else if (strchr(str
, '/') != NULL
) {
2266 syntax_error(gettext("route: %s: unexpected '/'\n"), str
);
2270 (void) memset(sin6
, 0, sizeof (struct sockaddr_in6
));
2271 sin6
->sin6_family
= AF_INET6
;
2273 hp
= getipnodebyname(str
, AF_INET6
, 0, &error_num
);
2276 (void) memmove(&sin6
->sin6_addr
, hp
->h_addr
, hp
->h_length
);
2279 if (error_num
== TRY_AGAIN
) {
2281 * This isn't a problem if we aren't going to use the address
2284 if (!exit_on_error
) {
2287 syntax_error(gettext("route: %s: bad address (try "
2288 "again later)\n"), s
);
2291 syntax_error(gettext("route: %s: bad address\n"), s
);
2296 * Parse <addr>/<n> syntax and return the integer n.
2297 * If <addr> is missing or <n> is not a valid integer, this function calls
2298 * syntax_error() and returns BAD_ADDR.
2299 * if n is not between 0 and max_plen inclusive, this functions calls
2300 * syntax_error() and returns BAD_ADDR.
2301 * If /<n> is not present, this function returns NO_PREFIX.
2302 * The string addr is not modified.
2305 in_getprefixlen(char *addr
, int max_plen
)
2310 str
= strchr(addr
, '/');
2312 syntax_error(gettext("route: %s: unexpected '/'\n"), addr
);
2320 prefixlen
= strtoul(str
, &end
, 10);
2321 if (errno
!= 0 || str
== end
) {
2322 syntax_error(gettext("route: bad prefix length %s\n"), str
);
2325 if (prefixlen
> max_plen
) {
2326 syntax_error(gettext("route: prefix length %s out of range\n"),
2334 * Convert a prefix length to a mask.
2335 * Returns B_TRUE if ok. B_FALSE otherwise.
2336 * Assumes the mask array is zeroed by the caller.
2339 in_prefixlentomask(int prefixlen
, int maxlen
, uchar_t
*mask
)
2341 if (prefixlen
< 0 || prefixlen
> maxlen
)
2344 while (prefixlen
> 0) {
2345 if (prefixlen
>= 8) {
2350 *mask
|= 1 << (8 - prefixlen
);
2357 rtmonitor(int argc
, char *argv
[])
2360 intmax_t msg
[2048 / sizeof (intmax_t)];
2367 if (argc
== 2 && **argv
== '-') {
2368 switch (keyword(*argv
+ 1)) {
2386 s
= socket(PF_ROUTE
, SOCK_RAW
, af
);
2388 quit("socket", errno
);
2391 n
= read(s
, msg
, sizeof (msg
));
2393 quit("read", errno
);
2394 (void) printf("got message of size %d\n", n
);
2395 print_rtmsg((struct rt_msghdr
*)msg
, n
);
2400 rtmsg(rtcmd_irep_t
*newrt
)
2404 char *cp
= m_rtmsg
.m_space
;
2408 (void) memset(&m_rtmsg
, 0, sizeof (m_rtmsg
));
2410 if (newrt
->ri_cmd
== RTM_GET
) {
2411 newrt
->ri_ifp
.sa
.sa_family
= AF_LINK
;
2412 newrt
->ri_addrs
|= RTA_IFP
;
2415 #define rtm m_rtmsg.m_rtm
2416 rtm
.rtm_type
= newrt
->ri_cmd
;
2417 rtm
.rtm_flags
= newrt
->ri_flags
;
2418 rtm
.rtm_version
= RTM_VERSION
;
2419 rtm
.rtm_seq
= ++seq
;
2420 rtm
.rtm_addrs
= newrt
->ri_addrs
;
2421 rtm
.rtm_rmx
= newrt
->ri_metrics
;
2422 rtm
.rtm_inits
= newrt
->ri_inits
;
2424 #define NEXTADDR(w, u) \
2425 if (newrt->ri_addrs & (w)) { \
2426 l = ROUNDUP_LONG(salen(&u.sa)); \
2427 (void) memmove(cp, &(u), l); \
2432 NEXTADDR(RTA_DST
, newrt
->ri_dst
);
2433 NEXTADDR(RTA_GATEWAY
, newrt
->ri_gate
);
2434 NEXTADDR(RTA_NETMASK
, newrt
->ri_mask
);
2435 NEXTADDR(RTA_IFP
, newrt
->ri_ifp
);
2436 NEXTADDR(RTA_IFA
, newrt
->ri_ifa
);
2438 * RTA_SRC has overloaded meaning. It can represent the
2439 * src address of incoming or outgoing packets.
2441 NEXTADDR(RTA_SRC
, newrt
->ri_src
);
2444 rtm
.rtm_msglen
= l
= cp
- (char *)&m_rtmsg
;
2447 print_rtmsg(&rtm
, l
);
2450 if ((rlen
= write(s
, (char *)&m_rtmsg
, l
)) < 0) {
2461 perror(gettext("writing to routing socket"));
2465 } else if (rlen
< (int)rtm
.rtm_msglen
) {
2466 (void) fprintf(stderr
,
2467 gettext("route: write to routing socket got only %d for "
2471 if (newrt
->ri_cmd
== RTM_GET
) {
2473 l
= read(s
, (char *)&m_rtmsg
, sizeof (m_rtmsg
));
2474 } while (l
> 0 && (rtm
.rtm_seq
!= seq
|| rtm
.rtm_pid
!= pid
));
2476 (void) fprintf(stderr
,
2477 gettext("route: read from routing socket: %s\n"),
2480 print_getmsg(newrt
, &rtm
, l
);
2487 static char *msgtypes
[] = {
2489 "RTM_ADD: Add Route",
2490 "RTM_DELETE: Delete Route",
2491 "RTM_CHANGE: Change Metrics or flags",
2492 "RTM_GET: Report Metrics",
2493 "RTM_LOSING: Kernel Suspects Partitioning",
2494 "RTM_REDIRECT: Told to use different route",
2495 "RTM_MISS: Lookup failed on this address",
2496 "RTM_LOCK: fix specified metrics",
2497 "RTM_OLDADD: caused by SIOCADDRT",
2498 "RTM_OLDDEL: caused by SIOCDELRT",
2499 "RTM_RESOLVE: Route created by cloning",
2500 "RTM_NEWADDR: address being brought up on iface",
2501 "RTM_DELADDR: address being brought down on iface",
2502 "RTM_IFINFO: iface status change",
2503 "RTM_CHGADDR: address being changed on iface",
2504 "RTM_FREEADDR: address being removed from iface",
2508 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2510 static char metricnames
[] =
2511 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2513 static char routeflags
[] =
2514 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2515 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2516 "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
2517 "\024KERNEL\025ZONE";
2518 static char ifnetflags
[] =
2519 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2520 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2521 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2522 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2523 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2524 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2525 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2527 static char addrnames
[] =
2528 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2531 print_rtmsg(struct rt_msghdr
*rtm
, int msglen
)
2533 struct if_msghdr
*ifm
;
2534 struct ifa_msghdr
*ifam
;
2538 if (rtm
->rtm_version
!= RTM_VERSION
) {
2539 (void) printf("routing message version %d not understood\n",
2543 if (rtm
->rtm_msglen
!= msglen
) {
2544 (void) printf("message length mismatch, in packet %d, "
2546 rtm
->rtm_msglen
, msglen
);
2547 if (msglen
> rtm
->rtm_msglen
)
2548 msglen
= rtm
->rtm_msglen
;
2551 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2552 * and the upper-bound of (NMSGTYPES - 1).
2554 if (rtm
->rtm_type
== 0 || rtm
->rtm_type
>= (NMSGTYPES
- 1)) {
2555 (void) printf("routing message type %d not understood\n",
2559 (void) printf("%s: len %d, ", msgtypes
[rtm
->rtm_type
], msglen
);
2560 switch (rtm
->rtm_type
) {
2562 ifm
= (struct if_msghdr
*)rtm
;
2563 (void) printf("if# %d, flags:", ifm
->ifm_index
);
2564 bprintf(stdout
, ifm
->ifm_flags
, ifnetflags
);
2565 pmsg_addrs((const char *)(ifm
+ 1), msglen
- sizeof (*ifm
),
2572 ifam
= (struct ifa_msghdr
*)rtm
;
2573 (void) printf("metric %d, flags:", ifam
->ifam_metric
);
2574 bprintf(stdout
, ifam
->ifam_flags
, routeflags
);
2575 pmsg_addrs((const char *)(ifam
+ 1), msglen
- sizeof (*ifam
),
2579 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2580 rtm
->rtm_pid
, rtm
->rtm_seq
, rtm
->rtm_errno
);
2581 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2582 pmsg_common(rtm
, msglen
);
2588 print_getmsg(rtcmd_irep_t
*req_rt
, struct rt_msghdr
*rtm
, int msglen
)
2590 struct sockaddr
*dst
= NULL
, *gate
= NULL
, *mask
= NULL
, *src
= NULL
;
2591 struct sockaddr_dl
*ifp
= NULL
;
2592 struct sockaddr
*sa
;
2596 (void) printf(" route to: %s\n", routename(&req_rt
->ri_dst
.sa
));
2597 if (rtm
->rtm_version
!= RTM_VERSION
) {
2598 (void) fprintf(stderr
,
2599 gettext("routing message version %d not understood\n"),
2603 if (rtm
->rtm_msglen
> (ushort_t
)msglen
) {
2604 (void) fprintf(stderr
,
2605 gettext("message length mismatch, in packet %d, "
2606 "returned %d\n"), rtm
->rtm_msglen
, msglen
);
2608 if (rtm
->rtm_errno
) {
2609 (void) fprintf(stderr
, "RTM_GET: %s (errno %d)\n",
2610 strerror(rtm
->rtm_errno
), rtm
->rtm_errno
);
2613 cp
= ((char *)(rtm
+ 1));
2614 if (rtm
->rtm_addrs
!= 0) {
2615 for (i
= 1; i
!= 0; i
<<= 1) {
2616 if (i
& rtm
->rtm_addrs
) {
2618 sa
= (struct sockaddr
*)cp
;
2630 if (sa
->sa_family
== AF_LINK
&&
2631 ((struct sockaddr_dl
*)sa
)->
2633 ifp
= (struct sockaddr_dl
*)sa
;
2643 if (dst
!= NULL
&& mask
!= NULL
)
2644 mask
->sa_family
= dst
->sa_family
; /* XXX */
2646 (void) printf("destination: %s\n", routename(dst
));
2648 boolean_t savenflag
= nflag
;
2651 (void) printf(" mask: %s\n", routename(mask
));
2654 if (gate
!= NULL
&& rtm
->rtm_flags
& RTF_GATEWAY
)
2655 (void) printf(" gateway: %s\n", routename(gate
));
2656 if (src
!= NULL
&& rtm
->rtm_flags
& RTF_SETSRC
)
2657 (void) printf(" setsrc: %s\n", routename(src
));
2662 (void) printf(" interface: %.*s index %d address ",
2663 ifp
->sdl_nlen
, ifp
->sdl_data
, ifp
->sdl_index
);
2664 for (i
= ifp
->sdl_nlen
;
2665 i
< ifp
->sdl_nlen
+ ifp
->sdl_alen
;
2667 (void) printf("%02x ",
2668 ifp
->sdl_data
[i
] & 0xFF);
2670 (void) printf("\n");
2672 (void) printf(" interface: %.*s\n",
2673 ifp
->sdl_nlen
, ifp
->sdl_data
);
2676 (void) printf(" flags: ");
2677 bprintf(stdout
, rtm
->rtm_flags
, routeflags
);
2679 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2680 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2682 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2683 "rttvar,ms hopcount mtu expire");
2684 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_recvpipe
, lock(RPIPE
));
2685 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_sendpipe
, lock(SPIPE
));
2686 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_ssthresh
, lock(SSTHRESH
));
2687 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rtt
), lock(RTT
));
2688 (void) printf("%8d%c ", msec(rtm
->rtm_rmx
.rmx_rttvar
), lock(RTTVAR
));
2689 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_hopcount
, lock(HOPCOUNT
));
2690 (void) printf("%8d%c ", rtm
->rtm_rmx
.rmx_mtu
, lock(MTU
));
2691 if (rtm
->rtm_rmx
.rmx_expire
)
2692 rtm
->rtm_rmx
.rmx_expire
-= time(0);
2693 (void) printf("%8d%c", rtm
->rtm_rmx
.rmx_expire
, lock(EXPIRE
));
2697 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2699 pmsg_common(rtm
, msglen
);
2701 const char *sptr
, *endptr
;
2702 const struct sockaddr
*sa
;
2705 /* Not verbose; just print out the exceptional cases */
2706 if (rtm
->rtm_addrs
&~ RTA_IGN
) {
2707 (void) printf("\nsockaddrs: ");
2708 bprintf(stdout
, rtm
->rtm_addrs
, addrnames
);
2710 sptr
= (const char *)(rtm
+ 1);
2711 endptr
= (const char *)rtm
+ msglen
;
2712 addrs
= rtm
->rtm_addrs
;
2713 while (addrs
!= 0 && sptr
+ sizeof (*sa
) <= endptr
) {
2716 sa
= (const struct sockaddr
*)sptr
;
2719 (void) putchar('\n');
2725 pmsg_common(const struct rt_msghdr
*rtm
, size_t msglen
)
2727 (void) printf("\nlocks: ");
2728 bprintf(stdout
, (int)rtm
->rtm_rmx
.rmx_locks
, metricnames
);
2729 (void) printf(" inits: ");
2730 bprintf(stdout
, (int)rtm
->rtm_inits
, metricnames
);
2731 pmsg_addrs((const char *)(rtm
+ 1), msglen
- sizeof (*rtm
),
2736 pmsg_addrs(const char *cp
, size_t msglen
, uint_t addrs
)
2738 const struct sockaddr
*sa
;
2743 (void) printf("\nsockaddrs: ");
2744 bprintf(stdout
, addrs
, addrnames
);
2745 (void) putchar('\n');
2746 maxptr
= cp
+ msglen
;
2747 for (i
= 1; i
!= 0 && cp
+ sizeof (*sa
) <= maxptr
; i
<<= 1) {
2750 sa
= (const struct sockaddr
*)cp
;
2751 (void) printf(" %s", routename(sa
));
2758 msglen
= maxptr
- cp
;
2760 (void) putchar('\n');
2761 (void) fflush(stdout
);
2765 bprintf(FILE *fp
, int b
, char *s
)
2768 boolean_t gotsome
= B_FALSE
;
2772 while ((i
= *s
++) != 0) {
2773 if (b
& (1 << (i
- 1))) {
2780 for (; (i
= *s
) > ' '; s
++)
2788 (void) putc('>', fp
);
2792 keyword(const char *cp
)
2794 struct keytab
*kt
= keywords
;
2796 while (kt
->kt_cp
&& strcmp(kt
->kt_cp
, cp
))
2802 sodump(su_t
*su
, char *which
)
2804 static char obuf
[INET6_ADDRSTRLEN
];
2806 switch (su
->sa
.sa_family
) {
2808 (void) printf("%s: link %s; ",
2809 which
, link_ntoa(&su
->sdl
));
2812 (void) printf("%s: inet %s; ",
2813 which
, inet_ntoa(su
->sin
.sin_addr
));
2816 if (inet_ntop(AF_INET6
, (void *)&su
->sin6
.sin6_addr
, obuf
,
2817 INET6_ADDRSTRLEN
) != NULL
) {
2818 (void) printf("%s: inet6 %s; ", which
, obuf
);
2823 quit(gettext("Internal Error"), EINVAL
);
2826 (void) fflush(stdout
);
2838 #define LETTER (4*3)
2841 sockaddr(char *addr
, struct sockaddr
*sa
)
2843 char *cp
= (char *)sa
;
2844 int size
= salen(sa
);
2845 char *cplim
= cp
+ size
;
2846 int byte
= 0, state
= VIRGIN
, new;
2848 (void) memset(cp
, 0, size
);
2851 if ((*addr
>= '0') && (*addr
<= '9')) {
2853 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2854 new = *addr
- 'a' + 10;
2855 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2856 new = *addr
- 'A' + 10;
2857 } else if (*addr
== 0) {
2863 switch (state
/* | INPUT */) {
2864 case GOTTWO
| DIGIT
:
2867 case VIRGIN
| DIGIT
:
2868 state
= GOTONE
; byte
= new; continue;
2869 case GOTONE
| DIGIT
:
2870 state
= GOTTWO
; byte
= new + (byte
<< 4); continue;
2871 default: /* | DELIM */
2872 state
= VIRGIN
; *cp
++ = byte
; byte
= 0; continue;
2881 } while (cp
< cplim
);
2885 salen(const struct sockaddr
*sa
)
2887 switch (sa
->sa_family
) {
2889 return (sizeof (struct sockaddr_in
));
2891 return (sizeof (struct sockaddr_dl
));
2893 return (sizeof (struct sockaddr_in6
));
2895 return (sizeof (struct sockaddr
));
2900 link_addr(const char *addr
, struct sockaddr_dl
*sdl
)
2902 char *cp
= sdl
->sdl_data
;
2903 char *cplim
= sizeof (struct sockaddr_dl
) + (char *)sdl
;
2904 int byte
= 0, state
= VIRGIN
, new;
2906 (void) memset(sdl
, 0, sizeof (struct sockaddr_dl
));
2907 sdl
->sdl_family
= AF_LINK
;
2910 if ((*addr
>= '0') && (*addr
<= '9')) {
2912 } else if ((*addr
>= 'a') && (*addr
<= 'f')) {
2913 new = *addr
- 'a' + 10;
2914 } else if ((*addr
>= 'A') && (*addr
<= 'F')) {
2915 new = *addr
- 'A' + 10;
2916 } else if (*addr
== 0) {
2918 } else if (state
== VIRGIN
&&
2919 (((*addr
>= 'A') && (*addr
<= 'Z')) ||
2920 ((*addr
>= 'a') && (*addr
<= 'z')))) {
2926 switch (state
/* | INPUT */) {
2927 case VIRGIN
| DIGIT
:
2928 case VIRGIN
| LETTER
:
2931 case VIRGIN
| DELIM
:
2933 sdl
->sdl_nlen
= cp
- sdl
->sdl_data
;
2935 case GOTTWO
| DIGIT
:
2942 case GOTONE
| DIGIT
:
2944 byte
= new + (byte
<< 4);
2946 default: /* | DELIM */
2959 } while (cp
< cplim
);
2960 sdl
->sdl_alen
= cp
- LLADDR(sdl
);
2963 static char hexlist
[] = "0123456789abcdef";
2966 link_ntoa(const struct sockaddr_dl
*sdl
)
2968 static char obuf
[64];
2971 uchar_t
*in
= (uchar_t
*)LLADDR(sdl
);
2972 uchar_t
*inlim
= in
+ sdl
->sdl_alen
;
2973 boolean_t firsttime
= B_TRUE
;
2975 if (sdl
->sdl_nlen
) {
2976 (void) memcpy(obuf
, sdl
->sdl_data
, sdl
->sdl_nlen
);
2977 out
+= sdl
->sdl_nlen
;
2981 while (in
< inlim
) {
2983 firsttime
= B_FALSE
;
2988 out
[1] = hexlist
[i
& 0xf];
2990 out
[0] = hexlist
[i
];
2993 *out
++ = hexlist
[i
];
3003 intmax_t buf
[512 / sizeof (intmax_t)];
3006 struct strbuf ctlbuf
, databuf
;
3007 struct T_optmgmt_req
*tor
= (struct T_optmgmt_req
*)buf
;
3008 struct T_optmgmt_ack
*toa
= (struct T_optmgmt_ack
*)buf
;
3009 struct T_error_ack
*tea
= (struct T_error_ack
*)buf
;
3011 mib_item_t
*first_item
= NULL
;
3012 mib_item_t
*last_item
= NULL
;
3015 tor
->PRIM_type
= T_SVR4_OPTMGMT_REQ
;
3016 tor
->OPT_offset
= sizeof (struct T_optmgmt_req
);
3017 tor
->OPT_length
= sizeof (struct opthdr
);
3018 tor
->MGMT_flags
= T_CURRENT
;
3019 req
= (struct opthdr
*)&tor
[1];
3020 req
->level
= MIB2_IP
; /* any MIB2_xxx value ok here */
3024 ctlbuf
.buf
= (char *)buf
;
3025 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
3027 if (putmsg(sd
, &ctlbuf
, NULL
, flags
) < 0) {
3028 perror("mibget: putmsg (ctl)");
3032 * each reply consists of a ctl part for one fixed structure
3033 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3034 * containing an opthdr structure. level/name identify the entry,
3035 * len is the size of the data part of the message.
3037 req
= (struct opthdr
*)&toa
[1];
3038 ctlbuf
.maxlen
= sizeof (buf
);
3039 for (j
= 1; ; j
++) {
3041 getcode
= getmsg(sd
, &ctlbuf
, NULL
, &flags
);
3043 perror("mibget: getmsg (ctl)");
3045 (void) fprintf(stderr
,
3046 "# level name len\n");
3048 for (last_item
= first_item
; last_item
!= NULL
;
3049 last_item
= last_item
->next_item
) {
3050 (void) printf("%d %4ld %5ld %ld\n",
3051 ++i
, last_item
->group
,
3059 ctlbuf
.len
>= sizeof (struct T_optmgmt_ack
) &&
3060 toa
->PRIM_type
== T_OPTMGMT_ACK
&&
3061 toa
->MGMT_flags
== T_SUCCESS
&&
3064 (void) printf("mibget getmsg() %d returned EOD "
3065 "(level %lu, name %lu)\n", j
, req
->level
,
3068 return (first_item
); /* this is EOD msg */
3071 if (ctlbuf
.len
>= sizeof (struct T_error_ack
) &&
3072 tea
->PRIM_type
== T_ERROR_ACK
) {
3073 (void) fprintf(stderr
, gettext("mibget %d gives "
3074 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3075 "0x%lx\n"), j
, tea
->TLI_error
, tea
->UNIX_error
);
3076 errno
= (tea
->TLI_error
== TSYSERR
) ?
3077 tea
->UNIX_error
: EPROTO
;
3081 if (getcode
!= MOREDATA
||
3082 ctlbuf
.len
< sizeof (struct T_optmgmt_ack
) ||
3083 toa
->PRIM_type
!= T_OPTMGMT_ACK
||
3084 toa
->MGMT_flags
!= T_SUCCESS
) {
3085 (void) printf("mibget getmsg(ctl) %d returned %d, "
3086 "ctlbuf.len = %d, PRIM_type = %ld\n",
3087 j
, getcode
, ctlbuf
.len
, toa
->PRIM_type
);
3088 if (toa
->PRIM_type
== T_OPTMGMT_ACK
) {
3089 (void) printf("T_OPTMGMT_ACK: "
3090 "MGMT_flags = 0x%lx, req->len = %ld\n",
3091 toa
->MGMT_flags
, req
->len
);
3097 temp
= malloc(sizeof (mib_item_t
));
3099 perror("mibget: malloc");
3102 if (last_item
!= NULL
)
3103 last_item
->next_item
= temp
;
3107 last_item
->next_item
= NULL
;
3108 last_item
->group
= req
->level
;
3109 last_item
->mib_id
= req
->name
;
3110 last_item
->length
= req
->len
;
3111 last_item
->valp
= malloc(req
->len
);
3113 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3115 j
, last_item
->group
, last_item
->mib_id
,
3119 databuf
.maxlen
= last_item
->length
;
3120 databuf
.buf
= (char *)last_item
->valp
;
3123 getcode
= getmsg(sd
, NULL
, &databuf
, &flags
);
3125 perror("mibget: getmsg (data)");
3127 } else if (getcode
!= 0) {
3128 (void) printf("mibget getmsg(data) returned %d, "
3129 "databuf.maxlen = %d, databuf.len = %d\n",
3130 getcode
, databuf
.maxlen
, databuf
.len
);
3136 * On error, free all the allocated mib_item_t objects.
3138 while (first_item
!= NULL
) {
3139 last_item
= first_item
;
3140 first_item
= first_item
->next_item
;