| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| /* Copyright (c) 1990 Mentat Inc. */ |
| |
| /* |
| * |
| * Copyright (c) 1983, 1989, 1991, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by the University of |
| * California, Berkeley and its contributors. |
| * 4. Neither the name of the University nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * @(#)route.c 8.6 (Berkeley) 4/28/95 |
| * @(#)linkaddr.c 8.1 (Berkeley) 6/4/93 |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <sys/param.h> |
| #include <sys/file.h> |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/stream.h> |
| #include <sys/sysmacros.h> |
| #include <sys/tihdr.h> |
| #include <sys/types.h> |
| #include <sys/ccompile.h> |
| |
| #include <net/if.h> |
| #include <net/route.h> |
| #include <net/if_dl.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #include <inet/mib2.h> |
| #include <inet/ip.h> |
| |
| #include <limits.h> |
| #include <locale.h> |
| |
| #include <errno.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <stropts.h> |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| #include <strings.h> |
| |
| #include <libtsnet.h> |
| #include <tsol/label.h> |
| |
| static struct keytab { |
| char *kt_cp; |
| int kt_i; |
| } keywords[] = { |
| #define K_ADD 1 |
| {"add", K_ADD}, |
| #define K_BLACKHOLE 2 |
| {"blackhole", K_BLACKHOLE}, |
| #define K_CHANGE 3 |
| {"change", K_CHANGE}, |
| #define K_CLONING 4 |
| {"cloning", K_CLONING}, |
| #define K_DELETE 5 |
| {"delete", K_DELETE}, |
| #define K_DST 6 |
| {"dst", K_DST}, |
| #define K_EXPIRE 7 |
| {"expire", K_EXPIRE}, |
| #define K_FLUSH 8 |
| {"flush", K_FLUSH}, |
| #define K_GATEWAY 9 |
| {"gateway", K_GATEWAY}, |
| #define K_GET 11 |
| {"get", K_GET}, |
| #define K_HOPCOUNT 12 |
| {"hopcount", K_HOPCOUNT}, |
| #define K_HOST 13 |
| {"host", K_HOST}, |
| #define K_IFA 14 |
| {"ifa", K_IFA}, |
| #define K_IFACE 15 |
| {"iface", K_IFACE}, |
| #define K_IFP 16 |
| {"ifp", K_IFP}, |
| #define K_INET 17 |
| {"inet", K_INET}, |
| #define K_INET6 18 |
| {"inet6", K_INET6}, |
| #define K_INTERFACE 19 |
| {"interface", K_INTERFACE}, |
| #define K_LINK 20 |
| {"link", K_LINK}, |
| #define K_LOCK 21 |
| {"lock", K_LOCK}, |
| #define K_LOCKREST 22 |
| {"lockrest", K_LOCKREST}, |
| #define K_MASK 23 |
| {"mask", K_MASK}, |
| #define K_MONITOR 24 |
| {"monitor", K_MONITOR}, |
| #define K_MTU 25 |
| {"mtu", K_MTU}, |
| #define K_NET 26 |
| {"net", K_NET}, |
| #define K_NETMASK 27 |
| {"netmask", K_NETMASK}, |
| #define K_NOSTATIC 28 |
| {"nostatic", K_NOSTATIC}, |
| #define K_PRIVATE 29 |
| {"private", K_PRIVATE}, |
| #define K_PROTO1 30 |
| {"proto1", K_PROTO1}, |
| #define K_PROTO2 31 |
| {"proto2", K_PROTO2}, |
| #define K_RECVPIPE 32 |
| {"recvpipe", K_RECVPIPE}, |
| #define K_REJECT 33 |
| {"reject", K_REJECT}, |
| #define K_RTT 34 |
| {"rtt", K_RTT}, |
| #define K_RTTVAR 35 |
| {"rttvar", K_RTTVAR}, |
| #define K_SA 36 |
| {"sa", K_SA}, |
| #define K_SENDPIPE 37 |
| {"sendpipe", K_SENDPIPE}, |
| #define K_SSTHRESH 38 |
| {"ssthresh", K_SSTHRESH}, |
| #define K_STATIC 39 |
| {"static", K_STATIC}, |
| #define K_XRESOLVE 40 |
| {"xresolve", K_XRESOLVE}, |
| #define K_MULTIRT 41 |
| {"multirt", K_MULTIRT}, |
| #define K_SETSRC 42 |
| {"setsrc", K_SETSRC}, |
| #define K_SHOW 43 |
| {"show", K_SHOW}, |
| #define K_SECATTR 43 |
| {"secattr", K_SECATTR}, |
| {0, 0} |
| }; |
| |
| /* |
| * Size of buffers used to hold command lines from the saved route file as |
| * well as error strings. |
| */ |
| #define BUF_SIZE 2048 |
| |
| typedef union sockunion { |
| struct sockaddr sa; |
| struct sockaddr_in sin; |
| struct sockaddr_dl sdl; |
| struct sockaddr_in6 sin6; |
| } su_t; |
| |
| /* |
| * This structure represents the digested information from parsing arguments |
| * to route add, change, delete, and get. |
| * |
| */ |
| typedef struct rtcmd_irep { |
| int ri_cmd; |
| int ri_flags; |
| int ri_af; |
| ulong_t ri_inits; |
| struct rt_metrics ri_metrics; |
| int ri_addrs; |
| su_t ri_dst; |
| char *ri_dest_str; |
| su_t ri_src; |
| su_t ri_gate; |
| struct hostent *ri_gate_hp; |
| char *ri_gate_str; |
| su_t ri_mask; |
| su_t ri_ifa; |
| su_t ri_ifp; |
| char *ri_ifp_str; |
| int ri_rtsa_cnt; /* number of gateway security attributes */ |
| struct rtsa_s ri_rtsa; /* enough space for one attribute */ |
| } rtcmd_irep_t; |
| |
| typedef struct mib_item_s { |
| struct mib_item_s *next_item; |
| long group; |
| long mib_id; |
| long length; |
| intmax_t *valp; |
| } mib_item_t; |
| |
| typedef enum { |
| ADDR_TYPE_ANY, |
| ADDR_TYPE_HOST, |
| ADDR_TYPE_NET |
| } addr_type_t; |
| |
| typedef enum { |
| SEARCH_MODE_NULL, |
| SEARCH_MODE_PRINT, |
| SEARCH_MODE_DEL |
| } search_mode_t; |
| |
| static boolean_t args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, |
| char *cmd_string); |
| static void bprintf(FILE *fp, int b, char *s); |
| static boolean_t compare_rtcmd(rtcmd_irep_t *srch_rt, |
| rtcmd_irep_t *file_rt); |
| static void delRouteEntry(mib2_ipRouteEntry_t *rp, |
| mib2_ipv6RouteEntry_t *rp6, int seqno); |
| static void del_rtcmd_irep(rtcmd_irep_t *rcip); |
| static void flushroutes(int argc, char *argv[]); |
| static boolean_t getaddr(rtcmd_irep_t *rcip, int which, char *s, |
| addr_type_t atype); |
| static boolean_t in6_getaddr(char *s, struct sockaddr_in6 *sin6, |
| int *plenp, struct hostent **hpp); |
| static boolean_t in_getaddr(char *s, struct sockaddr_in *sin, |
| int *plenp, int which, struct hostent **hpp, addr_type_t atype, |
| rtcmd_irep_t *rcip); |
| static int in_getprefixlen(char *addr, int max_plen); |
| static boolean_t in_prefixlentomask(int prefixlen, int maxlen, |
| uchar_t *mask); |
| static void inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, |
| struct sockaddr_in *sin); |
| static in_addr_t inet_makesubnetmask(in_addr_t addr, in_addr_t mask); |
| static int keyword(const char *cp); |
| static void link_addr(const char *addr, struct sockaddr_dl *sdl); |
| static char *link_ntoa(const struct sockaddr_dl *sdl); |
| static mib_item_t *mibget(int sd); |
| static char *netname(struct sockaddr *sa); |
| static int newroute(char **argv); |
| static rtcmd_irep_t *new_rtcmd_irep(void); |
| static void pmsg_addrs(const char *cp, size_t len, uint_t addrs); |
| static void pmsg_common(const struct rt_msghdr *rtm, size_t len); |
| static void print_getmsg(rtcmd_irep_t *req_rt, |
| struct rt_msghdr *rtm, int msglen); |
| static void print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, |
| boolean_t gw_good, boolean_t to_saved); |
| static void print_rtmsg(struct rt_msghdr *rtm, int msglen); |
| static void quit(char *s, int err) __NORETURN; |
| static char *routename(const struct sockaddr *sa); |
| static void rtmonitor(int argc, char *argv[]); |
| static int rtmsg(rtcmd_irep_t *rcip); |
| static int salen(const struct sockaddr *sa); |
| static void save_route(int argc, char **argv, int do_flush); |
| static void save_string(char **dst, char *src); |
| static int search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, |
| search_mode_t mode); |
| static void set_metric(rtcmd_irep_t *rcip, char *value, int key, |
| boolean_t lock); |
| static int show_saved_routes(int argc); |
| static void sockaddr(char *addr, struct sockaddr *sa); |
| static void sodump(su_t *su, char *which); |
| static void syntax_arg_missing(char *keyword); |
| static void syntax_bad_keyword(char *keyword); |
| static void syntax_error(char *err, ...); |
| static void usage(char *cp); |
| static void write_to_rtfile(FILE *fp, int argc, char **argv); |
| static void pmsg_secattr(const char *, size_t, const char *); |
| |
| static pid_t pid; |
| static int s; |
| static boolean_t nflag; |
| static int af = AF_INET; |
| static boolean_t qflag, tflag; |
| static boolean_t verbose; |
| static boolean_t debugonly; |
| static boolean_t fflag; |
| static boolean_t update_table; |
| static boolean_t perm_flag; |
| static boolean_t early_v6_keyword; |
| static char perm_file_sfx[] = "/etc/inet/static_routes"; |
| static char *perm_file; |
| static char temp_file_sfx[] = "/etc/inet/static_routes.tmp"; |
| static char *temp_file; |
| static struct in6_addr in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| /* |
| * WARNING: |
| * This next variable indicates whether certain functions exit when an error |
| * is detected in the user input. Currently, exit_on_error is only set false |
| * in search_rtfile(), when argument are being parsed. Only those functions |
| * used by search_rtfile() to parse its arguments are designed to work in |
| * both modes. Take particular care in setting this false to ensure that any |
| * functions you call that might act on this flag properly return errors when |
| * exit_on_error is false. |
| */ |
| static int exit_on_error = B_TRUE; |
| |
| static struct { |
| struct rt_msghdr m_rtm; |
| char m_space[BUF_SIZE]; |
| } m_rtmsg; |
| |
| /* |
| * Sizes of data structures extracted from the base mib. |
| * This allows the size of the tables entries to grow while preserving |
| * binary compatibility. |
| */ |
| static int ipRouteEntrySize; |
| static int ipv6RouteEntrySize; |
| |
| #define ROUNDUP_LONG(a) \ |
| ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long)) |
| #define ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n))) |
| #define C(x) ((x) & 0xff) |
| |
| /* |
| * return values from in_getprefixlen() |
| */ |
| #define BAD_ADDR -1 /* prefix is invalid */ |
| #define NO_PREFIX -2 /* no prefix was found */ |
| |
| void |
| usage(char *cp) |
| { |
| if (cp != NULL) { |
| (void) fprintf(stderr, gettext("route: botched keyword: %s\n"), |
| cp); |
| } |
| (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] " |
| "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n")); |
| exit(1); |
| /* NOTREACHED */ |
| } |
| |
| /*PRINTFLIKE1*/ |
| void |
| syntax_error(char *err, ...) |
| { |
| va_list args; |
| |
| if (exit_on_error) { |
| va_start(args, err); |
| (void) vfprintf(stderr, err, args); |
| va_end(args); |
| exit(1); |
| } |
| /* NOTREACHED */ |
| } |
| |
| void |
| syntax_bad_keyword(char *keyword) |
| { |
| syntax_error(gettext("route: botched keyword: %s\n"), keyword); |
| } |
| |
| void |
| syntax_arg_missing(char *keyword) |
| { |
| syntax_error(gettext("route: argument required following keyword %s\n"), |
| keyword); |
| } |
| |
| void |
| quit(char *s, int sverrno) |
| { |
| (void) fprintf(stderr, "route: "); |
| if (s != NULL) |
| (void) fprintf(stderr, "%s: ", s); |
| (void) fprintf(stderr, "%s\n", strerror(sverrno)); |
| exit(sverrno); |
| /* NOTREACHED */ |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| extern int optind; |
| extern char *optarg; |
| int ch; |
| int rval; |
| size_t size; |
| const char *root_dir = NULL; |
| |
| (void) setlocale(LC_ALL, ""); |
| |
| #if !defined(TEXT_DOMAIN) |
| #define TEXT_DOMAIN "SYS_TEST" |
| #endif |
| (void) textdomain(TEXT_DOMAIN); |
| |
| if (argc < 2) |
| usage(NULL); |
| |
| while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) { |
| switch (ch) { |
| case 'n': |
| nflag = B_TRUE; |
| break; |
| case 'q': |
| qflag = B_TRUE; |
| break; |
| case 'v': |
| verbose = B_TRUE; |
| break; |
| case 't': |
| tflag = B_TRUE; |
| break; |
| case 'd': |
| debugonly = B_TRUE; |
| break; |
| case 'f': |
| fflag = B_TRUE; |
| break; |
| case 'p': |
| perm_flag = B_TRUE; |
| break; |
| case 'R': |
| root_dir = optarg; |
| break; |
| case '?': |
| default: |
| usage(NULL); |
| /* NOTREACHED */ |
| } |
| } |
| argc -= optind; |
| argv += optind; |
| |
| pid = getpid(); |
| if (tflag) |
| s = open("/dev/null", O_WRONLY); |
| else |
| s = socket(PF_ROUTE, SOCK_RAW, 0); |
| if (s < 0) |
| quit("socket", errno); |
| |
| /* |
| * Handle the -p and -R flags. The -R flag only applies |
| * when the -p flag is set. |
| */ |
| if (root_dir == NULL) { |
| perm_file = perm_file_sfx; |
| temp_file = temp_file_sfx; |
| } else { |
| size = strlen(root_dir) + sizeof (perm_file_sfx); |
| perm_file = malloc(size); |
| if (perm_file == NULL) |
| quit("malloc", errno); |
| (void) snprintf(perm_file, size, "%s%s", root_dir, |
| perm_file_sfx); |
| size = strlen(root_dir) + sizeof (temp_file_sfx); |
| temp_file = malloc(size); |
| if (temp_file == NULL) |
| quit("malloc", errno); |
| (void) snprintf(temp_file, size, "%s%s", root_dir, |
| temp_file_sfx); |
| } |
| /* |
| * Whether or not to act on the routing table. The only time the |
| * routing table is not modified is when both -p and -R are present. |
| */ |
| update_table = (!perm_flag || root_dir == NULL); |
| if (tflag) |
| perm_flag = 0; |
| |
| if (fflag) { |
| /* |
| * Accept an address family keyword after the -f. Since the |
| * default address family is AF_INET, reassign af only for the |
| * other valid address families. |
| */ |
| if (*argv != NULL) { |
| switch (keyword(*argv)) { |
| case K_INET6: |
| af = AF_INET6; |
| early_v6_keyword = B_TRUE; |
| /* fallthrough */ |
| case K_INET: |
| /* Skip over the address family parameter. */ |
| argc--; |
| argv++; |
| break; |
| } |
| } |
| flushroutes(0, NULL); |
| } |
| |
| if (*argv != NULL) { |
| switch (keyword(*argv)) { |
| case K_GET: |
| case K_CHANGE: |
| case K_ADD: |
| case K_DELETE: |
| rval = 0; |
| if (update_table) { |
| rval = newroute(argv); |
| } |
| if (perm_flag && (rval == 0 || rval == EEXIST || |
| rval == ESRCH)) { |
| save_route(argc, argv, B_FALSE); |
| return (0); |
| } |
| return (rval); |
| case K_SHOW: |
| if (perm_flag) { |
| return (show_saved_routes(argc)); |
| } else { |
| syntax_error(gettext( |
| "route: show command requires -p\n")); |
| } |
| /* NOTREACHED */ |
| case K_MONITOR: |
| rtmonitor(argc, argv); |
| /* NOTREACHED */ |
| |
| case K_FLUSH: |
| flushroutes(argc, argv); |
| return (0); |
| } |
| } |
| if (!fflag) |
| usage(*argv); |
| return (0); |
| } |
| |
| /* |
| * Purge all entries in the routing tables not |
| * associated with network interfaces. |
| */ |
| void |
| flushroutes(int argc, char *argv[]) |
| { |
| int seqno; |
| int sd; /* mib stream */ |
| mib_item_t *item; |
| mib2_ipRouteEntry_t *rp; |
| mib2_ipv6RouteEntry_t *rp6; |
| int oerrno; |
| int off = 0; |
| int on = 1; |
| |
| if (argc > 1) { |
| argv++; |
| if (argc == 2 && **argv == '-') { |
| /* |
| * The address family (preceded by a dash) may be used |
| * to flush the routes of that particular family. |
| */ |
| switch (keyword(*argv + 1)) { |
| case K_INET: |
| af = AF_INET; |
| break; |
| case K_LINK: |
| af = AF_LINK; |
| break; |
| case K_INET6: |
| af = AF_INET6; |
| break; |
| default: |
| usage(*argv); |
| /* NOTREACHED */ |
| } |
| } else { |
| usage(*argv); |
| } |
| } |
| if (perm_flag) { |
| /* This flushes the persistent route file */ |
| save_route(0, NULL, B_TRUE); |
| } |
| if (!update_table) { |
| return; |
| } |
| |
| if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&off, |
| sizeof (off)) < 0) |
| quit("setsockopt", errno); |
| |
| sd = open("/dev/ip", O_RDWR); |
| oerrno = errno; |
| if (sd < 0) { |
| switch (errno) { |
| case EACCES: |
| (void) fprintf(stderr, |
| gettext("route: flush: insufficient privileges\n")); |
| exit(oerrno); |
| /* NOTREACHED */ |
| default: |
| quit(gettext("can't open mib stream"), oerrno); |
| /* NOTREACHED */ |
| } |
| } |
| if ((item = mibget(sd)) == NULL) |
| quit("mibget", errno); |
| if (verbose) { |
| (void) printf("Examining routing table from " |
| "T_SVR4_OPTMGMT_REQ\n"); |
| } |
| seqno = 0; /* ??? */ |
| switch (af) { |
| case AF_INET: |
| /* Extract ipRouteEntrySize */ |
| for (; item != NULL; item = item->next_item) { |
| if (item->mib_id != 0) |
| continue; |
| if (item->group == MIB2_IP) { |
| ipRouteEntrySize = |
| ((mib2_ip_t *)item->valp)->ipRouteEntrySize; |
| assert(IS_P2ALIGNED(ipRouteEntrySize, |
| sizeof (mib2_ipRouteEntry_t *))); |
| break; |
| } |
| } |
| if (ipRouteEntrySize == 0) { |
| (void) fprintf(stderr, |
| gettext("ipRouteEntrySize can't be determined.\n")); |
| exit(1); |
| } |
| for (; item != NULL; item = item->next_item) { |
| /* |
| * skip all the other trash that comes up the mib stream |
| */ |
| if (item->group != MIB2_IP || |
| item->mib_id != MIB2_IP_ROUTE) |
| continue; |
| for (rp = (mib2_ipRouteEntry_t *)item->valp; |
| (char *)rp < (char *)item->valp + item->length; |
| /* LINTED */ |
| rp = (mib2_ipRouteEntry_t *) |
| ((char *)rp + ipRouteEntrySize)) { |
| delRouteEntry(rp, NULL, seqno); |
| seqno++; |
| } |
| break; |
| } |
| break; |
| case AF_INET6: |
| /* Extract ipv6RouteEntrySize */ |
| for (; item != NULL; item = item->next_item) { |
| if (item->mib_id != 0) |
| continue; |
| if (item->group == MIB2_IP6) { |
| ipv6RouteEntrySize = |
| ((mib2_ipv6IfStatsEntry_t *)item->valp)-> |
| ipv6RouteEntrySize; |
| assert(IS_P2ALIGNED(ipv6RouteEntrySize, |
| sizeof (mib2_ipv6RouteEntry_t *))); |
| break; |
| } |
| } |
| if (ipv6RouteEntrySize == 0) { |
| (void) fprintf(stderr, gettext( |
| "ipv6RouteEntrySize cannot be determined.\n")); |
| exit(1); |
| } |
| for (; item != NULL; item = item->next_item) { |
| /* |
| * skip all the other trash that comes up the mib stream |
| */ |
| if (item->group != MIB2_IP6 || |
| item->mib_id != MIB2_IP6_ROUTE) |
| continue; |
| for (rp6 = (mib2_ipv6RouteEntry_t *)item->valp; |
| (char *)rp6 < (char *)item->valp + item->length; |
| /* LINTED */ |
| rp6 = (mib2_ipv6RouteEntry_t *) |
| ((char *)rp6 + ipv6RouteEntrySize)) { |
| delRouteEntry(NULL, rp6, seqno); |
| seqno++; |
| } |
| break; |
| } |
| break; |
| } |
| |
| if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK, (char *)&on, |
| sizeof (on)) < 0) |
| quit("setsockopt", errno); |
| } |
| |
| /* |
| * Given the contents of a mib_item_t of id type MIB2_IP_ROUTE or |
| * MIB2_IP6_ROUTE, construct and send an RTM_DELETE routing socket message in |
| * order to facilitate the flushing of RTF_GATEWAY routes. |
| */ |
| static void |
| delRouteEntry(mib2_ipRouteEntry_t *rp, mib2_ipv6RouteEntry_t *rp6, int seqno) |
| { |
| char *cp; |
| int ire_type; |
| int rlen; |
| struct rt_msghdr *rtm; |
| struct sockaddr_in sin; |
| struct sockaddr_in6 sin6; |
| int slen; |
| |
| if (rp != NULL) |
| ire_type = rp->ipRouteInfo.re_ire_type; |
| else |
| ire_type = rp6->ipv6RouteInfo.re_ire_type; |
| if (ire_type != IRE_DEFAULT && |
| ire_type != IRE_PREFIX && |
| ire_type != IRE_HOST && |
| ire_type != IRE_HOST_REDIRECT) |
| return; |
| |
| rtm = &m_rtmsg.m_rtm; |
| (void) memset(rtm, 0, sizeof (m_rtmsg)); |
| rtm->rtm_type = RTM_DELETE; |
| rtm->rtm_seq = seqno; |
| rtm->rtm_flags |= RTF_GATEWAY; |
| rtm->rtm_version = RTM_VERSION; |
| rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; |
| cp = m_rtmsg.m_space; |
| if (rp != NULL) { |
| slen = sizeof (struct sockaddr_in); |
| if (rp->ipRouteMask == IP_HOST_MASK) |
| rtm->rtm_flags |= RTF_HOST; |
| (void) memset(&sin, 0, slen); |
| sin.sin_family = AF_INET; |
| sin.sin_addr.s_addr = rp->ipRouteDest; |
| (void) memmove(cp, &sin, slen); |
| cp += slen; |
| sin.sin_addr.s_addr = rp->ipRouteNextHop; |
| (void) memmove(cp, &sin, slen); |
| cp += slen; |
| sin.sin_addr.s_addr = rp->ipRouteMask; |
| (void) memmove(cp, &sin, slen); |
| cp += slen; |
| } else { |
| slen = sizeof (struct sockaddr_in6); |
| if (rp6->ipv6RoutePfxLength == IPV6_ABITS) |
| rtm->rtm_flags |= RTF_HOST; |
| (void) memset(&sin6, 0, slen); |
| sin6.sin6_family = AF_INET6; |
| sin6.sin6_addr = rp6->ipv6RouteDest; |
| (void) memmove(cp, &sin6, slen); |
| cp += slen; |
| sin6.sin6_addr = rp6->ipv6RouteNextHop; |
| (void) memmove(cp, &sin6, slen); |
| cp += slen; |
| (void) memset(&sin6.sin6_addr, 0, sizeof (sin6.sin6_addr)); |
| (void) in_prefixlentomask(rp6->ipv6RoutePfxLength, IPV6_ABITS, |
| (uchar_t *)&sin6.sin6_addr.s6_addr); |
| (void) memmove(cp, &sin6, slen); |
| cp += slen; |
| } |
| rtm->rtm_msglen = cp - (char *)&m_rtmsg; |
| if (debugonly) { |
| /* |
| * In debugonly mode, the routing socket message to delete the |
| * current entry is not actually sent. However if verbose is |
| * also set, the routing socket message that would have been |
| * is printed. |
| */ |
| if (verbose) |
| print_rtmsg(rtm, rtm->rtm_msglen); |
| return; |
| } |
| |
| rlen = write(s, (char *)&m_rtmsg, rtm->rtm_msglen); |
| if (rlen < (int)rtm->rtm_msglen) { |
| if (rlen < 0) { |
| (void) fprintf(stderr, |
| gettext("route: write to routing socket: %s\n"), |
| strerror(errno)); |
| } else { |
| (void) fprintf(stderr, gettext("route: write to " |
| "routing socket got only %d for rlen\n"), rlen); |
| } |
| return; |
| } |
| if (qflag) { |
| /* |
| * In quiet mode, nothing is printed at all (unless the write() |
| * itself failed. |
| */ |
| return; |
| } |
| if (verbose) { |
| print_rtmsg(rtm, rlen); |
| } else { |
| struct sockaddr *sa = (struct sockaddr *)(rtm + 1); |
| |
| (void) printf("%-20.20s ", |
| rtm->rtm_flags & RTF_HOST ? routename(sa) : |
| netname(sa)); |
| /* LINTED */ |
| sa = (struct sockaddr *)(salen(sa) + (char *)sa); |
| (void) printf("%-20.20s ", routename(sa)); |
| (void) printf("done\n"); |
| } |
| } |
| |
| /* |
| * Return the name of the host whose address is given. |
| */ |
| char * |
| routename(const struct sockaddr *sa) |
| { |
| char *cp; |
| static char line[MAXHOSTNAMELEN + 1]; |
| struct hostent *hp = NULL; |
| static char domain[MAXHOSTNAMELEN + 1]; |
| static boolean_t first = B_TRUE; |
| struct in_addr in; |
| struct in6_addr in6; |
| int error_num; |
| ushort_t *s; |
| ushort_t *slim; |
| |
| if (first) { |
| first = B_FALSE; |
| if (gethostname(domain, MAXHOSTNAMELEN) == 0 && |
| (cp = strchr(domain, '.'))) |
| (void) strcpy(domain, cp + 1); |
| else |
| domain[0] = 0; |
| } |
| |
| if (salen(sa) == 0) { |
| (void) strcpy(line, "default"); |
| return (line); |
| } |
| switch (sa->sa_family) { |
| |
| case AF_INET: |
| /* LINTED */ |
| in = ((struct sockaddr_in *)sa)->sin_addr; |
| |
| cp = NULL; |
| if (in.s_addr == INADDR_ANY) |
| cp = "default"; |
| if (cp == NULL && !nflag) { |
| hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), |
| AF_INET); |
| if (hp != NULL) { |
| if (((cp = strchr(hp->h_name, '.')) != NULL) && |
| (strcmp(cp + 1, domain) == 0)) |
| *cp = 0; |
| cp = hp->h_name; |
| } |
| } |
| if (cp != NULL) { |
| (void) strncpy(line, cp, MAXHOSTNAMELEN); |
| line[MAXHOSTNAMELEN] = '\0'; |
| } else { |
| in.s_addr = ntohl(in.s_addr); |
| (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
| C(in.s_addr >> 16), C(in.s_addr >> 8), |
| C(in.s_addr)); |
| } |
| break; |
| |
| case AF_LINK: |
| return (link_ntoa((struct sockaddr_dl *)sa)); |
| |
| case AF_INET6: |
| /* LINTED */ |
| in6 = ((struct sockaddr_in6 *)sa)->sin6_addr; |
| |
| cp = NULL; |
| if (IN6_IS_ADDR_UNSPECIFIED(&in6)) |
| cp = "default"; |
| if (cp == NULL && !nflag) { |
| hp = getipnodebyaddr((char *)&in6, |
| sizeof (struct in6_addr), AF_INET6, &error_num); |
| if (hp != NULL) { |
| if (((cp = strchr(hp->h_name, '.')) != NULL) && |
| (strcmp(cp + 1, domain) == 0)) |
| *cp = 0; |
| cp = hp->h_name; |
| } |
| } |
| if (cp != NULL) { |
| (void) strncpy(line, cp, MAXHOSTNAMELEN); |
| line[MAXHOSTNAMELEN] = '\0'; |
| } else { |
| (void) inet_ntop(AF_INET6, (void *)&in6, line, |
| INET6_ADDRSTRLEN); |
| } |
| if (hp != NULL) |
| freehostent(hp); |
| |
| break; |
| |
| default: |
| s = (ushort_t *)sa; |
| |
| slim = s + ((salen(sa) + 1) >> 1); |
| cp = line + sprintf(line, "(%d)", sa->sa_family); |
| |
| while (++s < slim) /* start with sa->sa_data */ |
| cp += sprintf(cp, " %x", *s); |
| break; |
| } |
| return (line); |
| } |
| |
| /* |
| * Return the name of the network whose address is given. |
| * The address is assumed to be that of a net or subnet, not a host. |
| */ |
| static char * |
| netname(struct sockaddr *sa) |
| { |
| char *cp = NULL; |
| static char line[MAXHOSTNAMELEN + 1]; |
| struct netent *np; |
| in_addr_t net, mask; |
| int subnetshift; |
| struct in_addr in; |
| ushort_t *s; |
| ushort_t *slim; |
| |
| switch (sa->sa_family) { |
| |
| case AF_INET: |
| /* LINTED */ |
| in = ((struct sockaddr_in *)sa)->sin_addr; |
| |
| in.s_addr = ntohl(in.s_addr); |
| if (in.s_addr == INADDR_ANY) { |
| cp = "default"; |
| } else if (!nflag) { |
| if (IN_CLASSA(in.s_addr)) { |
| mask = IN_CLASSA_NET; |
| subnetshift = 8; |
| } else if (IN_CLASSB(in.s_addr)) { |
| mask = IN_CLASSB_NET; |
| subnetshift = 8; |
| } else { |
| mask = IN_CLASSC_NET; |
| subnetshift = 4; |
| } |
| /* |
| * If there are more bits than the standard mask |
| * would suggest, subnets must be in use. |
| * Guess at the subnet mask, assuming reasonable |
| * width subnet fields. |
| */ |
| while (in.s_addr &~ mask) |
| mask = (long)mask >> subnetshift; |
| net = in.s_addr & mask; |
| while ((mask & 1) == 0) |
| mask >>= 1, net >>= 1; |
| np = getnetbyaddr(net, AF_INET); |
| if (np != NULL) |
| cp = np->n_name; |
| } |
| if (cp != NULL) { |
| (void) strncpy(line, cp, MAXHOSTNAMELEN); |
| line[MAXHOSTNAMELEN] = '\0'; |
| } else if ((in.s_addr & 0xffffff) == 0) { |
| (void) sprintf(line, "%u", C(in.s_addr >> 24)); |
| } else if ((in.s_addr & 0xffff) == 0) { |
| (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), |
| C(in.s_addr >> 16)); |
| } else if ((in.s_addr & 0xff) == 0) { |
| (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), |
| C(in.s_addr >> 16), C(in.s_addr >> 8)); |
| } else { |
| (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
| C(in.s_addr >> 16), C(in.s_addr >> 8), |
| C(in.s_addr)); |
| } |
| break; |
| |
| case AF_LINK: |
| return (link_ntoa((struct sockaddr_dl *)sa)); |
| |
| case AF_INET6: |
| return (routename(sa)); |
| |
| default: |
| /* LINTED */ |
| s = (ushort_t *)sa->sa_data; |
| |
| slim = s + ((salen(sa) + 1) >> 1); |
| cp = line + sprintf(line, "af %d:", sa->sa_family); |
| |
| while (s < slim) |
| cp += sprintf(cp, " %x", *s++); |
| break; |
| } |
| return (line); |
| } |
| |
| /* |
| * Initialize a new structure. Keep in mind that ri_dst_str, ri_gate_str and |
| * ri_ifp_str will be freed by det_rtcmd_irep, so they should either be NULL |
| * or point to dynamically allocated memory. |
| */ |
| rtcmd_irep_t * |
| new_rtcmd_irep(void) |
| { |
| rtcmd_irep_t *rcip; |
| |
| rcip = calloc(1, sizeof (rtcmd_irep_t)); |
| if (rcip == NULL) { |
| quit("calloc", errno); |
| } |
| rcip->ri_af = af; |
| rcip->ri_flags = RTF_STATIC; |
| return (rcip); |
| } |
| |
| void |
| del_rtcmd_irep(rtcmd_irep_t *rcip) |
| { |
| free(rcip->ri_dest_str); |
| free(rcip->ri_gate_str); |
| free(rcip->ri_ifp_str); |
| if (rcip->ri_gate_hp != NULL) { |
| freehostent(rcip->ri_gate_hp); |
| } |
| free(rcip); |
| } |
| |
| void |
| save_string(char **dst, char *src) |
| { |
| free(*dst); |
| *dst = strdup(src); |
| if (*dst == NULL) { |
| quit("malloc", errno); |
| } |
| } |
| |
| /* |
| * Print the short form summary of a route command. |
| * Eg. "add net default: gateway 10.0.0.1" |
| * The final newline is not added, allowing the caller to append additional |
| * information. |
| */ |
| void |
| print_rtcmd_short(FILE *to, rtcmd_irep_t *rcip, boolean_t gw_good, |
| boolean_t to_saved) |
| { |
| char *cmd; |
| char obuf[INET6_ADDRSTRLEN]; |
| |
| switch (rcip->ri_cmd) { |
| case RTM_ADD: |
| cmd = "add"; |
| break; |
| case RTM_CHANGE: |
| cmd = "change"; |
| break; |
| case RTM_DELETE: |
| cmd = "delete"; |
| break; |
| case RTM_GET: |
| cmd = "get"; |
| break; |
| default: |
| assert(0); |
| } |
| |
| (void) fprintf(to, "%s%s %s %s", cmd, |
| (to_saved) ? " persistent" : "", |
| (rcip->ri_flags & RTF_HOST) ? "host" : "net", |
| (rcip->ri_dest_str == NULL) ? "NULL" : rcip->ri_dest_str); |
| |
| if (rcip->ri_gate_str != NULL) { |
| switch (rcip->ri_af) { |
| case AF_INET: |
| if (nflag) { |
| (void) fprintf(to, ": gateway %s", |
| inet_ntoa(rcip->ri_gate.sin.sin_addr)); |
| } else if (gw_good && |
| rcip->ri_gate_hp != NULL && |
| rcip->ri_gate_hp->h_addr_list[1] != NULL) { |
| /* |
| * Print the actual address used in the case |
| * where there was more than one address |
| * available for the name, and one was used |
| * successfully. |
| */ |
| (void) fprintf(to, ": gateway %s (%s)", |
| rcip->ri_gate_str, |
| inet_ntoa(rcip->ri_gate.sin.sin_addr)); |
| } else { |
| (void) fprintf(to, ": gateway %s", |
| rcip->ri_gate_str); |
| } |
| break; |
| case AF_INET6: |
| if (inet_ntop(AF_INET6, |
| &rcip->ri_gate.sin6.sin6_addr, obuf, |
| INET6_ADDRSTRLEN) != NULL) { |
| if (nflag) { |
| (void) fprintf(to, ": gateway %s", |
| obuf); |
| break; |
| } |
| if (gw_good && |
| rcip->ri_gate_hp->h_addr_list[1] != NULL) { |
| (void) fprintf(to, ": gateway %s (%s)", |
| rcip->ri_gate_str, obuf); |
| break; |
| } |
| } |
| /* FALLTHROUGH */ |
| default: |
| (void) fprintf(to, ": gateway %s", |
| rcip->ri_gate_str); |
| break; |
| } |
| } |
| } |
| |
| void |
| set_metric(rtcmd_irep_t *rcip, char *value, int key, boolean_t lock) |
| { |
| int flag = 0; |
| uint_t noval, *valp = &noval; |
| |
| switch (key) { |
| #define caseof(x, y, z) \ |
| case (x): valp = &(rcip->ri_metrics.z); flag = (y); break |
| |
| caseof(K_MTU, RTV_MTU, rmx_mtu); |
| caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); |
| caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); |
| caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); |
| caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); |
| caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); |
| caseof(K_RTT, RTV_RTT, rmx_rtt); |
| caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); |
| #undef caseof |
| } |
| rcip->ri_inits |= flag; |
| if (lock) |
| rcip->ri_metrics.rmx_locks |= flag; |
| *valp = atoi(value); |
| } |
| |
| /* |
| * Parse the options give in argv[], filling in rcip with the results. |
| * If cmd_string is non-null, argc and argv are ignored, and cmd_string is |
| * tokenized to produce the command line. Cmd_string is tokenized using |
| * strtok, which will overwrite whitespace in the string with nulls. |
| * |
| * Returns B_TRUE on success and B_FALSE on failure. |
| */ |
| boolean_t |
| args_to_rtcmd(rtcmd_irep_t *rcip, char **argv, char *cmd_string) |
| { |
| const char *ws = "\f\n\r\t\v "; |
| char *tok = cmd_string; |
| char *keyword_str; |
| addr_type_t atype = ADDR_TYPE_ANY; |
| boolean_t iflag = B_FALSE; |
| boolean_t locknext = B_FALSE; |
| boolean_t lockrest = B_FALSE; |
| boolean_t dash_keyword; |
| int key; |
| char *err; |
| |
| if (cmd_string == NULL) { |
| tok = argv[0]; |
| } else { |
| tok = strtok(cmd_string, ws); |
| } |
| |
| /* |
| * The command keywords are already fully checked by main() or |
| * search_rtfile(). |
| */ |
| switch (*tok) { |
| case 'a': |
| rcip->ri_cmd = RTM_ADD; |
| break; |
| case 'c': |
| rcip->ri_cmd = RTM_CHANGE; |
| break; |
| case 'd': |
| rcip->ri_cmd = RTM_DELETE; |
| break; |
| case 'g': |
| rcip->ri_cmd = RTM_GET; |
| break; |
| default: |
| /* NOTREACHED */ |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| |
| #define NEXTTOKEN \ |
| ((tok = (cmd_string == NULL ? *++argv : strtok(NULL, ws))) != NULL) |
| |
| while (NEXTTOKEN) { |
| keyword_str = tok; |
| if (*tok == '-') { |
| dash_keyword = B_TRUE; |
| key = keyword(tok + 1); |
| } else { |
| dash_keyword = B_FALSE; |
| key = keyword(tok); |
| if (key != K_HOST && key != K_NET) { |
| /* All others must be preceded by '-' */ |
| key = 0; |
| } |
| } |
| switch (key) { |
| case K_HOST: |
| if (atype == ADDR_TYPE_NET) { |
| syntax_error(gettext("route: -host and -net " |
| "are mutually exclusive\n")); |
| return (B_FALSE); |
| } |
| atype = ADDR_TYPE_HOST; |
| break; |
| case K_NET: |
| if (atype == ADDR_TYPE_HOST) { |
| syntax_error(gettext("route: -host and -net " |
| "are mutually exclusive\n")); |
| return (B_FALSE); |
| } |
| atype = ADDR_TYPE_NET; |
| break; |
| case K_LINK: |
| rcip->ri_af = AF_LINK; |
| break; |
| case K_INET: |
| rcip->ri_af = AF_INET; |
| break; |
| case K_SA: |
| rcip->ri_af = PF_ROUTE; |
| break; |
| case K_INET6: |
| rcip->ri_af = AF_INET6; |
| break; |
| case K_IFACE: |
| case K_INTERFACE: |
| iflag = B_TRUE; |
| /* fallthrough */ |
| case K_NOSTATIC: |
| rcip->ri_flags &= ~RTF_STATIC; |
| break; |
| case K_LOCK: |
| locknext = B_TRUE; |
| break; |
| case K_LOCKREST: |
| lockrest = B_TRUE; |
| break; |
| case K_REJECT: |
| rcip->ri_flags |= RTF_REJECT; |
| break; |
| case K_BLACKHOLE: |
| rcip->ri_flags |= RTF_BLACKHOLE; |
| break; |
| case K_PROTO1: |
| rcip->ri_flags |= RTF_PROTO1; |
| break; |
| case K_PROTO2: |
| rcip->ri_flags |= RTF_PROTO2; |
| break; |
| case K_CLONING: |
| rcip->ri_flags |= RTF_CLONING; |
| break; |
| case K_XRESOLVE: |
| rcip->ri_flags |= RTF_XRESOLVE; |
| break; |
| case K_STATIC: |
| rcip->ri_flags |= RTF_STATIC; |
| break; |
| case K_IFA: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_IFA, tok, atype)) { |
| return (B_FALSE); |
| } |
| break; |
| case K_IFP: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_IFP, tok, atype)) { |
| return (B_FALSE); |
| } |
| break; |
| case K_GATEWAY: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) { |
| return (B_FALSE); |
| } |
| break; |
| case K_DST: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_DST, tok, atype)) { |
| return (B_FALSE); |
| } |
| break; |
| case K_NETMASK: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_NETMASK, tok, atype)) { |
| return (B_FALSE); |
| } |
| atype = ADDR_TYPE_NET; |
| break; |
| case K_MTU: |
| case K_HOPCOUNT: |
| case K_EXPIRE: |
| case K_RECVPIPE: |
| case K_SENDPIPE: |
| case K_SSTHRESH: |
| case K_RTT: |
| case K_RTTVAR: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| set_metric(rcip, tok, key, locknext || lockrest); |
| locknext = B_FALSE; |
| break; |
| case K_PRIVATE: |
| rcip->ri_flags |= RTF_PRIVATE; |
| break; |
| case K_MULTIRT: |
| rcip->ri_flags |= RTF_MULTIRT; |
| break; |
| case K_SETSRC: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if (!getaddr(rcip, RTA_SRC, tok, atype)) { |
| return (B_FALSE); |
| } |
| rcip->ri_flags |= RTF_SETSRC; |
| break; |
| case K_SECATTR: |
| if (!NEXTTOKEN) { |
| syntax_arg_missing(keyword_str); |
| return (B_FALSE); |
| } |
| if ((rcip->ri_cmd == RTM_ADD || |
| rcip->ri_cmd == RTM_CHANGE) && |
| rcip->ri_rtsa_cnt++ < 1 && is_system_labeled()) { |
| int err; |
| |
| if (!rtsa_keyword(tok, &rcip->ri_rtsa, &err, |
| NULL)) { |
| (void) fprintf(stderr, gettext("route: " |
| "bad security attribute: %s\n"), |
| tsol_strerror(err, errno)); |
| return (B_FALSE); |
| } |
| } |
| if (rcip->ri_rtsa_cnt > 1) { |
| (void) fprintf(stderr, |
| gettext("route: can't specify more " |
| "than one security attribute\n")); |
| return (B_FALSE); |
| } |
| break; |
| default: |
| if (dash_keyword) { |
| syntax_bad_keyword(tok + 1); |
| return (B_FALSE); |
| } |
| if ((rcip->ri_addrs & RTA_DST) == 0) { |
| if (!getaddr(rcip, RTA_DST, tok, atype)) { |
| return (B_FALSE); |
| } |
| } else if ((rcip->ri_addrs & RTA_GATEWAY) == 0) { |
| /* |
| * For the gateway parameter, retrieve the |
| * pointer to the struct hostent so that all |
| * possible addresses can be tried until one |
| * is successful. |
| */ |
| if (!getaddr(rcip, RTA_GATEWAY, tok, atype)) { |
| return (B_FALSE); |
| } |
| } else { |
| ulong_t metric; |
| /* |
| * Assume that a regular number is a metric. |
| * Needed for compatibility with old route |
| * command syntax. |
| */ |
| errno = 0; |
| metric = strtoul(tok, &err, 10); |
| if (errno == 0 && *err == '\0' && |
| metric < 0x80000000ul) { |
| iflag = (metric == 0); |
| if (verbose) { |
| (void) printf("old usage of " |
| "trailing number, assuming " |
| "route %s\n", iflag ? |
| "to if" : "via gateway"); |
| } |
| continue; |
| } |
| if (!getaddr(rcip, RTA_NETMASK, tok, atype)) { |
| return (B_FALSE); |
| } |
| } |
| } |
| } |
| #undef NEXTTOKEN |
| |
| if ((rcip->ri_addrs & RTA_DST) == 0) { |
| syntax_error(gettext("route: destination required\n")); |
| return (B_FALSE); |
| } else if ((rcip->ri_cmd == RTM_ADD || rcip->ri_cmd == RTM_DELETE) && |
| (rcip->ri_addrs & RTA_GATEWAY) == 0) { |
| syntax_error(gettext( |
| "route: gateway required for add or delete command\n")); |
| return (B_FALSE); |
| } |
| |
| if (!iflag) { |
| rcip->ri_flags |= RTF_GATEWAY; |
| } |
| |
| if (atype != ADDR_TYPE_NET) { |
| if (rcip->ri_addrs & RTA_NETMASK) { |
| /* |
| * We know the netmask, so we can set the host flag |
| * based on whether the netmask is the host netmask. |
| */ |
| if (rcip->ri_af == AF_INET && |
| rcip->ri_mask.sin.sin_addr.s_addr == |
| IP_HOST_MASK) { |
| rcip->ri_flags |= RTF_HOST; |
| } |
| if (rcip->ri_af == AF_INET6 && |
| memcmp(&rcip->ri_mask.sin6.sin6_addr, |
| &in6_host_mask, |
| sizeof (struct in6_addr)) == 0) { |
| rcip->ri_flags |= RTF_HOST; |
| } |
| } else { |
| /* |
| * If no prefix mask has been saved at this point, it |
| * only makes sense to treat the destination address |
| * as a host address. |
| */ |
| rcip->ri_flags |= RTF_HOST; |
| } |
| } |
| return (B_TRUE); |
| } |
| |
| /* |
| * This command always seeks to the end of the file prior to writing. |
| */ |
| void |
| write_to_rtfile(FILE *fp, int argc, char **argv) |
| { |
| char file_line[BUF_SIZE]; |
| int len; |
| int i; |
| |
| len = 0; |
| if (early_v6_keyword) { |
| /* |
| * This flag is set when "inet6" was seen as an |
| * argument to the -f flag. Normally, when writing |
| * routes to the persistent route file, everything on |
| * the command line after "add" is saved verbatim. |
| * In this case, the arguments after "add" may not be |
| * sufficient, as the ipv6 keyword came before "add", |
| * yet must be present in the persistent route file. |
| */ |
| len += snprintf(file_line, BUF_SIZE, "-inet6 "); |
| } |
| for (i = 0; argc > 0 && len < BUF_SIZE; i++, argc--) { |
| len += snprintf(&file_line[len], BUF_SIZE - len, "%s ", |
| argv[i]); |
| } |
| if (len >= BUF_SIZE) |
| quit(gettext("Internal Error"), EINVAL); |
| file_line[len - 1] = '\n'; |
| if (fseek(fp, 0, SEEK_END) != 0 || |
| fputs(file_line, fp) == EOF) { |
| quit(gettext("failed to write to route file"), |
| errno); |
| } |
| } |
| |
| boolean_t |
| compare_rtcmd(rtcmd_irep_t *srch_rt, rtcmd_irep_t *file_rt) |
| { |
| if (strcmp(srch_rt->ri_dest_str, file_rt->ri_dest_str) != 0 || |
| memcmp(&srch_rt->ri_mask, &file_rt->ri_mask, sizeof (su_t)) != 0) { |
| return (B_FALSE); |
| } |
| return (srch_rt->ri_gate_str == NULL || |
| strcmp(srch_rt->ri_gate_str, file_rt->ri_gate_str) == 0); |
| } |
| |
| /* |
| * Search the route file for routes matching the supplied route. There are 3 |
| * modes of operation: |
| * SEARCH_MODE_RET - no side effects. |
| * SEARCH_MODE_PRINT - prints each matching line. |
| * SEARCH_MODE_DEL - copies all valid, non-matching lines to tmp_fp. |
| * |
| * In all cases, the number of matches is returned. If rt is NULL, all routes |
| * matching the global af value are considered matching. |
| */ |
| int |
| search_rtfile(FILE *fp, FILE *temp_fp, rtcmd_irep_t *rt, search_mode_t mode) |
| { |
| char *tmp_buf; |
| int match_cnt; |
| boolean_t match; |
| char file_line[BUF_SIZE + 4] = "add "; |
| rtcmd_irep_t *thisrt; |
| |
| match_cnt = 0; |
| |
| /* |
| * Leave space at the beginning of file_line for "add ". |
| */ |
| while (fgets(file_line + 4, BUF_SIZE, fp) != NULL) { |
| |
| if (file_line[4] == '#' || file_line[4] == '\n') { |
| /* Handle comments and blank lines */ |
| if (mode == SEARCH_MODE_DEL && |
| fputs(file_line + 4, temp_fp) == EOF) { |
| quit(gettext( |
| "route: failed to write to temp file"), |
| errno); |
| } |
| continue; |
| } |
| thisrt = new_rtcmd_irep(); |
| /* |
| * thisrt->ri_af defaults to whatever address family happens |
| * to be set in the global af, but routes in the persistent |
| * route file must be treated as AF_INET by default. |
| */ |
| thisrt->ri_af = AF_INET; |
| |
| exit_on_error = B_FALSE; |
| tmp_buf = strdup(file_line); |
| /* args_to_rtcmd() will mangle the string passed. */ |
| if (!args_to_rtcmd(thisrt, NULL, tmp_buf)) { |
| /* There was an error in args_to_rtcmd() or helpers */ |
| del_rtcmd_irep(thisrt); |
| free(tmp_buf); |
| continue; |
| } |
| exit_on_error = B_TRUE; |
| free(tmp_buf); |
| |
| if (thisrt->ri_gate_str == NULL) { |
| del_rtcmd_irep(thisrt); |
| continue; |
| } |
| match = (rt == NULL) ? (thisrt->ri_af == af) : |
| compare_rtcmd(rt, thisrt); |
| |
| if (match) match_cnt++; |
| if (match && mode == SEARCH_MODE_PRINT) { |
| (void) printf("persistent: route %s", file_line); |
| } |
| if (match && mode == SEARCH_MODE_DEL) { |
| thisrt->ri_cmd = RTM_DELETE; |
| print_rtcmd_short(stdout, thisrt, B_FALSE, B_TRUE); |
| (void) printf("\n"); |
| } |
| del_rtcmd_irep(thisrt); |
| |
| if (!match && mode == SEARCH_MODE_DEL && |
| fputs(file_line + 4, temp_fp) == EOF) { |
| quit(gettext("failed to write to temp file"), |
| errno); |
| } |
| } |
| return (match_cnt); |
| } |
| |
| /* |
| * Perform the route operation given in argv on the persistent route file. |
| * If do_flush is set, the persistent route file is flushed of all routes |
| * matching the global family, and the arguments are ignored. |
| */ |
| void |
| save_route(int argc, char **argv, int do_flush) |
| { |
| rtcmd_irep_t *rt; |
| int perm_fd; |
| FILE *perm_fp; |
| FILE *temp_fp; |
| mode_t fmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; |
| struct flock lock; |
| struct stat st; |
| const char commentstr[] = |
| "# File generated by route(1M) - do not edit.\n"; |
| |
| perm_fd = open(perm_file, O_RDWR | O_CREAT, fmode); |
| if (perm_fd == -1 || fstat(perm_fd, &st) == -1) |
| quit("failed to open route file", errno); |
| |
| lock.l_type = F_WRLCK; |
| lock.l_whence = SEEK_SET; |
| lock.l_start = 0; |
| lock.l_len = 0; |
| if (fcntl(perm_fd, F_SETLK, &lock) != 0) { |
| quit(gettext("failed to lock route file"), errno); |
| /* NOTREACHED */ |
| } |
| if (st.st_size == 0 && |
| write(perm_fd, commentstr, sizeof (commentstr) - 1) != |
| sizeof (commentstr) - 1) |
| quit(gettext("failed to open route file"), errno); |
| |
| if ((perm_fp = fdopen(perm_fd, "r+")) == NULL) { |
| quit(gettext("failed to open route file"), errno); |
| /* NOTREACHED */ |
| } |
| |
| if (!do_flush) { |
| rt = new_rtcmd_irep(); |
| (void) args_to_rtcmd(rt, argv, NULL); |
| } |
| if (do_flush || rt->ri_cmd == RTM_DELETE) { |
| if ((temp_fp = fopen(temp_file, "w")) == NULL) { |
| quit(gettext("failed to open temp file"), errno); |
| /* NOTREACHED */ |
| } |
| } |
| if (do_flush) { |
| (void) search_rtfile(perm_fp, temp_fp, NULL, SEARCH_MODE_DEL); |
| if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) { |
| quit(gettext("failed to update route file"), errno); |
| /* NOTREACHED */ |
| } |
| (void) fclose(perm_fp); |
| return; |
| } |
| |
| switch (rt->ri_cmd) { |
| case RTM_ADD: |
| if (search_rtfile(perm_fp, NULL, rt, SEARCH_MODE_NULL) > 0) { |
| /* Route is already in the file */ |
| print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE); |
| (void) fprintf(stderr, ": entry exists\n"); |
| exit(1); |
| } |
| write_to_rtfile(perm_fp, argc - 1, argv + 1); |
| print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE); |
| (void) printf("\n"); |
| break; |
| |
| case RTM_CHANGE: |
| syntax_error( |
| gettext("route: change command not supported with -p\n")); |
| /* NOTREACHED */ |
| |
| case RTM_DELETE: |
| if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_DEL) <= 0) { |
| /* Route not found */ |
| print_rtcmd_short(stderr, rt, B_FALSE, B_TRUE); |
| (void) fprintf(stderr, gettext(": not in file\n")); |
| exit(1); |
| } |
| if (fclose(temp_fp) != 0 || rename(temp_file, perm_file) != 0) { |
| quit(gettext("failed to update route file"), errno); |
| /* NOTREACHED */ |
| } |
| break; |
| |
| case RTM_GET: |
| if (search_rtfile(perm_fp, temp_fp, rt, SEARCH_MODE_PRINT) <= |
| 0) { |
| print_rtcmd_short(stdout, rt, B_FALSE, B_TRUE); |
| (void) printf(gettext(": not in file\n")); |
| } |
| break; |
| |
| default: |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| |
| /* |
| * Closing the file unlocks it. |
| */ |
| (void) fclose(perm_fp); |
| } |
| |
| int |
| show_saved_routes(int argc) |
| { |
| int perm_fd; |
| FILE *perm_fp; |
| struct flock lock; |
| int count = 0; |
| |
| if (argc != 1) { |
| syntax_error(gettext("route: invalid arguments for show\n")); |
| } |
| |
| perm_fd = open(perm_file, O_RDONLY, 0); |
| |
| if (perm_fd == -1) { |
| if (errno == ENOENT) { |
| (void) printf("No persistent routes are defined\n"); |
| return (0); |
| } else { |
| quit(gettext("failed to open route file"), errno); |
| } |
| } |
| lock.l_type = F_RDLCK; |
| lock.l_whence = SEEK_SET; |
| lock.l_start = 0; |
| lock.l_len = 0; |
| if (fcntl(perm_fd, F_SETLK, &lock) != 0) { |
| quit(gettext("failed to lock route file"), |
| errno); |
| /* NOTREACHED */ |
| } |
| if ((perm_fp = fdopen(perm_fd, "r")) == NULL) { |
| quit(gettext("failed to open route file"), errno); |
| /* NOTREACHED */ |
| } |
| count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT); |
| (void) fseek(perm_fp, 0, SEEK_SET); |
| af = AF_INET6; |
| count += search_rtfile(perm_fp, NULL, NULL, SEARCH_MODE_PRINT); |
| |
| if (count == 0) |
| (void) printf("No persistent routes are defined\n"); |
| |
| (void) fclose(perm_fp); |
| return (0); |
| } |
| |
| int |
| newroute(char **argv) |
| { |
| rtcmd_irep_t *newrt; |
| int ret, attempts, oerrno; |
| char *err; |
| char obuf[INET6_ADDRSTRLEN]; |
| #define hp (newrt->ri_gate_hp) |
| |
| newrt = new_rtcmd_irep(); |
| (void) args_to_rtcmd(newrt, argv, NULL); |
| |
| if (newrt->ri_cmd != RTM_GET && !tflag) { |
| /* Don't want to read back our messages */ |
| (void) shutdown(s, 0); |
| } |
| if (newrt->ri_addrs & RTA_IFP) { |
| newrt->ri_ifp.sdl.sdl_index = if_nametoindex(newrt->ri_ifp_str); |
| if (newrt->ri_ifp.sdl.sdl_index == 0) { |
| if (errno != ENXIO) { |
| quit("if_nametoindex", errno); |
| } else { |
| (void) fprintf(stderr, |
| gettext("route: %s: no such interface\n"), |
| newrt->ri_ifp_str); |
| exit(1); |
| } |
| } |
| newrt->ri_ifp.sdl.sdl_family = AF_LINK; |
| } |
| for (attempts = 1; ; attempts++) { |
| errno = 0; |
| if ((ret = rtmsg(newrt)) == 0) |
| break; |
| if (errno != ENETUNREACH && errno != ESRCH) |
| break; |
| if ((newrt->ri_addrs & RTA_GATEWAY) && hp != NULL && |
| hp->h_addr_list[attempts] != NULL) { |
| switch (af) { |
| case AF_INET: |
| (void) memmove(&newrt->ri_gate.sin.sin_addr, |
| hp->h_addr_list[attempts], hp->h_length); |
| continue; |
| case AF_INET6: |
| (void) memmove(&newrt->ri_gate.sin6.sin6_addr, |
| hp->h_addr_list[attempts], hp->h_length); |
| continue; |
| } |
| } |
| break; |
| } |
| oerrno = errno; |
| |
| if (newrt->ri_cmd != RTM_GET) { |
| print_rtcmd_short(stdout, newrt, (ret == 0), B_FALSE); |
| if (ret == 0) |
| (void) printf("\n"); |
| } else if (ret != 0) { |
| /* |
| * Note: there is nothing additional to print for get |
| * if ret == 0. |
| */ |
| if (nflag) { |
| switch (newrt->ri_af) { |
| case AF_INET: |
| (void) printf(" %s", |
| inet_ntoa(newrt->ri_dst.sin.sin_addr)); |
| break; |
| case AF_INET6: |
| if (inet_ntop(AF_INET6, |
| (void *)&newrt->ri_dst.sin6.sin6_addr, |
| obuf, INET6_ADDRSTRLEN) != NULL) { |
| (void) printf(" %s", obuf); |
| break; |
| } |
| /* FALLTHROUGH */ |
| default: |
| (void) printf("%s", newrt->ri_dest_str); |
| break; |
| } |
| } else { |
| (void) printf("%s", newrt->ri_dest_str); |
| } |
| } |
| |
| if (ret != 0) { |
| switch (oerrno) { |
| case ESRCH: |
| err = "not in table"; |
| break; |
| case EBUSY: |
| err = "entry in use"; |
| break; |
| case ENOBUFS: |
| err = "routing table overflow"; |
| break; |
| case EEXIST: |
| err = "entry exists"; |
| break; |
| case EPERM: |
| err = "insufficient privileges"; |
| break; |
| default: |
| err = strerror(oerrno); |
| break; |
| } |
| (void) printf(": %s\n", err); |
| } |
| |
| del_rtcmd_irep(newrt); |
| |
| return (oerrno); |
| #undef hp |
| } |
| |
| |
| /* |
| * Convert a network number to the corresponding IP address. |
| * If the RTA_NETMASK hasn't been specified yet set it based |
| * on the class of address. |
| */ |
| static void |
| inet_makenetandmask(rtcmd_irep_t *rcip, in_addr_t net, struct sockaddr_in *sin) |
| { |
| in_addr_t addr, mask; |
| |
| if (net == 0) { |
| mask = addr = 0; |
| } else if (net < 128) { |
| addr = net << IN_CLASSA_NSHIFT; |
| mask = IN_CLASSA_NET; |
| } else if (net < 65536) { |
| addr = net << IN_CLASSB_NSHIFT; |
| mask = IN_CLASSB_NET; |
| } else if (net < 16777216L) { |
| addr = net << IN_CLASSC_NSHIFT; |
| mask = IN_CLASSC_NET; |
| } else { |
| addr = net; |
| if ((addr & IN_CLASSA_HOST) == 0) |
| mask = IN_CLASSA_NET; |
| else if ((addr & IN_CLASSB_HOST) == 0) |
| mask = IN_CLASSB_NET; |
| else if ((addr & IN_CLASSC_HOST) == 0) |
| mask = IN_CLASSC_NET; |
| else { |
| if (IN_CLASSA(addr)) |
| mask = IN_CLASSA_NET; |
| else if (IN_CLASSB(addr)) |
| mask = IN_CLASSB_NET; |
| else if (IN_CLASSC(addr)) |
| mask = IN_CLASSC_NET; |
| else |
| mask = IP_HOST_MASK; |
| mask = inet_makesubnetmask(addr, mask); |
| } |
| } |
| sin->sin_addr.s_addr = htonl(addr); |
| |
| if (!(rcip->ri_addrs & RTA_NETMASK)) { |
| rcip->ri_addrs |= RTA_NETMASK; |
| sin = &rcip->ri_mask.sin; |
| sin->sin_addr.s_addr = htonl(mask); |
| sin->sin_family = AF_INET; |
| } |
| } |
| |
| static in_addr_t |
| inet_makesubnetmask(in_addr_t addr, in_addr_t mask) |
| { |
| int n; |
| struct ifconf ifc; |
| struct ifreq ifreq; |
| struct ifreq *ifr; |
| struct sockaddr_in *sin; |
| char *buf; |
| int numifs; |
| size_t bufsize; |
| int iosoc; |
| in_addr_t if_addr, if_mask; |
| in_addr_t if_subnetmask = 0; |
| short if_flags; |
| |
| if (mask == 0) |
| return (0); |
| if ((iosoc = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| quit("socket", errno); |
| if (ioctl(iosoc, SIOCGIFNUM, (char *)&numifs) < 0) |
| quit("ioctl", errno); |
| bufsize = numifs * sizeof (struct ifreq); |
| buf = malloc(bufsize); |
| if (buf == NULL) |
| quit("malloc", errno); |
| (void) memset(&ifc, 0, sizeof (ifc)); |
| ifc.ifc_len = bufsize; |
| ifc.ifc_buf = buf; |
| if (ioctl(iosoc, SIOCGIFCONF, (char *)&ifc) < 0) |
| quit("ioctl (get interface configuration)", errno); |
| /* Let's check to see if this is maybe a local subnet route. */ |
| ifr = ifc.ifc_req; |
| for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) { |
| ifreq = *ifr; |
| /* LINTED */ |
| sin = (struct sockaddr_in *)&ifr->ifr_addr; |
| if_addr = ntohl(sin->sin_addr.s_addr); |
| |
| if (ioctl(iosoc, SIOCGIFFLAGS, (char *)&ifreq) < 0) |
| quit("ioctl (get interface flags)", errno); |
| if ((ifreq.ifr_flags & IFF_UP) == 0) |
| continue; |
| if_flags = ifreq.ifr_flags; |
| |
| if (ioctl(iosoc, SIOCGIFNETMASK, (char *)&ifreq) < 0) |
| quit("ioctl (get netmask)", errno); |
| /* LINTED */ |
| sin = (struct sockaddr_in *)&ifreq.ifr_addr; |
| if_mask = ntohl(sin->sin_addr.s_addr); |
| if ((if_addr & mask) == (addr & mask)) { |
| /* |
| * Don't trust pt-pt interfaces if there are |
| * other interfaces. |
| */ |
| if (if_flags & IFF_POINTOPOINT) { |
| if_subnetmask = if_mask; |
| continue; |
| } |
| /* |
| * Fine. Just assume the same net mask as the |
| * directly attached subnet interface is using. |
| */ |
| return (if_mask); |
| } |
| } |
| if (if_subnetmask != 0) |
| return (if_subnetmask); |
| return (mask); |
| } |
| |
| /* |
| * Interpret an argument as a network address of some kind. |
| * |
| * If the address family is one looked up in getaddr() using one of the |
| * getipnodebyX() functions (currently only AF_INET6), then callers should |
| * freehostent() the returned "struct hostent" pointer if one was passed in. |
| * |
| * If exit_on_error is true, this function will cause route to exit on error by |
| * calling syntax_error(). Otherwise, it returns B_TRUE on success or B_FALSE |
| * on failure. |
| */ |
| static boolean_t |
| getaddr(rtcmd_irep_t *rcip, int which, char *s, addr_type_t atype) |
| { |
| su_t *su; |
| struct hostent **hpp; |
| struct hostent *hp; |
| int masklen; |
| |
| if (which == RTA_GATEWAY) { |
| hpp = &(rcip->ri_gate_hp); |
| } else { |
| hpp = &hp; |
| } |
| *hpp = NULL; |
| |
| rcip->ri_addrs |= which; |
| switch (which) { |
| case RTA_DST: |
| save_string(&rcip->ri_dest_str, s); |
| su = &rcip->ri_dst; |
| su->sa.sa_family = rcip->ri_af; |
| break; |
| case RTA_GATEWAY: |
| save_string(&rcip->ri_gate_str, s); |
| su = &rcip->ri_gate; |
| su->sa.sa_family = rcip->ri_af; |
| break; |
| case RTA_NETMASK: |
| su = &rcip->ri_mask; |
| su->sa.sa_family = rcip->ri_af; |
| break; |
| case RTA_IFP: |
| save_string(&rcip->ri_ifp_str, s); |
| return (B_TRUE); |
| /* |
| * RTA_SRC has overloaded meaning. It can represent the |
| * src address of incoming or outgoing packets. |
| */ |
| case RTA_IFA: |
| su = &rcip->ri_ifa; |
| su->sa.sa_family = rcip->ri_af; |
| break; |
| case RTA_SRC: |
| su = &rcip->ri_src; |
| su->sa.sa_family = rcip->ri_af; |
| break; |
| default: |
| /* NOTREACHED */ |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| if (strcmp(s, "default") == 0) { |
| if (which == RTA_DST) { |
| return (getaddr(rcip, RTA_NETMASK, s, ADDR_TYPE_NET)); |
| } |
| if (which == RTA_SRC) { |
| return (B_TRUE); |
| } |
| return (B_TRUE); |
| } |
| switch (rcip->ri_af) { |
| case AF_LINK: |
| link_addr(s, &su->sdl); |
| return (B_TRUE); |
| case PF_ROUTE: |
| sockaddr(s, &su->sa); |
| return (B_TRUE); |
| case AF_INET6: |
| switch (which) { |
| case RTA_DST: |
| if (!in6_getaddr(s, &su->sin6, &masklen, hpp)) { |
| return (B_FALSE); |
| } |
| if (masklen != NO_PREFIX) { |
| (void) memset(&rcip->ri_mask.sin6.sin6_addr, 0, |
| sizeof (rcip->ri_mask.sin6.sin6_addr)); |
| if (!in_prefixlentomask(masklen, IPV6_ABITS, |
| (uchar_t *)&rcip->ri_mask.sin6.sin6_addr)) { |
| syntax_error(gettext( |
| "route: bad prefix length: %d\n"), |
| masklen); |
| return (B_FALSE); |
| } |
| rcip->ri_mask.sin6.sin6_family = rcip->ri_af; |
| rcip->ri_addrs |= RTA_NETMASK; |
| } |
| return (B_TRUE); |
| case RTA_GATEWAY: |
| case RTA_IFA: |
| case RTA_SRC: |
| return (in6_getaddr(s, &su->sin6, NULL, hpp)); |
| case RTA_NETMASK: |
| syntax_error( |
| gettext("route: -netmask not supported for IPv6: " |
| "use <prefix>/<prefix-length> instead\n")); |
| return (B_FALSE); |
| default: |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| case AF_INET: |
| switch (which) { |
| case RTA_DST: |
| if (!in_getaddr(s, &su->sin, &masklen, which, hpp, |
| atype, rcip)) { |
| return (B_FALSE); |
| } |
| if (masklen != NO_PREFIX) { |
| (void) memset(&rcip->ri_mask.sin.sin_addr, 0, |
| sizeof (rcip->ri_mask.sin.sin_addr)); |
| if (!in_prefixlentomask(masklen, IP_ABITS, |
| (uchar_t *)&rcip->ri_mask.sin.sin_addr)) { |
| syntax_error(gettext( |
| "route: bad prefix length: %d\n"), |
| masklen); |
| return (B_FALSE); |
| } |
| rcip->ri_mask.sin.sin_family = rcip->ri_af; |
| rcip->ri_addrs |= RTA_NETMASK; |
| } |
| return (B_TRUE); |
| case RTA_GATEWAY: |
| case RTA_IFA: |
| case RTA_NETMASK: |
| case RTA_SRC: |
| return (in_getaddr(s, &su->sin, NULL, which, hpp, atype, |
| rcip)); |
| default: |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| default: |
| quit(gettext("Internal Error"), EINVAL); |
| /* NOTREACHED */ |
| } |
| return (B_TRUE); |
| } |
| |
| /* |
| * Interpret an argument as an IPv4 network address of some kind, |
| * returning B_TRUE on success or B_FALSE on failure. |
| * This function will cause an exit() on failure if exit_on_failure is set. |
| * |
| * Note that this tries host interpretation before network interpretation, |
| * except when -net has been given and the destination address is being parsed. |
| * |
| * If the plenp argument is non-NULL, allow <addr>/<n> syntax and |
| * pass out <n> in *plenp. |
| * If <n> doesn't parse return BAD_ADDR as *plenp. |
| * If no /<n> is present return NO_PREFIX as *plenp. |
| */ |
| static boolean_t |
| in_getaddr(char *s, struct sockaddr_in *sin, int *plenp, int which, |
| struct hostent **hpp, addr_type_t atype, rtcmd_irep_t *rcip) |
| { |
| struct hostent *hp; |
| struct netent *np; |
| in_addr_t val; |
| char str[BUFSIZ]; |
| |
| (void) strlcpy(str, s, sizeof (str)); |
| |
| /* |
| * If plenp is non-NULL, /<n> syntax for netmask is allowed. |
| */ |
| if (plenp != NULL) { |
| char *cp; |
| |
| *plenp = in_getprefixlen(str, IP_ABITS); |
| if (*plenp == BAD_ADDR) |
| return (B_FALSE); |
| cp = strchr(str, '/'); |
| if (cp != NULL) |
| *cp = '\0'; |
| } else if (strchr(str, '/') != NULL) { |
| syntax_error(gettext("route: %s: unexpected '/'\n"), str); |
| return (B_FALSE); |
| } |
| |
| (void) memset(sin, 0, sizeof (*sin)); |
| sin->sin_family = AF_INET; |
| |
| /* |
| * Handle 255.255.255.255 as a special case first. |
| */ |
| if (strcmp(str, "255.255.255.255") == 0) { |
| sin->sin_addr.s_addr = INADDR_BROADCAST; |
| return (B_TRUE); |
| } |
| |
| val = inet_addr(str); |
| if (val != (in_addr_t)-1) { |
| /* Numeric address */ |
| sin->sin_addr.s_addr = val; |
| if (which == RTA_DST) { |
| if (atype == ADDR_TYPE_NET || |
| (atype == ADDR_TYPE_ANY && |
| inet_lnaof(sin->sin_addr) == INADDR_ANY)) { |
| /* This looks like a network address. */ |
| inet_makenetandmask(rcip, ntohl(val), |
| sin); |
| } |
| } |
| return (B_TRUE); |
| } |
| /* Host or net name */ |
| if (which != RTA_DST || atype != ADDR_TYPE_NET) { |
| /* A host name is allowed. */ |
| if ((hp = gethostbyname(str)) != NULL) { |
| *hpp = hp; |
| (void) memmove(&sin->sin_addr, hp->h_addr, |
| hp->h_length); |
| return (B_TRUE); |
| } |
| } |
| if (atype != ADDR_TYPE_HOST) { |
| /* A network name is allowed */ |
| if ((np = getnetbyname(str)) != NULL && |
| (val = np->n_net) != 0) { |
| if (which == RTA_DST) { |
| inet_makenetandmask(rcip, val, sin); |
| } |
| return (B_TRUE); |
| } |
| } |
| syntax_error(gettext("%s: bad value\n"), s); |
| return (B_FALSE); |
| } |
| |
| /* |
| * Interpret an argument as an IPv6 network address of some kind, |
| * returning B_TRUE on success or B_FALSE on failure. |
| * This function will cause an exit() on failure if exit_on_failure is set. |
| * |
| * If the last argument is non-NULL allow a <addr>/<n> syntax and |
| * pass out <n> in *plenp. |
| * If <n> doesn't parse return BAD_ADDR as *plenp. |
| * If no /<n> is present return NO_PREFIX as *plenp. |
| */ |
| static boolean_t |
| in6_getaddr(char *s, struct sockaddr_in6 *sin6, int *plenp, |
| struct hostent **hpp) |
| { |
| struct hostent *hp; |
| char str[BUFSIZ]; |
| int error_num; |
| |
| (void) strlcpy(str, s, sizeof (str)); |
| |
| /* |
| * If plenp is non-NULL, /<n> syntax for netmask is allowed. |
| */ |
| if (plenp != NULL) { |
| char *cp; |
| |
| *plenp = in_getprefixlen(str, IPV6_ABITS); |
| if (*plenp == BAD_ADDR) |
| return (B_FALSE); |
| cp = strchr(str, '/'); |
| if (cp != NULL) |
| *cp = '\0'; |
| } else if (strchr(str, '/') != NULL) { |
| syntax_error(gettext("route: %s: unexpected '/'\n"), str); |
| return (B_FALSE); |
| } |
| |
| (void) memset(sin6, 0, sizeof (struct sockaddr_in6)); |
| sin6->sin6_family = AF_INET6; |
| |
| hp = getipnodebyname(str, AF_INET6, 0, &error_num); |
| if (hp != NULL) { |
| *hpp = hp; |
| (void) memmove(&sin6->sin6_addr, hp->h_addr, hp->h_length); |
| return (B_TRUE); |
| } |
| if (error_num == TRY_AGAIN) { |
| /* |
| * This isn't a problem if we aren't going to use the address |
| * right away. |
| */ |
| if (!exit_on_error) { |
| return (B_TRUE); |
| } |
| syntax_error(gettext("route: %s: bad address (try " |
| "again later)\n"), s); |
| return (B_FALSE); |
| } |
| syntax_error(gettext("route: %s: bad address\n"), s); |
| return (B_FALSE); |
| } |
| |
| /* |
| * Parse <addr>/<n> syntax and return the integer n. |
| * If <addr> is missing or <n> is not a valid integer, this function calls |
| * syntax_error() and returns BAD_ADDR. |
| * if n is not between 0 and max_plen inclusive, this functions calls |
| * syntax_error() and returns BAD_ADDR. |
| * If /<n> is not present, this function returns NO_PREFIX. |
| * The string addr is not modified. |
| */ |
| int |
| in_getprefixlen(char *addr, int max_plen) |
| { |
| int prefixlen; |
| char *str, *end; |
| |
| str = strchr(addr, '/'); |
| if (str == addr) { |
| syntax_error(gettext("route: %s: unexpected '/'\n"), addr); |
| return (BAD_ADDR); |
| } |
| if (str == NULL) |
| return (NO_PREFIX); |
| str++; |
| |
| errno = 0; |
| prefixlen = strtoul(str, &end, 10); |
| if (errno != 0 || str == end) { |
| syntax_error(gettext("route: bad prefix length %s\n"), str); |
| return (BAD_ADDR); |
| } |
| if (prefixlen > max_plen) { |
| syntax_error(gettext("route: prefix length %s out of range\n"), |
| str); |
| return (BAD_ADDR); |
| } |
| return (prefixlen); |
| } |
| |
| /* |
| * Convert a prefix length to a mask. |
| * Returns B_TRUE if ok. B_FALSE otherwise. |
| * Assumes the mask array is zeroed by the caller. |
| */ |
| boolean_t |
| in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask) |
| { |
| if (prefixlen < 0 || prefixlen > maxlen) |
| return (B_FALSE); |
| |
| while (prefixlen > 0) { |
| if (prefixlen >= 8) { |
| *mask++ = 0xFF; |
| prefixlen -= 8; |
| continue; |
| } |
| *mask |= 1 << (8 - prefixlen); |
| prefixlen--; |
| } |
| return (B_TRUE); |
| } |
| |
| void |
| rtmonitor(int argc, char *argv[]) |
| { |
| int n; |
| intmax_t msg[2048 / sizeof (intmax_t)]; |
| |
| if (tflag) |
| exit(0); |
| verbose = B_TRUE; |
| if (argc > 1) { |
| argv++; |
| if (argc == 2 && **argv == '-') { |
| switch (keyword(*argv + 1)) { |
| case K_INET: |
| af = AF_INET; |
| break; |
| case K_LINK: |
| af = AF_LINK; |
| break; |
| case K_INET6: |
| af = AF_INET6; |
| break; |
| default: |
| usage(*argv); |
| /* NOTREACHED */ |
| } |
| } else { |
| usage(*argv); |
| } |
| (void) close(s); |
| s = socket(PF_ROUTE, SOCK_RAW, af); |
| if (s < 0) |
| quit("socket", errno); |
| } |
| for (;;) { |
| n = read(s, msg, sizeof (msg)); |
| if (n <= 0) |
| quit("read", errno); |
| (void) printf("got message of size %d\n", n); |
| print_rtmsg((struct rt_msghdr *)msg, n); |
| } |
| } |
| |
| int |
| rtmsg(rtcmd_irep_t *newrt) |
| { |
| static int seq; |
| int rlen; |
| char *cp = m_rtmsg.m_space; |
| int l; |
| |
| errno = 0; |
| (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg)); |
| |
| if (newrt->ri_cmd == RTM_GET) { |
| newrt->ri_ifp.sa.sa_family = AF_LINK; |
| newrt->ri_addrs |= RTA_IFP; |
| } |
| |
| #define rtm m_rtmsg.m_rtm |
| rtm.rtm_type = newrt->ri_cmd; |
| rtm.rtm_flags = newrt->ri_flags; |
| rtm.rtm_version = RTM_VERSION; |
| rtm.rtm_seq = ++seq; |
| rtm.rtm_addrs = newrt->ri_addrs; |
| rtm.rtm_rmx = newrt->ri_metrics; |
| rtm.rtm_inits = newrt->ri_inits; |
| |
| #define NEXTADDR(w, u) \ |
| if (newrt->ri_addrs & (w)) { \ |
| l = ROUNDUP_LONG(salen(&u.sa)); \ |
| (void) memmove(cp, &(u), l); \ |
| cp += l; \ |
| if (verbose) \ |
| sodump(&(u), #u); \ |
| } |
| NEXTADDR(RTA_DST, newrt->ri_dst); |
| NEXTADDR(RTA_GATEWAY, newrt->ri_gate); |
| NEXTADDR(RTA_NETMASK, newrt->ri_mask); |
| NEXTADDR(RTA_IFP, newrt->ri_ifp); |
| NEXTADDR(RTA_IFA, newrt->ri_ifa); |
| /* |
| * RTA_SRC has overloaded meaning. It can represent the |
| * src address of incoming or outgoing packets. |
| */ |
| NEXTADDR(RTA_SRC, newrt->ri_src); |
| #undef NEXTADDR |
| |
| if (newrt->ri_rtsa_cnt > 0) { |
| /* LINTED: aligned */ |
| rtm_ext_t *rtm_ext = (rtm_ext_t *)cp; |
| tsol_rtsecattr_t *rtsecattr; |
| |
| rtm_ext->rtmex_type = RTMEX_GATEWAY_SECATTR; |
| rtm_ext->rtmex_len = TSOL_RTSECATTR_SIZE(1); |
| |
| rtsecattr = (tsol_rtsecattr_t *)(rtm_ext + 1); |
| rtsecattr->rtsa_cnt = 1; |
| |
| bcopy(&newrt->ri_rtsa, rtsecattr->rtsa_attr, |
| sizeof (newrt->ri_rtsa)); |
| cp = (char *)(rtsecattr->rtsa_attr + 1); |
| } |
| |
| rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; |
| |
| if (verbose) |
| print_rtmsg(&rtm, l); |
| if (debugonly) |
| return (0); |
| if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { |
| switch (errno) { |
| case ESRCH: |
| case EBUSY: |
| case ENOBUFS: |
| case EEXIST: |
| case ENETUNREACH: |
| case EHOSTUNREACH: |
| case EPERM: |
| break; |
| default: |
| perror(gettext("writing to routing socket")); |
| break; |
| } |
| return (-1); |
| } else if (rlen < (int)rtm.rtm_msglen) { |
| (void) fprintf(stderr, |
| gettext("route: write to routing socket got only %d for " |
| "len\n"), rlen); |
| return (-1); |
| } |
| if (newrt->ri_cmd == RTM_GET) { |
| do { |
| l = read(s, (char *)&m_rtmsg, sizeof (m_rtmsg)); |
| } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); |
| if (l < 0) { |
| (void) fprintf(stderr, |
| gettext("route: read from routing socket: %s\n"), |
| strerror(errno)); |
| } else { |
| print_getmsg(newrt, &rtm, l); |
| } |
| } |
| #undef rtm |
| return (0); |
| } |
| |
| static char *msgtypes[] = { |
| "", |
| "RTM_ADD: Add Route", |
| "RTM_DELETE: Delete Route", |
| "RTM_CHANGE: Change Metrics or flags", |
| "RTM_GET: Report Metrics", |
| "RTM_LOSING: Kernel Suspects Partitioning", |
| "RTM_REDIRECT: Told to use different route", |
| "RTM_MISS: Lookup failed on this address", |
| "RTM_LOCK: fix specified metrics", |
| "RTM_OLDADD: caused by SIOCADDRT", |
| "RTM_OLDDEL: caused by SIOCDELRT", |
| "RTM_RESOLVE: Route created by cloning", |
| "RTM_NEWADDR: address being added to iface", |
| "RTM_DELADDR: address being removed from iface", |
| "RTM_IFINFO: iface status change", |
| 0, |
| }; |
| |
| #define NMSGTYPES (sizeof (msgtypes) / sizeof (msgtypes[0])) |
| |
| static char metricnames[] = |
| "\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount" |
| "\1mtu"; |
| static char routeflags[] = |
| "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" |
| "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" |
| "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC"; |
| static char ifnetflags[] = |
| "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP" |
| "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST" |
| "\015MULTI_BCAST\016UNNUMBERED\017DHCP\020PRIVATE" |
| "\021NOXMIT\022NOLOCAL\023DEPRECATED\024ADDRCONF" |
| "\025ROUTER\026NONUD\027ANYCAST\030NORTEXCH\031IPv4\032IPv6" |
| "\033MIP\034NOFAILOVER\035FAILED\036STANDBY\037INACTIVE\040OFFLINE" |
| "\041XRESOLV\042COS\043PREFERRED\044TEMPORARY"; |
| static char addrnames[] = |
| "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC"; |
| |
| void |
| print_rtmsg(struct rt_msghdr *rtm, int msglen) |
| { |
| struct if_msghdr *ifm; |
| struct ifa_msghdr *ifam; |
| |
| if (!verbose) |
| return; |
| if (rtm->rtm_version != RTM_VERSION) { |
| (void) printf("routing message version %d not understood\n", |
| rtm->rtm_version); |
| return; |
| } |
| if (rtm->rtm_msglen != msglen) { |
| (void) printf("message length mismatch, in packet %d, " |
| "returned %d\n", |
| rtm->rtm_msglen, msglen); |
| if (msglen > rtm->rtm_msglen) |
| msglen = rtm->rtm_msglen; |
| } |
| /* |
| * Since rtm->rtm_type is unsigned, we'll just check the case of zero |
| * and the upper-bound of (NMSGTYPES - 1). |
| */ |
| if (rtm->rtm_type == 0 || rtm->rtm_type >= (NMSGTYPES - 1)) { |
| (void) printf("routing message type %d not understood\n", |
| rtm->rtm_type); |
| return; |
| } |
| (void) printf("%s: len %d, ", msgtypes[rtm->rtm_type], msglen); |
| switch (rtm->rtm_type) { |
| case RTM_IFINFO: |
| ifm = (struct if_msghdr *)rtm; |
| (void) printf("if# %d, flags:", ifm->ifm_index); |
| bprintf(stdout, ifm->ifm_flags, ifnetflags); |
| pmsg_addrs((const char *)(ifm + 1), msglen - sizeof (*ifm), |
| ifm->ifm_addrs); |
| break; |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| ifam = (struct ifa_msghdr *)rtm; |
| (void) printf("metric %d, flags:", ifam->ifam_metric); |
| bprintf(stdout, ifam->ifam_flags, routeflags); |
| pmsg_addrs((const char *)(ifam + 1), msglen - sizeof (*ifam), |
| ifam->ifam_addrs); |
| break; |
| default: |
| (void) printf("pid: %ld, seq %d, errno %d, flags:", |
| rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); |
| bprintf(stdout, rtm->rtm_flags, routeflags); |
| pmsg_common(rtm, msglen); |
| break; |
| } |
| } |
| |
| void |
| print_getmsg(rtcmd_irep_t *req_rt, struct rt_msghdr *rtm, int msglen) |
| { |
| struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL; |
| struct sockaddr_dl *ifp = NULL; |
| struct sockaddr *sa; |
| char *cp; |
| int i; |
| |
| (void) printf(" route to: %s\n", routename(&req_rt->ri_dst.sa)); |
| if (rtm->rtm_version != RTM_VERSION) { |
| (void) fprintf(stderr, |
| gettext("routing message version %d not understood\n"), |
| rtm->rtm_version); |
| return; |
| } |
| if (rtm->rtm_msglen > (ushort_t)msglen) { |
| (void) fprintf(stderr, |
| gettext("message length mismatch, in packet %d, " |
| "returned %d\n"), rtm->rtm_msglen, msglen); |
| } |
| if (rtm->rtm_errno) { |
| (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", |
| strerror(rtm->rtm_errno), rtm->rtm_errno); |
| return; |
| } |
| cp = ((char *)(rtm + 1)); |
| if (rtm->rtm_addrs != 0) { |
| for (i = 1; i != 0; i <<= 1) { |
| if (i & rtm->rtm_addrs) { |
| /* LINTED */ |
| sa = (struct sockaddr *)cp; |
| switch (i) { |
| case RTA_DST: |
| dst = sa; |
| break; |
| case RTA_GATEWAY: |
| gate = sa; |
| break; |
| case RTA_NETMASK: |
| mask = sa; |
| break; |
| case RTA_IFP: |
| if (sa->sa_family == AF_LINK && |
| ((struct sockaddr_dl *)sa)-> |
| sdl_nlen != 0) |
| ifp = (struct sockaddr_dl *)sa; |
| break; |
| case RTA_SRC: |
| src = sa; |
| break; |
| } |
| ADVANCE(cp, sa); |
| } |
| |