dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.sbin / route.c
blob2f69a6755a80f19e4a2822b1791082f8a373fd9b
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_SETSRC 42
165 {"setsrc", K_SETSRC},
166 #define K_SHOW 43
167 {"show", K_SHOW},
168 #define K_INDIRECT 44
169 {"indirect", K_INDIRECT},
170 {0, 0}
174 * Size of buffers used to hold command lines from the saved route file as
175 * well as error strings.
177 #define BUF_SIZE 2048
179 typedef union sockunion {
180 struct sockaddr sa;
181 struct sockaddr_in sin;
182 struct sockaddr_dl sdl;
183 struct sockaddr_in6 sin6;
184 } su_t;
187 * This structure represents the digested information from parsing arguments
188 * to route add, change, delete, and get.
191 typedef struct rtcmd_irep {
192 int ri_cmd;
193 int ri_flags;
194 int ri_af;
195 ulong_t ri_inits;
196 struct rt_metrics ri_metrics;
197 int ri_addrs;
198 su_t ri_dst;
199 char *ri_dest_str;
200 su_t ri_src;
201 su_t ri_gate;
202 struct hostent *ri_gate_hp;
203 char *ri_gate_str;
204 su_t ri_mask;
205 su_t ri_ifa;
206 su_t ri_ifp;
207 char *ri_ifp_str;
208 } rtcmd_irep_t;
210 typedef struct mib_item_s {
211 struct mib_item_s *next_item;
212 long group;
213 long mib_id;
214 long length;
215 intmax_t *valp;
216 } mib_item_t;
218 typedef enum {
219 ADDR_TYPE_ANY,
220 ADDR_TYPE_HOST,
221 ADDR_TYPE_NET
222 } addr_type_t;
224 typedef enum {
225 SEARCH_MODE_NULL,
226 SEARCH_MODE_PRINT,
227 SEARCH_MODE_DEL
228 } search_mode_t;
230 static boolean_t args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
231 char *cmd_string);
232 static void bprintf(FILE *fp, int b, char *s);
233 static boolean_t compare_rtcmd(rtcmd_irep_t *srch_rt,
234 rtcmd_irep_t *file_rt);
235 static void delRouteEntry(mib2_ipRouteEntry_t *rp,
236 mib2_ipv6RouteEntry_t *rp6, int seqno);
237 static void del_rtcmd_irep(rtcmd_irep_t *rcip);
238 static void flushroutes(int argc, char *argv[]);
239 static boolean_t getaddr(rtcmd_irep_t *rcip, int which, char *s,
240 addr_type_t atype);
241 static boolean_t in6_getaddr(char *s, struct sockaddr_in6 *sin6,
242 int *plenp, struct hostent **hpp);
243 static boolean_t in_getaddr(char *s, struct sockaddr_in *sin,
244 int *plenp, int which, struct hostent **hpp, addr_type_t atype,
245 rtcmd_irep_t *rcip);
246 static int in_getprefixlen(char *addr, int max_plen);
247 static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
248 uchar_t *mask);
249 static void inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
250 struct sockaddr_in *sin);
251 static in_addr_t inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
252 static int keyword(const char *cp);
253 static void link_addr(const char *addr, struct sockaddr_dl *sdl);
254 static char *link_ntoa(const struct sockaddr_dl *sdl);
255 static mib_item_t *mibget(int sd);
256 static char *netname(struct sockaddr *sa);
257 static int newroute(char **argv);
258 static rtcmd_irep_t *new_rtcmd_irep(void);
259 static void pmsg_addrs(const char *cp, size_t len, uint_t addrs);
260 static void pmsg_common(const struct rt_msghdr *rtm, size_t len);
261 static void print_getmsg(rtcmd_irep_t *req_rt,
262 struct rt_msghdr *rtm, int msglen);
263 static void print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
264 boolean_t gw_good, boolean_t to_saved);
265 static void print_rtmsg(struct rt_msghdr *rtm, int msglen);
266 static void quit(char *s, int err) __NORETURN;
267 static char *routename(const struct sockaddr *sa);
268 static void rtmonitor(int argc, char *argv[]);
269 static int rtmsg(rtcmd_irep_t *rcip);
270 static int salen(const struct sockaddr *sa);
271 static void save_route(int argc, char **argv, int do_flush);
272 static void save_string(char **dst, char *src);
273 static int search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
274 search_mode_t mode);
275 static void set_metric(rtcmd_irep_t *rcip, char *value, int key,
276 boolean_t lock);
277 static int show_saved_routes(int argc);
278 static void sockaddr(char *addr, struct sockaddr *sa);
279 static void sodump(su_t *su, char *which);
280 static void syntax_arg_missing(char *keyword);
281 static void syntax_bad_keyword(char *keyword);
282 static void syntax_error(char *err, ...);
283 static void usage(char *cp);
284 static void write_to_rtfile(FILE *fp, int argc, char **argv);
286 static pid_t pid;
287 static int s;
288 static boolean_t nflag;
289 static int af = AF_INET;
290 static boolean_t qflag, tflag;
291 static boolean_t verbose;
292 static boolean_t debugonly;
293 static boolean_t fflag;
294 static boolean_t update_table;
295 static boolean_t perm_flag;
296 static boolean_t early_v6_keyword;
297 static char perm_file_sfx[] = "/etc/inet/static_routes";
298 static char *perm_file;
299 static char temp_file_sfx[] = "/etc/inet/static_routes.tmp";
300 static char *temp_file;
301 static struct in6_addr in6_host_mask = { { { 0xff, 0xff, 0xff, 0xff, 0xff,
302 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } };
304 * WARNING:
305 * This next variable indicates whether certain functions exit when an error
306 * is detected in the user input. Currently, exit_on_error is only set false
307 * in search_rtfile(), when argument are being parsed. Only those functions
308 * used by search_rtfile() to parse its arguments are designed to work in
309 * both modes. Take particular care in setting this false to ensure that any
310 * functions you call that might act on this flag properly return errors when
311 * exit_on_error is false.
313 static int exit_on_error = B_TRUE;
315 static struct {
316 struct rt_msghdr m_rtm;
317 char m_space[BUF_SIZE];
318 } m_rtmsg;
321 * Sizes of data structures extracted from the base mib.
322 * This allows the size of the tables entries to grow while preserving
323 * binary compatibility.
325 static int ipRouteEntrySize;
326 static int ipv6RouteEntrySize;
328 #define ROUNDUP_LONG(a) \
329 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
330 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
331 #define C(x) ((x) & 0xff)
334 * return values from in_getprefixlen()
336 #define BAD_ADDR -1 /* prefix is invalid */
337 #define NO_PREFIX -2 /* no prefix was found */
339 void
340 usage(char *cp)
342 if (cp != NULL) {
343 (void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
344 cp);
346 (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
347 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
348 exit(1);
349 /* NOTREACHED */
352 /*PRINTFLIKE1*/
353 void
354 syntax_error(char *err, ...)
356 va_list args;
358 if (exit_on_error) {
359 va_start(args, err);
360 (void) vfprintf(stderr, err, args);
361 va_end(args);
362 exit(1);
364 /* NOTREACHED */
367 void
368 syntax_bad_keyword(char *keyword)
370 syntax_error(gettext("route: botched keyword: %s\n"), keyword);
373 void
374 syntax_arg_missing(char *keyword)
376 syntax_error(gettext("route: argument required following keyword %s\n"),
377 keyword);
380 void
381 quit(char *s, int sverrno)
383 (void) fprintf(stderr, "route: ");
384 if (s != NULL)
385 (void) fprintf(stderr, "%s: ", s);
386 (void) fprintf(stderr, "%s\n", strerror(sverrno));
387 exit(sverrno);
388 /* NOTREACHED */
392 main(int argc, char **argv)
394 extern int optind;
395 extern char *optarg;
396 int ch;
397 int rval;
398 size_t size;
399 const char *root_dir = NULL;
401 (void) setlocale(LC_ALL, "");
403 #if !defined(TEXT_DOMAIN)
404 #define TEXT_DOMAIN "SYS_TEST"
405 #endif
406 (void) textdomain(TEXT_DOMAIN);
408 if (argc < 2)
409 usage(NULL);
411 while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
412 switch (ch) {
413 case 'n':
414 nflag = B_TRUE;
415 break;
416 case 'q':
417 qflag = B_TRUE;
418 break;
419 case 'v':
420 verbose = B_TRUE;
421 break;
422 case 't':
423 tflag = B_TRUE;
424 break;
425 case 'd':
426 debugonly = B_TRUE;
427 break;
428 case 'f':
429 fflag = B_TRUE;
430 break;
431 case 'p':
432 perm_flag = B_TRUE;
433 break;
434 case 'R':
435 root_dir = optarg;
436 break;
437 case '?':
438 default:
439 usage(NULL);
440 /* NOTREACHED */
443 argc -= optind;
444 argv += optind;
446 pid = getpid();
447 if (tflag)
448 s = open("/dev/null", O_WRONLY);
449 else
450 s = socket(PF_ROUTE, SOCK_RAW, 0);
451 if (s < 0)
452 quit("socket", errno);
455 * Handle the -p and -R flags. The -R flag only applies
456 * when the -p flag is set.
458 if (root_dir == NULL) {
459 perm_file = perm_file_sfx;
460 temp_file = temp_file_sfx;
461 } else {
462 size = strlen(root_dir) + sizeof (perm_file_sfx);
463 perm_file = malloc(size);
464 if (perm_file == NULL)
465 quit("malloc", errno);
466 (void) snprintf(perm_file, size, "%s%s", root_dir,
467 perm_file_sfx);
468 size = strlen(root_dir) + sizeof (temp_file_sfx);
469 temp_file = malloc(size);
470 if (temp_file == NULL)
471 quit("malloc", errno);
472 (void) snprintf(temp_file, size, "%s%s", root_dir,
473 temp_file_sfx);
476 * Whether or not to act on the routing table. The only time the
477 * routing table is not modified is when both -p and -R are present.
479 update_table = (!perm_flag || root_dir == NULL);
480 if (tflag)
481 perm_flag = 0;
483 if (fflag) {
485 * Accept an address family keyword after the -f. Since the
486 * default address family is AF_INET, reassign af only for the
487 * other valid address families.
489 if (*argv != NULL) {
490 switch (keyword(*argv)) {
491 case K_INET6:
492 af = AF_INET6;
493 early_v6_keyword = B_TRUE;
494 /* fallthrough */
495 case K_INET:
496 /* Skip over the address family parameter. */
497 argc--;
498 argv++;
499 break;
502 flushroutes(0, NULL);
505 if (*argv != NULL) {
506 switch (keyword(*argv)) {
507 case K_GET:
508 case K_CHANGE:
509 case K_ADD:
510 case K_DELETE:
511 rval = 0;
512 if (update_table) {
513 rval = newroute(argv);
515 if (perm_flag && (rval == 0 || rval == EEXIST ||
516 rval == ESRCH)) {
517 save_route(argc, argv, B_FALSE);
518 return (0);
520 return (rval);
521 case K_SHOW:
522 if (perm_flag) {
523 return (show_saved_routes(argc));
524 } else {
525 syntax_error(gettext(
526 "route: show command requires -p\n"));
528 /* NOTREACHED */
529 case K_MONITOR:
530 rtmonitor(argc, argv);
531 /* NOTREACHED */
533 case K_FLUSH:
534 flushroutes(argc, argv);
535 return (0);
538 if (!fflag)
539 usage(*argv);
540 return (0);
544 * Purge all entries in the routing tables not
545 * associated with network interfaces.
547 void
548 flushroutes(int argc, char *argv[])
550 int seqno;
551 int sd; /* mib stream */
552 mib_item_t *item;
553 mib2_ipRouteEntry_t *rp;
554 mib2_ipv6RouteEntry_t *rp6;
555 int oerrno;
556 int off = 0;
557 int on = 1;
559 if (argc > 1) {
560 argv++;
561 if (argc == 2 && **argv == '-') {
563 * The address family (preceded by a dash) may be used
564 * to flush the routes of that particular family.
566 switch (keyword(*argv + 1)) {
567 case K_INET:
568 af = AF_INET;
569 break;
570 case K_LINK:
571 af = AF_LINK;
572 break;
573 case K_INET6:
574 af = AF_INET6;
575 break;
576 default:
577 usage(*argv);
578 /* NOTREACHED */
580 } else {
581 usage(*argv);
584 if (perm_flag) {
585 /* This flushes the persistent route file */
586 save_route(0, NULL, B_TRUE);
588 if (!update_table) {
589 return;
592 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
593 sizeof (off)) < 0)
594 quit("setsockopt", errno);
596 sd = open("/dev/ip", O_RDWR);
597 oerrno = errno;
598 if (sd < 0) {
599 switch (errno) {
600 case EACCES:
601 (void) fprintf(stderr,
602 gettext("route: flush: insufficient privileges\n"));
603 exit(oerrno);
604 /* NOTREACHED */
605 default:
606 quit(gettext("can't open mib stream"), oerrno);
607 /* NOTREACHED */
610 if ((item = mibget(sd)) == NULL)
611 quit("mibget", errno);
612 if (verbose) {
613 (void) printf("Examining routing table from "
614 "T_SVR4_OPTMGMT_REQ\n");
616 seqno = 0; /* ??? */
617 switch (af) {
618 case AF_INET:
619 /* Extract ipRouteEntrySize */
620 for (; item != NULL; item = item->next_item) {
621 if (item->mib_id != 0)
622 continue;
623 if (item->group == MIB2_IP) {
624 ipRouteEntrySize =
625 ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
626 assert(IS_P2ALIGNED(ipRouteEntrySize,
627 sizeof (mib2_ipRouteEntry_t *)));
628 break;
631 if (ipRouteEntrySize == 0) {
632 (void) fprintf(stderr,
633 gettext("ipRouteEntrySize can't be determined.\n"));
634 exit(1);
636 for (; item != NULL; item = item->next_item) {
638 * skip all the other trash that comes up the mib stream
640 if (item->group != MIB2_IP ||
641 item->mib_id != MIB2_IP_ROUTE)
642 continue;
643 for (rp = (mib2_ipRouteEntry_t *)item->valp;
644 (char *)rp < (char *)item->valp + item->length;
645 /* LINTED */
646 rp = (mib2_ipRouteEntry_t *)
647 ((char *)rp + ipRouteEntrySize)) {
648 delRouteEntry(rp, NULL, seqno);
649 seqno++;
651 break;
653 break;
654 case AF_INET6:
655 /* Extract ipv6RouteEntrySize */
656 for (; item != NULL; item = item->next_item) {
657 if (item->mib_id != 0)
658 continue;
659 if (item->group == MIB2_IP6) {
660 ipv6RouteEntrySize =
661 ((mib2_ipv6IfStatsEntry_t *)item->valp)->
662 ipv6RouteEntrySize;
663 assert(IS_P2ALIGNED(ipv6RouteEntrySize,
664 sizeof (mib2_ipv6RouteEntry_t *)));
665 break;
668 if (ipv6RouteEntrySize == 0) {
669 (void) fprintf(stderr, gettext(
670 "ipv6RouteEntrySize cannot be determined.\n"));
671 exit(1);
673 for (; item != NULL; item = item->next_item) {
675 * skip all the other trash that comes up the mib stream
677 if (item->group != MIB2_IP6 ||
678 item->mib_id != MIB2_IP6_ROUTE)
679 continue;
680 for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
681 (char *)rp6 < (char *)item->valp + item->length;
682 /* LINTED */
683 rp6 = (mib2_ipv6RouteEntry_t *)
684 ((char *)rp6 + ipv6RouteEntrySize)) {
685 delRouteEntry(NULL, rp6, seqno);
686 seqno++;
688 break;
690 break;
693 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
694 sizeof (on)) < 0)
695 quit("setsockopt", errno);
699 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
700 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
701 * order to facilitate the flushing of RTF_GATEWAY routes.
703 static void
704 delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
706 char *cp;
707 int ire_type;
708 int rlen;
709 struct rt_msghdr *rtm;
710 struct sockaddr_in sin;
711 struct sockaddr_in6 sin6;
712 int slen;
714 if (rp != NULL)
715 ire_type = rp->ipRouteInfo.re_ire_type;
716 else
717 ire_type = rp6->ipv6RouteInfo.re_ire_type;
718 if (ire_type != IRE_DEFAULT &&
719 ire_type != IRE_PREFIX &&
720 ire_type != IRE_HOST &&
721 ire_type != IRE_HOST_REDIRECT)
722 return;
724 rtm = &m_rtmsg.m_rtm;
725 (void) memset(rtm, 0, sizeof (m_rtmsg));
726 rtm->rtm_type = RTM_DELETE;
727 rtm->rtm_seq = seqno;
728 rtm->rtm_flags |= RTF_GATEWAY;
729 rtm->rtm_version = RTM_VERSION;
730 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
731 cp = m_rtmsg.m_space;
732 if (rp != NULL) {
733 slen = sizeof (struct sockaddr_in);
734 if (rp->ipRouteMask == IP_HOST_MASK)
735 rtm->rtm_flags |= RTF_HOST;
736 (void) memset(&sin, 0, slen);
737 sin.sin_family = AF_INET;
738 sin.sin_addr.s_addr = rp->ipRouteDest;
739 (void) memmove(cp, &sin, slen);
740 cp += slen;
741 sin.sin_addr.s_addr = rp->ipRouteNextHop;
742 (void) memmove(cp, &sin, slen);
743 cp += slen;
744 sin.sin_addr.s_addr = rp->ipRouteMask;
745 (void) memmove(cp, &sin, slen);
746 cp += slen;
747 } else {
748 slen = sizeof (struct sockaddr_in6);
749 if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
750 rtm->rtm_flags |= RTF_HOST;
751 (void) memset(&sin6, 0, slen);
752 sin6.sin6_family = AF_INET6;
753 sin6.sin6_addr = rp6->ipv6RouteDest;
754 (void) memmove(cp, &sin6, slen);
755 cp += slen;
756 sin6.sin6_addr = rp6->ipv6RouteNextHop;
757 (void) memmove(cp, &sin6, slen);
758 cp += slen;
759 (void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
760 (void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
761 (uchar_t *)&sin6.sin6_addr.s6_addr);
762 (void) memmove(cp, &sin6, slen);
763 cp += slen;
765 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
766 if (debugonly) {
768 * In debugonly mode, the routing socket message to delete the
769 * current entry is not actually sent. However if verbose is
770 * also set, the routing socket message that would have been
771 * is printed.
773 if (verbose)
774 print_rtmsg(rtm, rtm->rtm_msglen);
775 return;
778 rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
779 if (rlen < (int)rtm->rtm_msglen) {
780 if (rlen < 0) {
781 (void) fprintf(stderr,
782 gettext("route: write to routing socket: %s\n"),
783 strerror(errno));
784 } else {
785 (void) fprintf(stderr, gettext("route: write to "
786 "routing socket got only %d for rlen\n"), rlen);
788 return;
790 if (qflag) {
792 * In quiet mode, nothing is printed at all (unless the write()
793 * itself failed.
795 return;
797 if (verbose) {
798 print_rtmsg(rtm, rlen);
799 } else {
800 struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
802 (void) printf("%-20.20s ",
803 rtm->rtm_flags & RTF_HOST ? routename(sa) :
804 netname(sa));
805 /* LINTED */
806 sa = (struct sockaddr *)(salen(sa) + (char *)sa);
807 (void) printf("%-20.20s ", routename(sa));
808 (void) printf("done\n");
813 * Return the name of the host whose address is given.
815 char *
816 routename(const struct sockaddr *sa)
818 char *cp;
819 static char line[MAXHOSTNAMELEN + 1];
820 struct hostent *hp = NULL;
821 static char domain[MAXHOSTNAMELEN + 1];
822 static boolean_t first = B_TRUE;
823 struct in_addr in;
824 struct in6_addr in6;
825 int error_num;
826 ushort_t *s;
827 ushort_t *slim;
829 if (first) {
830 first = B_FALSE;
831 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
832 (cp = strchr(domain, '.')))
833 (void) strcpy(domain, cp + 1);
834 else
835 domain[0] = 0;
838 if (salen(sa) == 0) {
839 (void) strcpy(line, "default");
840 return (line);
842 switch (sa->sa_family) {
844 case AF_INET:
845 /* LINTED */
846 in = ((struct sockaddr_in *)sa)->sin_addr;
848 cp = NULL;
849 if (in.s_addr == INADDR_ANY)
850 cp = "default";
851 if (cp == NULL && !nflag) {
852 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
853 AF_INET);
854 if (hp != NULL) {
855 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
856 (strcmp(cp + 1, domain) == 0))
857 *cp = 0;
858 cp = hp->h_name;
861 if (cp != NULL) {
862 (void) strncpy(line, cp, MAXHOSTNAMELEN);
863 line[MAXHOSTNAMELEN] = '\0';
864 } else {
865 in.s_addr = ntohl(in.s_addr);
866 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
867 C(in.s_addr >> 16), C(in.s_addr >> 8),
868 C(in.s_addr));
870 break;
872 case AF_LINK:
873 return (link_ntoa((struct sockaddr_dl *)sa));
875 case AF_INET6:
876 /* LINTED */
877 in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
879 cp = NULL;
880 if (IN6_IS_ADDR_UNSPECIFIED(&in6))
881 cp = "default";
882 if (cp == NULL && !nflag) {
883 hp = getipnodebyaddr((char *)&in6,
884 sizeof (struct in6_addr), AF_INET6, &error_num);
885 if (hp != NULL) {
886 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
887 (strcmp(cp + 1, domain) == 0))
888 *cp = 0;
889 cp = hp->h_name;
892 if (cp != NULL) {
893 (void) strncpy(line, cp, MAXHOSTNAMELEN);
894 line[MAXHOSTNAMELEN] = '\0';
895 } else {
896 (void) inet_ntop(AF_INET6, (void *)&in6, line,
897 INET6_ADDRSTRLEN);
899 if (hp != NULL)
900 freehostent(hp);
902 break;
904 default:
905 s = (ushort_t *)sa;
907 slim = s + ((salen(sa) + 1) >> 1);
908 cp = line + sprintf(line, "(%d)", sa->sa_family);
910 while (++s < slim) /* start with sa->sa_data */
911 cp += sprintf(cp, " %x", *s);
912 break;
914 return (line);
918 * Return the name of the network whose address is given.
919 * The address is assumed to be that of a net or subnet, not a host.
921 static char *
922 netname(struct sockaddr *sa)
924 char *cp = NULL;
925 static char line[MAXHOSTNAMELEN + 1];
926 struct netent *np;
927 in_addr_t net, mask;
928 int subnetshift;
929 struct in_addr in;
930 ushort_t *s;
931 ushort_t *slim;
933 switch (sa->sa_family) {
935 case AF_INET:
936 /* LINTED */
937 in = ((struct sockaddr_in *)sa)->sin_addr;
939 in.s_addr = ntohl(in.s_addr);
940 if (in.s_addr == INADDR_ANY) {
941 cp = "default";
942 } else if (!nflag) {
943 if (IN_CLASSA(in.s_addr)) {
944 mask = IN_CLASSA_NET;
945 subnetshift = 8;
946 } else if (IN_CLASSB(in.s_addr)) {
947 mask = IN_CLASSB_NET;
948 subnetshift = 8;
949 } else {
950 mask = IN_CLASSC_NET;
951 subnetshift = 4;
954 * If there are more bits than the standard mask
955 * would suggest, subnets must be in use.
956 * Guess at the subnet mask, assuming reasonable
957 * width subnet fields.
959 while (in.s_addr &~ mask)
960 mask = (long)mask >> subnetshift;
961 net = in.s_addr & mask;
962 while ((mask & 1) == 0)
963 mask >>= 1, net >>= 1;
964 np = getnetbyaddr(net, AF_INET);
965 if (np != NULL)
966 cp = np->n_name;
968 if (cp != NULL) {
969 (void) strncpy(line, cp, MAXHOSTNAMELEN);
970 line[MAXHOSTNAMELEN] = '\0';
971 } else if ((in.s_addr & 0xffffff) == 0) {
972 (void) sprintf(line, "%u", C(in.s_addr >> 24));
973 } else if ((in.s_addr & 0xffff) == 0) {
974 (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
975 C(in.s_addr >> 16));
976 } else if ((in.s_addr & 0xff) == 0) {
977 (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
978 C(in.s_addr >> 16), C(in.s_addr >> 8));
979 } else {
980 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
981 C(in.s_addr >> 16), C(in.s_addr >> 8),
982 C(in.s_addr));
984 break;
986 case AF_LINK:
987 return (link_ntoa((struct sockaddr_dl *)sa));
989 case AF_INET6:
990 return (routename(sa));
992 default:
993 /* LINTED */
994 s = (ushort_t *)sa->sa_data;
996 slim = s + ((salen(sa) + 1) >> 1);
997 cp = line + sprintf(line, "af %d:", sa->sa_family);
999 while (s < slim)
1000 cp += sprintf(cp, " %x", *s++);
1001 break;
1003 return (line);
1007 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1008 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1009 * or point to dynamically allocated memory.
1011 rtcmd_irep_t *
1012 new_rtcmd_irep(void)
1014 rtcmd_irep_t *rcip;
1016 rcip = calloc(1, sizeof (rtcmd_irep_t));
1017 if (rcip == NULL) {
1018 quit("calloc", errno);
1020 rcip->ri_af = af;
1021 rcip->ri_flags = RTF_STATIC;
1022 return (rcip);
1025 void
1026 del_rtcmd_irep(rtcmd_irep_t *rcip)
1028 free(rcip->ri_dest_str);
1029 free(rcip->ri_gate_str);
1030 free(rcip->ri_ifp_str);
1032 * IPv6 host entries come from getipnodebyname, which dynamically
1033 * allocates memory. IPv4 host entries come from gethostbyname, which
1034 * returns static memory and cannot be freed with freehostent.
1036 if (rcip->ri_gate_hp != NULL &&
1037 rcip->ri_gate_hp->h_addrtype == AF_INET6)
1038 freehostent(rcip->ri_gate_hp);
1039 free(rcip);
1042 void
1043 save_string(char **dst, char *src)
1045 free(*dst);
1046 *dst = strdup(src);
1047 if (*dst == NULL) {
1048 quit("malloc", errno);
1053 * Print the short form summary of a route command.
1054 * Eg. "add net default: gateway 10.0.0.1"
1055 * The final newline is not added, allowing the caller to append additional
1056 * information.
1058 void
1059 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
1060 boolean_t to_saved)
1062 char *cmd;
1063 char obuf[INET6_ADDRSTRLEN];
1065 switch (rcip->ri_cmd) {
1066 case RTM_ADD:
1067 cmd = "add";
1068 break;
1069 case RTM_CHANGE:
1070 cmd = "change";
1071 break;
1072 case RTM_DELETE:
1073 cmd = "delete";
1074 break;
1075 case RTM_GET:
1076 cmd = "get";
1077 break;
1078 default:
1079 assert(0);
1082 (void) fprintf(to, "%s%s %s %s", cmd,
1083 (to_saved) ? " persistent" : "",
1084 (rcip->ri_flags & RTF_HOST) ? "host" : "net",
1085 (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str);
1087 if (rcip->ri_gate_str != NULL) {
1088 switch (rcip->ri_af) {
1089 case AF_INET:
1090 if (nflag) {
1091 (void) fprintf(to, ": gateway %s",
1092 inet_ntoa(rcip->ri_gate.sin.sin_addr));
1093 } else if (gw_good &&
1094 rcip->ri_gate_hp != NULL &&
1095 rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1097 * Print the actual address used in the case
1098 * where there was more than one address
1099 * available for the name, and one was used
1100 * successfully.
1102 (void) fprintf(to, ": gateway %s (%s)",
1103 rcip->ri_gate_str,
1104 inet_ntoa(rcip->ri_gate.sin.sin_addr));
1105 } else {
1106 (void) fprintf(to, ": gateway %s",
1107 rcip->ri_gate_str);
1109 break;
1110 case AF_INET6:
1111 if (inet_ntop(AF_INET6,
1112 &rcip->ri_gate.sin6.sin6_addr, obuf,
1113 INET6_ADDRSTRLEN) != NULL) {
1114 if (nflag) {
1115 (void) fprintf(to, ": gateway %s",
1116 obuf);
1117 break;
1119 if (gw_good &&
1120 rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1121 (void) fprintf(to, ": gateway %s (%s)",
1122 rcip->ri_gate_str, obuf);
1123 break;
1126 /* FALLTHROUGH */
1127 default:
1128 (void) fprintf(to, ": gateway %s",
1129 rcip->ri_gate_str);
1130 break;
1135 void
1136 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
1138 int flag = 0;
1139 uint_t noval, *valp = &noval;
1141 switch (key) {
1142 #define caseof(x, y, z) \
1143 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1145 caseof(K_MTU, RTV_MTU, rmx_mtu);
1146 caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
1147 caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
1148 caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
1149 caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
1150 caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
1151 caseof(K_RTT, RTV_RTT, rmx_rtt);
1152 caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
1153 #undef caseof
1155 rcip->ri_inits |= flag;
1156 if (lock)
1157 rcip->ri_metrics.rmx_locks |= flag;
1158 *valp = atoi(value);
1162 * Parse the options give in argv[], filling in rcip with the results.
1163 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1164 * tokenized to produce the command line. Cmd_string is tokenized using
1165 * strtok, which will overwrite whitespace in the string with nulls.
1167 * Returns B_TRUE on success and B_FALSE on failure.
1169 boolean_t
1170 args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string)
1172 const char *ws = "\f\n\r\t\v ";
1173 char *tok = cmd_string;
1174 char *keyword_str;
1175 addr_type_t atype = ADDR_TYPE_ANY;
1176 boolean_t iflag = B_FALSE;
1177 boolean_t locknext = B_FALSE;
1178 boolean_t lockrest = B_FALSE;
1179 boolean_t dash_keyword;
1180 int key;
1181 char *err;
1183 if (cmd_string == NULL) {
1184 tok = argv[0];
1185 } else {
1186 tok = strtok(cmd_string, ws);
1190 * The command keywords are already fully checked by main() or
1191 * search_rtfile().
1193 switch (*tok) {
1194 case 'a':
1195 rcip->ri_cmd = RTM_ADD;
1196 break;
1197 case 'c':
1198 rcip->ri_cmd = RTM_CHANGE;
1199 break;
1200 case 'd':
1201 rcip->ri_cmd = RTM_DELETE;
1202 break;
1203 case 'g':
1204 rcip->ri_cmd = RTM_GET;
1205 break;
1206 default:
1207 /* NOTREACHED */
1208 quit(gettext("Internal Error"), EINVAL);
1209 /* NOTREACHED */
1212 #define NEXTTOKEN \
1213 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1215 while (NEXTTOKEN) {
1216 keyword_str = tok;
1217 if (*tok == '-') {
1218 dash_keyword = B_TRUE;
1219 key = keyword(tok + 1);
1220 } else {
1221 dash_keyword = B_FALSE;
1222 key = keyword(tok);
1223 if (key != K_HOST && key != K_NET) {
1224 /* All others must be preceded by '-' */
1225 key = 0;
1228 switch (key) {
1229 case K_HOST:
1230 if (atype == ADDR_TYPE_NET) {
1231 syntax_error(gettext("route: -host and -net "
1232 "are mutually exclusive\n"));
1233 return (B_FALSE);
1235 atype = ADDR_TYPE_HOST;
1236 break;
1237 case K_NET:
1238 if (atype == ADDR_TYPE_HOST) {
1239 syntax_error(gettext("route: -host and -net "
1240 "are mutually exclusive\n"));
1241 return (B_FALSE);
1243 atype = ADDR_TYPE_NET;
1244 break;
1245 case K_LINK:
1246 rcip->ri_af = AF_LINK;
1247 break;
1248 case K_INET:
1249 rcip->ri_af = AF_INET;
1250 break;
1251 case K_SA:
1252 rcip->ri_af = PF_ROUTE;
1253 break;
1254 case K_INET6:
1255 rcip->ri_af = AF_INET6;
1256 break;
1257 case K_IFACE:
1258 case K_INTERFACE:
1259 iflag = B_TRUE;
1260 /* fallthrough */
1261 case K_NOSTATIC:
1262 rcip->ri_flags &= ~RTF_STATIC;
1263 break;
1264 case K_LOCK:
1265 locknext = B_TRUE;
1266 break;
1267 case K_LOCKREST:
1268 lockrest = B_TRUE;
1269 break;
1270 case K_REJECT:
1271 rcip->ri_flags |= RTF_REJECT;
1272 break;
1273 case K_BLACKHOLE:
1274 rcip->ri_flags |= RTF_BLACKHOLE;
1275 break;
1276 case K_PROTO1:
1277 rcip->ri_flags |= RTF_PROTO1;
1278 break;
1279 case K_PROTO2:
1280 rcip->ri_flags |= RTF_PROTO2;
1281 break;
1282 case K_CLONING:
1283 rcip->ri_flags |= RTF_CLONING;
1284 break;
1285 case K_XRESOLVE:
1286 rcip->ri_flags |= RTF_XRESOLVE;
1287 break;
1288 case K_STATIC:
1289 rcip->ri_flags |= RTF_STATIC;
1290 break;
1291 case K_IFA:
1292 if (!NEXTTOKEN) {
1293 syntax_arg_missing(keyword_str);
1294 return (B_FALSE);
1296 if (!getaddr(rcip, RTA_IFA, tok, atype)) {
1297 return (B_FALSE);
1299 break;
1300 case K_IFP:
1301 if (!NEXTTOKEN) {
1302 syntax_arg_missing(keyword_str);
1303 return (B_FALSE);
1305 if (!getaddr(rcip, RTA_IFP, tok, atype)) {
1306 return (B_FALSE);
1308 break;
1309 case K_GATEWAY:
1310 if (!NEXTTOKEN) {
1311 syntax_arg_missing(keyword_str);
1312 return (B_FALSE);
1314 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1315 return (B_FALSE);
1317 break;
1318 case K_DST:
1319 if (!NEXTTOKEN) {
1320 syntax_arg_missing(keyword_str);
1321 return (B_FALSE);
1323 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1324 return (B_FALSE);
1326 break;
1327 case K_NETMASK:
1328 if (!NEXTTOKEN) {
1329 syntax_arg_missing(keyword_str);
1330 return (B_FALSE);
1332 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1333 return (B_FALSE);
1335 atype = ADDR_TYPE_NET;
1336 break;
1337 case K_MTU:
1338 case K_HOPCOUNT:
1339 case K_EXPIRE:
1340 case K_RECVPIPE:
1341 case K_SENDPIPE:
1342 case K_SSTHRESH:
1343 case K_RTT:
1344 case K_RTTVAR:
1345 if (!NEXTTOKEN) {
1346 syntax_arg_missing(keyword_str);
1347 return (B_FALSE);
1349 set_metric(rcip, tok, key, locknext || lockrest);
1350 locknext = B_FALSE;
1351 break;
1352 case K_PRIVATE:
1353 rcip->ri_flags |= RTF_PRIVATE;
1354 break;
1355 case K_SETSRC:
1356 if (!NEXTTOKEN) {
1357 syntax_arg_missing(keyword_str);
1358 return (B_FALSE);
1360 if (!getaddr(rcip, RTA_SRC, tok, atype)) {
1361 return (B_FALSE);
1363 rcip->ri_flags |= RTF_SETSRC;
1364 break;
1365 case K_INDIRECT:
1366 rcip->ri_flags |= RTF_INDIRECT;
1367 break;
1368 default:
1369 if (dash_keyword) {
1370 syntax_bad_keyword(tok + 1);
1371 return (B_FALSE);
1373 if ((rcip->ri_addrs & RTA_DST) == 0) {
1374 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1375 return (B_FALSE);
1377 } else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
1379 * For the gateway parameter, retrieve the
1380 * pointer to the struct hostent so that all
1381 * possible addresses can be tried until one
1382 * is successful.
1384 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1385 return (B_FALSE);
1387 } else {
1388 ulong_t metric;
1390 * Assume that a regular number is a metric.
1391 * Needed for compatibility with old route
1392 * command syntax.
1394 errno = 0;
1395 metric = strtoul(tok, &err, 10);
1396 if (errno == 0 && *err == '\0' &&
1397 metric < 0x80000000ul) {
1398 iflag = (metric == 0);
1399 if (verbose) {
1400 (void) printf("old usage of "
1401 "trailing number, assuming "
1402 "route %s\n", iflag ?
1403 "to if" : "via gateway");
1405 continue;
1407 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1408 return (B_FALSE);
1413 #undef NEXTTOKEN
1415 if ((rcip->ri_addrs & RTA_DST) == 0) {
1416 syntax_error(gettext("route: destination required\n"));
1417 return (B_FALSE);
1418 } else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
1419 (rcip->ri_addrs & RTA_GATEWAY) == 0) {
1420 syntax_error(gettext(
1421 "route: gateway required for add or delete command\n"));
1422 return (B_FALSE);
1425 if (!iflag) {
1426 rcip->ri_flags |= RTF_GATEWAY;
1429 if (atype != ADDR_TYPE_NET) {
1430 if (rcip->ri_addrs & RTA_NETMASK) {
1432 * We know the netmask, so we can set the host flag
1433 * based on whether the netmask is the host netmask.
1435 if (rcip->ri_af == AF_INET &&
1436 rcip->ri_mask.sin.sin_addr.s_addr ==
1437 IP_HOST_MASK) {
1438 rcip->ri_flags |= RTF_HOST;
1440 if (rcip->ri_af == AF_INET6 &&
1441 memcmp(&rcip->ri_mask.sin6.sin6_addr,
1442 &in6_host_mask,
1443 sizeof (struct in6_addr)) == 0) {
1444 rcip->ri_flags |= RTF_HOST;
1446 } else {
1448 * If no prefix mask has been saved at this point, it
1449 * only makes sense to treat the destination address
1450 * as a host address.
1452 rcip->ri_flags |= RTF_HOST;
1455 return (B_TRUE);
1459 * This command always seeks to the end of the file prior to writing.
1461 void
1462 write_to_rtfile(FILE *fp, int argc, char **argv)
1464 char file_line[BUF_SIZE];
1465 int len;
1466 int i;
1468 len = 0;
1469 if (early_v6_keyword) {
1471 * This flag is set when "inet6" was seen as an
1472 * argument to the -f flag. Normally, when writing
1473 * routes to the persistent route file, everything on
1474 * the command line after "add" is saved verbatim.
1475 * In this case, the arguments after "add" may not be
1476 * sufficient, as the ipv6 keyword came before "add",
1477 * yet must be present in the persistent route file.
1479 len += snprintf(file_line, BUF_SIZE, "-inet6 ");
1481 for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
1482 len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
1483 argv[i]);
1485 if (len >= BUF_SIZE)
1486 quit(gettext("Internal Error"), EINVAL);
1487 file_line[len - 1] = '\n';
1488 if (fseek(fp, 0, SEEK_END) != 0 ||
1489 fputs(file_line, fp) == EOF) {
1490 quit(gettext("failed to write to route file"),
1491 errno);
1495 boolean_t
1496 compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
1498 if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
1499 memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
1500 return (B_FALSE);
1502 return (srch_rt->ri_gate_str == NULL ||
1503 strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
1507 * Search the route file for routes matching the supplied route. There are 3
1508 * modes of operation:
1509 * SEARCH_MODE_RET - no side effects.
1510 * SEARCH_MODE_PRINT - prints each matching line.
1511 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1513 * In all cases, the number of matches is returned. If rt is NULL, all routes
1514 * matching the global af value are considered matching.
1517 search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
1519 char *tmp_buf;
1520 int match_cnt;
1521 boolean_t match;
1522 char file_line[BUF_SIZE + 4] = "add ";
1523 rtcmd_irep_t *thisrt;
1525 match_cnt = 0;
1528 * Leave space at the beginning of file_line for "add ".
1530 while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
1532 if (file_line[4] == '#' || file_line[4] == '\n') {
1533 /* Handle comments and blank lines */
1534 if (mode == SEARCH_MODE_DEL &&
1535 fputs(file_line + 4, temp_fp) == EOF) {
1536 quit(gettext(
1537 "route: failed to write to temp file"),
1538 errno);
1540 continue;
1542 thisrt = new_rtcmd_irep();
1544 * thisrt->ri_af defaults to whatever address family happens
1545 * to be set in the global af, but routes in the persistent
1546 * route file must be treated as AF_INET by default.
1548 thisrt->ri_af = AF_INET;
1550 exit_on_error = B_FALSE;
1551 tmp_buf = strdup(file_line);
1552 /* args_to_rtcmd() will mangle the string passed. */
1553 if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
1554 /* There was an error in args_to_rtcmd() or helpers */
1555 del_rtcmd_irep(thisrt);
1556 free(tmp_buf);
1557 continue;
1559 exit_on_error = B_TRUE;
1560 free(tmp_buf);
1562 if (thisrt->ri_gate_str == NULL) {
1563 del_rtcmd_irep(thisrt);
1564 continue;
1566 match = (rt == NULL) ? (thisrt->ri_af == af) :
1567 compare_rtcmd(rt, thisrt);
1569 if (match) match_cnt++;
1570 if (match && mode == SEARCH_MODE_PRINT) {
1571 (void) printf("persistent: route %s", file_line);
1573 if (match && mode == SEARCH_MODE_DEL) {
1574 thisrt->ri_cmd = RTM_DELETE;
1575 print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
1576 (void) printf("\n");
1578 del_rtcmd_irep(thisrt);
1580 if (!match && mode == SEARCH_MODE_DEL &&
1581 fputs(file_line + 4, temp_fp) == EOF) {
1582 quit(gettext("failed to write to temp file"),
1583 errno);
1586 return (match_cnt);
1590 * Perform the route operation given in argv on the persistent route file.
1591 * If do_flush is set, the persistent route file is flushed of all routes
1592 * matching the global family, and the arguments are ignored.
1594 void
1595 save_route(int argc, char **argv, int do_flush)
1597 rtcmd_irep_t *rt;
1598 int perm_fd;
1599 FILE *perm_fp;
1600 FILE *temp_fp;
1601 mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1602 struct flock lock;
1603 struct stat st;
1604 const char commentstr[] =
1605 "# File generated by route(1M) - do not edit.\n";
1607 perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
1608 if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
1609 quit("failed to open route file", errno);
1611 lock.l_type = F_WRLCK;
1612 lock.l_whence = SEEK_SET;
1613 lock.l_start = 0;
1614 lock.l_len = 0;
1615 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1616 quit(gettext("failed to lock route file"), errno);
1617 /* NOTREACHED */
1619 if (st.st_size == 0 &&
1620 write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
1621 sizeof (commentstr) - 1)
1622 quit(gettext("failed to open route file"), errno);
1624 if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
1625 quit(gettext("failed to open route file"), errno);
1626 /* NOTREACHED */
1629 if (!do_flush) {
1630 rt = new_rtcmd_irep();
1631 (void) args_to_rtcmd(rt, argv, NULL);
1633 if (do_flush || rt->ri_cmd == RTM_DELETE) {
1634 if ((temp_fp = fopen(temp_file, "w")) == NULL) {
1635 quit(gettext("failed to open temp file"), errno);
1636 /* NOTREACHED */
1639 if (do_flush) {
1640 (void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
1641 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1642 quit(gettext("failed to update route file"), errno);
1643 /* NOTREACHED */
1645 (void) fclose(perm_fp);
1646 return;
1649 switch (rt->ri_cmd) {
1650 case RTM_ADD:
1651 if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
1652 /* Route is already in the file */
1653 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1654 (void) fprintf(stderr, ": entry exists\n");
1655 exit(1);
1657 write_to_rtfile(perm_fp, argc - 1, argv + 1);
1658 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1659 (void) printf("\n");
1660 break;
1662 case RTM_CHANGE:
1663 syntax_error(
1664 gettext("route: change command not supported with -p\n"));
1665 /* NOTREACHED */
1667 case RTM_DELETE:
1668 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
1669 /* Route not found */
1670 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1671 (void) fprintf(stderr, gettext(": not in file\n"));
1672 exit(1);
1674 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1675 quit(gettext("failed to update route file"), errno);
1676 /* NOTREACHED */
1678 break;
1680 case RTM_GET:
1681 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1682 0) {
1683 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1684 (void) printf(gettext(": not in file\n"));
1686 break;
1688 default:
1689 quit(gettext("Internal Error"), EINVAL);
1690 /* NOTREACHED */
1694 * Closing the file unlocks it.
1696 (void) fclose(perm_fp);
1700 show_saved_routes(int argc)
1702 int perm_fd;
1703 FILE *perm_fp;
1704 struct flock lock;
1705 int count = 0;
1707 if (argc != 1) {
1708 syntax_error(gettext("route: invalid arguments for show\n"));
1711 perm_fd = open(perm_file, O_RDONLY, 0);
1713 if (perm_fd == -1) {
1714 if (errno == ENOENT) {
1715 (void) printf("No persistent routes are defined\n");
1716 return (0);
1717 } else {
1718 quit(gettext("failed to open route file"), errno);
1721 lock.l_type = F_RDLCK;
1722 lock.l_whence = SEEK_SET;
1723 lock.l_start = 0;
1724 lock.l_len = 0;
1725 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1726 quit(gettext("failed to lock route file"),
1727 errno);
1728 /* NOTREACHED */
1730 if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1731 quit(gettext("failed to open route file"), errno);
1732 /* NOTREACHED */
1734 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1735 (void) fseek(perm_fp, 0, SEEK_SET);
1736 af = AF_INET6;
1737 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1739 if (count == 0)
1740 (void) printf("No persistent routes are defined\n");
1742 (void) fclose(perm_fp);
1743 return (0);
1747 newroute(char **argv)
1749 rtcmd_irep_t *newrt;
1750 int ret, attempts, oerrno;
1751 char *err;
1752 char obuf[INET6_ADDRSTRLEN];
1753 #define hp (newrt->ri_gate_hp)
1755 newrt = new_rtcmd_irep();
1756 (void) args_to_rtcmd(newrt, argv, NULL);
1758 if (newrt->ri_cmd != RTM_GET && !tflag) {
1759 /* Don't want to read back our messages */
1760 (void) shutdown(s, 0);
1762 if (newrt->ri_addrs & RTA_IFP) {
1763 newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
1764 if (newrt->ri_ifp.sdl.sdl_index == 0) {
1765 if (errno != ENXIO) {
1766 quit("if_nametoindex", errno);
1767 } else {
1768 (void) fprintf(stderr,
1769 gettext("route: %s: no such interface\n"),
1770 newrt->ri_ifp_str);
1771 exit(1);
1774 newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1776 for (attempts = 1; ; attempts++) {
1777 errno = 0;
1778 if ((ret = rtmsg(newrt)) == 0)
1779 break;
1780 if (errno != ENETUNREACH && errno != ESRCH)
1781 break;
1782 if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
1783 hp->h_addr_list[attempts] != NULL) {
1784 switch (af) {
1785 case AF_INET:
1786 (void) memmove(&newrt->ri_gate.sin.sin_addr,
1787 hp->h_addr_list[attempts], hp->h_length);
1788 continue;
1789 case AF_INET6:
1790 (void) memmove(&newrt->ri_gate.sin6.sin6_addr,
1791 hp->h_addr_list[attempts], hp->h_length);
1792 continue;
1795 break;
1797 oerrno = errno;
1799 if (newrt->ri_cmd != RTM_GET) {
1800 print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
1801 if (ret == 0)
1802 (void) printf("\n");
1803 } else if (ret != 0) {
1805 * Note: there is nothing additional to print for get
1806 * if ret == 0.
1808 if (nflag) {
1809 switch (newrt->ri_af) {
1810 case AF_INET:
1811 (void) printf(" %s",
1812 inet_ntoa(newrt->ri_dst.sin.sin_addr));
1813 break;
1814 case AF_INET6:
1815 if (inet_ntop(AF_INET6,
1816 (void *)&newrt->ri_dst.sin6.sin6_addr,
1817 obuf, INET6_ADDRSTRLEN) != NULL) {
1818 (void) printf(" %s", obuf);
1819 break;
1821 /* FALLTHROUGH */
1822 default:
1823 (void) printf("%s", newrt->ri_dest_str);
1824 break;
1826 } else {
1827 (void) printf("%s", newrt->ri_dest_str);
1831 if (ret != 0) {
1832 switch (oerrno) {
1833 case ESRCH:
1834 err = "not in table";
1835 break;
1836 case EBUSY:
1837 err = "entry in use";
1838 break;
1839 case ENOBUFS:
1840 err = "routing table overflow";
1841 break;
1842 case EEXIST:
1843 err = "entry exists";
1844 break;
1845 case EPERM:
1846 err = "insufficient privileges";
1847 break;
1848 default:
1849 err = strerror(oerrno);
1850 break;
1852 (void) printf(": %s\n", err);
1855 del_rtcmd_irep(newrt);
1857 return (oerrno);
1858 #undef hp
1863 * Convert a network number to the corresponding IP address.
1864 * If the RTA_NETMASK hasn't been specified yet set it based
1865 * on the class of address.
1867 static void
1868 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
1870 in_addr_t addr, mask;
1872 if (net == 0) {
1873 mask = addr = 0;
1874 } else if (net < 128) {
1875 addr = net << IN_CLASSA_NSHIFT;
1876 mask = IN_CLASSA_NET;
1877 } else if (net < 65536) {
1878 addr = net << IN_CLASSB_NSHIFT;
1879 mask = IN_CLASSB_NET;
1880 } else if (net < 16777216L) {
1881 addr = net << IN_CLASSC_NSHIFT;
1882 mask = IN_CLASSC_NET;
1883 } else {
1884 addr = net;
1885 if ((addr & IN_CLASSA_HOST) == 0)
1886 mask = IN_CLASSA_NET;
1887 else if ((addr & IN_CLASSB_HOST) == 0)
1888 mask = IN_CLASSB_NET;
1889 else if ((addr & IN_CLASSC_HOST) == 0)
1890 mask = IN_CLASSC_NET;
1891 else {
1892 if (IN_CLASSA(addr))
1893 mask = IN_CLASSA_NET;
1894 else if (IN_CLASSB(addr))
1895 mask = IN_CLASSB_NET;
1896 else if (IN_CLASSC(addr))
1897 mask = IN_CLASSC_NET;
1898 else
1899 mask = IP_HOST_MASK;
1900 mask = inet_makesubnetmask(addr, mask);
1903 sin->sin_addr.s_addr = htonl(addr);
1905 /* Class E default mask is 32 */
1906 if (IN_CLASSE(addr))
1907 mask = IN_CLASSE_NET;
1909 if (!(rcip->ri_addrs & RTA_NETMASK)) {
1910 rcip->ri_addrs |= RTA_NETMASK;
1911 sin = &rcip->ri_mask.sin;
1912 sin->sin_addr.s_addr = htonl(mask);
1913 sin->sin_family = AF_INET;
1917 static in_addr_t
1918 inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
1920 int n;
1921 struct ifconf ifc;
1922 struct ifreq ifreq;
1923 struct ifreq *ifr;
1924 struct sockaddr_in *sin;
1925 char *buf;
1926 int numifs;
1927 size_t bufsize;
1928 int iosoc;
1929 in_addr_t if_addr, if_mask;
1930 in_addr_t if_subnetmask = 0;
1931 short if_flags;
1933 if (mask == 0)
1934 return (0);
1935 if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1936 quit("socket", errno);
1937 if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
1938 quit("ioctl", errno);
1939 bufsize = numifs * sizeof (struct ifreq);
1940 buf = malloc(bufsize);
1941 if (buf == NULL)
1942 quit("malloc", errno);
1943 (void) memset(&ifc, 0, sizeof (ifc));
1944 ifc.ifc_len = bufsize;
1945 ifc.ifc_buf = buf;
1946 if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
1947 quit("ioctl (get interface configuration)", errno);
1948 /* Let's check to see if this is maybe a local subnet route. */
1949 ifr = ifc.ifc_req;
1950 for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
1951 ifreq = *ifr;
1952 /* LINTED */
1953 sin = (struct sockaddr_in *)&ifr->ifr_addr;
1954 if_addr = ntohl(sin->sin_addr.s_addr);
1956 if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
1957 quit("ioctl (get interface flags)", errno);
1958 if ((ifreq.ifr_flags & IFF_UP) == 0)
1959 continue;
1960 if_flags = ifreq.ifr_flags;
1962 if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
1963 quit("ioctl (get netmask)", errno);
1964 /* LINTED */
1965 sin = (struct sockaddr_in *)&ifreq.ifr_addr;
1966 if_mask = ntohl(sin->sin_addr.s_addr);
1967 if ((if_addr & mask) == (addr & mask)) {
1969 * Don't trust pt-pt interfaces if there are
1970 * other interfaces.
1972 if (if_flags & IFF_POINTOPOINT) {
1973 if_subnetmask = if_mask;
1974 continue;
1977 * Fine. Just assume the same net mask as the
1978 * directly attached subnet interface is using.
1980 return (if_mask);
1983 if (if_subnetmask != 0)
1984 return (if_subnetmask);
1985 return (mask);
1989 * Interpret an argument as a network address of some kind.
1991 * If the address family is one looked up in getaddr() using one of the
1992 * getipnodebyX() functions (currently only AF_INET6), then callers should
1993 * freehostent() the returned "struct hostent" pointer if one was passed in.
1995 * If exit_on_error is true, this function will cause route to exit on error by
1996 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
1997 * on failure.
1999 static boolean_t
2000 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
2002 su_t *su;
2003 struct hostent **hpp;
2004 struct hostent *hp;
2005 int masklen;
2007 if (which == RTA_GATEWAY) {
2008 hpp = &(rcip->ri_gate_hp);
2009 } else {
2010 hpp = &hp;
2012 *hpp = NULL;
2014 rcip->ri_addrs |= which;
2015 switch (which) {
2016 case RTA_DST:
2017 save_string(&rcip->ri_dest_str, s);
2018 su = &rcip->ri_dst;
2019 su->sa.sa_family = rcip->ri_af;
2020 break;
2021 case RTA_GATEWAY:
2022 save_string(&rcip->ri_gate_str, s);
2023 su = &rcip->ri_gate;
2024 su->sa.sa_family = rcip->ri_af;
2025 break;
2026 case RTA_NETMASK:
2027 su = &rcip->ri_mask;
2028 su->sa.sa_family = rcip->ri_af;
2029 break;
2030 case RTA_IFP:
2031 save_string(&rcip->ri_ifp_str, s);
2032 return (B_TRUE);
2034 * RTA_SRC has overloaded meaning. It can represent the
2035 * src address of incoming or outgoing packets.
2037 case RTA_IFA:
2038 su = &rcip->ri_ifa;
2039 su->sa.sa_family = rcip->ri_af;
2040 break;
2041 case RTA_SRC:
2042 su = &rcip->ri_src;
2043 su->sa.sa_family = rcip->ri_af;
2044 break;
2045 default:
2046 /* NOTREACHED */
2047 quit(gettext("Internal Error"), EINVAL);
2048 /* NOTREACHED */
2050 if (strcmp(s, "default") == 0) {
2051 if (which == RTA_DST) {
2052 return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
2054 if (which == RTA_SRC) {
2055 return (B_TRUE);
2057 return (B_TRUE);
2059 switch (rcip->ri_af) {
2060 case AF_LINK:
2061 link_addr(s, &su->sdl);
2062 return (B_TRUE);
2063 case PF_ROUTE:
2064 sockaddr(s, &su->sa);
2065 return (B_TRUE);
2066 case AF_INET6:
2067 switch (which) {
2068 case RTA_DST:
2069 if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
2070 return (B_FALSE);
2072 if (masklen != NO_PREFIX) {
2073 (void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
2074 sizeof (rcip->ri_mask.sin6.sin6_addr));
2075 if (!in_prefixlentomask(masklen, IPV6_ABITS,
2076 (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
2077 syntax_error(gettext(
2078 "route: bad prefix length: %d\n"),
2079 masklen);
2080 return (B_FALSE);
2082 rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2083 rcip->ri_addrs |= RTA_NETMASK;
2085 return (B_TRUE);
2086 case RTA_GATEWAY:
2087 case RTA_IFA:
2088 case RTA_SRC:
2089 return (in6_getaddr(s, &su->sin6, NULL, hpp));
2090 case RTA_NETMASK:
2091 syntax_error(
2092 gettext("route: -netmask not supported for IPv6: "
2093 "use <prefix>/<prefix-length> instead\n"));
2094 return (B_FALSE);
2095 default:
2096 quit(gettext("Internal Error"), EINVAL);
2097 /* NOTREACHED */
2099 case AF_INET:
2100 switch (which) {
2101 case RTA_DST:
2102 if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
2103 atype, rcip)) {
2104 return (B_FALSE);
2106 if (masklen != NO_PREFIX) {
2107 (void) memset(&rcip->ri_mask.sin.sin_addr, 0,
2108 sizeof (rcip->ri_mask.sin.sin_addr));
2109 if (!in_prefixlentomask(masklen, IP_ABITS,
2110 (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
2111 syntax_error(gettext(
2112 "route: bad prefix length: %d\n"),
2113 masklen);
2114 return (B_FALSE);
2116 rcip->ri_mask.sin.sin_family = rcip->ri_af;
2117 rcip->ri_addrs |= RTA_NETMASK;
2119 return (B_TRUE);
2120 case RTA_GATEWAY:
2121 case RTA_IFA:
2122 case RTA_NETMASK:
2123 case RTA_SRC:
2124 return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2125 rcip));
2126 default:
2127 quit(gettext("Internal Error"), EINVAL);
2128 /* NOTREACHED */
2130 default:
2131 quit(gettext("Internal Error"), EINVAL);
2132 /* NOTREACHED */
2134 return (B_TRUE);
2138 * Interpret an argument as an IPv4 network address of some kind,
2139 * returning B_TRUE on success or B_FALSE on failure.
2140 * This function will cause an exit() on failure if exit_on_failure is set.
2142 * Note that this tries host interpretation before network interpretation,
2143 * except when -net has been given and the destination address is being parsed.
2145 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2146 * pass out <n> in *plenp.
2147 * If <n> doesn't parse return BAD_ADDR as *plenp.
2148 * If no /<n> is present return NO_PREFIX as *plenp.
2150 static boolean_t
2151 in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
2152 struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
2154 struct hostent *hp;
2155 struct netent *np;
2156 in_addr_t val;
2157 char str[BUFSIZ];
2159 (void) strlcpy(str, s, sizeof (str));
2162 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2164 if (plenp != NULL) {
2165 char *cp;
2167 *plenp = in_getprefixlen(str, IP_ABITS);
2168 if (*plenp == BAD_ADDR)
2169 return (B_FALSE);
2170 cp = strchr(str, '/');
2171 if (cp != NULL)
2172 *cp = '\0';
2173 } else if (strchr(str, '/') != NULL) {
2174 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2175 return (B_FALSE);
2178 (void) memset(sin, 0, sizeof (*sin));
2179 sin->sin_family = AF_INET;
2182 * Handle 255.255.255.255 as a special case first.
2184 if (strcmp(str, "255.255.255.255") == 0) {
2185 sin->sin_addr.s_addr = INADDR_BROADCAST;
2186 return (B_TRUE);
2189 val = inet_addr(str);
2190 if (val != (in_addr_t)-1) {
2191 /* Numeric address */
2192 sin->sin_addr.s_addr = val;
2193 if (which == RTA_DST) {
2194 if (atype == ADDR_TYPE_NET ||
2195 (atype == ADDR_TYPE_ANY &&
2196 inet_lnaof(sin->sin_addr) == INADDR_ANY)) {
2197 /* This looks like a network address. */
2198 inet_makenetandmask(rcip, ntohl(val),
2199 sin);
2202 return (B_TRUE);
2204 /* Host or net name */
2205 if (which != RTA_DST || atype != ADDR_TYPE_NET) {
2206 /* A host name is allowed. */
2207 if ((hp = gethostbyname(str)) != NULL) {
2208 *hpp = hp;
2209 (void) memmove(&sin->sin_addr, hp->h_addr,
2210 hp->h_length);
2211 return (B_TRUE);
2214 if (atype != ADDR_TYPE_HOST) {
2215 /* A network name is allowed */
2216 if ((np = getnetbyname(str)) != NULL &&
2217 (val = np->n_net) != 0) {
2218 if (which == RTA_DST) {
2219 inet_makenetandmask(rcip, val, sin);
2221 return (B_TRUE);
2224 syntax_error(gettext("%s: bad value\n"), s);
2225 return (B_FALSE);
2229 * Interpret an argument as an IPv6 network address of some kind,
2230 * returning B_TRUE on success or B_FALSE on failure.
2231 * This function will cause an exit() on failure if exit_on_failure is set.
2233 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2234 * pass out <n> in *plenp.
2235 * If <n> doesn't parse return BAD_ADDR as *plenp.
2236 * If no /<n> is present return NO_PREFIX as *plenp.
2238 static boolean_t
2239 in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
2240 struct hostent **hpp)
2242 struct hostent *hp;
2243 char str[BUFSIZ];
2244 int error_num;
2246 (void) strlcpy(str, s, sizeof (str));
2249 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2251 if (plenp != NULL) {
2252 char *cp;
2254 *plenp = in_getprefixlen(str, IPV6_ABITS);
2255 if (*plenp == BAD_ADDR)
2256 return (B_FALSE);
2257 cp = strchr(str, '/');
2258 if (cp != NULL)
2259 *cp = '\0';
2260 } else if (strchr(str, '/') != NULL) {
2261 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2262 return (B_FALSE);
2265 (void) memset(sin6, 0, sizeof (struct sockaddr_in6));
2266 sin6->sin6_family = AF_INET6;
2268 hp = getipnodebyname(str, AF_INET6, 0, &error_num);
2269 if (hp != NULL) {
2270 *hpp = hp;
2271 (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
2272 return (B_TRUE);
2274 if (error_num == TRY_AGAIN) {
2276 * This isn't a problem if we aren't going to use the address
2277 * right away.
2279 if (!exit_on_error) {
2280 return (B_TRUE);
2282 syntax_error(gettext("route: %s: bad address (try "
2283 "again later)\n"), s);
2284 return (B_FALSE);
2286 syntax_error(gettext("route: %s: bad address\n"), s);
2287 return (B_FALSE);
2291 * Parse <addr>/<n> syntax and return the integer n.
2292 * If <addr> is missing or <n> is not a valid integer, this function calls
2293 * syntax_error() and returns BAD_ADDR.
2294 * if n is not between 0 and max_plen inclusive, this functions calls
2295 * syntax_error() and returns BAD_ADDR.
2296 * If /<n> is not present, this function returns NO_PREFIX.
2297 * The string addr is not modified.
2300 in_getprefixlen(char *addr, int max_plen)
2302 int prefixlen;
2303 char *str, *end;
2305 str = strchr(addr, '/');
2306 if (str == addr) {
2307 syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
2308 return (BAD_ADDR);
2310 if (str == NULL)
2311 return (NO_PREFIX);
2312 str++;
2314 errno = 0;
2315 prefixlen = strtoul(str, &end, 10);
2316 if (errno != 0 || str == end) {
2317 syntax_error(gettext("route: bad prefix length %s\n"), str);
2318 return (BAD_ADDR);
2320 if (prefixlen > max_plen) {
2321 syntax_error(gettext("route: prefix length %s out of range\n"),
2322 str);
2323 return (BAD_ADDR);
2325 return (prefixlen);
2329 * Convert a prefix length to a mask.
2330 * Returns B_TRUE if ok. B_FALSE otherwise.
2331 * Assumes the mask array is zeroed by the caller.
2333 boolean_t
2334 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
2336 if (prefixlen < 0 || prefixlen > maxlen)
2337 return (B_FALSE);
2339 while (prefixlen > 0) {
2340 if (prefixlen >= 8) {
2341 *mask++ = 0xFF;
2342 prefixlen -= 8;
2343 continue;
2345 *mask |= 1 << (8 - prefixlen);
2346 prefixlen--;
2348 return (B_TRUE);
2351 void
2352 rtmonitor(int argc, char *argv[])
2354 int n;
2355 intmax_t msg[2048 / sizeof (intmax_t)];
2357 if (tflag)
2358 exit(0);
2359 verbose = B_TRUE;
2360 if (argc > 1) {
2361 argv++;
2362 if (argc == 2 && **argv == '-') {
2363 switch (keyword(*argv + 1)) {
2364 case K_INET:
2365 af = AF_INET;
2366 break;
2367 case K_LINK:
2368 af = AF_LINK;
2369 break;
2370 case K_INET6:
2371 af = AF_INET6;
2372 break;
2373 default:
2374 usage(*argv);
2375 /* NOTREACHED */
2377 } else {
2378 usage(*argv);
2380 (void) close(s);
2381 s = socket(PF_ROUTE, SOCK_RAW, af);
2382 if (s < 0)
2383 quit("socket", errno);
2385 for (;;) {
2386 n = read(s, msg, sizeof (msg));
2387 if (n <= 0)
2388 quit("read", errno);
2389 (void) printf("got message of size %d\n", n);
2390 print_rtmsg((struct rt_msghdr *)msg, n);
2395 rtmsg(rtcmd_irep_t *newrt)
2397 static int seq;
2398 int rlen;
2399 char *cp = m_rtmsg.m_space;
2400 int l;
2402 errno = 0;
2403 (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
2405 if (newrt->ri_cmd == RTM_GET) {
2406 newrt->ri_ifp.sa.sa_family = AF_LINK;
2407 newrt->ri_addrs |= RTA_IFP;
2410 #define rtm m_rtmsg.m_rtm
2411 rtm.rtm_type = newrt->ri_cmd;
2412 rtm.rtm_flags = newrt->ri_flags;
2413 rtm.rtm_version = RTM_VERSION;
2414 rtm.rtm_seq = ++seq;
2415 rtm.rtm_addrs = newrt->ri_addrs;
2416 rtm.rtm_rmx = newrt->ri_metrics;
2417 rtm.rtm_inits = newrt->ri_inits;
2419 #define NEXTADDR(w, u) \
2420 if (newrt->ri_addrs & (w)) { \
2421 l = ROUNDUP_LONG(salen(&u.sa)); \
2422 (void) memmove(cp, &(u), l); \
2423 cp += l; \
2424 if (verbose) \
2425 sodump(&(u), #u); \
2427 NEXTADDR(RTA_DST, newrt->ri_dst);
2428 NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
2429 NEXTADDR(RTA_NETMASK, newrt->ri_mask);
2430 NEXTADDR(RTA_IFP, newrt->ri_ifp);
2431 NEXTADDR(RTA_IFA, newrt->ri_ifa);
2433 * RTA_SRC has overloaded meaning. It can represent the
2434 * src address of incoming or outgoing packets.
2436 NEXTADDR(RTA_SRC, newrt->ri_src);
2437 #undef NEXTADDR
2439 rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
2441 if (verbose)
2442 print_rtmsg(&rtm, l);
2443 if (debugonly)
2444 return (0);
2445 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
2446 switch (errno) {
2447 case ESRCH:
2448 case EBUSY:
2449 case ENOBUFS:
2450 case EEXIST:
2451 case ENETUNREACH:
2452 case EHOSTUNREACH:
2453 case EPERM:
2454 break;
2455 default:
2456 perror(gettext("writing to routing socket"));
2457 break;
2459 return (-1);
2460 } else if (rlen < (int)rtm.rtm_msglen) {
2461 (void) fprintf(stderr,
2462 gettext("route: write to routing socket got only %d for "
2463 "len\n"), rlen);
2464 return (-1);
2466 if (newrt->ri_cmd == RTM_GET) {
2467 do {
2468 l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
2469 } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
2470 if (l < 0) {
2471 (void) fprintf(stderr,
2472 gettext("route: read from routing socket: %s\n"),
2473 strerror(errno));
2474 } else {
2475 print_getmsg(newrt, &rtm, l);
2478 #undef rtm
2479 return (0);
2482 static char *msgtypes[] = {
2484 "RTM_ADD: Add Route",
2485 "RTM_DELETE: Delete Route",
2486 "RTM_CHANGE: Change Metrics or flags",
2487 "RTM_GET: Report Metrics",
2488 "RTM_LOSING: Kernel Suspects Partitioning",
2489 "RTM_REDIRECT: Told to use different route",
2490 "RTM_MISS: Lookup failed on this address",
2491 "RTM_LOCK: fix specified metrics",
2492 "RTM_OLDADD: caused by SIOCADDRT",
2493 "RTM_OLDDEL: caused by SIOCDELRT",
2494 "RTM_RESOLVE: Route created by cloning",
2495 "RTM_NEWADDR: address being brought up on iface",
2496 "RTM_DELADDR: address being brought down on iface",
2497 "RTM_IFINFO: iface status change",
2498 "RTM_CHGADDR: address being changed on iface",
2499 "RTM_FREEADDR: address being removed from iface",
2503 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2505 static char metricnames[] =
2506 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2507 "\1mtu";
2508 static char routeflags[] =
2509 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2510 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2511 "\016PRIVATE\017PROTO2\020PROTO1\022SETSRC\023INDIRECT"
2512 "\024KERNEL\025ZONE";
2513 static char ifnetflags[] =
2514 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2515 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2516 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2517 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2518 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2519 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2520 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2521 "\047DUPLICATE";
2522 static char addrnames[] =
2523 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2525 void
2526 print_rtmsg(struct rt_msghdr *rtm, int msglen)
2528 struct if_msghdr *ifm;
2529 struct ifa_msghdr *ifam;
2531 if (!verbose)
2532 return;
2533 if (rtm->rtm_version != RTM_VERSION) {
2534 (void) printf("routing message version %d not understood\n",
2535 rtm->rtm_version);
2536 return;
2538 if (rtm->rtm_msglen != msglen) {
2539 (void) printf("message length mismatch, in packet %d, "
2540 "returned %d\n",
2541 rtm->rtm_msglen, msglen);
2542 if (msglen > rtm->rtm_msglen)
2543 msglen = rtm->rtm_msglen;
2546 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2547 * and the upper-bound of (NMSGTYPES - 1).
2549 if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
2550 (void) printf("routing message type %d not understood\n",
2551 rtm->rtm_type);
2552 return;
2554 (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
2555 switch (rtm->rtm_type) {
2556 case RTM_IFINFO:
2557 ifm = (struct if_msghdr *)rtm;
2558 (void) printf("if# %d, flags:", ifm->ifm_index);
2559 bprintf(stdout, ifm->ifm_flags, ifnetflags);
2560 pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm),
2561 ifm->ifm_addrs);
2562 break;
2563 case RTM_NEWADDR:
2564 case RTM_DELADDR:
2565 case RTM_CHGADDR:
2566 case RTM_FREEADDR:
2567 ifam = (struct ifa_msghdr *)rtm;
2568 (void) printf("metric %d, flags:", ifam->ifam_metric);
2569 bprintf(stdout, ifam->ifam_flags, routeflags);
2570 pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam),
2571 ifam->ifam_addrs);
2572 break;
2573 default:
2574 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2575 rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
2576 bprintf(stdout, rtm->rtm_flags, routeflags);
2577 pmsg_common(rtm, msglen);
2578 break;
2582 void
2583 print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
2585 struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
2586 struct sockaddr_dl *ifp = NULL;
2587 struct sockaddr *sa;
2588 char *cp;
2589 int i;
2591 (void) printf(" route to: %s\n", routename(&req_rt->ri_dst.sa));
2592 if (rtm->rtm_version != RTM_VERSION) {
2593 (void) fprintf(stderr,
2594 gettext("routing message version %d not understood\n"),
2595 rtm->rtm_version);
2596 return;
2598 if (rtm->rtm_msglen > (ushort_t)msglen) {
2599 (void) fprintf(stderr,
2600 gettext("message length mismatch, in packet %d, "
2601 "returned %d\n"), rtm->rtm_msglen, msglen);
2603 if (rtm->rtm_errno) {
2604 (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
2605 strerror(rtm->rtm_errno), rtm->rtm_errno);
2606 return;
2608 cp = ((char *)(rtm + 1));
2609 if (rtm->rtm_addrs != 0) {
2610 for (i = 1; i != 0; i <<= 1) {
2611 if (i & rtm->rtm_addrs) {
2612 /* LINTED */
2613 sa = (struct sockaddr *)cp;
2614 switch (i) {
2615 case RTA_DST:
2616 dst = sa;
2617 break;
2618 case RTA_GATEWAY:
2619 gate = sa;
2620 break;
2621 case RTA_NETMASK:
2622 mask = sa;
2623 break;
2624 case RTA_IFP:
2625 if (sa->sa_family == AF_LINK &&
2626 ((struct sockaddr_dl *)sa)->
2627 sdl_nlen != 0)
2628 ifp = (struct sockaddr_dl *)sa;
2629 break;
2630 case RTA_SRC:
2631 src = sa;
2632 break;
2634 ADVANCE(cp, sa);
2638 if (dst != NULL && mask != NULL)
2639 mask->sa_family = dst->sa_family; /* XXX */
2640 if (dst != NULL)
2641 (void) printf("destination: %s\n", routename(dst));
2642 if (mask != NULL) {
2643 boolean_t savenflag = nflag;
2645 nflag = B_TRUE;
2646 (void) printf(" mask: %s\n", routename(mask));
2647 nflag = savenflag;
2649 if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
2650 (void) printf(" gateway: %s\n", routename(gate));
2651 if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
2652 (void) printf(" setsrc: %s\n", routename(src));
2653 if (ifp != NULL) {
2654 if (verbose) {
2655 int i;
2657 (void) printf(" interface: %.*s index %d address ",
2658 ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
2659 for (i = ifp->sdl_nlen;
2660 i < ifp->sdl_nlen + ifp->sdl_alen;
2661 i++) {
2662 (void) printf("%02x ",
2663 ifp->sdl_data[i] & 0xFF);
2665 (void) printf("\n");
2666 } else {
2667 (void) printf(" interface: %.*s\n",
2668 ifp->sdl_nlen, ifp->sdl_data);
2671 (void) printf(" flags: ");
2672 bprintf(stdout, rtm->rtm_flags, routeflags);
2674 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2675 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2677 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2678 "rttvar,ms hopcount mtu expire");
2679 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
2680 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
2681 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
2682 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
2683 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
2684 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
2685 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
2686 if (rtm->rtm_rmx.rmx_expire)
2687 rtm->rtm_rmx.rmx_expire -= time(0);
2688 (void) printf("%8d%c", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
2689 #undef lock
2690 #undef msec
2691 #define RTA_IGN \
2692 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2693 if (verbose) {
2694 pmsg_common(rtm, msglen);
2695 } else {
2696 const char *sptr, *endptr;
2697 const struct sockaddr *sa;
2698 uint_t addrs;
2700 /* Not verbose; just print out the exceptional cases */
2701 if (rtm->rtm_addrs &~ RTA_IGN) {
2702 (void) printf("\nsockaddrs: ");
2703 bprintf(stdout, rtm->rtm_addrs, addrnames);
2705 sptr = (const char *)(rtm + 1);
2706 endptr = (const char *)rtm + msglen;
2707 addrs = rtm->rtm_addrs;
2708 while (addrs != 0 && sptr + sizeof (*sa) <= endptr) {
2709 addrs &= addrs - 1;
2710 /* LINTED */
2711 sa = (const struct sockaddr *)sptr;
2712 ADVANCE(sptr, sa);
2714 (void) putchar('\n');
2716 #undef RTA_IGN
2719 static void
2720 pmsg_common(const struct rt_msghdr *rtm, size_t msglen)
2722 (void) printf("\nlocks: ");
2723 bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
2724 (void) printf(" inits: ");
2725 bprintf(stdout, (int)rtm->rtm_inits, metricnames);
2726 pmsg_addrs((const char *)(rtm + 1), msglen - sizeof (*rtm),
2727 rtm->rtm_addrs);
2730 static void
2731 pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
2733 const struct sockaddr *sa;
2734 const char *maxptr;
2735 int i;
2737 if (addrs != 0) {
2738 (void) printf("\nsockaddrs: ");
2739 bprintf(stdout, addrs, addrnames);
2740 (void) putchar('\n');
2741 maxptr = cp + msglen;
2742 for (i = 1; i != 0 && cp + sizeof (*sa) <= maxptr; i <<= 1) {
2743 if (i & addrs) {
2744 /* LINTED */
2745 sa = (const struct sockaddr *)cp;
2746 (void) printf(" %s", routename(sa));
2747 ADVANCE(cp, sa);
2750 if (i != 0)
2751 msglen = 0;
2752 else
2753 msglen = maxptr - cp;
2755 (void) putchar('\n');
2756 (void) fflush(stdout);
2759 void
2760 bprintf(FILE *fp, int b, char *s)
2762 int i;
2763 boolean_t gotsome = B_FALSE;
2765 if (b == 0)
2766 return;
2767 while ((i = *s++) != 0) {
2768 if (b & (1 << (i - 1))) {
2769 if (!gotsome)
2770 i = '<';
2771 else
2772 i = ',';
2773 (void) putc(i, fp);
2774 gotsome = B_TRUE;
2775 for (; (i = *s) > ' '; s++)
2776 (void) putc(i, fp);
2777 } else {
2778 while (*s > ' ')
2779 s++;
2782 if (gotsome)
2783 (void) putc('>', fp);
2787 keyword(const char *cp)
2789 struct keytab *kt = keywords;
2791 while (kt->kt_cp && strcmp(kt->kt_cp, cp))
2792 kt++;
2793 return (kt->kt_i);
2796 void
2797 sodump(su_t *su, char *which)
2799 static char obuf[INET6_ADDRSTRLEN];
2801 switch (su->sa.sa_family) {
2802 case AF_LINK:
2803 (void) printf("%s: link %s; ",
2804 which, link_ntoa(&su->sdl));
2805 break;
2806 case AF_INET:
2807 (void) printf("%s: inet %s; ",
2808 which, inet_ntoa(su->sin.sin_addr));
2809 break;
2810 case AF_INET6:
2811 if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
2812 INET6_ADDRSTRLEN) != NULL) {
2813 (void) printf("%s: inet6 %s; ", which, obuf);
2814 break;
2816 /* FALLTHROUGH */
2817 default:
2818 quit(gettext("Internal Error"), EINVAL);
2819 /* NOTREACHED */
2821 (void) fflush(stdout);
2824 /* States */
2825 #define VIRGIN 0
2826 #define GOTONE 1
2827 #define GOTTWO 2
2828 #define RESET 3
2829 /* Inputs */
2830 #define DIGIT (4*0)
2831 #define END (4*1)
2832 #define DELIM (4*2)
2833 #define LETTER (4*3)
2835 void
2836 sockaddr(char *addr, struct sockaddr *sa)
2838 char *cp = (char *)sa;
2839 int size = salen(sa);
2840 char *cplim = cp + size;
2841 int byte = 0, state = VIRGIN, new;
2843 (void) memset(cp, 0, size);
2844 cp++;
2845 do {
2846 if ((*addr >= '0') && (*addr <= '9')) {
2847 new = *addr - '0';
2848 } else if ((*addr >= 'a') && (*addr <= 'f')) {
2849 new = *addr - 'a' + 10;
2850 } else if ((*addr >= 'A') && (*addr <= 'F')) {
2851 new = *addr - 'A' + 10;
2852 } else if (*addr == 0) {
2853 state |= END;
2854 } else {
2855 state |= DELIM;
2857 addr++;
2858 switch (state /* | INPUT */) {
2859 case GOTTWO | DIGIT:
2860 *cp++ = byte;
2861 /* FALLTHROUGH */
2862 case VIRGIN | DIGIT:
2863 state = GOTONE; byte = new; continue;
2864 case GOTONE | DIGIT:
2865 state = GOTTWO; byte = new + (byte << 4); continue;
2866 default: /* | DELIM */
2867 state = VIRGIN; *cp++ = byte; byte = 0; continue;
2868 case GOTONE | END:
2869 case GOTTWO | END:
2870 *cp++ = byte;
2871 /* FALLTHROUGH */
2872 case VIRGIN | END:
2873 break;
2875 break;
2876 } while (cp < cplim);
2880 salen(const struct sockaddr *sa)
2882 switch (sa->sa_family) {
2883 case AF_INET:
2884 return (sizeof (struct sockaddr_in));
2885 case AF_LINK:
2886 return (sizeof (struct sockaddr_dl));
2887 case AF_INET6:
2888 return (sizeof (struct sockaddr_in6));
2889 default:
2890 return (sizeof (struct sockaddr));
2894 void
2895 link_addr(const char *addr, struct sockaddr_dl *sdl)
2897 char *cp = sdl->sdl_data;
2898 char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
2899 int byte = 0, state = VIRGIN, new;
2901 (void) memset(sdl, 0, sizeof (struct sockaddr_dl));
2902 sdl->sdl_family = AF_LINK;
2903 do {
2904 state &= ~LETTER;
2905 if ((*addr >= '0') && (*addr <= '9')) {
2906 new = *addr - '0';
2907 } else if ((*addr >= 'a') && (*addr <= 'f')) {
2908 new = *addr - 'a' + 10;
2909 } else if ((*addr >= 'A') && (*addr <= 'F')) {
2910 new = *addr - 'A' + 10;
2911 } else if (*addr == 0) {
2912 state |= END;
2913 } else if (state == VIRGIN &&
2914 (((*addr >= 'A') && (*addr <= 'Z')) ||
2915 ((*addr >= 'a') && (*addr <= 'z')))) {
2916 state |= LETTER;
2917 } else {
2918 state |= DELIM;
2920 addr++;
2921 switch (state /* | INPUT */) {
2922 case VIRGIN | DIGIT:
2923 case VIRGIN | LETTER:
2924 *cp++ = addr[-1];
2925 continue;
2926 case VIRGIN | DELIM:
2927 state = RESET;
2928 sdl->sdl_nlen = cp - sdl->sdl_data;
2929 continue;
2930 case GOTTWO | DIGIT:
2931 *cp++ = byte;
2932 /* FALLTHROUGH */
2933 case RESET | DIGIT:
2934 state = GOTONE;
2935 byte = new;
2936 continue;
2937 case GOTONE | DIGIT:
2938 state = GOTTWO;
2939 byte = new + (byte << 4);
2940 continue;
2941 default: /* | DELIM */
2942 state = RESET;
2943 *cp++ = byte;
2944 byte = 0;
2945 continue;
2946 case GOTONE | END:
2947 case GOTTWO | END:
2948 *cp++ = byte;
2949 /* FALLTHROUGH */
2950 case RESET | END:
2951 break;
2953 break;
2954 } while (cp < cplim);
2955 sdl->sdl_alen = cp - LLADDR(sdl);
2958 static char hexlist[] = "0123456789abcdef";
2960 char *
2961 link_ntoa(const struct sockaddr_dl *sdl)
2963 static char obuf[64];
2964 char *out = obuf;
2965 int i;
2966 uchar_t *in = (uchar_t *)LLADDR(sdl);
2967 uchar_t *inlim = in + sdl->sdl_alen;
2968 boolean_t firsttime = B_TRUE;
2970 if (sdl->sdl_nlen) {
2971 (void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
2972 out += sdl->sdl_nlen;
2973 if (sdl->sdl_alen)
2974 *out++ = ':';
2976 while (in < inlim) {
2977 if (firsttime)
2978 firsttime = B_FALSE;
2979 else
2980 *out++ = '.';
2981 i = *in++;
2982 if (i > 0xf) {
2983 out[1] = hexlist[i & 0xf];
2984 i >>= 4;
2985 out[0] = hexlist[i];
2986 out += 2;
2987 } else {
2988 *out++ = hexlist[i];
2991 *out = 0;
2992 return (obuf);
2995 static mib_item_t *
2996 mibget(int sd)
2998 intmax_t buf[512 / sizeof (intmax_t)];
2999 int flags;
3000 int i, j, getcode;
3001 struct strbuf ctlbuf, databuf;
3002 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
3003 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
3004 struct T_error_ack *tea = (struct T_error_ack *)buf;
3005 struct opthdr *req;
3006 mib_item_t *first_item = NULL;
3007 mib_item_t *last_item = NULL;
3008 mib_item_t *temp;
3010 tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
3011 tor->OPT_offset = sizeof (struct T_optmgmt_req);
3012 tor->OPT_length = sizeof (struct opthdr);
3013 tor->MGMT_flags = T_CURRENT;
3014 req = (struct opthdr *)&tor[1];
3015 req->level = MIB2_IP; /* any MIB2_xxx value ok here */
3016 req->name = 0;
3017 req->len = 0;
3019 ctlbuf.buf = (char *)buf;
3020 ctlbuf.len = tor->OPT_length + tor->OPT_offset;
3021 flags = 0;
3022 if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
3023 perror("mibget: putmsg (ctl)");
3024 return (NULL);
3027 * each reply consists of a ctl part for one fixed structure
3028 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3029 * containing an opthdr structure. level/name identify the entry,
3030 * len is the size of the data part of the message.
3032 req = (struct opthdr *)&toa[1];
3033 ctlbuf.maxlen = sizeof (buf);
3034 for (j = 1; ; j++) {
3035 flags = 0;
3036 getcode = getmsg(sd, &ctlbuf, NULL, &flags);
3037 if (getcode < 0) {
3038 perror("mibget: getmsg (ctl)");
3039 if (verbose) {
3040 (void) fprintf(stderr,
3041 "# level name len\n");
3042 i = 0;
3043 for (last_item = first_item; last_item != NULL;
3044 last_item = last_item->next_item) {
3045 (void) printf("%d %4ld %5ld %ld\n",
3046 ++i, last_item->group,
3047 last_item->mib_id,
3048 last_item->length);
3051 break;
3053 if (getcode == 0 &&
3054 ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
3055 toa->PRIM_type == T_OPTMGMT_ACK &&
3056 toa->MGMT_flags == T_SUCCESS &&
3057 req->len == 0) {
3058 if (verbose) {
3059 (void) printf("mibget getmsg() %d returned EOD "
3060 "(level %lu, name %lu)\n", j, req->level,
3061 req->name);
3063 return (first_item); /* this is EOD msg */
3066 if (ctlbuf.len >= sizeof (struct T_error_ack) &&
3067 tea->PRIM_type == T_ERROR_ACK) {
3068 (void) fprintf(stderr, gettext("mibget %d gives "
3069 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3070 "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
3071 errno = (tea->TLI_error == TSYSERR) ?
3072 tea->UNIX_error : EPROTO;
3073 break;
3076 if (getcode != MOREDATA ||
3077 ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
3078 toa->PRIM_type != T_OPTMGMT_ACK ||
3079 toa->MGMT_flags != T_SUCCESS) {
3080 (void) printf("mibget getmsg(ctl) %d returned %d, "
3081 "ctlbuf.len = %d, PRIM_type = %ld\n",
3082 j, getcode, ctlbuf.len, toa->PRIM_type);
3083 if (toa->PRIM_type == T_OPTMGMT_ACK) {
3084 (void) printf("T_OPTMGMT_ACK: "
3085 "MGMT_flags = 0x%lx, req->len = %ld\n",
3086 toa->MGMT_flags, req->len);
3088 errno = ENOMSG;
3089 break;
3092 temp = malloc(sizeof (mib_item_t));
3093 if (temp == NULL) {
3094 perror("mibget: malloc");
3095 break;
3097 if (last_item != NULL)
3098 last_item->next_item = temp;
3099 else
3100 first_item = temp;
3101 last_item = temp;
3102 last_item->next_item = NULL;
3103 last_item->group = req->level;
3104 last_item->mib_id = req->name;
3105 last_item->length = req->len;
3106 last_item->valp = malloc(req->len);
3107 if (verbose) {
3108 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3109 "length = %ld\n",
3110 j, last_item->group, last_item->mib_id,
3111 last_item->length);
3114 databuf.maxlen = last_item->length;
3115 databuf.buf = (char *)last_item->valp;
3116 databuf.len = 0;
3117 flags = 0;
3118 getcode = getmsg(sd, NULL, &databuf, &flags);
3119 if (getcode < 0) {
3120 perror("mibget: getmsg (data)");
3121 break;
3122 } else if (getcode != 0) {
3123 (void) printf("mibget getmsg(data) returned %d, "
3124 "databuf.maxlen = %d, databuf.len = %d\n",
3125 getcode, databuf.maxlen, databuf.len);
3126 break;
3131 * On error, free all the allocated mib_item_t objects.
3133 while (first_item != NULL) {
3134 last_item = first_item;
3135 first_item = first_item->next_item;
3136 free(last_item);
3138 return (NULL);