kill tsol ("Trusted Solaris") aka TX ("Trusted Extensions")
[unleashed.git] / usr / src / cmd / cmd-inet / usr.sbin / route.c
blobe00810ae96ed2e5fc639d856652974d16052df4a
1 /*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
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
17 * are met:
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
41 * SUCH DAMAGE.
43 * @(#)route.c 8.6 (Berkeley) 4/28/95
44 * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93
47 #include <sys/param.h>
48 #include <sys/file.h>
49 #include <sys/socket.h>
50 #include <sys/ioctl.h>
51 #include <sys/stat.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>
58 #include <net/if.h>
59 #include <net/route.h>
60 #include <net/if_dl.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #include <netdb.h>
64 #include <inet/mib2.h>
65 #include <inet/ip.h>
67 #include <limits.h>
68 #include <locale.h>
70 #include <errno.h>
71 #include <unistd.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <stddef.h>
75 #include <string.h>
76 #include <stropts.h>
77 #include <fcntl.h>
78 #include <stdarg.h>
79 #include <assert.h>
80 #include <strings.h>
82 static struct keytab {
83 char *kt_cp;
84 int kt_i;
85 } keywords[] = {
86 #define K_ADD 1
87 {"add", K_ADD},
88 #define K_BLACKHOLE 2
89 {"blackhole", K_BLACKHOLE},
90 #define K_CHANGE 3
91 {"change", K_CHANGE},
92 #define K_CLONING 4
93 {"cloning", K_CLONING},
94 #define K_DELETE 5
95 {"delete", K_DELETE},
96 #define K_DST 6
97 {"dst", K_DST},
98 #define K_EXPIRE 7
99 {"expire", K_EXPIRE},
100 #define K_FLUSH 8
101 {"flush", K_FLUSH},
102 #define K_GATEWAY 9
103 {"gateway", K_GATEWAY},
104 #define K_GET 11
105 {"get", K_GET},
106 #define K_HOPCOUNT 12
107 {"hopcount", K_HOPCOUNT},
108 #define K_HOST 13
109 {"host", K_HOST},
110 #define K_IFA 14
111 {"ifa", K_IFA},
112 #define K_IFACE 15
113 {"iface", K_IFACE},
114 #define K_IFP 16
115 {"ifp", K_IFP},
116 #define K_INET 17
117 {"inet", K_INET},
118 #define K_INET6 18
119 {"inet6", K_INET6},
120 #define K_INTERFACE 19
121 {"interface", K_INTERFACE},
122 #define K_LINK 20
123 {"link", K_LINK},
124 #define K_LOCK 21
125 {"lock", K_LOCK},
126 #define K_LOCKREST 22
127 {"lockrest", K_LOCKREST},
128 #define K_MASK 23
129 {"mask", K_MASK},
130 #define K_MONITOR 24
131 {"monitor", K_MONITOR},
132 #define K_MTU 25
133 {"mtu", K_MTU},
134 #define K_NET 26
135 {"net", K_NET},
136 #define K_NETMASK 27
137 {"netmask", K_NETMASK},
138 #define K_NOSTATIC 28
139 {"nostatic", K_NOSTATIC},
140 #define K_PRIVATE 29
141 {"private", K_PRIVATE},
142 #define K_PROTO1 30
143 {"proto1", K_PROTO1},
144 #define K_PROTO2 31
145 {"proto2", K_PROTO2},
146 #define K_RECVPIPE 32
147 {"recvpipe", K_RECVPIPE},
148 #define K_REJECT 33
149 {"reject", K_REJECT},
150 #define K_RTT 34
151 {"rtt", K_RTT},
152 #define K_RTTVAR 35
153 {"rttvar", K_RTTVAR},
154 #define K_SA 36
155 {"sa", K_SA},
156 #define K_SENDPIPE 37
157 {"sendpipe", K_SENDPIPE},
158 #define K_SSTHRESH 38
159 {"ssthresh", K_SSTHRESH},
160 #define K_STATIC 39
161 {"static", K_STATIC},
162 #define K_XRESOLVE 40
163 {"xresolve", K_XRESOLVE},
164 #define K_MULTIRT 41
165 {"multirt", K_MULTIRT},
166 #define K_SETSRC 42
167 {"setsrc", K_SETSRC},
168 #define K_SHOW 43
169 {"show", K_SHOW},
170 #define K_INDIRECT 44
171 {"indirect", K_INDIRECT},
172 {0, 0}
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 {
182 struct sockaddr sa;
183 struct sockaddr_in sin;
184 struct sockaddr_dl sdl;
185 struct sockaddr_in6 sin6;
186 } su_t;
189 * This structure represents the digested information from parsing arguments
190 * to route add, change, delete, and get.
193 typedef struct rtcmd_irep {
194 int ri_cmd;
195 int ri_flags;
196 int ri_af;
197 ulong_t ri_inits;
198 struct rt_metrics ri_metrics;
199 int ri_addrs;
200 su_t ri_dst;
201 char *ri_dest_str;
202 su_t ri_src;
203 su_t ri_gate;
204 struct hostent *ri_gate_hp;
205 char *ri_gate_str;
206 su_t ri_mask;
207 su_t ri_ifa;
208 su_t ri_ifp;
209 char *ri_ifp_str;
210 } rtcmd_irep_t;
212 typedef struct mib_item_s {
213 struct mib_item_s *next_item;
214 long group;
215 long mib_id;
216 long length;
217 intmax_t *valp;
218 } mib_item_t;
220 typedef enum {
221 ADDR_TYPE_ANY,
222 ADDR_TYPE_HOST,
223 ADDR_TYPE_NET
224 } addr_type_t;
226 typedef enum {
227 SEARCH_MODE_NULL,
228 SEARCH_MODE_PRINT,
229 SEARCH_MODE_DEL
230 } search_mode_t;
232 static boolean_t args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
233 char *cmd_string);
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,
242 addr_type_t atype);
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,
247 rtcmd_irep_t *rcip);
248 static int in_getprefixlen(char *addr, int max_plen);
249 static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
250 uchar_t *mask);
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,
276 search_mode_t mode);
277 static void set_metric(rtcmd_irep_t *rcip, char *value, int key,
278 boolean_t lock);
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);
288 static pid_t pid;
289 static int s;
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};
306 * WARNING:
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;
317 static struct {
318 struct rt_msghdr m_rtm;
319 char m_space[BUF_SIZE];
320 } m_rtmsg;
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 */
341 void
342 usage(char *cp)
344 if (cp != NULL) {
345 (void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
346 cp);
348 (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
349 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
350 exit(1);
351 /* NOTREACHED */
354 /*PRINTFLIKE1*/
355 void
356 syntax_error(char *err, ...)
358 va_list args;
360 if (exit_on_error) {
361 va_start(args, err);
362 (void) vfprintf(stderr, err, args);
363 va_end(args);
364 exit(1);
366 /* NOTREACHED */
369 void
370 syntax_bad_keyword(char *keyword)
372 syntax_error(gettext("route: botched keyword: %s\n"), keyword);
375 void
376 syntax_arg_missing(char *keyword)
378 syntax_error(gettext("route: argument required following keyword %s\n"),
379 keyword);
382 void
383 quit(char *s, int sverrno)
385 (void) fprintf(stderr, "route: ");
386 if (s != NULL)
387 (void) fprintf(stderr, "%s: ", s);
388 (void) fprintf(stderr, "%s\n", strerror(sverrno));
389 exit(sverrno);
390 /* NOTREACHED */
394 main(int argc, char **argv)
396 extern int optind;
397 extern char *optarg;
398 int ch;
399 int rval;
400 size_t size;
401 const char *root_dir = NULL;
403 (void) setlocale(LC_ALL, "");
405 #if !defined(TEXT_DOMAIN)
406 #define TEXT_DOMAIN "SYS_TEST"
407 #endif
408 (void) textdomain(TEXT_DOMAIN);
410 if (argc < 2)
411 usage(NULL);
413 while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
414 switch (ch) {
415 case 'n':
416 nflag = B_TRUE;
417 break;
418 case 'q':
419 qflag = B_TRUE;
420 break;
421 case 'v':
422 verbose = B_TRUE;
423 break;
424 case 't':
425 tflag = B_TRUE;
426 break;
427 case 'd':
428 debugonly = B_TRUE;
429 break;
430 case 'f':
431 fflag = B_TRUE;
432 break;
433 case 'p':
434 perm_flag = B_TRUE;
435 break;
436 case 'R':
437 root_dir = optarg;
438 break;
439 case '?':
440 default:
441 usage(NULL);
442 /* NOTREACHED */
445 argc -= optind;
446 argv += optind;
448 pid = getpid();
449 if (tflag)
450 s = open("/dev/null", O_WRONLY);
451 else
452 s = socket(PF_ROUTE, SOCK_RAW, 0);
453 if (s < 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;
463 } else {
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,
469 perm_file_sfx);
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,
475 temp_file_sfx);
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);
482 if (tflag)
483 perm_flag = 0;
485 if (fflag) {
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.
491 if (*argv != NULL) {
492 switch (keyword(*argv)) {
493 case K_INET6:
494 af = AF_INET6;
495 early_v6_keyword = B_TRUE;
496 /* fallthrough */
497 case K_INET:
498 /* Skip over the address family parameter. */
499 argc--;
500 argv++;
501 break;
504 flushroutes(0, NULL);
507 if (*argv != NULL) {
508 switch (keyword(*argv)) {
509 case K_GET:
510 case K_CHANGE:
511 case K_ADD:
512 case K_DELETE:
513 rval = 0;
514 if (update_table) {
515 rval = newroute(argv);
517 if (perm_flag && (rval == 0 || rval == EEXIST ||
518 rval == ESRCH)) {
519 save_route(argc, argv, B_FALSE);
520 return (0);
522 return (rval);
523 case K_SHOW:
524 if (perm_flag) {
525 return (show_saved_routes(argc));
526 } else {
527 syntax_error(gettext(
528 "route: show command requires -p\n"));
530 /* NOTREACHED */
531 case K_MONITOR:
532 rtmonitor(argc, argv);
533 /* NOTREACHED */
535 case K_FLUSH:
536 flushroutes(argc, argv);
537 return (0);
540 if (!fflag)
541 usage(*argv);
542 return (0);
546 * Purge all entries in the routing tables not
547 * associated with network interfaces.
549 void
550 flushroutes(int argc, char *argv[])
552 int seqno;
553 int sd; /* mib stream */
554 mib_item_t *item;
555 mib2_ipRouteEntry_t *rp;
556 mib2_ipv6RouteEntry_t *rp6;
557 int oerrno;
558 int off = 0;
559 int on = 1;
561 if (argc > 1) {
562 argv++;
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)) {
569 case K_INET:
570 af = AF_INET;
571 break;
572 case K_LINK:
573 af = AF_LINK;
574 break;
575 case K_INET6:
576 af = AF_INET6;
577 break;
578 default:
579 usage(*argv);
580 /* NOTREACHED */
582 } else {
583 usage(*argv);
586 if (perm_flag) {
587 /* This flushes the persistent route file */
588 save_route(0, NULL, B_TRUE);
590 if (!update_table) {
591 return;
594 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
595 sizeof (off)) < 0)
596 quit("setsockopt", errno);
598 sd = open("/dev/ip", O_RDWR);
599 oerrno = errno;
600 if (sd < 0) {
601 switch (errno) {
602 case EACCES:
603 (void) fprintf(stderr,
604 gettext("route: flush: insufficient privileges\n"));
605 exit(oerrno);
606 /* NOTREACHED */
607 default:
608 quit(gettext("can't open mib stream"), oerrno);
609 /* NOTREACHED */
612 if ((item = mibget(sd)) == NULL)
613 quit("mibget", errno);
614 if (verbose) {
615 (void) printf("Examining routing table from "
616 "T_SVR4_OPTMGMT_REQ\n");
618 seqno = 0; /* ??? */
619 switch (af) {
620 case AF_INET:
621 /* Extract ipRouteEntrySize */
622 for (; item != NULL; item = item->next_item) {
623 if (item->mib_id != 0)
624 continue;
625 if (item->group == MIB2_IP) {
626 ipRouteEntrySize =
627 ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
628 assert(IS_P2ALIGNED(ipRouteEntrySize,
629 sizeof (mib2_ipRouteEntry_t *)));
630 break;
633 if (ipRouteEntrySize == 0) {
634 (void) fprintf(stderr,
635 gettext("ipRouteEntrySize can't be determined.\n"));
636 exit(1);
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)
644 continue;
645 for (rp = (mib2_ipRouteEntry_t *)item->valp;
646 (char *)rp < (char *)item->valp + item->length;
647 /* LINTED */
648 rp = (mib2_ipRouteEntry_t *)
649 ((char *)rp + ipRouteEntrySize)) {
650 delRouteEntry(rp, NULL, seqno);
651 seqno++;
653 break;
655 break;
656 case AF_INET6:
657 /* Extract ipv6RouteEntrySize */
658 for (; item != NULL; item = item->next_item) {
659 if (item->mib_id != 0)
660 continue;
661 if (item->group == MIB2_IP6) {
662 ipv6RouteEntrySize =
663 ((mib2_ipv6IfStatsEntry_t *)item->valp)->
664 ipv6RouteEntrySize;
665 assert(IS_P2ALIGNED(ipv6RouteEntrySize,
666 sizeof (mib2_ipv6RouteEntry_t *)));
667 break;
670 if (ipv6RouteEntrySize == 0) {
671 (void) fprintf(stderr, gettext(
672 "ipv6RouteEntrySize cannot be determined.\n"));
673 exit(1);
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)
681 continue;
682 for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
683 (char *)rp6 < (char *)item->valp + item->length;
684 /* LINTED */
685 rp6 = (mib2_ipv6RouteEntry_t *)
686 ((char *)rp6 + ipv6RouteEntrySize)) {
687 delRouteEntry(NULL, rp6, seqno);
688 seqno++;
690 break;
692 break;
695 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
696 sizeof (on)) < 0)
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.
705 static void
706 delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
708 char *cp;
709 int ire_type;
710 int rlen;
711 struct rt_msghdr *rtm;
712 struct sockaddr_in sin;
713 struct sockaddr_in6 sin6;
714 int slen;
716 if (rp != NULL)
717 ire_type = rp->ipRouteInfo.re_ire_type;
718 else
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)
724 return;
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;
734 if (rp != NULL) {
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);
742 cp += slen;
743 sin.sin_addr.s_addr = rp->ipRouteNextHop;
744 (void) memmove(cp, &sin, slen);
745 cp += slen;
746 sin.sin_addr.s_addr = rp->ipRouteMask;
747 (void) memmove(cp, &sin, slen);
748 cp += slen;
749 } else {
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);
757 cp += slen;
758 sin6.sin6_addr = rp6->ipv6RouteNextHop;
759 (void) memmove(cp, &sin6, slen);
760 cp += 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);
765 cp += slen;
767 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
768 if (debugonly) {
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
773 * is printed.
775 if (verbose)
776 print_rtmsg(rtm, rtm->rtm_msglen);
777 return;
780 rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
781 if (rlen < (int)rtm->rtm_msglen) {
782 if (rlen < 0) {
783 (void) fprintf(stderr,
784 gettext("route: write to routing socket: %s\n"),
785 strerror(errno));
786 } else {
787 (void) fprintf(stderr, gettext("route: write to "
788 "routing socket got only %d for rlen\n"), rlen);
790 return;
792 if (qflag) {
794 * In quiet mode, nothing is printed at all (unless the write()
795 * itself failed.
797 return;
799 if (verbose) {
800 print_rtmsg(rtm, rlen);
801 } else {
802 struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
804 (void) printf("%-20.20s ",
805 rtm->rtm_flags & RTF_HOST ? routename(sa) :
806 netname(sa));
807 /* LINTED */
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.
817 char *
818 routename(const struct sockaddr *sa)
820 char *cp;
821 static char line[MAXHOSTNAMELEN + 1];
822 struct hostent *hp = NULL;
823 static char domain[MAXHOSTNAMELEN + 1];
824 static boolean_t first = B_TRUE;
825 struct in_addr in;
826 struct in6_addr in6;
827 int error_num;
828 ushort_t *s;
829 ushort_t *slim;
831 if (first) {
832 first = B_FALSE;
833 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
834 (cp = strchr(domain, '.')))
835 (void) strcpy(domain, cp + 1);
836 else
837 domain[0] = 0;
840 if (salen(sa) == 0) {
841 (void) strcpy(line, "default");
842 return (line);
844 switch (sa->sa_family) {
846 case AF_INET:
847 /* LINTED */
848 in = ((struct sockaddr_in *)sa)->sin_addr;
850 cp = NULL;
851 if (in.s_addr == INADDR_ANY)
852 cp = "default";
853 if (cp == NULL && !nflag) {
854 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
855 AF_INET);
856 if (hp != NULL) {
857 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
858 (strcmp(cp + 1, domain) == 0))
859 *cp = 0;
860 cp = hp->h_name;
863 if (cp != NULL) {
864 (void) strncpy(line, cp, MAXHOSTNAMELEN);
865 line[MAXHOSTNAMELEN] = '\0';
866 } else {
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),
870 C(in.s_addr));
872 break;
874 case AF_LINK:
875 return (link_ntoa((struct sockaddr_dl *)sa));
877 case AF_INET6:
878 /* LINTED */
879 in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
881 cp = NULL;
882 if (IN6_IS_ADDR_UNSPECIFIED(&in6))
883 cp = "default";
884 if (cp == NULL && !nflag) {
885 hp = getipnodebyaddr((char *)&in6,
886 sizeof (struct in6_addr), AF_INET6, &error_num);
887 if (hp != NULL) {
888 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
889 (strcmp(cp + 1, domain) == 0))
890 *cp = 0;
891 cp = hp->h_name;
894 if (cp != NULL) {
895 (void) strncpy(line, cp, MAXHOSTNAMELEN);
896 line[MAXHOSTNAMELEN] = '\0';
897 } else {
898 (void) inet_ntop(AF_INET6, (void *)&in6, line,
899 INET6_ADDRSTRLEN);
901 if (hp != NULL)
902 freehostent(hp);
904 break;
906 default:
907 s = (ushort_t *)sa;
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);
914 break;
916 return (line);
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.
923 static char *
924 netname(struct sockaddr *sa)
926 char *cp = NULL;
927 static char line[MAXHOSTNAMELEN + 1];
928 struct netent *np;
929 in_addr_t net, mask;
930 int subnetshift;
931 struct in_addr in;
932 ushort_t *s;
933 ushort_t *slim;
935 switch (sa->sa_family) {
937 case AF_INET:
938 /* LINTED */
939 in = ((struct sockaddr_in *)sa)->sin_addr;
941 in.s_addr = ntohl(in.s_addr);
942 if (in.s_addr == INADDR_ANY) {
943 cp = "default";
944 } else if (!nflag) {
945 if (IN_CLASSA(in.s_addr)) {
946 mask = IN_CLASSA_NET;
947 subnetshift = 8;
948 } else if (IN_CLASSB(in.s_addr)) {
949 mask = IN_CLASSB_NET;
950 subnetshift = 8;
951 } else {
952 mask = IN_CLASSC_NET;
953 subnetshift = 4;
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);
967 if (np != NULL)
968 cp = np->n_name;
970 if (cp != NULL) {
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),
977 C(in.s_addr >> 16));
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));
981 } else {
982 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
983 C(in.s_addr >> 16), C(in.s_addr >> 8),
984 C(in.s_addr));
986 break;
988 case AF_LINK:
989 return (link_ntoa((struct sockaddr_dl *)sa));
991 case AF_INET6:
992 return (routename(sa));
994 default:
995 /* LINTED */
996 s = (ushort_t *)sa->sa_data;
998 slim = s + ((salen(sa) + 1) >> 1);
999 cp = line + sprintf(line, "af %d:", sa->sa_family);
1001 while (s < slim)
1002 cp += sprintf(cp, " %x", *s++);
1003 break;
1005 return (line);
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.
1013 rtcmd_irep_t *
1014 new_rtcmd_irep(void)
1016 rtcmd_irep_t *rcip;
1018 rcip = calloc(1, sizeof (rtcmd_irep_t));
1019 if (rcip == NULL) {
1020 quit("calloc", errno);
1022 rcip->ri_af = af;
1023 rcip->ri_flags = RTF_STATIC;
1024 return (rcip);
1027 void
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);
1041 free(rcip);
1044 void
1045 save_string(char **dst, char *src)
1047 free(*dst);
1048 *dst = strdup(src);
1049 if (*dst == NULL) {
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
1058 * information.
1060 void
1061 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
1062 boolean_t to_saved)
1064 char *cmd;
1065 char obuf[INET6_ADDRSTRLEN];
1067 switch (rcip->ri_cmd) {
1068 case RTM_ADD:
1069 cmd = "add";
1070 break;
1071 case RTM_CHANGE:
1072 cmd = "change";
1073 break;
1074 case RTM_DELETE:
1075 cmd = "delete";
1076 break;
1077 case RTM_GET:
1078 cmd = "get";
1079 break;
1080 default:
1081 assert(0);
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) {
1091 case AF_INET:
1092 if (nflag) {
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
1102 * successfully.
1104 (void) fprintf(to, ": gateway %s (%s)",
1105 rcip->ri_gate_str,
1106 inet_ntoa(rcip->ri_gate.sin.sin_addr));
1107 } else {
1108 (void) fprintf(to, ": gateway %s",
1109 rcip->ri_gate_str);
1111 break;
1112 case AF_INET6:
1113 if (inet_ntop(AF_INET6,
1114 &rcip->ri_gate.sin6.sin6_addr, obuf,
1115 INET6_ADDRSTRLEN) != NULL) {
1116 if (nflag) {
1117 (void) fprintf(to, ": gateway %s",
1118 obuf);
1119 break;
1121 if (gw_good &&
1122 rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1123 (void) fprintf(to, ": gateway %s (%s)",
1124 rcip->ri_gate_str, obuf);
1125 break;
1128 /* FALLTHROUGH */
1129 default:
1130 (void) fprintf(to, ": gateway %s",
1131 rcip->ri_gate_str);
1132 break;
1137 void
1138 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
1140 int flag = 0;
1141 uint_t noval, *valp = &noval;
1143 switch (key) {
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);
1155 #undef caseof
1157 rcip->ri_inits |= flag;
1158 if (lock)
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.
1171 boolean_t
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;
1176 char *keyword_str;
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;
1182 int key;
1183 char *err;
1185 if (cmd_string == NULL) {
1186 tok = argv[0];
1187 } else {
1188 tok = strtok(cmd_string, ws);
1192 * The command keywords are already fully checked by main() or
1193 * search_rtfile().
1195 switch (*tok) {
1196 case 'a':
1197 rcip->ri_cmd = RTM_ADD;
1198 break;
1199 case 'c':
1200 rcip->ri_cmd = RTM_CHANGE;
1201 break;
1202 case 'd':
1203 rcip->ri_cmd = RTM_DELETE;
1204 break;
1205 case 'g':
1206 rcip->ri_cmd = RTM_GET;
1207 break;
1208 default:
1209 /* NOTREACHED */
1210 quit(gettext("Internal Error"), EINVAL);
1211 /* NOTREACHED */
1214 #define NEXTTOKEN \
1215 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1217 while (NEXTTOKEN) {
1218 keyword_str = tok;
1219 if (*tok == '-') {
1220 dash_keyword = B_TRUE;
1221 key = keyword(tok + 1);
1222 } else {
1223 dash_keyword = B_FALSE;
1224 key = keyword(tok);
1225 if (key != K_HOST && key != K_NET) {
1226 /* All others must be preceded by '-' */
1227 key = 0;
1230 switch (key) {
1231 case K_HOST:
1232 if (atype == ADDR_TYPE_NET) {
1233 syntax_error(gettext("route: -host and -net "
1234 "are mutually exclusive\n"));
1235 return (B_FALSE);
1237 atype = ADDR_TYPE_HOST;
1238 break;
1239 case K_NET:
1240 if (atype == ADDR_TYPE_HOST) {
1241 syntax_error(gettext("route: -host and -net "
1242 "are mutually exclusive\n"));
1243 return (B_FALSE);
1245 atype = ADDR_TYPE_NET;
1246 break;
1247 case K_LINK:
1248 rcip->ri_af = AF_LINK;
1249 break;
1250 case K_INET:
1251 rcip->ri_af = AF_INET;
1252 break;
1253 case K_SA:
1254 rcip->ri_af = PF_ROUTE;
1255 break;
1256 case K_INET6:
1257 rcip->ri_af = AF_INET6;
1258 break;
1259 case K_IFACE:
1260 case K_INTERFACE:
1261 iflag = B_TRUE;
1262 /* fallthrough */
1263 case K_NOSTATIC:
1264 rcip->ri_flags &= ~RTF_STATIC;
1265 break;
1266 case K_LOCK:
1267 locknext = B_TRUE;
1268 break;
1269 case K_LOCKREST:
1270 lockrest = B_TRUE;
1271 break;
1272 case K_REJECT:
1273 rcip->ri_flags |= RTF_REJECT;
1274 break;
1275 case K_BLACKHOLE:
1276 rcip->ri_flags |= RTF_BLACKHOLE;
1277 break;
1278 case K_PROTO1:
1279 rcip->ri_flags |= RTF_PROTO1;
1280 break;
1281 case K_PROTO2:
1282 rcip->ri_flags |= RTF_PROTO2;
1283 break;
1284 case K_CLONING:
1285 rcip->ri_flags |= RTF_CLONING;
1286 break;
1287 case K_XRESOLVE:
1288 rcip->ri_flags |= RTF_XRESOLVE;
1289 break;
1290 case K_STATIC:
1291 rcip->ri_flags |= RTF_STATIC;
1292 break;
1293 case K_IFA:
1294 if (!NEXTTOKEN) {
1295 syntax_arg_missing(keyword_str);
1296 return (B_FALSE);
1298 if (!getaddr(rcip, RTA_IFA, tok, atype)) {
1299 return (B_FALSE);
1301 break;
1302 case K_IFP:
1303 if (!NEXTTOKEN) {
1304 syntax_arg_missing(keyword_str);
1305 return (B_FALSE);
1307 if (!getaddr(rcip, RTA_IFP, tok, atype)) {
1308 return (B_FALSE);
1310 break;
1311 case K_GATEWAY:
1312 if (!NEXTTOKEN) {
1313 syntax_arg_missing(keyword_str);
1314 return (B_FALSE);
1316 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1317 return (B_FALSE);
1319 break;
1320 case K_DST:
1321 if (!NEXTTOKEN) {
1322 syntax_arg_missing(keyword_str);
1323 return (B_FALSE);
1325 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1326 return (B_FALSE);
1328 break;
1329 case K_NETMASK:
1330 if (!NEXTTOKEN) {
1331 syntax_arg_missing(keyword_str);
1332 return (B_FALSE);
1334 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1335 return (B_FALSE);
1337 atype = ADDR_TYPE_NET;
1338 break;
1339 case K_MTU:
1340 case K_HOPCOUNT:
1341 case K_EXPIRE:
1342 case K_RECVPIPE:
1343 case K_SENDPIPE:
1344 case K_SSTHRESH:
1345 case K_RTT:
1346 case K_RTTVAR:
1347 if (!NEXTTOKEN) {
1348 syntax_arg_missing(keyword_str);
1349 return (B_FALSE);
1351 set_metric(rcip, tok, key, locknext || lockrest);
1352 locknext = B_FALSE;
1353 break;
1354 case K_PRIVATE:
1355 rcip->ri_flags |= RTF_PRIVATE;
1356 break;
1357 case K_MULTIRT:
1358 rcip->ri_flags |= RTF_MULTIRT;
1359 break;
1360 case K_SETSRC:
1361 if (!NEXTTOKEN) {
1362 syntax_arg_missing(keyword_str);
1363 return (B_FALSE);
1365 if (!getaddr(rcip, RTA_SRC, tok, atype)) {
1366 return (B_FALSE);
1368 rcip->ri_flags |= RTF_SETSRC;
1369 break;
1370 case K_INDIRECT:
1371 rcip->ri_flags |= RTF_INDIRECT;
1372 break;
1373 default:
1374 if (dash_keyword) {
1375 syntax_bad_keyword(tok + 1);
1376 return (B_FALSE);
1378 if ((rcip->ri_addrs & RTA_DST) == 0) {
1379 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1380 return (B_FALSE);
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
1387 * is successful.
1389 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1390 return (B_FALSE);
1392 } else {
1393 ulong_t metric;
1395 * Assume that a regular number is a metric.
1396 * Needed for compatibility with old route
1397 * command syntax.
1399 errno = 0;
1400 metric = strtoul(tok, &err, 10);
1401 if (errno == 0 && *err == '\0' &&
1402 metric < 0x80000000ul) {
1403 iflag = (metric == 0);
1404 if (verbose) {
1405 (void) printf("old usage of "
1406 "trailing number, assuming "
1407 "route %s\n", iflag ?
1408 "to if" : "via gateway");
1410 continue;
1412 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1413 return (B_FALSE);
1418 #undef NEXTTOKEN
1420 if ((rcip->ri_addrs & RTA_DST) == 0) {
1421 syntax_error(gettext("route: destination required\n"));
1422 return (B_FALSE);
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"));
1427 return (B_FALSE);
1430 if (!iflag) {
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 ==
1442 IP_HOST_MASK) {
1443 rcip->ri_flags |= RTF_HOST;
1445 if (rcip->ri_af == AF_INET6 &&
1446 memcmp(&rcip->ri_mask.sin6.sin6_addr,
1447 &in6_host_mask,
1448 sizeof (struct in6_addr)) == 0) {
1449 rcip->ri_flags |= RTF_HOST;
1451 } else {
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;
1460 return (B_TRUE);
1464 * This command always seeks to the end of the file prior to writing.
1466 void
1467 write_to_rtfile(FILE *fp, int argc, char **argv)
1469 char file_line[BUF_SIZE];
1470 int len;
1471 int i;
1473 len = 0;
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 ",
1488 argv[i]);
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"),
1496 errno);
1500 boolean_t
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) {
1505 return (B_FALSE);
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)
1524 char *tmp_buf;
1525 int match_cnt;
1526 boolean_t match;
1527 char file_line[BUF_SIZE + 4] = "add ";
1528 rtcmd_irep_t *thisrt;
1530 match_cnt = 0;
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) {
1541 quit(gettext(
1542 "route: failed to write to temp file"),
1543 errno);
1545 continue;
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);
1561 free(tmp_buf);
1562 continue;
1564 exit_on_error = B_TRUE;
1565 free(tmp_buf);
1567 if (thisrt->ri_gate_str == NULL) {
1568 del_rtcmd_irep(thisrt);
1569 continue;
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"),
1588 errno);
1591 return (match_cnt);
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.
1599 void
1600 save_route(int argc, char **argv, int do_flush)
1602 rtcmd_irep_t *rt;
1603 int perm_fd;
1604 FILE *perm_fp;
1605 FILE *temp_fp;
1606 mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1607 struct flock lock;
1608 struct stat st;
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;
1618 lock.l_start = 0;
1619 lock.l_len = 0;
1620 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1621 quit(gettext("failed to lock route file"), errno);
1622 /* NOTREACHED */
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);
1631 /* NOTREACHED */
1634 if (!do_flush) {
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);
1641 /* NOTREACHED */
1644 if (do_flush) {
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);
1648 /* NOTREACHED */
1650 (void) fclose(perm_fp);
1651 return;
1654 switch (rt->ri_cmd) {
1655 case RTM_ADD:
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");
1660 exit(1);
1662 write_to_rtfile(perm_fp, argc - 1, argv + 1);
1663 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1664 (void) printf("\n");
1665 break;
1667 case RTM_CHANGE:
1668 syntax_error(
1669 gettext("route: change command not supported with -p\n"));
1670 /* NOTREACHED */
1672 case RTM_DELETE:
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"));
1677 exit(1);
1679 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1680 quit(gettext("failed to update route file"), errno);
1681 /* NOTREACHED */
1683 break;
1685 case RTM_GET:
1686 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1687 0) {
1688 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1689 (void) printf(gettext(": not in file\n"));
1691 break;
1693 default:
1694 quit(gettext("Internal Error"), EINVAL);
1695 /* NOTREACHED */
1699 * Closing the file unlocks it.
1701 (void) fclose(perm_fp);
1705 show_saved_routes(int argc)
1707 int perm_fd;
1708 FILE *perm_fp;
1709 struct flock lock;
1710 int count = 0;
1712 if (argc != 1) {
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");
1721 return (0);
1722 } else {
1723 quit(gettext("failed to open route file"), errno);
1726 lock.l_type = F_RDLCK;
1727 lock.l_whence = SEEK_SET;
1728 lock.l_start = 0;
1729 lock.l_len = 0;
1730 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1731 quit(gettext("failed to lock route file"),
1732 errno);
1733 /* NOTREACHED */
1735 if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1736 quit(gettext("failed to open route file"), errno);
1737 /* NOTREACHED */
1739 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1740 (void) fseek(perm_fp, 0, SEEK_SET);
1741 af = AF_INET6;
1742 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1744 if (count == 0)
1745 (void) printf("No persistent routes are defined\n");
1747 (void) fclose(perm_fp);
1748 return (0);
1752 newroute(char **argv)
1754 rtcmd_irep_t *newrt;
1755 int ret, attempts, oerrno;
1756 char *err;
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);
1772 } else {
1773 (void) fprintf(stderr,
1774 gettext("route: %s: no such interface\n"),
1775 newrt->ri_ifp_str);
1776 exit(1);
1779 newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1781 for (attempts = 1; ; attempts++) {
1782 errno = 0;
1783 if ((ret = rtmsg(newrt)) == 0)
1784 break;
1785 if (errno != ENETUNREACH && errno != ESRCH)
1786 break;
1787 if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
1788 hp->h_addr_list[attempts] != NULL) {
1789 switch (af) {
1790 case AF_INET:
1791 (void) memmove(&newrt->ri_gate.sin.sin_addr,
1792 hp->h_addr_list[attempts], hp->h_length);
1793 continue;
1794 case AF_INET6:
1795 (void) memmove(&newrt->ri_gate.sin6.sin6_addr,
1796 hp->h_addr_list[attempts], hp->h_length);
1797 continue;
1800 break;
1802 oerrno = errno;
1804 if (newrt->ri_cmd != RTM_GET) {
1805 print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
1806 if (ret == 0)
1807 (void) printf("\n");
1808 } else if (ret != 0) {
1810 * Note: there is nothing additional to print for get
1811 * if ret == 0.
1813 if (nflag) {
1814 switch (newrt->ri_af) {
1815 case AF_INET:
1816 (void) printf(" %s",
1817 inet_ntoa(newrt->ri_dst.sin.sin_addr));
1818 break;
1819 case AF_INET6:
1820 if (inet_ntop(AF_INET6,
1821 (void *)&newrt->ri_dst.sin6.sin6_addr,
1822 obuf, INET6_ADDRSTRLEN) != NULL) {
1823 (void) printf(" %s", obuf);
1824 break;
1826 /* FALLTHROUGH */
1827 default:
1828 (void) printf("%s", newrt->ri_dest_str);
1829 break;
1831 } else {
1832 (void) printf("%s", newrt->ri_dest_str);
1836 if (ret != 0) {
1837 switch (oerrno) {
1838 case ESRCH:
1839 err = "not in table";
1840 break;
1841 case EBUSY:
1842 err = "entry in use";
1843 break;
1844 case ENOBUFS:
1845 err = "routing table overflow";
1846 break;
1847 case EEXIST:
1848 err = "entry exists";
1849 break;
1850 case EPERM:
1851 err = "insufficient privileges";
1852 break;
1853 default:
1854 err = strerror(oerrno);
1855 break;
1857 (void) printf(": %s\n", err);
1860 del_rtcmd_irep(newrt);
1862 return (oerrno);
1863 #undef hp
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.
1872 static void
1873 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
1875 in_addr_t addr, mask;
1877 if (net == 0) {
1878 mask = addr = 0;
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;
1888 } else {
1889 addr = 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;
1896 else {
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;
1903 else
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;
1922 static in_addr_t
1923 inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
1925 int n;
1926 struct ifconf ifc;
1927 struct ifreq ifreq;
1928 struct ifreq *ifr;
1929 struct sockaddr_in *sin;
1930 char *buf;
1931 int numifs;
1932 size_t bufsize;
1933 int iosoc;
1934 in_addr_t if_addr, if_mask;
1935 in_addr_t if_subnetmask = 0;
1936 short if_flags;
1938 if (mask == 0)
1939 return (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);
1946 if (buf == NULL)
1947 quit("malloc", errno);
1948 (void) memset(&ifc, 0, sizeof (ifc));
1949 ifc.ifc_len = bufsize;
1950 ifc.ifc_buf = buf;
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. */
1954 ifr = ifc.ifc_req;
1955 for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
1956 ifreq = *ifr;
1957 /* LINTED */
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)
1964 continue;
1965 if_flags = ifreq.ifr_flags;
1967 if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
1968 quit("ioctl (get netmask)", errno);
1969 /* LINTED */
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
1975 * other interfaces.
1977 if (if_flags & IFF_POINTOPOINT) {
1978 if_subnetmask = if_mask;
1979 continue;
1982 * Fine. Just assume the same net mask as the
1983 * directly attached subnet interface is using.
1985 return (if_mask);
1988 if (if_subnetmask != 0)
1989 return (if_subnetmask);
1990 return (mask);
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
2002 * on failure.
2004 static boolean_t
2005 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
2007 su_t *su;
2008 struct hostent **hpp;
2009 struct hostent *hp;
2010 int masklen;
2012 if (which == RTA_GATEWAY) {
2013 hpp = &(rcip->ri_gate_hp);
2014 } else {
2015 hpp = &hp;
2017 *hpp = NULL;
2019 rcip->ri_addrs |= which;
2020 switch (which) {
2021 case RTA_DST:
2022 save_string(&rcip->ri_dest_str, s);
2023 su = &rcip->ri_dst;
2024 su->sa.sa_family = rcip->ri_af;
2025 break;
2026 case RTA_GATEWAY:
2027 save_string(&rcip->ri_gate_str, s);
2028 su = &rcip->ri_gate;
2029 su->sa.sa_family = rcip->ri_af;
2030 break;
2031 case RTA_NETMASK:
2032 su = &rcip->ri_mask;
2033 su->sa.sa_family = rcip->ri_af;
2034 break;
2035 case RTA_IFP:
2036 save_string(&rcip->ri_ifp_str, s);
2037 return (B_TRUE);
2039 * RTA_SRC has overloaded meaning. It can represent the
2040 * src address of incoming or outgoing packets.
2042 case RTA_IFA:
2043 su = &rcip->ri_ifa;
2044 su->sa.sa_family = rcip->ri_af;
2045 break;
2046 case RTA_SRC:
2047 su = &rcip->ri_src;
2048 su->sa.sa_family = rcip->ri_af;
2049 break;
2050 default:
2051 /* NOTREACHED */
2052 quit(gettext("Internal Error"), EINVAL);
2053 /* NOTREACHED */
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) {
2060 return (B_TRUE);
2062 return (B_TRUE);
2064 switch (rcip->ri_af) {
2065 case AF_LINK:
2066 link_addr(s, &su->sdl);
2067 return (B_TRUE);
2068 case PF_ROUTE:
2069 sockaddr(s, &su->sa);
2070 return (B_TRUE);
2071 case AF_INET6:
2072 switch (which) {
2073 case RTA_DST:
2074 if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
2075 return (B_FALSE);
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"),
2084 masklen);
2085 return (B_FALSE);
2087 rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2088 rcip->ri_addrs |= RTA_NETMASK;
2090 return (B_TRUE);
2091 case RTA_GATEWAY:
2092 case RTA_IFA:
2093 case RTA_SRC:
2094 return (in6_getaddr(s, &su->sin6, NULL, hpp));
2095 case RTA_NETMASK:
2096 syntax_error(
2097 gettext("route: -netmask not supported for IPv6: "
2098 "use <prefix>/<prefix-length> instead\n"));
2099 return (B_FALSE);
2100 default:
2101 quit(gettext("Internal Error"), EINVAL);
2102 /* NOTREACHED */
2104 case AF_INET:
2105 switch (which) {
2106 case RTA_DST:
2107 if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
2108 atype, rcip)) {
2109 return (B_FALSE);
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"),
2118 masklen);
2119 return (B_FALSE);
2121 rcip->ri_mask.sin.sin_family = rcip->ri_af;
2122 rcip->ri_addrs |= RTA_NETMASK;
2124 return (B_TRUE);
2125 case RTA_GATEWAY:
2126 case RTA_IFA:
2127 case RTA_NETMASK:
2128 case RTA_SRC:
2129 return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2130 rcip));
2131 default:
2132 quit(gettext("Internal Error"), EINVAL);
2133 /* NOTREACHED */
2135 default:
2136 quit(gettext("Internal Error"), EINVAL);
2137 /* NOTREACHED */
2139 return (B_TRUE);
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.
2155 static boolean_t
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)
2159 struct hostent *hp;
2160 struct netent *np;
2161 in_addr_t val;
2162 char str[BUFSIZ];
2164 (void) strlcpy(str, s, sizeof (str));
2167 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2169 if (plenp != NULL) {
2170 char *cp;
2172 *plenp = in_getprefixlen(str, IP_ABITS);
2173 if (*plenp == BAD_ADDR)
2174 return (B_FALSE);
2175 cp = strchr(str, '/');
2176 if (cp != NULL)
2177 *cp = '\0';
2178 } else if (strchr(str, '/') != NULL) {
2179 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2180 return (B_FALSE);
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;
2191 return (B_TRUE);
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),
2204 sin);
2207 return (B_TRUE);
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) {
2213 *hpp = hp;
2214 (void) memmove(&sin->sin_addr, hp->h_addr,
2215 hp->h_length);
2216 return (B_TRUE);
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);
2226 return (B_TRUE);
2229 syntax_error(gettext("%s: bad value\n"), s);
2230 return (B_FALSE);
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.
2243 static boolean_t
2244 in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
2245 struct hostent **hpp)
2247 struct hostent *hp;
2248 char str[BUFSIZ];
2249 int error_num;
2251 (void) strlcpy(str, s, sizeof (str));
2254 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2256 if (plenp != NULL) {
2257 char *cp;
2259 *plenp = in_getprefixlen(str, IPV6_ABITS);
2260 if (*plenp == BAD_ADDR)
2261 return (B_FALSE);
2262 cp = strchr(str, '/');
2263 if (cp != NULL)
2264 *cp = '\0';
2265 } else if (strchr(str, '/') != NULL) {
2266 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2267 return (B_FALSE);
2270 (void) memset(sin6, 0, sizeof (struct sockaddr_in6));
2271 sin6->sin6_family = AF_INET6;
2273 hp = getipnodebyname(str, AF_INET6, 0, &error_num);
2274 if (hp != NULL) {
2275 *hpp = hp;
2276 (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
2277 return (B_TRUE);
2279 if (error_num == TRY_AGAIN) {
2281 * This isn't a problem if we aren't going to use the address
2282 * right away.
2284 if (!exit_on_error) {
2285 return (B_TRUE);
2287 syntax_error(gettext("route: %s: bad address (try "
2288 "again later)\n"), s);
2289 return (B_FALSE);
2291 syntax_error(gettext("route: %s: bad address\n"), s);
2292 return (B_FALSE);
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)
2307 int prefixlen;
2308 char *str, *end;
2310 str = strchr(addr, '/');
2311 if (str == addr) {
2312 syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
2313 return (BAD_ADDR);
2315 if (str == NULL)
2316 return (NO_PREFIX);
2317 str++;
2319 errno = 0;
2320 prefixlen = strtoul(str, &end, 10);
2321 if (errno != 0 || str == end) {
2322 syntax_error(gettext("route: bad prefix length %s\n"), str);
2323 return (BAD_ADDR);
2325 if (prefixlen > max_plen) {
2326 syntax_error(gettext("route: prefix length %s out of range\n"),
2327 str);
2328 return (BAD_ADDR);
2330 return (prefixlen);
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.
2338 boolean_t
2339 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
2341 if (prefixlen < 0 || prefixlen > maxlen)
2342 return (B_FALSE);
2344 while (prefixlen > 0) {
2345 if (prefixlen >= 8) {
2346 *mask++ = 0xFF;
2347 prefixlen -= 8;
2348 continue;
2350 *mask |= 1 << (8 - prefixlen);
2351 prefixlen--;
2353 return (B_TRUE);
2356 void
2357 rtmonitor(int argc, char *argv[])
2359 int n;
2360 intmax_t msg[2048 / sizeof (intmax_t)];
2362 if (tflag)
2363 exit(0);
2364 verbose = B_TRUE;
2365 if (argc > 1) {
2366 argv++;
2367 if (argc == 2 && **argv == '-') {
2368 switch (keyword(*argv + 1)) {
2369 case K_INET:
2370 af = AF_INET;
2371 break;
2372 case K_LINK:
2373 af = AF_LINK;
2374 break;
2375 case K_INET6:
2376 af = AF_INET6;
2377 break;
2378 default:
2379 usage(*argv);
2380 /* NOTREACHED */
2382 } else {
2383 usage(*argv);
2385 (void) close(s);
2386 s = socket(PF_ROUTE, SOCK_RAW, af);
2387 if (s < 0)
2388 quit("socket", errno);
2390 for (;;) {
2391 n = read(s, msg, sizeof (msg));
2392 if (n <= 0)
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)
2402 static int seq;
2403 int rlen;
2404 char *cp = m_rtmsg.m_space;
2405 int l;
2407 errno = 0;
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); \
2428 cp += l; \
2429 if (verbose) \
2430 sodump(&(u), #u); \
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);
2442 #undef NEXTADDR
2444 rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
2446 if (verbose)
2447 print_rtmsg(&rtm, l);
2448 if (debugonly)
2449 return (0);
2450 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
2451 switch (errno) {
2452 case ESRCH:
2453 case EBUSY:
2454 case ENOBUFS:
2455 case EEXIST:
2456 case ENETUNREACH:
2457 case EHOSTUNREACH:
2458 case EPERM:
2459 break;
2460 default:
2461 perror(gettext("writing to routing socket"));
2462 break;
2464 return (-1);
2465 } else if (rlen < (int)rtm.rtm_msglen) {
2466 (void) fprintf(stderr,
2467 gettext("route: write to routing socket got only %d for "
2468 "len\n"), rlen);
2469 return (-1);
2471 if (newrt->ri_cmd == RTM_GET) {
2472 do {
2473 l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
2474 } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
2475 if (l < 0) {
2476 (void) fprintf(stderr,
2477 gettext("route: read from routing socket: %s\n"),
2478 strerror(errno));
2479 } else {
2480 print_getmsg(newrt, &rtm, l);
2483 #undef rtm
2484 return (0);
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"
2512 "\1mtu";
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"
2526 "\047DUPLICATE";
2527 static char addrnames[] =
2528 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2530 void
2531 print_rtmsg(struct rt_msghdr *rtm, int msglen)
2533 struct if_msghdr *ifm;
2534 struct ifa_msghdr *ifam;
2536 if (!verbose)
2537 return;
2538 if (rtm->rtm_version != RTM_VERSION) {
2539 (void) printf("routing message version %d not understood\n",
2540 rtm->rtm_version);
2541 return;
2543 if (rtm->rtm_msglen != msglen) {
2544 (void) printf("message length mismatch, in packet %d, "
2545 "returned %d\n",
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",
2556 rtm->rtm_type);
2557 return;
2559 (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
2560 switch (rtm->rtm_type) {
2561 case RTM_IFINFO:
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),
2566 ifm->ifm_addrs);
2567 break;
2568 case RTM_NEWADDR:
2569 case RTM_DELADDR:
2570 case RTM_CHGADDR:
2571 case RTM_FREEADDR:
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),
2576 ifam->ifam_addrs);
2577 break;
2578 default:
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);
2583 break;
2587 void
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;
2593 char *cp;
2594 int i;
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"),
2600 rtm->rtm_version);
2601 return;
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);
2611 return;
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) {
2617 /* LINTED */
2618 sa = (struct sockaddr *)cp;
2619 switch (i) {
2620 case RTA_DST:
2621 dst = sa;
2622 break;
2623 case RTA_GATEWAY:
2624 gate = sa;
2625 break;
2626 case RTA_NETMASK:
2627 mask = sa;
2628 break;
2629 case RTA_IFP:
2630 if (sa->sa_family == AF_LINK &&
2631 ((struct sockaddr_dl *)sa)->
2632 sdl_nlen != 0)
2633 ifp = (struct sockaddr_dl *)sa;
2634 break;
2635 case RTA_SRC:
2636 src = sa;
2637 break;
2639 ADVANCE(cp, sa);
2643 if (dst != NULL && mask != NULL)
2644 mask->sa_family = dst->sa_family; /* XXX */
2645 if (dst != NULL)
2646 (void) printf("destination: %s\n", routename(dst));
2647 if (mask != NULL) {
2648 boolean_t savenflag = nflag;
2650 nflag = B_TRUE;
2651 (void) printf(" mask: %s\n", routename(mask));
2652 nflag = savenflag;
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));
2658 if (ifp != NULL) {
2659 if (verbose) {
2660 int i;
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;
2666 i++) {
2667 (void) printf("%02x ",
2668 ifp->sdl_data[i] & 0xFF);
2670 (void) printf("\n");
2671 } else {
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));
2694 #undef lock
2695 #undef msec
2696 #define RTA_IGN \
2697 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2698 if (verbose) {
2699 pmsg_common(rtm, msglen);
2700 } else {
2701 const char *sptr, *endptr;
2702 const struct sockaddr *sa;
2703 uint_t addrs;
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) {
2714 addrs &= addrs - 1;
2715 /* LINTED */
2716 sa = (const struct sockaddr *)sptr;
2717 ADVANCE(sptr, sa);
2719 (void) putchar('\n');
2721 #undef RTA_IGN
2724 static void
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),
2732 rtm->rtm_addrs);
2735 static void
2736 pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
2738 const struct sockaddr *sa;
2739 const char *maxptr;
2740 int i;
2742 if (addrs != 0) {
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) {
2748 if (i & addrs) {
2749 /* LINTED */
2750 sa = (const struct sockaddr *)cp;
2751 (void) printf(" %s", routename(sa));
2752 ADVANCE(cp, sa);
2755 if (i != 0)
2756 msglen = 0;
2757 else
2758 msglen = maxptr - cp;
2760 (void) putchar('\n');
2761 (void) fflush(stdout);
2764 void
2765 bprintf(FILE *fp, int b, char *s)
2767 int i;
2768 boolean_t gotsome = B_FALSE;
2770 if (b == 0)
2771 return;
2772 while ((i = *s++) != 0) {
2773 if (b & (1 << (i - 1))) {
2774 if (!gotsome)
2775 i = '<';
2776 else
2777 i = ',';
2778 (void) putc(i, fp);
2779 gotsome = B_TRUE;
2780 for (; (i = *s) > ' '; s++)
2781 (void) putc(i, fp);
2782 } else {
2783 while (*s > ' ')
2784 s++;
2787 if (gotsome)
2788 (void) putc('>', fp);
2792 keyword(const char *cp)
2794 struct keytab *kt = keywords;
2796 while (kt->kt_cp && strcmp(kt->kt_cp, cp))
2797 kt++;
2798 return (kt->kt_i);
2801 void
2802 sodump(su_t *su, char *which)
2804 static char obuf[INET6_ADDRSTRLEN];
2806 switch (su->sa.sa_family) {
2807 case AF_LINK:
2808 (void) printf("%s: link %s; ",
2809 which, link_ntoa(&su->sdl));
2810 break;
2811 case AF_INET:
2812 (void) printf("%s: inet %s; ",
2813 which, inet_ntoa(su->sin.sin_addr));
2814 break;
2815 case AF_INET6:
2816 if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
2817 INET6_ADDRSTRLEN) != NULL) {
2818 (void) printf("%s: inet6 %s; ", which, obuf);
2819 break;
2821 /* FALLTHROUGH */
2822 default:
2823 quit(gettext("Internal Error"), EINVAL);
2824 /* NOTREACHED */
2826 (void) fflush(stdout);
2829 /* States */
2830 #define VIRGIN 0
2831 #define GOTONE 1
2832 #define GOTTWO 2
2833 #define RESET 3
2834 /* Inputs */
2835 #define DIGIT (4*0)
2836 #define END (4*1)
2837 #define DELIM (4*2)
2838 #define LETTER (4*3)
2840 void
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);
2849 cp++;
2850 do {
2851 if ((*addr >= '0') && (*addr <= '9')) {
2852 new = *addr - '0';
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) {
2858 state |= END;
2859 } else {
2860 state |= DELIM;
2862 addr++;
2863 switch (state /* | INPUT */) {
2864 case GOTTWO | DIGIT:
2865 *cp++ = byte;
2866 /* FALLTHROUGH */
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;
2873 case GOTONE | END:
2874 case GOTTWO | END:
2875 *cp++ = byte;
2876 /* FALLTHROUGH */
2877 case VIRGIN | END:
2878 break;
2880 break;
2881 } while (cp < cplim);
2885 salen(const struct sockaddr *sa)
2887 switch (sa->sa_family) {
2888 case AF_INET:
2889 return (sizeof (struct sockaddr_in));
2890 case AF_LINK:
2891 return (sizeof (struct sockaddr_dl));
2892 case AF_INET6:
2893 return (sizeof (struct sockaddr_in6));
2894 default:
2895 return (sizeof (struct sockaddr));
2899 void
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;
2908 do {
2909 state &= ~LETTER;
2910 if ((*addr >= '0') && (*addr <= '9')) {
2911 new = *addr - '0';
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) {
2917 state |= END;
2918 } else if (state == VIRGIN &&
2919 (((*addr >= 'A') && (*addr <= 'Z')) ||
2920 ((*addr >= 'a') && (*addr <= 'z')))) {
2921 state |= LETTER;
2922 } else {
2923 state |= DELIM;
2925 addr++;
2926 switch (state /* | INPUT */) {
2927 case VIRGIN | DIGIT:
2928 case VIRGIN | LETTER:
2929 *cp++ = addr[-1];
2930 continue;
2931 case VIRGIN | DELIM:
2932 state = RESET;
2933 sdl->sdl_nlen = cp - sdl->sdl_data;
2934 continue;
2935 case GOTTWO | DIGIT:
2936 *cp++ = byte;
2937 /* FALLTHROUGH */
2938 case RESET | DIGIT:
2939 state = GOTONE;
2940 byte = new;
2941 continue;
2942 case GOTONE | DIGIT:
2943 state = GOTTWO;
2944 byte = new + (byte << 4);
2945 continue;
2946 default: /* | DELIM */
2947 state = RESET;
2948 *cp++ = byte;
2949 byte = 0;
2950 continue;
2951 case GOTONE | END:
2952 case GOTTWO | END:
2953 *cp++ = byte;
2954 /* FALLTHROUGH */
2955 case RESET | END:
2956 break;
2958 break;
2959 } while (cp < cplim);
2960 sdl->sdl_alen = cp - LLADDR(sdl);
2963 static char hexlist[] = "0123456789abcdef";
2965 char *
2966 link_ntoa(const struct sockaddr_dl *sdl)
2968 static char obuf[64];
2969 char *out = obuf;
2970 int i;
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;
2978 if (sdl->sdl_alen)
2979 *out++ = ':';
2981 while (in < inlim) {
2982 if (firsttime)
2983 firsttime = B_FALSE;
2984 else
2985 *out++ = '.';
2986 i = *in++;
2987 if (i > 0xf) {
2988 out[1] = hexlist[i & 0xf];
2989 i >>= 4;
2990 out[0] = hexlist[i];
2991 out += 2;
2992 } else {
2993 *out++ = hexlist[i];
2996 *out = 0;
2997 return (obuf);
3000 static mib_item_t *
3001 mibget(int sd)
3003 intmax_t buf[512 / sizeof (intmax_t)];
3004 int flags;
3005 int i, j, getcode;
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;
3010 struct opthdr *req;
3011 mib_item_t *first_item = NULL;
3012 mib_item_t *last_item = NULL;
3013 mib_item_t *temp;
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 */
3021 req->name = 0;
3022 req->len = 0;
3024 ctlbuf.buf = (char *)buf;
3025 ctlbuf.len = tor->OPT_length + tor->OPT_offset;
3026 flags = 0;
3027 if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
3028 perror("mibget: putmsg (ctl)");
3029 return (NULL);
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++) {
3040 flags = 0;
3041 getcode = getmsg(sd, &ctlbuf, NULL, &flags);
3042 if (getcode < 0) {
3043 perror("mibget: getmsg (ctl)");
3044 if (verbose) {
3045 (void) fprintf(stderr,
3046 "# level name len\n");
3047 i = 0;
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,
3052 last_item->mib_id,
3053 last_item->length);
3056 break;
3058 if (getcode == 0 &&
3059 ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
3060 toa->PRIM_type == T_OPTMGMT_ACK &&
3061 toa->MGMT_flags == T_SUCCESS &&
3062 req->len == 0) {
3063 if (verbose) {
3064 (void) printf("mibget getmsg() %d returned EOD "
3065 "(level %lu, name %lu)\n", j, req->level,
3066 req->name);
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;
3078 break;
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);
3093 errno = ENOMSG;
3094 break;
3097 temp = malloc(sizeof (mib_item_t));
3098 if (temp == NULL) {
3099 perror("mibget: malloc");
3100 break;
3102 if (last_item != NULL)
3103 last_item->next_item = temp;
3104 else
3105 first_item = temp;
3106 last_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);
3112 if (verbose) {
3113 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3114 "length = %ld\n",
3115 j, last_item->group, last_item->mib_id,
3116 last_item->length);
3119 databuf.maxlen = last_item->length;
3120 databuf.buf = (char *)last_item->valp;
3121 databuf.len = 0;
3122 flags = 0;
3123 getcode = getmsg(sd, NULL, &databuf, &flags);
3124 if (getcode < 0) {
3125 perror("mibget: getmsg (data)");
3126 break;
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);
3131 break;
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;
3141 free(last_item);
3143 return (NULL);