1 /*
2 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4
5 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
6 /* All Rights Reserved */
7
8 /* Copyright (c) 1990 Mentat Inc. */
9
10 /*
11 *
12 * Copyright (c) 1983, 1989, 1991, 1993
13 * The Regents of the University of California. All rights reserved.
14 *
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.
30 *
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.
42 *
43 * @(#)route.c 8.6 (Berkeley) 4/28/95
44 * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93
45 */
46
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>
57
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>
66
67 #include <limits.h>
68 #include <locale.h>
69
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>
81
82 #include <libtsnet.h>
83 #include <tsol/label.h>
84
85 static struct keytab {
86 char *kt_cp;
87 int kt_i;
88 } keywords[] = {
89 #define K_ADD 1
90 {"add", K_ADD},
91 #define K_BLACKHOLE 2
92 {"blackhole", K_BLACKHOLE},
93 #define K_CHANGE 3
94 {"change", K_CHANGE},
95 #define K_CLONING 4
96 {"cloning", K_CLONING},
97 #define K_DELETE 5
98 {"delete", K_DELETE},
99 #define K_DST 6
100 {"dst", K_DST},
101 #define K_EXPIRE 7
102 {"expire", K_EXPIRE},
103 #define K_FLUSH 8
104 {"flush", K_FLUSH},
105 #define K_GATEWAY 9
106 {"gateway", K_GATEWAY},
107 #define K_GET 11
108 {"get", K_GET},
109 #define K_HOPCOUNT 12
110 {"hopcount", K_HOPCOUNT},
111 #define K_HOST 13
112 {"host", K_HOST},
113 #define K_IFA 14
114 {"ifa", K_IFA},
115 #define K_IFACE 15
116 {"iface", K_IFACE},
117 #define K_IFP 16
118 {"ifp", K_IFP},
119 #define K_INET 17
120 {"inet", K_INET},
121 #define K_INET6 18
122 {"inet6", K_INET6},
123 #define K_INTERFACE 19
124 {"interface", K_INTERFACE},
125 #define K_LINK 20
126 {"link", K_LINK},
127 #define K_LOCK 21
128 {"lock", K_LOCK},
129 #define K_LOCKREST 22
130 {"lockrest", K_LOCKREST},
131 #define K_MASK 23
132 {"mask", K_MASK},
133 #define K_MONITOR 24
134 {"monitor", K_MONITOR},
135 #define K_MTU 25
136 {"mtu", K_MTU},
137 #define K_NET 26
138 {"net", K_NET},
139 #define K_NETMASK 27
140 {"netmask", K_NETMASK},
141 #define K_NOSTATIC 28
142 {"nostatic", K_NOSTATIC},
143 #define K_PRIVATE 29
144 {"private", K_PRIVATE},
145 #define K_PROTO1 30
146 {"proto1", K_PROTO1},
147 #define K_PROTO2 31
148 {"proto2", K_PROTO2},
149 #define K_RECVPIPE 32
150 {"recvpipe", K_RECVPIPE},
151 #define K_REJECT 33
152 {"reject", K_REJECT},
153 #define K_RTT 34
154 {"rtt", K_RTT},
155 #define K_RTTVAR 35
156 {"rttvar", K_RTTVAR},
157 #define K_SA 36
158 {"sa", K_SA},
159 #define K_SENDPIPE 37
160 {"sendpipe", K_SENDPIPE},
161 #define K_SSTHRESH 38
162 {"ssthresh", K_SSTHRESH},
163 #define K_STATIC 39
164 {"static", K_STATIC},
165 #define K_XRESOLVE 40
166 {"xresolve", K_XRESOLVE},
167 #define K_MULTIRT 41
168 {"multirt", K_MULTIRT},
169 #define K_SETSRC 42
170 {"setsrc", K_SETSRC},
171 #define K_SHOW 43
172 {"show", K_SHOW},
173 #define K_SECATTR 43
174 {"secattr", K_SECATTR},
175 #define K_INDIRECT 44
176 {"indirect", K_INDIRECT},
177 {0, 0}
178 };
179
180 /*
181 * Size of buffers used to hold command lines from the saved route file as
182 * well as error strings.
183 */
184 #define BUF_SIZE 2048
185
186 typedef union sockunion {
187 struct sockaddr sa;
188 struct sockaddr_in sin;
189 struct sockaddr_dl sdl;
190 struct sockaddr_in6 sin6;
191 } su_t;
192
193 /*
194 * This structure represents the digested information from parsing arguments
195 * to route add, change, delete, and get.
196 *
197 */
198 typedef struct rtcmd_irep {
199 int ri_cmd;
200 int ri_flags;
201 int ri_af;
202 ulong_t ri_inits;
203 struct rt_metrics ri_metrics;
204 int ri_addrs;
205 su_t ri_dst;
206 char *ri_dest_str;
207 su_t ri_src;
208 su_t ri_gate;
209 struct hostent *ri_gate_hp;
210 char *ri_gate_str;
211 su_t ri_mask;
212 su_t ri_ifa;
213 su_t ri_ifp;
214 char *ri_ifp_str;
215 int ri_rtsa_cnt; /* number of gateway security attributes */
216 struct rtsa_s ri_rtsa; /* enough space for one attribute */
217 } rtcmd_irep_t;
218
219 typedef struct mib_item_s {
220 struct mib_item_s *next_item;
221 long group;
222 long mib_id;
223 long length;
224 intmax_t *valp;
225 } mib_item_t;
226
227 typedef enum {
228 ADDR_TYPE_ANY,
229 ADDR_TYPE_HOST,
230 ADDR_TYPE_NET
231 } addr_type_t;
232
233 typedef enum {
234 SEARCH_MODE_NULL,
235 SEARCH_MODE_PRINT,
236 SEARCH_MODE_DEL
237 } search_mode_t;
238
239 static boolean_t args_to_rtcmd(rtcmd_irep_t *rcip, char **argv,
240 char *cmd_string);
241 static void bprintf(FILE *fp, int b, char *s);
242 static boolean_t compare_rtcmd(rtcmd_irep_t *srch_rt,
243 rtcmd_irep_t *file_rt);
244 static void delRouteEntry(mib2_ipRouteEntry_t *rp,
245 mib2_ipv6RouteEntry_t *rp6, int seqno);
246 static void del_rtcmd_irep(rtcmd_irep_t *rcip);
247 static void flushroutes(int argc, char *argv[]);
248 static boolean_t getaddr(rtcmd_irep_t *rcip, int which, char *s,
249 addr_type_t atype);
250 static boolean_t in6_getaddr(char *s, struct sockaddr_in6 *sin6,
251 int *plenp, struct hostent **hpp);
252 static boolean_t in_getaddr(char *s, struct sockaddr_in *sin,
253 int *plenp, int which, struct hostent **hpp, addr_type_t atype,
254 rtcmd_irep_t *rcip);
255 static int in_getprefixlen(char *addr, int max_plen);
256 static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
257 uchar_t *mask);
258 static void inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net,
259 struct sockaddr_in *sin);
260 static in_addr_t inet_makesubnetmask(in_addr_t addr, in_addr_t mask);
261 static int keyword(const char *cp);
262 static void link_addr(const char *addr, struct sockaddr_dl *sdl);
263 static char *link_ntoa(const struct sockaddr_dl *sdl);
264 static mib_item_t *mibget(int sd);
265 static char *netname(struct sockaddr *sa);
266 static int newroute(char **argv);
267 static rtcmd_irep_t *new_rtcmd_irep(void);
268 static void pmsg_addrs(const char *cp, size_t len, uint_t addrs);
269 static void pmsg_common(const struct rt_msghdr *rtm, size_t len);
270 static void print_getmsg(rtcmd_irep_t *req_rt,
271 struct rt_msghdr *rtm, int msglen);
272 static void print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip,
273 boolean_t gw_good, boolean_t to_saved);
274 static void print_rtmsg(struct rt_msghdr *rtm, int msglen);
275 static void quit(char *s, int err) __NORETURN;
276 static char *routename(const struct sockaddr *sa);
277 static void rtmonitor(int argc, char *argv[]);
278 static int rtmsg(rtcmd_irep_t *rcip);
279 static int salen(const struct sockaddr *sa);
280 static void save_route(int argc, char **argv, int do_flush);
281 static void save_string(char **dst, char *src);
282 static int search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt,
283 search_mode_t mode);
284 static void set_metric(rtcmd_irep_t *rcip, char *value, int key,
285 boolean_t lock);
286 static int show_saved_routes(int argc);
287 static void sockaddr(char *addr, struct sockaddr *sa);
288 static void sodump(su_t *su, char *which);
289 static void syntax_arg_missing(char *keyword);
290 static void syntax_bad_keyword(char *keyword);
291 static void syntax_error(char *err, ...);
292 static void usage(char *cp);
293 static void write_to_rtfile(FILE *fp, int argc, char **argv);
294 static void pmsg_secattr(const char *, size_t, const char *);
295
296 static pid_t pid;
297 static int s;
298 static boolean_t nflag;
299 static int af = AF_INET;
300 static boolean_t qflag, tflag;
301 static boolean_t verbose;
302 static boolean_t debugonly;
303 static boolean_t fflag;
304 static boolean_t update_table;
305 static boolean_t perm_flag;
306 static boolean_t early_v6_keyword;
307 static char perm_file_sfx[] = "/etc/inet/static_routes";
308 static char *perm_file;
309 static char temp_file_sfx[] = "/etc/inet/static_routes.tmp";
310 static char *temp_file;
311 static struct in6_addr in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
312 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
313 /*
314 * WARNING:
315 * This next variable indicates whether certain functions exit when an error
316 * is detected in the user input. Currently, exit_on_error is only set false
317 * in search_rtfile(), when argument are being parsed. Only those functions
318 * used by search_rtfile() to parse its arguments are designed to work in
319 * both modes. Take particular care in setting this false to ensure that any
320 * functions you call that might act on this flag properly return errors when
321 * exit_on_error is false.
322 */
323 static int exit_on_error = B_TRUE;
324
325 static struct {
326 struct rt_msghdr m_rtm;
327 char m_space[BUF_SIZE];
328 } m_rtmsg;
329
330 /*
331 * Sizes of data structures extracted from the base mib.
332 * This allows the size of the tables entries to grow while preserving
333 * binary compatibility.
334 */
335 static int ipRouteEntrySize;
336 static int ipv6RouteEntrySize;
337
338 #define ROUNDUP_LONG(a) \
339 ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long))
340 #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n)))
341 #define C(x) ((x) & 0xff)
342
343 /*
344 * return values from in_getprefixlen()
345 */
346 #define BAD_ADDR -1 /* prefix is invalid */
347 #define NO_PREFIX -2 /* no prefix was found */
348
349 void
usage(char * cp)350 usage(char *cp)
351 {
352 if (cp != NULL) {
353 (void) fprintf(stderr, gettext("route: botched keyword: %s\n"),
354 cp);
355 }
356 (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
357 "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
358 exit(1);
359 /* NOTREACHED */
360 }
361
362 /*PRINTFLIKE1*/
363 void
syntax_error(char * err,...)364 syntax_error(char *err, ...)
365 {
366 va_list args;
367
368 if (exit_on_error) {
369 va_start(args, err);
370 (void) vfprintf(stderr, err, args);
371 va_end(args);
372 exit(1);
373 }
374 /* NOTREACHED */
375 }
376
377 void
syntax_bad_keyword(char * keyword)378 syntax_bad_keyword(char *keyword)
379 {
380 syntax_error(gettext("route: botched keyword: %s\n"), keyword);
381 }
382
383 void
syntax_arg_missing(char * keyword)384 syntax_arg_missing(char *keyword)
385 {
386 syntax_error(gettext("route: argument required following keyword %s\n"),
387 keyword);
388 }
389
390 void
quit(char * s,int sverrno)391 quit(char *s, int sverrno)
392 {
393 (void) fprintf(stderr, "route: ");
394 if (s != NULL)
395 (void) fprintf(stderr, "%s: ", s);
396 (void) fprintf(stderr, "%s\n", strerror(sverrno));
397 exit(sverrno);
398 /* NOTREACHED */
399 }
400
401 int
main(int argc,char ** argv)402 main(int argc, char **argv)
403 {
404 extern int optind;
405 extern char *optarg;
406 int ch;
407 int rval;
408 size_t size;
409 const char *root_dir = NULL;
410
411 (void) setlocale(LC_ALL, "");
412
413 #if !defined(TEXT_DOMAIN)
414 #define TEXT_DOMAIN "SYS_TEST"
415 #endif
416 (void) textdomain(TEXT_DOMAIN);
417
418 if (argc < 2)
419 usage(NULL);
420
421 while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
422 switch (ch) {
423 case 'n':
424 nflag = B_TRUE;
425 break;
426 case 'q':
427 qflag = B_TRUE;
428 break;
429 case 'v':
430 verbose = B_TRUE;
431 break;
432 case 't':
433 tflag = B_TRUE;
434 break;
435 case 'd':
436 debugonly = B_TRUE;
437 break;
438 case 'f':
439 fflag = B_TRUE;
440 break;
441 case 'p':
442 perm_flag = B_TRUE;
443 break;
444 case 'R':
445 root_dir = optarg;
446 break;
447 case '?':
448 default:
449 usage(NULL);
450 /* NOTREACHED */
451 }
452 }
453 argc -= optind;
454 argv += optind;
455
456 pid = getpid();
457 if (tflag)
458 s = open("/dev/null", O_WRONLY);
459 else
460 s = socket(PF_ROUTE, SOCK_RAW, 0);
461 if (s < 0)
462 quit("socket", errno);
463
464 /*
465 * Handle the -p and -R flags. The -R flag only applies
466 * when the -p flag is set.
467 */
468 if (root_dir == NULL) {
469 perm_file = perm_file_sfx;
470 temp_file = temp_file_sfx;
471 } else {
472 size = strlen(root_dir) + sizeof (perm_file_sfx);
473 perm_file = malloc(size);
474 if (perm_file == NULL)
475 quit("malloc", errno);
476 (void) snprintf(perm_file, size, "%s%s", root_dir,
477 perm_file_sfx);
478 size = strlen(root_dir) + sizeof (temp_file_sfx);
479 temp_file = malloc(size);
480 if (temp_file == NULL)
481 quit("malloc", errno);
482 (void) snprintf(temp_file, size, "%s%s", root_dir,
483 temp_file_sfx);
484 }
485 /*
486 * Whether or not to act on the routing table. The only time the
487 * routing table is not modified is when both -p and -R are present.
488 */
489 update_table = (!perm_flag || root_dir == NULL);
490 if (tflag)
491 perm_flag = 0;
492
493 if (fflag) {
494 /*
495 * Accept an address family keyword after the -f. Since the
496 * default address family is AF_INET, reassign af only for the
497 * other valid address families.
498 */
499 if (*argv != NULL) {
500 switch (keyword(*argv)) {
501 case K_INET6:
502 af = AF_INET6;
503 early_v6_keyword = B_TRUE;
504 /* fallthrough */
505 case K_INET:
506 /* Skip over the address family parameter. */
507 argc--;
508 argv++;
509 break;
510 }
511 }
512 flushroutes(0, NULL);
513 }
514
515 if (*argv != NULL) {
516 switch (keyword(*argv)) {
517 case K_GET:
518 case K_CHANGE:
519 case K_ADD:
520 case K_DELETE:
521 rval = 0;
522 if (update_table) {
523 rval = newroute(argv);
524 }
525 if (perm_flag && (rval == 0 || rval == EEXIST ||
526 rval == ESRCH)) {
527 save_route(argc, argv, B_FALSE);
528 return (0);
529 }
530 return (rval);
531 case K_SHOW:
532 if (perm_flag) {
533 return (show_saved_routes(argc));
534 } else {
535 syntax_error(gettext(
536 "route: show command requires -p\n"));
537 }
538 /* NOTREACHED */
539 case K_MONITOR:
540 rtmonitor(argc, argv);
541 /* NOTREACHED */
542
543 case K_FLUSH:
544 flushroutes(argc, argv);
545 return (0);
546 }
547 }
548 if (!fflag)
549 usage(*argv);
550 return (0);
551 }
552
553 /*
554 * Purge all entries in the routing tables not
555 * associated with network interfaces.
556 */
557 void
flushroutes(int argc,char * argv[])558 flushroutes(int argc, char *argv[])
559 {
560 int seqno;
561 int sd; /* mib stream */
562 mib_item_t *item;
563 mib2_ipRouteEntry_t *rp;
564 mib2_ipv6RouteEntry_t *rp6;
565 int oerrno;
566 int off = 0;
567 int on = 1;
568
569 if (argc > 1) {
570 argv++;
571 if (argc == 2 && **argv == '-') {
572 /*
573 * The address family (preceded by a dash) may be used
574 * to flush the routes of that particular family.
575 */
576 switch (keyword(*argv + 1)) {
577 case K_INET:
578 af = AF_INET;
579 break;
580 case K_LINK:
581 af = AF_LINK;
582 break;
583 case K_INET6:
584 af = AF_INET6;
585 break;
586 default:
587 usage(*argv);
588 /* NOTREACHED */
589 }
590 } else {
591 usage(*argv);
592 }
593 }
594 if (perm_flag) {
595 /* This flushes the persistent route file */
596 save_route(0, NULL, B_TRUE);
597 }
598 if (!update_table) {
599 return;
600 }
601
602 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off,
603 sizeof (off)) < 0)
604 quit("setsockopt", errno);
605
606 sd = open("/dev/ip", O_RDWR);
607 oerrno = errno;
608 if (sd < 0) {
609 switch (errno) {
610 case EACCES:
611 (void) fprintf(stderr,
612 gettext("route: flush: insufficient privileges\n"));
613 exit(oerrno);
614 /* NOTREACHED */
615 default:
616 quit(gettext("can't open mib stream"), oerrno);
617 /* NOTREACHED */
618 }
619 }
620 if ((item = mibget(sd)) == NULL)
621 quit("mibget", errno);
622 if (verbose) {
623 (void) printf("Examining routing table from "
624 "T_SVR4_OPTMGMT_REQ\n");
625 }
626 seqno = 0; /* ??? */
627 switch (af) {
628 case AF_INET:
629 /* Extract ipRouteEntrySize */
630 for (; item != NULL; item = item->next_item) {
631 if (item->mib_id != 0)
632 continue;
633 if (item->group == MIB2_IP) {
634 ipRouteEntrySize =
635 ((mib2_ip_t *)item->valp)->ipRouteEntrySize;
636 assert(IS_P2ALIGNED(ipRouteEntrySize,
637 sizeof (mib2_ipRouteEntry_t *)));
638 break;
639 }
640 }
641 if (ipRouteEntrySize == 0) {
642 (void) fprintf(stderr,
643 gettext("ipRouteEntrySize can't be determined.\n"));
644 exit(1);
645 }
646 for (; item != NULL; item = item->next_item) {
647 /*
648 * skip all the other trash that comes up the mib stream
649 */
650 if (item->group != MIB2_IP ||
651 item->mib_id != MIB2_IP_ROUTE)
652 continue;
653 for (rp = (mib2_ipRouteEntry_t *)item->valp;
654 (char *)rp < (char *)item->valp + item->length;
655 /* LINTED */
656 rp = (mib2_ipRouteEntry_t *)
657 ((char *)rp + ipRouteEntrySize)) {
658 delRouteEntry(rp, NULL, seqno);
659 seqno++;
660 }
661 break;
662 }
663 break;
664 case AF_INET6:
665 /* Extract ipv6RouteEntrySize */
666 for (; item != NULL; item = item->next_item) {
667 if (item->mib_id != 0)
668 continue;
669 if (item->group == MIB2_IP6) {
670 ipv6RouteEntrySize =
671 ((mib2_ipv6IfStatsEntry_t *)item->valp)->
672 ipv6RouteEntrySize;
673 assert(IS_P2ALIGNED(ipv6RouteEntrySize,
674 sizeof (mib2_ipv6RouteEntry_t *)));
675 break;
676 }
677 }
678 if (ipv6RouteEntrySize == 0) {
679 (void) fprintf(stderr, gettext(
680 "ipv6RouteEntrySize cannot be determined.\n"));
681 exit(1);
682 }
683 for (; item != NULL; item = item->next_item) {
684 /*
685 * skip all the other trash that comes up the mib stream
686 */
687 if (item->group != MIB2_IP6 ||
688 item->mib_id != MIB2_IP6_ROUTE)
689 continue;
690 for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp;
691 (char *)rp6 < (char *)item->valp + item->length;
692 /* LINTED */
693 rp6 = (mib2_ipv6RouteEntry_t *)
694 ((char *)rp6 + ipv6RouteEntrySize)) {
695 delRouteEntry(NULL, rp6, seqno);
696 seqno++;
697 }
698 break;
699 }
700 break;
701 }
702
703 if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on,
704 sizeof (on)) < 0)
705 quit("setsockopt", errno);
706 }
707
708 /*
709 * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or
710 * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in
711 * order to facilitate the flushing of RTF_GATEWAY routes.
712 */
713 static void
delRouteEntry(mib2_ipRouteEntry_t * rp,mib2_ipv6RouteEntry_t * rp6,int seqno)714 delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno)
715 {
716 char *cp;
717 int ire_type;
718 int rlen;
719 struct rt_msghdr *rtm;
720 struct sockaddr_in sin;
721 struct sockaddr_in6 sin6;
722 int slen;
723
724 if (rp != NULL)
725 ire_type = rp->ipRouteInfo.re_ire_type;
726 else
727 ire_type = rp6->ipv6RouteInfo.re_ire_type;
728 if (ire_type != IRE_DEFAULT &&
729 ire_type != IRE_PREFIX &&
730 ire_type != IRE_HOST &&
731 ire_type != IRE_HOST_REDIRECT)
732 return;
733
734 rtm = &m_rtmsg.m_rtm;
735 (void) memset(rtm, 0, sizeof (m_rtmsg));
736 rtm->rtm_type = RTM_DELETE;
737 rtm->rtm_seq = seqno;
738 rtm->rtm_flags |= RTF_GATEWAY;
739 rtm->rtm_version = RTM_VERSION;
740 rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
741 cp = m_rtmsg.m_space;
742 if (rp != NULL) {
743 slen = sizeof (struct sockaddr_in);
744 if (rp->ipRouteMask == IP_HOST_MASK)
745 rtm->rtm_flags |= RTF_HOST;
746 (void) memset(&sin, 0, slen);
747 sin.sin_family = AF_INET;
748 sin.sin_addr.s_addr = rp->ipRouteDest;
749 (void) memmove(cp, &sin, slen);
750 cp += slen;
751 sin.sin_addr.s_addr = rp->ipRouteNextHop;
752 (void) memmove(cp, &sin, slen);
753 cp += slen;
754 sin.sin_addr.s_addr = rp->ipRouteMask;
755 (void) memmove(cp, &sin, slen);
756 cp += slen;
757 } else {
758 slen = sizeof (struct sockaddr_in6);
759 if (rp6->ipv6RoutePfxLength == IPV6_ABITS)
760 rtm->rtm_flags |= RTF_HOST;
761 (void) memset(&sin6, 0, slen);
762 sin6.sin6_family = AF_INET6;
763 sin6.sin6_addr = rp6->ipv6RouteDest;
764 (void) memmove(cp, &sin6, slen);
765 cp += slen;
766 sin6.sin6_addr = rp6->ipv6RouteNextHop;
767 (void) memmove(cp, &sin6, slen);
768 cp += slen;
769 (void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr));
770 (void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS,
771 (uchar_t *)&sin6.sin6_addr.s6_addr);
772 (void) memmove(cp, &sin6, slen);
773 cp += slen;
774 }
775 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
776 if (debugonly) {
777 /*
778 * In debugonly mode, the routing socket message to delete the
779 * current entry is not actually sent. However if verbose is
780 * also set, the routing socket message that would have been
781 * is printed.
782 */
783 if (verbose)
784 print_rtmsg(rtm, rtm->rtm_msglen);
785 return;
786 }
787
788 rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen);
789 if (rlen < (int)rtm->rtm_msglen) {
790 if (rlen < 0) {
791 (void) fprintf(stderr,
792 gettext("route: write to routing socket: %s\n"),
793 strerror(errno));
794 } else {
795 (void) fprintf(stderr, gettext("route: write to "
796 "routing socket got only %d for rlen\n"), rlen);
797 }
798 return;
799 }
800 if (qflag) {
801 /*
802 * In quiet mode, nothing is printed at all (unless the write()
803 * itself failed.
804 */
805 return;
806 }
807 if (verbose) {
808 print_rtmsg(rtm, rlen);
809 } else {
810 struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
811
812 (void) printf("%-20.20s ",
813 rtm->rtm_flags & RTF_HOST ? routename(sa) :
814 netname(sa));
815 /* LINTED */
816 sa = (struct sockaddr *)(salen(sa) + (char *)sa);
817 (void) printf("%-20.20s ", routename(sa));
818 (void) printf("done\n");
819 }
820 }
821
822 /*
823 * Return the name of the host whose address is given.
824 */
825 char *
routename(const struct sockaddr * sa)826 routename(const struct sockaddr *sa)
827 {
828 char *cp;
829 static char line[MAXHOSTNAMELEN + 1];
830 struct hostent *hp = NULL;
831 static char domain[MAXHOSTNAMELEN + 1];
832 static boolean_t first = B_TRUE;
833 struct in_addr in;
834 struct in6_addr in6;
835 int error_num;
836 ushort_t *s;
837 ushort_t *slim;
838
839 if (first) {
840 first = B_FALSE;
841 if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
842 (cp = strchr(domain, '.')))
843 (void) strcpy(domain, cp + 1);
844 else
845 domain[0] = 0;
846 }
847
848 if (salen(sa) == 0) {
849 (void) strcpy(line, "default");
850 return (line);
851 }
852 switch (sa->sa_family) {
853
854 case AF_INET:
855 /* LINTED */
856 in = ((struct sockaddr_in *)sa)->sin_addr;
857
858 cp = NULL;
859 if (in.s_addr == INADDR_ANY)
860 cp = "default";
861 if (cp == NULL && !nflag) {
862 hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
863 AF_INET);
864 if (hp != NULL) {
865 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
866 (strcmp(cp + 1, domain) == 0))
867 *cp = 0;
868 cp = hp->h_name;
869 }
870 }
871 if (cp != NULL) {
872 (void) strncpy(line, cp, MAXHOSTNAMELEN);
873 line[MAXHOSTNAMELEN] = '\0';
874 } else {
875 in.s_addr = ntohl(in.s_addr);
876 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
877 C(in.s_addr >> 16), C(in.s_addr >> 8),
878 C(in.s_addr));
879 }
880 break;
881
882 case AF_LINK:
883 return (link_ntoa((struct sockaddr_dl *)sa));
884
885 case AF_INET6:
886 /* LINTED */
887 in6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
888
889 cp = NULL;
890 if (IN6_IS_ADDR_UNSPECIFIED(&in6))
891 cp = "default";
892 if (cp == NULL && !nflag) {
893 hp = getipnodebyaddr((char *)&in6,
894 sizeof (struct in6_addr), AF_INET6, &error_num);
895 if (hp != NULL) {
896 if (((cp = strchr(hp->h_name, '.')) != NULL) &&
897 (strcmp(cp + 1, domain) == 0))
898 *cp = 0;
899 cp = hp->h_name;
900 }
901 }
902 if (cp != NULL) {
903 (void) strncpy(line, cp, MAXHOSTNAMELEN);
904 line[MAXHOSTNAMELEN] = '\0';
905 } else {
906 (void) inet_ntop(AF_INET6, (void *)&in6, line,
907 INET6_ADDRSTRLEN);
908 }
909 if (hp != NULL)
910 freehostent(hp);
911
912 break;
913
914 default:
915 s = (ushort_t *)sa;
916
917 slim = s + ((salen(sa) + 1) >> 1);
918 cp = line + sprintf(line, "(%d)", sa->sa_family);
919
920 while (++s < slim) /* start with sa->sa_data */
921 cp += sprintf(cp, " %x", *s);
922 break;
923 }
924 return (line);
925 }
926
927 /*
928 * Return the name of the network whose address is given.
929 * The address is assumed to be that of a net or subnet, not a host.
930 */
931 static char *
netname(struct sockaddr * sa)932 netname(struct sockaddr *sa)
933 {
934 char *cp = NULL;
935 static char line[MAXHOSTNAMELEN + 1];
936 struct netent *np;
937 in_addr_t net, mask;
938 int subnetshift;
939 struct in_addr in;
940 ushort_t *s;
941 ushort_t *slim;
942
943 switch (sa->sa_family) {
944
945 case AF_INET:
946 /* LINTED */
947 in = ((struct sockaddr_in *)sa)->sin_addr;
948
949 in.s_addr = ntohl(in.s_addr);
950 if (in.s_addr == INADDR_ANY) {
951 cp = "default";
952 } else if (!nflag) {
953 if (IN_CLASSA(in.s_addr)) {
954 mask = IN_CLASSA_NET;
955 subnetshift = 8;
956 } else if (IN_CLASSB(in.s_addr)) {
957 mask = IN_CLASSB_NET;
958 subnetshift = 8;
959 } else {
960 mask = IN_CLASSC_NET;
961 subnetshift = 4;
962 }
963 /*
964 * If there are more bits than the standard mask
965 * would suggest, subnets must be in use.
966 * Guess at the subnet mask, assuming reasonable
967 * width subnet fields.
968 */
969 while (in.s_addr &~ mask)
970 mask = (long)mask >> subnetshift;
971 net = in.s_addr & mask;
972 while ((mask & 1) == 0)
973 mask >>= 1, net >>= 1;
974 np = getnetbyaddr(net, AF_INET);
975 if (np != NULL)
976 cp = np->n_name;
977 }
978 if (cp != NULL) {
979 (void) strncpy(line, cp, MAXHOSTNAMELEN);
980 line[MAXHOSTNAMELEN] = '\0';
981 } else if ((in.s_addr & 0xffffff) == 0) {
982 (void) sprintf(line, "%u", C(in.s_addr >> 24));
983 } else if ((in.s_addr & 0xffff) == 0) {
984 (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
985 C(in.s_addr >> 16));
986 } else if ((in.s_addr & 0xff) == 0) {
987 (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
988 C(in.s_addr >> 16), C(in.s_addr >> 8));
989 } else {
990 (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
991 C(in.s_addr >> 16), C(in.s_addr >> 8),
992 C(in.s_addr));
993 }
994 break;
995
996 case AF_LINK:
997 return (link_ntoa((struct sockaddr_dl *)sa));
998
999 case AF_INET6:
1000 return (routename(sa));
1001
1002 default:
1003 /* LINTED */
1004 s = (ushort_t *)sa->sa_data;
1005
1006 slim = s + ((salen(sa) + 1) >> 1);
1007 cp = line + sprintf(line, "af %d:", sa->sa_family);
1008
1009 while (s < slim)
1010 cp += sprintf(cp, " %x", *s++);
1011 break;
1012 }
1013 return (line);
1014 }
1015
1016 /*
1017 * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and
1018 * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL
1019 * or point to dynamically allocated memory.
1020 */
1021 rtcmd_irep_t *
new_rtcmd_irep(void)1022 new_rtcmd_irep(void)
1023 {
1024 rtcmd_irep_t *rcip;
1025
1026 rcip = calloc(1, sizeof (rtcmd_irep_t));
1027 if (rcip == NULL) {
1028 quit("calloc", errno);
1029 }
1030 rcip->ri_af = af;
1031 rcip->ri_flags = RTF_STATIC;
1032 return (rcip);
1033 }
1034
1035 void
del_rtcmd_irep(rtcmd_irep_t * rcip)1036 del_rtcmd_irep(rtcmd_irep_t *rcip)
1037 {
1038 free(rcip->ri_dest_str);
1039 free(rcip->ri_gate_str);
1040 free(rcip->ri_ifp_str);
1041 /*
1042 * IPv6 host entries come from getipnodebyname, which dynamically
1043 * allocates memory. IPv4 host entries come from gethostbyname, which
1044 * returns static memory and cannot be freed with freehostent.
1045 */
1046 if (rcip->ri_gate_hp != NULL &&
1047 rcip->ri_gate_hp->h_addrtype == AF_INET6)
1048 freehostent(rcip->ri_gate_hp);
1049 free(rcip);
1050 }
1051
1052 void
save_string(char ** dst,char * src)1053 save_string(char **dst, char *src)
1054 {
1055 free(*dst);
1056 *dst = strdup(src);
1057 if (*dst == NULL) {
1058 quit("malloc", errno);
1059 }
1060 }
1061
1062 /*
1063 * Print the short form summary of a route command.
1064 * Eg. "add net default: gateway 10.0.0.1"
1065 * The final newline is not added, allowing the caller to append additional
1066 * information.
1067 */
1068 void
print_rtcmd_short(FILE * to,rtcmd_irep_t * rcip,boolean_t gw_good,boolean_t to_saved)1069 print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good,
1070 boolean_t to_saved)
1071 {
1072 char *cmd;
1073 char obuf[INET6_ADDRSTRLEN];
1074
1075 switch (rcip->ri_cmd) {
1076 case RTM_ADD:
1077 cmd = "add";
1078 break;
1079 case RTM_CHANGE:
1080 cmd = "change";
1081 break;
1082 case RTM_DELETE:
1083 cmd = "delete";
1084 break;
1085 case RTM_GET:
1086 cmd = "get";
1087 break;
1088 default:
1089 assert(0);
1090 }
1091
1092 (void) fprintf(to, "%s%s %s %s", cmd,
1093 (to_saved) ? " persistent" : "",
1094 (rcip->ri_flags & RTF_HOST) ? "host" : "net",
1095 (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str);
1096
1097 if (rcip->ri_gate_str != NULL) {
1098 switch (rcip->ri_af) {
1099 case AF_INET:
1100 if (nflag) {
1101 (void) fprintf(to, ": gateway %s",
1102 inet_ntoa(rcip->ri_gate.sin.sin_addr));
1103 } else if (gw_good &&
1104 rcip->ri_gate_hp != NULL &&
1105 rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1106 /*
1107 * Print the actual address used in the case
1108 * where there was more than one address
1109 * available for the name, and one was used
1110 * successfully.
1111 */
1112 (void) fprintf(to, ": gateway %s (%s)",
1113 rcip->ri_gate_str,
1114 inet_ntoa(rcip->ri_gate.sin.sin_addr));
1115 } else {
1116 (void) fprintf(to, ": gateway %s",
1117 rcip->ri_gate_str);
1118 }
1119 break;
1120 case AF_INET6:
1121 if (inet_ntop(AF_INET6,
1122 &rcip->ri_gate.sin6.sin6_addr, obuf,
1123 INET6_ADDRSTRLEN) != NULL) {
1124 if (nflag) {
1125 (void) fprintf(to, ": gateway %s",
1126 obuf);
1127 break;
1128 }
1129 if (gw_good &&
1130 rcip->ri_gate_hp->h_addr_list[1] != NULL) {
1131 (void) fprintf(to, ": gateway %s (%s)",
1132 rcip->ri_gate_str, obuf);
1133 break;
1134 }
1135 }
1136 /* FALLTHROUGH */
1137 default:
1138 (void) fprintf(to, ": gateway %s",
1139 rcip->ri_gate_str);
1140 break;
1141 }
1142 }
1143 }
1144
1145 void
set_metric(rtcmd_irep_t * rcip,char * value,int key,boolean_t lock)1146 set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock)
1147 {
1148 int flag = 0;
1149 uint_t noval, *valp = &noval;
1150
1151 switch (key) {
1152 #define caseof(x, y, z) \
1153 case (x): valp = &(rcip->ri_metrics.z); flag = (y); break
1154
1155 caseof(K_MTU, RTV_MTU, rmx_mtu);
1156 caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
1157 caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
1158 caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
1159 caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
1160 caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
1161 caseof(K_RTT, RTV_RTT, rmx_rtt);
1162 caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
1163 #undef caseof
1164 }
1165 rcip->ri_inits |= flag;
1166 if (lock)
1167 rcip->ri_metrics.rmx_locks |= flag;
1168 *valp = atoi(value);
1169 }
1170
1171 /*
1172 * Parse the options give in argv[], filling in rcip with the results.
1173 * If cmd_string is non-null, argc and argv are ignored, and cmd_string is
1174 * tokenized to produce the command line. Cmd_string is tokenized using
1175 * strtok, which will overwrite whitespace in the string with nulls.
1176 *
1177 * Returns B_TRUE on success and B_FALSE on failure.
1178 */
1179 boolean_t
args_to_rtcmd(rtcmd_irep_t * rcip,char ** argv,char * cmd_string)1180 args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string)
1181 {
1182 const char *ws = "\f\n\r\t\v ";
1183 char *tok = cmd_string;
1184 char *keyword_str;
1185 addr_type_t atype = ADDR_TYPE_ANY;
1186 boolean_t iflag = B_FALSE;
1187 boolean_t locknext = B_FALSE;
1188 boolean_t lockrest = B_FALSE;
1189 boolean_t dash_keyword;
1190 int key;
1191 char *err;
1192
1193 if (cmd_string == NULL) {
1194 tok = argv[0];
1195 } else {
1196 tok = strtok(cmd_string, ws);
1197 }
1198
1199 /*
1200 * The command keywords are already fully checked by main() or
1201 * search_rtfile().
1202 */
1203 switch (*tok) {
1204 case 'a':
1205 rcip->ri_cmd = RTM_ADD;
1206 break;
1207 case 'c':
1208 rcip->ri_cmd = RTM_CHANGE;
1209 break;
1210 case 'd':
1211 rcip->ri_cmd = RTM_DELETE;
1212 break;
1213 case 'g':
1214 rcip->ri_cmd = RTM_GET;
1215 break;
1216 default:
1217 /* NOTREACHED */
1218 quit(gettext("Internal Error"), EINVAL);
1219 /* NOTREACHED */
1220 }
1221
1222 #define NEXTTOKEN \
1223 ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL)
1224
1225 while (NEXTTOKEN) {
1226 keyword_str = tok;
1227 if (*tok == '-') {
1228 dash_keyword = B_TRUE;
1229 key = keyword(tok + 1);
1230 } else {
1231 dash_keyword = B_FALSE;
1232 key = keyword(tok);
1233 if (key != K_HOST && key != K_NET) {
1234 /* All others must be preceded by '-' */
1235 key = 0;
1236 }
1237 }
1238 switch (key) {
1239 case K_HOST:
1240 if (atype == ADDR_TYPE_NET) {
1241 syntax_error(gettext("route: -host and -net "
1242 "are mutually exclusive\n"));
1243 return (B_FALSE);
1244 }
1245 atype = ADDR_TYPE_HOST;
1246 break;
1247 case K_NET:
1248 if (atype == ADDR_TYPE_HOST) {
1249 syntax_error(gettext("route: -host and -net "
1250 "are mutually exclusive\n"));
1251 return (B_FALSE);
1252 }
1253 atype = ADDR_TYPE_NET;
1254 break;
1255 case K_LINK:
1256 rcip->ri_af = AF_LINK;
1257 break;
1258 case K_INET:
1259 rcip->ri_af = AF_INET;
1260 break;
1261 case K_SA:
1262 rcip->ri_af = PF_ROUTE;
1263 break;
1264 case K_INET6:
1265 rcip->ri_af = AF_INET6;
1266 break;
1267 case K_IFACE:
1268 case K_INTERFACE:
1269 iflag = B_TRUE;
1270 /* fallthrough */
1271 case K_NOSTATIC:
1272 rcip->ri_flags &= ~RTF_STATIC;
1273 break;
1274 case K_LOCK:
1275 locknext = B_TRUE;
1276 break;
1277 case K_LOCKREST:
1278 lockrest = B_TRUE;
1279 break;
1280 case K_REJECT:
1281 rcip->ri_flags |= RTF_REJECT;
1282 break;
1283 case K_BLACKHOLE:
1284 rcip->ri_flags |= RTF_BLACKHOLE;
1285 break;
1286 case K_PROTO1:
1287 rcip->ri_flags |= RTF_PROTO1;
1288 break;
1289 case K_PROTO2:
1290 rcip->ri_flags |= RTF_PROTO2;
1291 break;
1292 case K_CLONING:
1293 rcip->ri_flags |= RTF_CLONING;
1294 break;
1295 case K_XRESOLVE:
1296 rcip->ri_flags |= RTF_XRESOLVE;
1297 break;
1298 case K_STATIC:
1299 rcip->ri_flags |= RTF_STATIC;
1300 break;
1301 case K_IFA:
1302 if (!NEXTTOKEN) {
1303 syntax_arg_missing(keyword_str);
1304 return (B_FALSE);
1305 }
1306 if (!getaddr(rcip, RTA_IFA, tok, atype)) {
1307 return (B_FALSE);
1308 }
1309 break;
1310 case K_IFP:
1311 if (!NEXTTOKEN) {
1312 syntax_arg_missing(keyword_str);
1313 return (B_FALSE);
1314 }
1315 if (!getaddr(rcip, RTA_IFP, tok, atype)) {
1316 return (B_FALSE);
1317 }
1318 break;
1319 case K_GATEWAY:
1320 if (!NEXTTOKEN) {
1321 syntax_arg_missing(keyword_str);
1322 return (B_FALSE);
1323 }
1324 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1325 return (B_FALSE);
1326 }
1327 break;
1328 case K_DST:
1329 if (!NEXTTOKEN) {
1330 syntax_arg_missing(keyword_str);
1331 return (B_FALSE);
1332 }
1333 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1334 return (B_FALSE);
1335 }
1336 break;
1337 case K_NETMASK:
1338 if (!NEXTTOKEN) {
1339 syntax_arg_missing(keyword_str);
1340 return (B_FALSE);
1341 }
1342 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1343 return (B_FALSE);
1344 }
1345 atype = ADDR_TYPE_NET;
1346 break;
1347 case K_MTU:
1348 case K_HOPCOUNT:
1349 case K_EXPIRE:
1350 case K_RECVPIPE:
1351 case K_SENDPIPE:
1352 case K_SSTHRESH:
1353 case K_RTT:
1354 case K_RTTVAR:
1355 if (!NEXTTOKEN) {
1356 syntax_arg_missing(keyword_str);
1357 return (B_FALSE);
1358 }
1359 set_metric(rcip, tok, key, locknext || lockrest);
1360 locknext = B_FALSE;
1361 break;
1362 case K_PRIVATE:
1363 rcip->ri_flags |= RTF_PRIVATE;
1364 break;
1365 case K_MULTIRT:
1366 rcip->ri_flags |= RTF_MULTIRT;
1367 break;
1368 case K_SETSRC:
1369 if (!NEXTTOKEN) {
1370 syntax_arg_missing(keyword_str);
1371 return (B_FALSE);
1372 }
1373 if (!getaddr(rcip, RTA_SRC, tok, atype)) {
1374 return (B_FALSE);
1375 }
1376 rcip->ri_flags |= RTF_SETSRC;
1377 break;
1378 case K_SECATTR:
1379 if (!NEXTTOKEN) {
1380 syntax_arg_missing(keyword_str);
1381 return (B_FALSE);
1382 }
1383 if (is_system_labeled()) {
1384 int err;
1385
1386 if (rcip->ri_rtsa_cnt >= 1) {
1387 syntax_error(gettext("route: can't "
1388 "specify more than one security "
1389 "attribute\n"));
1390 return (B_FALSE);
1391 }
1392 if (!rtsa_keyword(tok, &rcip->ri_rtsa, &err,
1393 NULL)) {
1394 syntax_error(gettext("route: "
1395 "bad security attribute: %s\n"),
1396 tsol_strerror(err, errno));
1397 return (B_FALSE);
1398 }
1399 rcip->ri_rtsa_cnt++;
1400 } else {
1401 syntax_error(gettext("route: "
1402 "system is not labeled; cannot specify "
1403 "security attributes.\n"));
1404 return (B_FALSE);
1405 }
1406 break;
1407 case K_INDIRECT:
1408 rcip->ri_flags |= RTF_INDIRECT;
1409 break;
1410 default:
1411 if (dash_keyword) {
1412 syntax_bad_keyword(tok + 1);
1413 return (B_FALSE);
1414 }
1415 if ((rcip->ri_addrs & RTA_DST) == 0) {
1416 if (!getaddr(rcip, RTA_DST, tok, atype)) {
1417 return (B_FALSE);
1418 }
1419 } else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) {
1420 /*
1421 * For the gateway parameter, retrieve the
1422 * pointer to the struct hostent so that all
1423 * possible addresses can be tried until one
1424 * is successful.
1425 */
1426 if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) {
1427 return (B_FALSE);
1428 }
1429 } else {
1430 ulong_t metric;
1431 /*
1432 * Assume that a regular number is a metric.
1433 * Needed for compatibility with old route
1434 * command syntax.
1435 */
1436 errno = 0;
1437 metric = strtoul(tok, &err, 10);
1438 if (errno == 0 && *err == '\0' &&
1439 metric < 0x80000000ul) {
1440 iflag = (metric == 0);
1441 if (verbose) {
1442 (void) printf("old usage of "
1443 "trailing number, assuming "
1444 "route %s\n", iflag ?
1445 "to if" : "via gateway");
1446 }
1447 continue;
1448 }
1449 if (!getaddr(rcip, RTA_NETMASK, tok, atype)) {
1450 return (B_FALSE);
1451 }
1452 }
1453 }
1454 }
1455 #undef NEXTTOKEN
1456
1457 if ((rcip->ri_addrs & RTA_DST) == 0) {
1458 syntax_error(gettext("route: destination required\n"));
1459 return (B_FALSE);
1460 } else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) &&
1461 (rcip->ri_addrs & RTA_GATEWAY) == 0) {
1462 syntax_error(gettext(
1463 "route: gateway required for add or delete command\n"));
1464 return (B_FALSE);
1465 }
1466
1467 if (!iflag) {
1468 rcip->ri_flags |= RTF_GATEWAY;
1469 }
1470
1471 if (atype != ADDR_TYPE_NET) {
1472 if (rcip->ri_addrs & RTA_NETMASK) {
1473 /*
1474 * We know the netmask, so we can set the host flag
1475 * based on whether the netmask is the host netmask.
1476 */
1477 if (rcip->ri_af == AF_INET &&
1478 rcip->ri_mask.sin.sin_addr.s_addr ==
1479 IP_HOST_MASK) {
1480 rcip->ri_flags |= RTF_HOST;
1481 }
1482 if (rcip->ri_af == AF_INET6 &&
1483 memcmp(&rcip->ri_mask.sin6.sin6_addr,
1484 &in6_host_mask,
1485 sizeof (struct in6_addr)) == 0) {
1486 rcip->ri_flags |= RTF_HOST;
1487 }
1488 } else {
1489 /*
1490 * If no prefix mask has been saved at this point, it
1491 * only makes sense to treat the destination address
1492 * as a host address.
1493 */
1494 rcip->ri_flags |= RTF_HOST;
1495 }
1496 }
1497 return (B_TRUE);
1498 }
1499
1500 /*
1501 * This command always seeks to the end of the file prior to writing.
1502 */
1503 void
write_to_rtfile(FILE * fp,int argc,char ** argv)1504 write_to_rtfile(FILE *fp, int argc, char **argv)
1505 {
1506 char file_line[BUF_SIZE];
1507 int len;
1508 int i;
1509
1510 len = 0;
1511 if (early_v6_keyword) {
1512 /*
1513 * This flag is set when "inet6" was seen as an
1514 * argument to the -f flag. Normally, when writing
1515 * routes to the persistent route file, everything on
1516 * the command line after "add" is saved verbatim.
1517 * In this case, the arguments after "add" may not be
1518 * sufficient, as the ipv6 keyword came before "add",
1519 * yet must be present in the persistent route file.
1520 */
1521 len += snprintf(file_line, BUF_SIZE, "-inet6 ");
1522 }
1523 for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) {
1524 len += snprintf(&file_line[len], BUF_SIZE - len, "%s ",
1525 argv[i]);
1526 }
1527 if (len >= BUF_SIZE)
1528 quit(gettext("Internal Error"), EINVAL);
1529 file_line[len - 1] = '\n';
1530 if (fseek(fp, 0, SEEK_END) != 0 ||
1531 fputs(file_line, fp) == EOF) {
1532 quit(gettext("failed to write to route file"),
1533 errno);
1534 }
1535 }
1536
1537 boolean_t
compare_rtcmd(rtcmd_irep_t * srch_rt,rtcmd_irep_t * file_rt)1538 compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt)
1539 {
1540 if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 ||
1541 memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) {
1542 return (B_FALSE);
1543 }
1544 return (srch_rt->ri_gate_str == NULL ||
1545 strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0);
1546 }
1547
1548 /*
1549 * Search the route file for routes matching the supplied route. There are 3
1550 * modes of operation:
1551 * SEARCH_MODE_RET - no side effects.
1552 * SEARCH_MODE_PRINT - prints each matching line.
1553 * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp.
1554 *
1555 * In all cases, the number of matches is returned. If rt is NULL, all routes
1556 * matching the global af value are considered matching.
1557 */
1558 int
search_rtfile(FILE * fp,FILE * temp_fp,rtcmd_irep_t * rt,search_mode_t mode)1559 search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode)
1560 {
1561 char *tmp_buf;
1562 int match_cnt;
1563 boolean_t match;
1564 char file_line[BUF_SIZE + 4] = "add ";
1565 rtcmd_irep_t *thisrt;
1566
1567 match_cnt = 0;
1568
1569 /*
1570 * Leave space at the beginning of file_line for "add ".
1571 */
1572 while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) {
1573
1574 if (file_line[4] == '#' || file_line[4] == '\n') {
1575 /* Handle comments and blank lines */
1576 if (mode == SEARCH_MODE_DEL &&
1577 fputs(file_line + 4, temp_fp) == EOF) {
1578 quit(gettext(
1579 "route: failed to write to temp file"),
1580 errno);
1581 }
1582 continue;
1583 }
1584 thisrt = new_rtcmd_irep();
1585 /*
1586 * thisrt->ri_af defaults to whatever address family happens
1587 * to be set in the global af, but routes in the persistent
1588 * route file must be treated as AF_INET by default.
1589 */
1590 thisrt->ri_af = AF_INET;
1591
1592 exit_on_error = B_FALSE;
1593 tmp_buf = strdup(file_line);
1594 /* args_to_rtcmd() will mangle the string passed. */
1595 if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) {
1596 /* There was an error in args_to_rtcmd() or helpers */
1597 del_rtcmd_irep(thisrt);
1598 free(tmp_buf);
1599 continue;
1600 }
1601 exit_on_error = B_TRUE;
1602 free(tmp_buf);
1603
1604 if (thisrt->ri_gate_str == NULL) {
1605 del_rtcmd_irep(thisrt);
1606 continue;
1607 }
1608 match = (rt == NULL) ? (thisrt->ri_af == af) :
1609 compare_rtcmd(rt, thisrt);
1610
1611 if (match) match_cnt++;
1612 if (match && mode == SEARCH_MODE_PRINT) {
1613 (void) printf("persistent: route %s", file_line);
1614 }
1615 if (match && mode == SEARCH_MODE_DEL) {
1616 thisrt->ri_cmd = RTM_DELETE;
1617 print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE);
1618 (void) printf("\n");
1619 }
1620 del_rtcmd_irep(thisrt);
1621
1622 if (!match && mode == SEARCH_MODE_DEL &&
1623 fputs(file_line + 4, temp_fp) == EOF) {
1624 quit(gettext("failed to write to temp file"),
1625 errno);
1626 }
1627 }
1628 return (match_cnt);
1629 }
1630
1631 /*
1632 * Perform the route operation given in argv on the persistent route file.
1633 * If do_flush is set, the persistent route file is flushed of all routes
1634 * matching the global family, and the arguments are ignored.
1635 */
1636 void
save_route(int argc,char ** argv,int do_flush)1637 save_route(int argc, char **argv, int do_flush)
1638 {
1639 rtcmd_irep_t *rt;
1640 int perm_fd;
1641 FILE *perm_fp;
1642 FILE *temp_fp;
1643 mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1644 struct flock lock;
1645 struct stat st;
1646 const char commentstr[] =
1647 "# File generated by route(8) - do not edit.\n";
1648
1649 perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode);
1650 if (perm_fd == -1 || fstat(perm_fd, &st) == -1)
1651 quit("failed to open route file", errno);
1652
1653 lock.l_type = F_WRLCK;
1654 lock.l_whence = SEEK_SET;
1655 lock.l_start = 0;
1656 lock.l_len = 0;
1657 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1658 quit(gettext("failed to lock route file"), errno);
1659 /* NOTREACHED */
1660 }
1661 if (st.st_size == 0 &&
1662 write(perm_fd, commentstr, sizeof (commentstr) - 1) !=
1663 sizeof (commentstr) - 1)
1664 quit(gettext("failed to open route file"), errno);
1665
1666 if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) {
1667 quit(gettext("failed to open route file"), errno);
1668 /* NOTREACHED */
1669 }
1670
1671 if (!do_flush) {
1672 rt = new_rtcmd_irep();
1673 (void) args_to_rtcmd(rt, argv, NULL);
1674 }
1675 if (do_flush || rt->ri_cmd == RTM_DELETE) {
1676 if ((temp_fp = fopen(temp_file, "w")) == NULL) {
1677 quit(gettext("failed to open temp file"), errno);
1678 /* NOTREACHED */
1679 }
1680 }
1681 if (do_flush) {
1682 (void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL);
1683 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1684 quit(gettext("failed to update route file"), errno);
1685 /* NOTREACHED */
1686 }
1687 (void) fclose(perm_fp);
1688 return;
1689 }
1690
1691 switch (rt->ri_cmd) {
1692 case RTM_ADD:
1693 if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) {
1694 /* Route is already in the file */
1695 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1696 (void) fprintf(stderr, ": entry exists\n");
1697 exit(1);
1698 }
1699 write_to_rtfile(perm_fp, argc - 1, argv + 1);
1700 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1701 (void) printf("\n");
1702 break;
1703
1704 case RTM_CHANGE:
1705 syntax_error(
1706 gettext("route: change command not supported with -p\n"));
1707 /* NOTREACHED */
1708
1709 case RTM_DELETE:
1710 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) {
1711 /* Route not found */
1712 print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE);
1713 (void) fprintf(stderr, gettext(": not in file\n"));
1714 exit(1);
1715 }
1716 if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) {
1717 quit(gettext("failed to update route file"), errno);
1718 /* NOTREACHED */
1719 }
1720 break;
1721
1722 case RTM_GET:
1723 if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <=
1724 0) {
1725 print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE);
1726 (void) printf(gettext(": not in file\n"));
1727 }
1728 break;
1729
1730 default:
1731 quit(gettext("Internal Error"), EINVAL);
1732 /* NOTREACHED */
1733 }
1734
1735 /*
1736 * Closing the file unlocks it.
1737 */
1738 (void) fclose(perm_fp);
1739 }
1740
1741 int
show_saved_routes(int argc)1742 show_saved_routes(int argc)
1743 {
1744 int perm_fd;
1745 FILE *perm_fp;
1746 struct flock lock;
1747 int count = 0;
1748
1749 if (argc != 1) {
1750 syntax_error(gettext("route: invalid arguments for show\n"));
1751 }
1752
1753 perm_fd = open(perm_file, O_RDONLY, 0);
1754
1755 if (perm_fd == -1) {
1756 if (errno == ENOENT) {
1757 (void) printf("No persistent routes are defined\n");
1758 return (0);
1759 } else {
1760 quit(gettext("failed to open route file"), errno);
1761 }
1762 }
1763 lock.l_type = F_RDLCK;
1764 lock.l_whence = SEEK_SET;
1765 lock.l_start = 0;
1766 lock.l_len = 0;
1767 if (fcntl(perm_fd, F_SETLK, &lock) != 0) {
1768 quit(gettext("failed to lock route file"),
1769 errno);
1770 /* NOTREACHED */
1771 }
1772 if ((perm_fp = fdopen(perm_fd, "r")) == NULL) {
1773 quit(gettext("failed to open route file"), errno);
1774 /* NOTREACHED */
1775 }
1776 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1777 (void) fseek(perm_fp, 0, SEEK_SET);
1778 af = AF_INET6;
1779 count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT);
1780
1781 if (count == 0)
1782 (void) printf("No persistent routes are defined\n");
1783
1784 (void) fclose(perm_fp);
1785 return (0);
1786 }
1787
1788 int
newroute(char ** argv)1789 newroute(char **argv)
1790 {
1791 rtcmd_irep_t *newrt;
1792 int ret, attempts, oerrno;
1793 char *err;
1794 char obuf[INET6_ADDRSTRLEN];
1795 #define hp (newrt->ri_gate_hp)
1796
1797 newrt = new_rtcmd_irep();
1798 (void) args_to_rtcmd(newrt, argv, NULL);
1799
1800 if (newrt->ri_cmd != RTM_GET && !tflag) {
1801 /* Don't want to read back our messages */
1802 (void) shutdown(s, 0);
1803 }
1804 if (newrt->ri_addrs & RTA_IFP) {
1805 newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str);
1806 if (newrt->ri_ifp.sdl.sdl_index == 0) {
1807 if (errno != ENXIO) {
1808 quit("if_nametoindex", errno);
1809 } else {
1810 (void) fprintf(stderr,
1811 gettext("route: %s: no such interface\n"),
1812 newrt->ri_ifp_str);
1813 exit(1);
1814 }
1815 }
1816 newrt->ri_ifp.sdl.sdl_family = AF_LINK;
1817 }
1818 for (attempts = 1; ; attempts++) {
1819 errno = 0;
1820 if ((ret = rtmsg(newrt)) == 0)
1821 break;
1822 if (errno != ENETUNREACH && errno != ESRCH)
1823 break;
1824 if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL &&
1825 hp->h_addr_list[attempts] != NULL) {
1826 switch (af) {
1827 case AF_INET:
1828 (void) memmove(&newrt->ri_gate.sin.sin_addr,
1829 hp->h_addr_list[attempts], hp->h_length);
1830 continue;
1831 case AF_INET6:
1832 (void) memmove(&newrt->ri_gate.sin6.sin6_addr,
1833 hp->h_addr_list[attempts], hp->h_length);
1834 continue;
1835 }
1836 }
1837 break;
1838 }
1839 oerrno = errno;
1840
1841 if (newrt->ri_cmd != RTM_GET) {
1842 print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE);
1843 if (ret == 0)
1844 (void) printf("\n");
1845 } else if (ret != 0) {
1846 /*
1847 * Note: there is nothing additional to print for get
1848 * if ret == 0.
1849 */
1850 if (nflag) {
1851 switch (newrt->ri_af) {
1852 case AF_INET:
1853 (void) printf(" %s",
1854 inet_ntoa(newrt->ri_dst.sin.sin_addr));
1855 break;
1856 case AF_INET6:
1857 if (inet_ntop(AF_INET6,
1858 (void *)&newrt->ri_dst.sin6.sin6_addr,
1859 obuf, INET6_ADDRSTRLEN) != NULL) {
1860 (void) printf(" %s", obuf);
1861 break;
1862 }
1863 /* FALLTHROUGH */
1864 default:
1865 (void) printf("%s", newrt->ri_dest_str);
1866 break;
1867 }
1868 } else {
1869 (void) printf("%s", newrt->ri_dest_str);
1870 }
1871 }
1872
1873 if (ret != 0) {
1874 switch (oerrno) {
1875 case ESRCH:
1876 err = "not in table";
1877 break;
1878 case EBUSY:
1879 err = "entry in use";
1880 break;
1881 case ENOBUFS:
1882 err = "routing table overflow";
1883 break;
1884 case EEXIST:
1885 err = "entry exists";
1886 break;
1887 case EPERM:
1888 err = "insufficient privileges";
1889 break;
1890 default:
1891 err = strerror(oerrno);
1892 break;
1893 }
1894 (void) printf(": %s\n", err);
1895 }
1896
1897 del_rtcmd_irep(newrt);
1898
1899 return (oerrno);
1900 #undef hp
1901 }
1902
1903
1904 /*
1905 * Convert a network number to the corresponding IP address.
1906 * If the RTA_NETMASK hasn't been specified yet set it based
1907 * on the class of address.
1908 */
1909 static void
inet_makenetandmask(rtcmd_irep_t * rcip,in_addr_t net,struct sockaddr_in * sin)1910 inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin)
1911 {
1912 in_addr_t addr, mask;
1913
1914 if (net == 0) {
1915 mask = addr = 0;
1916 } else if (net < 128) {
1917 addr = net << IN_CLASSA_NSHIFT;
1918 mask = IN_CLASSA_NET;
1919 } else if (net < 65536) {
1920 addr = net << IN_CLASSB_NSHIFT;
1921 mask = IN_CLASSB_NET;
1922 } else if (net < 16777216L) {
1923 addr = net << IN_CLASSC_NSHIFT;
1924 mask = IN_CLASSC_NET;
1925 } else {
1926 addr = net;
1927 if ((addr & IN_CLASSA_HOST) == 0)
1928 mask = IN_CLASSA_NET;
1929 else if ((addr & IN_CLASSB_HOST) == 0)
1930 mask = IN_CLASSB_NET;
1931 else if ((addr & IN_CLASSC_HOST) == 0)
1932 mask = IN_CLASSC_NET;
1933 else {
1934 if (IN_CLASSA(addr))
1935 mask = IN_CLASSA_NET;
1936 else if (IN_CLASSB(addr))
1937 mask = IN_CLASSB_NET;
1938 else if (IN_CLASSC(addr))
1939 mask = IN_CLASSC_NET;
1940 else
1941 mask = IP_HOST_MASK;
1942 mask = inet_makesubnetmask(addr, mask);
1943 }
1944 }
1945 sin->sin_addr.s_addr = htonl(addr);
1946
1947 /* Class E default mask is 32 */
1948 if (IN_CLASSE(addr))
1949 mask = IN_CLASSE_NET;
1950
1951 if (!(rcip->ri_addrs & RTA_NETMASK)) {
1952 rcip->ri_addrs |= RTA_NETMASK;
1953 sin = &rcip->ri_mask.sin;
1954 sin->sin_addr.s_addr = htonl(mask);
1955 sin->sin_family = AF_INET;
1956 }
1957 }
1958
1959 static in_addr_t
inet_makesubnetmask(in_addr_t addr,in_addr_t mask)1960 inet_makesubnetmask(in_addr_t addr, in_addr_t mask)
1961 {
1962 int n;
1963 struct ifconf ifc;
1964 struct ifreq ifreq;
1965 struct ifreq *ifr;
1966 struct sockaddr_in *sin;
1967 char *buf;
1968 int numifs;
1969 size_t bufsize;
1970 int iosoc;
1971 in_addr_t if_addr, if_mask;
1972 in_addr_t if_subnetmask = 0;
1973 short if_flags;
1974
1975 if (mask == 0)
1976 return (0);
1977 if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1978 quit("socket", errno);
1979 if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0)
1980 quit("ioctl", errno);
1981 bufsize = numifs * sizeof (struct ifreq);
1982 buf = malloc(bufsize);
1983 if (buf == NULL)
1984 quit("malloc", errno);
1985 (void) memset(&ifc, 0, sizeof (ifc));
1986 ifc.ifc_len = bufsize;
1987 ifc.ifc_buf = buf;
1988 if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0)
1989 quit("ioctl (get interface configuration)", errno);
1990 /* Let's check to see if this is maybe a local subnet route. */
1991 ifr = ifc.ifc_req;
1992 for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
1993 ifreq = *ifr;
1994 /* LINTED */
1995 sin = (struct sockaddr_in *)&ifr->ifr_addr;
1996 if_addr = ntohl(sin->sin_addr.s_addr);
1997
1998 if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0)
1999 quit("ioctl (get interface flags)", errno);
2000 if ((ifreq.ifr_flags & IFF_UP) == 0)
2001 continue;
2002 if_flags = ifreq.ifr_flags;
2003
2004 if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0)
2005 quit("ioctl (get netmask)", errno);
2006 /* LINTED */
2007 sin = (struct sockaddr_in *)&ifreq.ifr_addr;
2008 if_mask = ntohl(sin->sin_addr.s_addr);
2009 if ((if_addr & mask) == (addr & mask)) {
2010 /*
2011 * Don't trust pt-pt interfaces if there are
2012 * other interfaces.
2013 */
2014 if (if_flags & IFF_POINTOPOINT) {
2015 if_subnetmask = if_mask;
2016 continue;
2017 }
2018 /*
2019 * Fine. Just assume the same net mask as the
2020 * directly attached subnet interface is using.
2021 */
2022 return (if_mask);
2023 }
2024 }
2025 if (if_subnetmask != 0)
2026 return (if_subnetmask);
2027 return (mask);
2028 }
2029
2030 /*
2031 * Interpret an argument as a network address of some kind.
2032 *
2033 * If the address family is one looked up in getaddr() using one of the
2034 * getipnodebyX() functions (currently only AF_INET6), then callers should
2035 * freehostent() the returned "struct hostent" pointer if one was passed in.
2036 *
2037 * If exit_on_error is true, this function will cause route to exit on error by
2038 * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE
2039 * on failure.
2040 */
2041 static boolean_t
getaddr(rtcmd_irep_t * rcip,int which,char * s,addr_type_t atype)2042 getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype)
2043 {
2044 su_t *su;
2045 struct hostent **hpp;
2046 struct hostent *hp;
2047 int masklen;
2048
2049 if (which == RTA_GATEWAY) {
2050 hpp = &(rcip->ri_gate_hp);
2051 } else {
2052 hpp = &hp;
2053 }
2054 *hpp = NULL;
2055
2056 rcip->ri_addrs |= which;
2057 switch (which) {
2058 case RTA_DST:
2059 save_string(&rcip->ri_dest_str, s);
2060 su = &rcip->ri_dst;
2061 su->sa.sa_family = rcip->ri_af;
2062 break;
2063 case RTA_GATEWAY:
2064 save_string(&rcip->ri_gate_str, s);
2065 su = &rcip->ri_gate;
2066 su->sa.sa_family = rcip->ri_af;
2067 break;
2068 case RTA_NETMASK:
2069 su = &rcip->ri_mask;
2070 su->sa.sa_family = rcip->ri_af;
2071 break;
2072 case RTA_IFP:
2073 save_string(&rcip->ri_ifp_str, s);
2074 return (B_TRUE);
2075 /*
2076 * RTA_SRC has overloaded meaning. It can represent the
2077 * src address of incoming or outgoing packets.
2078 */
2079 case RTA_IFA:
2080 su = &rcip->ri_ifa;
2081 su->sa.sa_family = rcip->ri_af;
2082 break;
2083 case RTA_SRC:
2084 su = &rcip->ri_src;
2085 su->sa.sa_family = rcip->ri_af;
2086 break;
2087 default:
2088 /* NOTREACHED */
2089 quit(gettext("Internal Error"), EINVAL);
2090 /* NOTREACHED */
2091 }
2092 if (strcmp(s, "default") == 0) {
2093 if (which == RTA_DST) {
2094 return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET));
2095 }
2096 if (which == RTA_SRC) {
2097 return (B_TRUE);
2098 }
2099 return (B_TRUE);
2100 }
2101 switch (rcip->ri_af) {
2102 case AF_LINK:
2103 link_addr(s, &su->sdl);
2104 return (B_TRUE);
2105 case PF_ROUTE:
2106 sockaddr(s, &su->sa);
2107 return (B_TRUE);
2108 case AF_INET6:
2109 switch (which) {
2110 case RTA_DST:
2111 if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) {
2112 return (B_FALSE);
2113 }
2114 if (masklen != NO_PREFIX) {
2115 (void) memset(&rcip->ri_mask.sin6.sin6_addr, 0,
2116 sizeof (rcip->ri_mask.sin6.sin6_addr));
2117 if (!in_prefixlentomask(masklen, IPV6_ABITS,
2118 (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) {
2119 syntax_error(gettext(
2120 "route: bad prefix length: %d\n"),
2121 masklen);
2122 return (B_FALSE);
2123 }
2124 rcip->ri_mask.sin6.sin6_family = rcip->ri_af;
2125 rcip->ri_addrs |= RTA_NETMASK;
2126 }
2127 return (B_TRUE);
2128 case RTA_GATEWAY:
2129 case RTA_IFA:
2130 case RTA_SRC:
2131 return (in6_getaddr(s, &su->sin6, NULL, hpp));
2132 case RTA_NETMASK:
2133 syntax_error(
2134 gettext("route: -netmask not supported for IPv6: "
2135 "use <prefix>/<prefix-length> instead\n"));
2136 return (B_FALSE);
2137 default:
2138 quit(gettext("Internal Error"), EINVAL);
2139 /* NOTREACHED */
2140 }
2141 case AF_INET:
2142 switch (which) {
2143 case RTA_DST:
2144 if (!in_getaddr(s, &su->sin, &masklen, which, hpp,
2145 atype, rcip)) {
2146 return (B_FALSE);
2147 }
2148 if (masklen != NO_PREFIX) {
2149 (void) memset(&rcip->ri_mask.sin.sin_addr, 0,
2150 sizeof (rcip->ri_mask.sin.sin_addr));
2151 if (!in_prefixlentomask(masklen, IP_ABITS,
2152 (uchar_t *)&rcip->ri_mask.sin.sin_addr)) {
2153 syntax_error(gettext(
2154 "route: bad prefix length: %d\n"),
2155 masklen);
2156 return (B_FALSE);
2157 }
2158 rcip->ri_mask.sin.sin_family = rcip->ri_af;
2159 rcip->ri_addrs |= RTA_NETMASK;
2160 }
2161 return (B_TRUE);
2162 case RTA_GATEWAY:
2163 case RTA_IFA:
2164 case RTA_NETMASK:
2165 case RTA_SRC:
2166 return (in_getaddr(s, &su->sin, NULL, which, hpp, atype,
2167 rcip));
2168 default:
2169 quit(gettext("Internal Error"), EINVAL);
2170 /* NOTREACHED */
2171 }
2172 default:
2173 quit(gettext("Internal Error"), EINVAL);
2174 /* NOTREACHED */
2175 }
2176 return (B_TRUE);
2177 }
2178
2179 /*
2180 * Interpret an argument as an IPv4 network address of some kind,
2181 * returning B_TRUE on success or B_FALSE on failure.
2182 * This function will cause an exit() on failure if exit_on_failure is set.
2183 *
2184 * Note that this tries host interpretation before network interpretation,
2185 * except when -net has been given and the destination address is being parsed.
2186 *
2187 * If the plenp argument is non-NULL, allow <addr>/<n> syntax and
2188 * pass out <n> in *plenp.
2189 * If <n> doesn't parse return BAD_ADDR as *plenp.
2190 * If no /<n> is present return NO_PREFIX as *plenp.
2191 */
2192 static boolean_t
in_getaddr(char * s,struct sockaddr_in * sin,int * plenp,int which,struct hostent ** hpp,addr_type_t atype,rtcmd_irep_t * rcip)2193 in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which,
2194 struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip)
2195 {
2196 struct hostent *hp;
2197 struct netent *np;
2198 in_addr_t val;
2199 char str[BUFSIZ];
2200
2201 (void) strlcpy(str, s, sizeof (str));
2202
2203 /*
2204 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2205 */
2206 if (plenp != NULL) {
2207 char *cp;
2208
2209 *plenp = in_getprefixlen(str, IP_ABITS);
2210 if (*plenp == BAD_ADDR)
2211 return (B_FALSE);
2212 cp = strchr(str, '/');
2213 if (cp != NULL)
2214 *cp = '\0';
2215 } else if (strchr(str, '/') != NULL) {
2216 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2217 return (B_FALSE);
2218 }
2219
2220 (void) memset(sin, 0, sizeof (*sin));
2221 sin->sin_family = AF_INET;
2222
2223 /*
2224 * Handle 255.255.255.255 as a special case first.
2225 */
2226 if (strcmp(str, "255.255.255.255") == 0) {
2227 sin->sin_addr.s_addr = INADDR_BROADCAST;
2228 return (B_TRUE);
2229 }
2230
2231 val = inet_addr(str);
2232 if (val != (in_addr_t)-1) {
2233 /* Numeric address */
2234 sin->sin_addr.s_addr = val;
2235 if (which == RTA_DST) {
2236 if (atype == ADDR_TYPE_NET ||
2237 (atype == ADDR_TYPE_ANY &&
2238 inet_lnaof(sin->sin_addr) == INADDR_ANY)) {
2239 /* This looks like a network address. */
2240 inet_makenetandmask(rcip, ntohl(val),
2241 sin);
2242 }
2243 }
2244 return (B_TRUE);
2245 }
2246 /* Host or net name */
2247 if (which != RTA_DST || atype != ADDR_TYPE_NET) {
2248 /* A host name is allowed. */
2249 if ((hp = gethostbyname(str)) != NULL) {
2250 *hpp = hp;
2251 (void) memmove(&sin->sin_addr, hp->h_addr,
2252 hp->h_length);
2253 return (B_TRUE);
2254 }
2255 }
2256 if (atype != ADDR_TYPE_HOST) {
2257 /* A network name is allowed */
2258 if ((np = getnetbyname(str)) != NULL &&
2259 (val = np->n_net) != 0) {
2260 if (which == RTA_DST) {
2261 inet_makenetandmask(rcip, val, sin);
2262 }
2263 return (B_TRUE);
2264 }
2265 }
2266 syntax_error(gettext("%s: bad value\n"), s);
2267 return (B_FALSE);
2268 }
2269
2270 /*
2271 * Interpret an argument as an IPv6 network address of some kind,
2272 * returning B_TRUE on success or B_FALSE on failure.
2273 * This function will cause an exit() on failure if exit_on_failure is set.
2274 *
2275 * If the last argument is non-NULL allow a <addr>/<n> syntax and
2276 * pass out <n> in *plenp.
2277 * If <n> doesn't parse return BAD_ADDR as *plenp.
2278 * If no /<n> is present return NO_PREFIX as *plenp.
2279 */
2280 static boolean_t
in6_getaddr(char * s,struct sockaddr_in6 * sin6,int * plenp,struct hostent ** hpp)2281 in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp,
2282 struct hostent **hpp)
2283 {
2284 struct hostent *hp;
2285 char str[BUFSIZ];
2286 int error_num;
2287
2288 (void) strlcpy(str, s, sizeof (str));
2289
2290 /*
2291 * If plenp is non-NULL, /<n> syntax for netmask is allowed.
2292 */
2293 if (plenp != NULL) {
2294 char *cp;
2295
2296 *plenp = in_getprefixlen(str, IPV6_ABITS);
2297 if (*plenp == BAD_ADDR)
2298 return (B_FALSE);
2299 cp = strchr(str, '/');
2300 if (cp != NULL)
2301 *cp = '\0';
2302 } else if (strchr(str, '/') != NULL) {
2303 syntax_error(gettext("route: %s: unexpected '/'\n"), str);
2304 return (B_FALSE);
2305 }
2306
2307 (void) memset(sin6, 0, sizeof (struct sockaddr_in6));
2308 sin6->sin6_family = AF_INET6;
2309
2310 hp = getipnodebyname(str, AF_INET6, 0, &error_num);
2311 if (hp != NULL) {
2312 *hpp = hp;
2313 (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length);
2314 return (B_TRUE);
2315 }
2316 if (error_num == TRY_AGAIN) {
2317 /*
2318 * This isn't a problem if we aren't going to use the address
2319 * right away.
2320 */
2321 if (!exit_on_error) {
2322 return (B_TRUE);
2323 }
2324 syntax_error(gettext("route: %s: bad address (try "
2325 "again later)\n"), s);
2326 return (B_FALSE);
2327 }
2328 syntax_error(gettext("route: %s: bad address\n"), s);
2329 return (B_FALSE);
2330 }
2331
2332 /*
2333 * Parse <addr>/<n> syntax and return the integer n.
2334 * If <addr> is missing or <n> is not a valid integer, this function calls
2335 * syntax_error() and returns BAD_ADDR.
2336 * if n is not between 0 and max_plen inclusive, this functions calls
2337 * syntax_error() and returns BAD_ADDR.
2338 * If /<n> is not present, this function returns NO_PREFIX.
2339 * The string addr is not modified.
2340 */
2341 int
in_getprefixlen(char * addr,int max_plen)2342 in_getprefixlen(char *addr, int max_plen)
2343 {
2344 int prefixlen;
2345 char *str, *end;
2346
2347 str = strchr(addr, '/');
2348 if (str == addr) {
2349 syntax_error(gettext("route: %s: unexpected '/'\n"), addr);
2350 return (BAD_ADDR);
2351 }
2352 if (str == NULL)
2353 return (NO_PREFIX);
2354 str++;
2355
2356 errno = 0;
2357 prefixlen = strtoul(str, &end, 10);
2358 if (errno != 0 || str == end) {
2359 syntax_error(gettext("route: bad prefix length %s\n"), str);
2360 return (BAD_ADDR);
2361 }
2362 if (prefixlen > max_plen) {
2363 syntax_error(gettext("route: prefix length %s out of range\n"),
2364 str);
2365 return (BAD_ADDR);
2366 }
2367 return (prefixlen);
2368 }
2369
2370 /*
2371 * Convert a prefix length to a mask.
2372 * Returns B_TRUE if ok. B_FALSE otherwise.
2373 * Assumes the mask array is zeroed by the caller.
2374 */
2375 boolean_t
in_prefixlentomask(int prefixlen,int maxlen,uchar_t * mask)2376 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
2377 {
2378 if (prefixlen < 0 || prefixlen > maxlen)
2379 return (B_FALSE);
2380
2381 while (prefixlen > 0) {
2382 if (prefixlen >= 8) {
2383 *mask++ = 0xFF;
2384 prefixlen -= 8;
2385 continue;
2386 }
2387 *mask |= 1 << (8 - prefixlen);
2388 prefixlen--;
2389 }
2390 return (B_TRUE);
2391 }
2392
2393 void
rtmonitor(int argc,char * argv[])2394 rtmonitor(int argc, char *argv[])
2395 {
2396 int n;
2397 intmax_t msg[2048 / sizeof (intmax_t)];
2398
2399 if (tflag)
2400 exit(0);
2401 verbose = B_TRUE;
2402 if (argc > 1) {
2403 argv++;
2404 if (argc == 2 && **argv == '-') {
2405 switch (keyword(*argv + 1)) {
2406 case K_INET:
2407 af = AF_INET;
2408 break;
2409 case K_LINK:
2410 af = AF_LINK;
2411 break;
2412 case K_INET6:
2413 af = AF_INET6;
2414 break;
2415 default:
2416 usage(*argv);
2417 /* NOTREACHED */
2418 }
2419 } else {
2420 usage(*argv);
2421 }
2422 (void) close(s);
2423 s = socket(PF_ROUTE, SOCK_RAW, af);
2424 if (s < 0)
2425 quit("socket", errno);
2426 }
2427 for (;;) {
2428 n = read(s, msg, sizeof (msg));
2429 if (n <= 0)
2430 quit("read", errno);
2431 (void) printf("got message of size %d\n", n);
2432 print_rtmsg((struct rt_msghdr *)msg, n);
2433 }
2434 }
2435
2436 int
rtmsg(rtcmd_irep_t * newrt)2437 rtmsg(rtcmd_irep_t *newrt)
2438 {
2439 static int seq;
2440 int rlen;
2441 char *cp = m_rtmsg.m_space;
2442 int l;
2443
2444 errno = 0;
2445 (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg));
2446
2447 if (newrt->ri_cmd == RTM_GET) {
2448 newrt->ri_ifp.sa.sa_family = AF_LINK;
2449 newrt->ri_addrs |= RTA_IFP;
2450 }
2451
2452 #define rtm m_rtmsg.m_rtm
2453 rtm.rtm_type = newrt->ri_cmd;
2454 rtm.rtm_flags = newrt->ri_flags;
2455 rtm.rtm_version = RTM_VERSION;
2456 rtm.rtm_seq = ++seq;
2457 rtm.rtm_addrs = newrt->ri_addrs;
2458 rtm.rtm_rmx = newrt->ri_metrics;
2459 rtm.rtm_inits = newrt->ri_inits;
2460
2461 #define NEXTADDR(w, u) \
2462 if (newrt->ri_addrs & (w)) { \
2463 l = ROUNDUP_LONG(salen(&u.sa)); \
2464 (void) memmove(cp, &(u), l); \
2465 cp += l; \
2466 if (verbose) \
2467 sodump(&(u), #u); \
2468 }
2469 NEXTADDR(RTA_DST, newrt->ri_dst);
2470 NEXTADDR(RTA_GATEWAY, newrt->ri_gate);
2471 NEXTADDR(RTA_NETMASK, newrt->ri_mask);
2472 NEXTADDR(RTA_IFP, newrt->ri_ifp);
2473 NEXTADDR(RTA_IFA, newrt->ri_ifa);
2474 /*
2475 * RTA_SRC has overloaded meaning. It can represent the
2476 * src address of incoming or outgoing packets.
2477 */
2478 NEXTADDR(RTA_SRC, newrt->ri_src);
2479 #undef NEXTADDR
2480
2481 if (newrt->ri_rtsa_cnt > 0) {
2482 /* LINTED: aligned */
2483 rtm_ext_t *rtm_ext = (rtm_ext_t *)cp;
2484 tsol_rtsecattr_t *rtsecattr;
2485
2486 rtm_ext->rtmex_type = RTMEX_GATEWAY_SECATTR;
2487 rtm_ext->rtmex_len = TSOL_RTSECATTR_SIZE(1);
2488
2489 rtsecattr = (tsol_rtsecattr_t *)(rtm_ext + 1);
2490 rtsecattr->rtsa_cnt = 1;
2491
2492 bcopy(&newrt->ri_rtsa, rtsecattr->rtsa_attr,
2493 sizeof (newrt->ri_rtsa));
2494 cp = (char *)(rtsecattr->rtsa_attr + 1);
2495 }
2496
2497 rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
2498
2499 if (verbose)
2500 print_rtmsg(&rtm, l);
2501 if (debugonly)
2502 return (0);
2503 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
2504 switch (errno) {
2505 case ESRCH:
2506 case EBUSY:
2507 case ENOBUFS:
2508 case EEXIST:
2509 case ENETUNREACH:
2510 case EHOSTUNREACH:
2511 case EPERM:
2512 break;
2513 default:
2514 perror(gettext("writing to routing socket"));
2515 break;
2516 }
2517 return (-1);
2518 } else if (rlen < (int)rtm.rtm_msglen) {
2519 (void) fprintf(stderr,
2520 gettext("route: write to routing socket got only %d for "
2521 "len\n"), rlen);
2522 return (-1);
2523 }
2524 if (newrt->ri_cmd == RTM_GET) {
2525 do {
2526 l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg));
2527 } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
2528 if (l < 0) {
2529 (void) fprintf(stderr,
2530 gettext("route: read from routing socket: %s\n"),
2531 strerror(errno));
2532 } else {
2533 print_getmsg(newrt, &rtm, l);
2534 }
2535 }
2536 #undef rtm
2537 return (0);
2538 }
2539
2540 static char *msgtypes[] = {
2541 "",
2542 "RTM_ADD: Add Route",
2543 "RTM_DELETE: Delete Route",
2544 "RTM_CHANGE: Change Metrics or flags",
2545 "RTM_GET: Report Metrics",
2546 "RTM_LOSING: Kernel Suspects Partitioning",
2547 "RTM_REDIRECT: Told to use different route",
2548 "RTM_MISS: Lookup failed on this address",
2549 "RTM_LOCK: fix specified metrics",
2550 "RTM_OLDADD: caused by SIOCADDRT",
2551 "RTM_OLDDEL: caused by SIOCDELRT",
2552 "RTM_RESOLVE: Route created by cloning",
2553 "RTM_NEWADDR: address being brought up on iface",
2554 "RTM_DELADDR: address being brought down on iface",
2555 "RTM_IFINFO: iface status change",
2556 "RTM_CHGADDR: address being changed on iface",
2557 "RTM_FREEADDR: address being removed from iface",
2558 0,
2559 };
2560
2561 #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0]))
2562
2563 static char metricnames[] =
2564 "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
2565 "\1mtu";
2566 static char routeflags[] =
2567 "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
2568 "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"
2569 "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"
2570 "\024KERNEL\025ZONE";
2571 static char ifnetflags[] =
2572 "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP"
2573 "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST"
2574 "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE"
2575 "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF"
2576 "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6"
2577 "\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE"
2578 "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY\045FIXEDMTU\046VIRTUAL"
2579 "\047DUPLICATE";
2580 static char addrnames[] =
2581 "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC";
2582
2583 void
print_rtmsg(struct rt_msghdr * rtm,int msglen)2584 print_rtmsg(struct rt_msghdr *rtm, int msglen)
2585 {
2586 struct if_msghdr *ifm;
2587 struct ifa_msghdr *ifam;
2588
2589 if (!verbose)
2590 return;
2591 if (rtm->rtm_version != RTM_VERSION) {
2592 (void) printf("routing message version %d not understood\n",
2593 rtm->rtm_version);
2594 return;
2595 }
2596 if (rtm->rtm_msglen != msglen) {
2597 (void) printf("message length mismatch, in packet %d, "
2598 "returned %d\n",
2599 rtm->rtm_msglen, msglen);
2600 if (msglen > rtm->rtm_msglen)
2601 msglen = rtm->rtm_msglen;
2602 }
2603 /*
2604 * Since rtm->rtm_type is unsigned, we'll just check the case of zero
2605 * and the upper-bound of (NMSGTYPES - 1).
2606 */
2607 if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) {
2608 (void) printf("routing message type %d not understood\n",
2609 rtm->rtm_type);
2610 return;
2611 }
2612 (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen);
2613 switch (rtm->rtm_type) {
2614 case RTM_IFINFO:
2615 ifm = (struct if_msghdr *)rtm;
2616 (void) printf("if# %d, flags:", ifm->ifm_index);
2617 bprintf(stdout, ifm->ifm_flags, ifnetflags);
2618 pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm),
2619 ifm->ifm_addrs);
2620 break;
2621 case RTM_NEWADDR:
2622 case RTM_DELADDR:
2623 case RTM_CHGADDR:
2624 case RTM_FREEADDR:
2625 ifam = (struct ifa_msghdr *)rtm;
2626 (void) printf("metric %d, flags:", ifam->ifam_metric);
2627 bprintf(stdout, ifam->ifam_flags, routeflags);
2628 pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam),
2629 ifam->ifam_addrs);
2630 break;
2631 default:
2632 (void) printf("pid: %ld, seq %d, errno %d, flags:",
2633 rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
2634 bprintf(stdout, rtm->rtm_flags, routeflags);
2635 pmsg_common(rtm, msglen);
2636 break;
2637 }
2638 }
2639
2640 void
print_getmsg(rtcmd_irep_t * req_rt,struct rt_msghdr * rtm,int msglen)2641 print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen)
2642 {
2643 struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
2644 struct sockaddr_dl *ifp = NULL;
2645 struct sockaddr *sa;
2646 char *cp;
2647 int i;
2648
2649 (void) printf(" route to: %s\n", routename(&req_rt->ri_dst.sa));
2650 if (rtm->rtm_version != RTM_VERSION) {
2651 (void) fprintf(stderr,
2652 gettext("routing message version %d not understood\n"),
2653 rtm->rtm_version);
2654 return;
2655 }
2656 if (rtm->rtm_msglen > (ushort_t)msglen) {
2657 (void) fprintf(stderr,
2658 gettext("message length mismatch, in packet %d, "
2659 "returned %d\n"), rtm->rtm_msglen, msglen);
2660 }
2661 if (rtm->rtm_errno) {
2662 (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n",
2663 strerror(rtm->rtm_errno), rtm->rtm_errno);
2664 return;
2665 }
2666 cp = ((char *)(rtm + 1));
2667 if (rtm->rtm_addrs != 0) {
2668 for (i = 1; i != 0; i <<= 1) {
2669 if (i & rtm->rtm_addrs) {
2670 /* LINTED */
2671 sa = (struct sockaddr *)cp;
2672 switch (i) {
2673 case RTA_DST:
2674 dst = sa;
2675 break;
2676 case RTA_GATEWAY:
2677 gate = sa;
2678 break;
2679 case RTA_NETMASK:
2680 mask = sa;
2681 break;
2682 case RTA_IFP:
2683 if (sa->sa_family == AF_LINK &&
2684 ((struct sockaddr_dl *)sa)->
2685 sdl_nlen != 0)
2686 ifp = (struct sockaddr_dl *)sa;
2687 break;
2688 case RTA_SRC:
2689 src = sa;
2690 break;
2691 }
2692 ADVANCE(cp, sa);
2693 }
2694 }
2695 }
2696 if (dst != NULL && mask != NULL)
2697 mask->sa_family = dst->sa_family; /* XXX */
2698 if (dst != NULL)
2699 (void) printf("destination: %s\n", routename(dst));
2700 if (mask != NULL) {
2701 boolean_t savenflag = nflag;
2702
2703 nflag = B_TRUE;
2704 (void) printf(" mask: %s\n", routename(mask));
2705 nflag = savenflag;
2706 }
2707 if (gate != NULL && rtm->rtm_flags & RTF_GATEWAY)
2708 (void) printf(" gateway: %s\n", routename(gate));
2709 if (src != NULL && rtm->rtm_flags & RTF_SETSRC)
2710 (void) printf(" setsrc: %s\n", routename(src));
2711 if (ifp != NULL) {
2712 if (verbose) {
2713 int i;
2714
2715 (void) printf(" interface: %.*s index %d address ",
2716 ifp->sdl_nlen, ifp->sdl_data, ifp->sdl_index);
2717 for (i = ifp->sdl_nlen;
2718 i < ifp->sdl_nlen + ifp->sdl_alen;
2719 i++) {
2720 (void) printf("%02x ",
2721 ifp->sdl_data[i] & 0xFF);
2722 }
2723 (void) printf("\n");
2724 } else {
2725 (void) printf(" interface: %.*s\n",
2726 ifp->sdl_nlen, ifp->sdl_data);
2727 }
2728 }
2729 (void) printf(" flags: ");
2730 bprintf(stdout, rtm->rtm_flags, routeflags);
2731
2732 #define lock(f) ((rtm->rtm_rmx.rmx_locks & RTV_ ## f) ? 'L' : ' ')
2733 #define msec(u) (((u) + 500) / 1000) /* usec to msec */
2734
2735 (void) printf("\n%s\n", " recvpipe sendpipe ssthresh rtt,ms "
2736 "rttvar,ms hopcount mtu expire");
2737 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
2738 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
2739 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
2740 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
2741 (void) printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
2742 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
2743 (void) printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
2744 if (rtm->rtm_rmx.rmx_expire)
2745 rtm->rtm_rmx.rmx_expire -= time(0);
2746 (void) printf("%8d%c", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
2747 #undef lock
2748 #undef msec
2749 #define RTA_IGN \
2750 (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD|RTA_SRC)
2751 if (verbose) {
2752 pmsg_common(rtm, msglen);
2753 } else {
2754 const char *sptr, *endptr;
2755 const struct sockaddr *sa;
2756 uint_t addrs;
2757
2758 /* Not verbose; just print out the exceptional cases */
2759 if (rtm->rtm_addrs &~ RTA_IGN) {
2760 (void) printf("\nsockaddrs: ");
2761 bprintf(stdout, rtm->rtm_addrs, addrnames);
2762 }
2763 sptr = (const char *)(rtm + 1);
2764 endptr = (const char *)rtm + msglen;
2765 addrs = rtm->rtm_addrs;
2766 while (addrs != 0 && sptr + sizeof (*sa) <= endptr) {
2767 addrs &= addrs - 1;
2768 /* LINTED */
2769 sa = (const struct sockaddr *)sptr;
2770 ADVANCE(sptr, sa);
2771 }
2772 if (addrs == 0)
2773 pmsg_secattr(sptr, endptr - sptr, " secattr: ");
2774 (void) putchar('\n');
2775 }
2776 #undef RTA_IGN
2777 }
2778
2779 static void
pmsg_common(const struct rt_msghdr * rtm,size_t msglen)2780 pmsg_common(const struct rt_msghdr *rtm, size_t msglen)
2781 {
2782 (void) printf("\nlocks: ");
2783 bprintf(stdout, (int)rtm->rtm_rmx.rmx_locks, metricnames);
2784 (void) printf(" inits: ");
2785 bprintf(stdout, (int)rtm->rtm_inits, metricnames);
2786 pmsg_addrs((const char *)(rtm + 1), msglen - sizeof (*rtm),
2787 rtm->rtm_addrs);
2788 }
2789
2790 static void
pmsg_addrs(const char * cp,size_t msglen,uint_t addrs)2791 pmsg_addrs(const char *cp, size_t msglen, uint_t addrs)
2792 {
2793 const struct sockaddr *sa;
2794 const char *maxptr;
2795 int i;
2796
2797 if (addrs != 0) {
2798 (void) printf("\nsockaddrs: ");
2799 bprintf(stdout, addrs, addrnames);
2800 (void) putchar('\n');
2801 maxptr = cp + msglen;
2802 for (i = 1; i != 0 && cp + sizeof (*sa) <= maxptr; i <<= 1) {
2803 if (i & addrs) {
2804 /* LINTED */
2805 sa = (const struct sockaddr *)cp;
2806 (void) printf(" %s", routename(sa));
2807 ADVANCE(cp, sa);
2808 }
2809 }
2810 if (i != 0)
2811 msglen = 0;
2812 else
2813 msglen = maxptr - cp;
2814 }
2815 pmsg_secattr(cp, msglen, "secattr: ");
2816 (void) putchar('\n');
2817 (void) fflush(stdout);
2818 }
2819
2820 void
bprintf(FILE * fp,int b,char * s)2821 bprintf(FILE *fp, int b, char *s)
2822 {
2823 int i;
2824 boolean_t gotsome = B_FALSE;
2825
2826 if (b == 0)
2827 return;
2828 while ((i = *s++) != 0) {
2829 if (b & (1 << (i - 1))) {
2830 if (!gotsome)
2831 i = '<';
2832 else
2833 i = ',';
2834 (void) putc(i, fp);
2835 gotsome = B_TRUE;
2836 for (; (i = *s) > ' '; s++)
2837 (void) putc(i, fp);
2838 } else {
2839 while (*s > ' ')
2840 s++;
2841 }
2842 }
2843 if (gotsome)
2844 (void) putc('>', fp);
2845 }
2846
2847 int
keyword(const char * cp)2848 keyword(const char *cp)
2849 {
2850 struct keytab *kt = keywords;
2851
2852 while (kt->kt_cp && strcmp(kt->kt_cp, cp))
2853 kt++;
2854 return (kt->kt_i);
2855 }
2856
2857 void
sodump(su_t * su,char * which)2858 sodump(su_t *su, char *which)
2859 {
2860 static char obuf[INET6_ADDRSTRLEN];
2861
2862 switch (su->sa.sa_family) {
2863 case AF_LINK:
2864 (void) printf("%s: link %s; ",
2865 which, link_ntoa(&su->sdl));
2866 break;
2867 case AF_INET:
2868 (void) printf("%s: inet %s; ",
2869 which, inet_ntoa(su->sin.sin_addr));
2870 break;
2871 case AF_INET6:
2872 if (inet_ntop(AF_INET6, (void *)&su->sin6.sin6_addr, obuf,
2873 INET6_ADDRSTRLEN) != NULL) {
2874 (void) printf("%s: inet6 %s; ", which, obuf);
2875 break;
2876 }
2877 /* FALLTHROUGH */
2878 default:
2879 quit(gettext("Internal Error"), EINVAL);
2880 /* NOTREACHED */
2881 }
2882 (void) fflush(stdout);
2883 }
2884
2885 /* States */
2886 #define VIRGIN 0
2887 #define GOTONE 1
2888 #define GOTTWO 2
2889 #define RESET 3
2890 /* Inputs */
2891 #define DIGIT (4*0)
2892 #define END (4*1)
2893 #define DELIM (4*2)
2894 #define LETTER (4*3)
2895
2896 void
sockaddr(char * addr,struct sockaddr * sa)2897 sockaddr(char *addr, struct sockaddr *sa)
2898 {
2899 char *cp = (char *)sa;
2900 int size = salen(sa);
2901 char *cplim = cp + size;
2902 int byte = 0, state = VIRGIN, new;
2903
2904 (void) memset(cp, 0, size);
2905 cp++;
2906 do {
2907 if ((*addr >= '0') && (*addr <= '9')) {
2908 new = *addr - '0';
2909 } else if ((*addr >= 'a') && (*addr <= 'f')) {
2910 new = *addr - 'a' + 10;
2911 } else if ((*addr >= 'A') && (*addr <= 'F')) {
2912 new = *addr - 'A' + 10;
2913 } else if (*addr == 0) {
2914 state |= END;
2915 } else {
2916 state |= DELIM;
2917 }
2918 addr++;
2919 switch (state /* | INPUT */) {
2920 case GOTTWO | DIGIT:
2921 *cp++ = byte;
2922 /* FALLTHROUGH */
2923 case VIRGIN | DIGIT:
2924 state = GOTONE; byte = new; continue;
2925 case GOTONE | DIGIT:
2926 state = GOTTWO; byte = new + (byte << 4); continue;
2927 default: /* | DELIM */
2928 state = VIRGIN; *cp++ = byte; byte = 0; continue;
2929 case GOTONE | END:
2930 case GOTTWO | END:
2931 *cp++ = byte;
2932 /* FALLTHROUGH */
2933 case VIRGIN | END:
2934 break;
2935 }
2936 break;
2937 } while (cp < cplim);
2938 }
2939
2940 int
salen(const struct sockaddr * sa)2941 salen(const struct sockaddr *sa)
2942 {
2943 switch (sa->sa_family) {
2944 case AF_INET:
2945 return (sizeof (struct sockaddr_in));
2946 case AF_LINK:
2947 return (sizeof (struct sockaddr_dl));
2948 case AF_INET6:
2949 return (sizeof (struct sockaddr_in6));
2950 default:
2951 return (sizeof (struct sockaddr));
2952 }
2953 }
2954
2955 void
link_addr(const char * addr,struct sockaddr_dl * sdl)2956 link_addr(const char *addr, struct sockaddr_dl *sdl)
2957 {
2958 char *cp = sdl->sdl_data;
2959 char *cplim = sizeof (struct sockaddr_dl) + (char *)sdl;
2960 int byte = 0, state = VIRGIN, new;
2961
2962 (void) memset(sdl, 0, sizeof (struct sockaddr_dl));
2963 sdl->sdl_family = AF_LINK;
2964 do {
2965 state &= ~LETTER;
2966 if ((*addr >= '0') && (*addr <= '9')) {
2967 new = *addr - '0';
2968 } else if ((*addr >= 'a') && (*addr <= 'f')) {
2969 new = *addr - 'a' + 10;
2970 } else if ((*addr >= 'A') && (*addr <= 'F')) {
2971 new = *addr - 'A' + 10;
2972 } else if (*addr == 0) {
2973 state |= END;
2974 } else if (state == VIRGIN &&
2975 (((*addr >= 'A') && (*addr <= 'Z')) ||
2976 ((*addr >= 'a') && (*addr <= 'z')))) {
2977 state |= LETTER;
2978 } else {
2979 state |= DELIM;
2980 }
2981 addr++;
2982 switch (state /* | INPUT */) {
2983 case VIRGIN | DIGIT:
2984 case VIRGIN | LETTER:
2985 *cp++ = addr[-1];
2986 continue;
2987 case VIRGIN | DELIM:
2988 state = RESET;
2989 sdl->sdl_nlen = cp - sdl->sdl_data;
2990 continue;
2991 case GOTTWO | DIGIT:
2992 *cp++ = byte;
2993 /* FALLTHROUGH */
2994 case RESET | DIGIT:
2995 state = GOTONE;
2996 byte = new;
2997 continue;
2998 case GOTONE | DIGIT:
2999 state = GOTTWO;
3000 byte = new + (byte << 4);
3001 continue;
3002 default: /* | DELIM */
3003 state = RESET;
3004 *cp++ = byte;
3005 byte = 0;
3006 continue;
3007 case GOTONE | END:
3008 case GOTTWO | END:
3009 *cp++ = byte;
3010 /* FALLTHROUGH */
3011 case RESET | END:
3012 break;
3013 }
3014 break;
3015 } while (cp < cplim);
3016 sdl->sdl_alen = cp - LLADDR(sdl);
3017 }
3018
3019 static char hexlist[] = "0123456789abcdef";
3020
3021 char *
link_ntoa(const struct sockaddr_dl * sdl)3022 link_ntoa(const struct sockaddr_dl *sdl)
3023 {
3024 static char obuf[64];
3025 char *out = obuf;
3026 int i;
3027 uchar_t *in = (uchar_t *)LLADDR(sdl);
3028 uchar_t *inlim = in + sdl->sdl_alen;
3029 boolean_t firsttime = B_TRUE;
3030
3031 if (sdl->sdl_nlen) {
3032 (void) memcpy(obuf, sdl->sdl_data, sdl->sdl_nlen);
3033 out += sdl->sdl_nlen;
3034 if (sdl->sdl_alen)
3035 *out++ = ':';
3036 }
3037 while (in < inlim) {
3038 if (firsttime)
3039 firsttime = B_FALSE;
3040 else
3041 *out++ = '.';
3042 i = *in++;
3043 if (i > 0xf) {
3044 out[1] = hexlist[i & 0xf];
3045 i >>= 4;
3046 out[0] = hexlist[i];
3047 out += 2;
3048 } else {
3049 *out++ = hexlist[i];
3050 }
3051 }
3052 *out = 0;
3053 return (obuf);
3054 }
3055
3056 static mib_item_t *
mibget(int sd)3057 mibget(int sd)
3058 {
3059 intmax_t buf[512 / sizeof (intmax_t)];
3060 int flags;
3061 int i, j, getcode;
3062 struct strbuf ctlbuf, databuf;
3063 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;
3064 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;
3065 struct T_error_ack *tea = (struct T_error_ack *)buf;
3066 struct opthdr *req;
3067 mib_item_t *first_item = NULL;
3068 mib_item_t *last_item = NULL;
3069 mib_item_t *temp;
3070
3071 tor->PRIM_type = T_SVR4_OPTMGMT_REQ;
3072 tor->OPT_offset = sizeof (struct T_optmgmt_req);
3073 tor->OPT_length = sizeof (struct opthdr);
3074 tor->MGMT_flags = T_CURRENT;
3075 req = (struct opthdr *)&tor[1];
3076 req->level = MIB2_IP; /* any MIB2_xxx value ok here */
3077 req->name = 0;
3078 req->len = 0;
3079
3080 ctlbuf.buf = (char *)buf;
3081 ctlbuf.len = tor->OPT_length + tor->OPT_offset;
3082 flags = 0;
3083 if (putmsg(sd, &ctlbuf, NULL, flags) < 0) {
3084 perror("mibget: putmsg (ctl)");
3085 return (NULL);
3086 }
3087 /*
3088 * each reply consists of a ctl part for one fixed structure
3089 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
3090 * containing an opthdr structure. level/name identify the entry,
3091 * len is the size of the data part of the message.
3092 */
3093 req = (struct opthdr *)&toa[1];
3094 ctlbuf.maxlen = sizeof (buf);
3095 for (j = 1; ; j++) {
3096 flags = 0;
3097 getcode = getmsg(sd, &ctlbuf, NULL, &flags);
3098 if (getcode < 0) {
3099 perror("mibget: getmsg (ctl)");
3100 if (verbose) {
3101 (void) fprintf(stderr,
3102 "# level name len\n");
3103 i = 0;
3104 for (last_item = first_item; last_item != NULL;
3105 last_item = last_item->next_item) {
3106 (void) printf("%d %4ld %5ld %ld\n",
3107 ++i, last_item->group,
3108 last_item->mib_id,
3109 last_item->length);
3110 }
3111 }
3112 break;
3113 }
3114 if (getcode == 0 &&
3115 ctlbuf.len >= sizeof (struct T_optmgmt_ack) &&
3116 toa->PRIM_type == T_OPTMGMT_ACK &&
3117 toa->MGMT_flags == T_SUCCESS &&
3118 req->len == 0) {
3119 if (verbose) {
3120 (void) printf("mibget getmsg() %d returned EOD "
3121 "(level %lu, name %lu)\n", j, req->level,
3122 req->name);
3123 }
3124 return (first_item); /* this is EOD msg */
3125 }
3126
3127 if (ctlbuf.len >= sizeof (struct T_error_ack) &&
3128 tea->PRIM_type == T_ERROR_ACK) {
3129 (void) fprintf(stderr, gettext("mibget %d gives "
3130 "T_ERROR_ACK: TLI_error = 0x%lx, UNIX_error = "
3131 "0x%lx\n"), j, tea->TLI_error, tea->UNIX_error);
3132 errno = (tea->TLI_error == TSYSERR) ?
3133 tea->UNIX_error : EPROTO;
3134 break;
3135 }
3136
3137 if (getcode != MOREDATA ||
3138 ctlbuf.len < sizeof (struct T_optmgmt_ack) ||
3139 toa->PRIM_type != T_OPTMGMT_ACK ||
3140 toa->MGMT_flags != T_SUCCESS) {
3141 (void) printf("mibget getmsg(ctl) %d returned %d, "
3142 "ctlbuf.len = %d, PRIM_type = %ld\n",
3143 j, getcode, ctlbuf.len, toa->PRIM_type);
3144 if (toa->PRIM_type == T_OPTMGMT_ACK) {
3145 (void) printf("T_OPTMGMT_ACK: "
3146 "MGMT_flags = 0x%lx, req->len = %ld\n",
3147 toa->MGMT_flags, req->len);
3148 }
3149 errno = ENOMSG;
3150 break;
3151 }
3152
3153 temp = malloc(sizeof (mib_item_t));
3154 if (temp == NULL) {
3155 perror("mibget: malloc");
3156 break;
3157 }
3158 if (last_item != NULL)
3159 last_item->next_item = temp;
3160 else
3161 first_item = temp;
3162 last_item = temp;
3163 last_item->next_item = NULL;
3164 last_item->group = req->level;
3165 last_item->mib_id = req->name;
3166 last_item->length = req->len;
3167 last_item->valp = malloc(req->len);
3168 if (verbose) {
3169 (void) printf("msg %d: group = %4ld mib_id = %5ld "
3170 "length = %ld\n",
3171 j, last_item->group, last_item->mib_id,
3172 last_item->length);
3173 }
3174
3175 databuf.maxlen = last_item->length;
3176 databuf.buf = (char *)last_item->valp;
3177 databuf.len = 0;
3178 flags = 0;
3179 getcode = getmsg(sd, NULL, &databuf, &flags);
3180 if (getcode < 0) {
3181 perror("mibget: getmsg (data)");
3182 break;
3183 } else if (getcode != 0) {
3184 (void) printf("mibget getmsg(data) returned %d, "
3185 "databuf.maxlen = %d, databuf.len = %d\n",
3186 getcode, databuf.maxlen, databuf.len);
3187 break;
3188 }
3189 }
3190
3191 /*
3192 * On error, free all the allocated mib_item_t objects.
3193 */
3194 while (first_item != NULL) {
3195 last_item = first_item;
3196 first_item = first_item->next_item;
3197 free(last_item);
3198 }
3199 return (NULL);
3200 }
3201
3202 /*
3203 * print label security attributes for gateways.
3204 */
3205 static void
pmsg_secattr(const char * sptr,size_t msglen,const char * labelstr)3206 pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr)
3207 {
3208 rtm_ext_t rtm_ext;
3209 tsol_rtsecattr_t sp;
3210 struct rtsa_s *rtsa = &sp.rtsa_attr[0];
3211 const char *endptr;
3212 char buf[256];
3213 int i;
3214
3215 if (!is_system_labeled())
3216 return;
3217
3218 endptr = sptr + msglen;
3219
3220 for (;;) {
3221 if (sptr + sizeof (rtm_ext_t) + sizeof (sp) > endptr)
3222 return;
3223
3224 bcopy(sptr, &rtm_ext, sizeof (rtm_ext));
3225 sptr += sizeof (rtm_ext);
3226 if (rtm_ext.rtmex_type == RTMEX_GATEWAY_SECATTR)
3227 break;
3228 sptr += rtm_ext.rtmex_len;
3229 }
3230
3231 /* bail if this entry is corrupt or overruns buffer length */
3232 if (rtm_ext.rtmex_len < sizeof (sp) ||
3233 sptr + rtm_ext.rtmex_len > endptr)
3234 return;
3235
3236 /* run up just to the end of this extension */
3237 endptr = sptr + rtm_ext.rtmex_len;
3238
3239 bcopy(sptr, &sp, sizeof (sp));
3240 sptr += sizeof (sp);
3241
3242 if (sptr + (sp.rtsa_cnt - 1) * sizeof (*rtsa) != endptr)
3243 return;
3244
3245 for (i = 0; i < sp.rtsa_cnt; i++) {
3246 if (i > 0) {
3247 /* first element is part of sp initalized above */
3248 bcopy(sptr, rtsa, sizeof (*rtsa));
3249 sptr += sizeof (*rtsa);
3250 }
3251 (void) printf("\n%s%s", labelstr, rtsa_to_str(rtsa, buf,
3252 sizeof (buf)));
3253 }
3254 }
3255