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