/* $OpenBSD: route.c,v 1.267 2024/05/09 08:35:40 florian Exp $ */ /* $NetBSD: route.c,v 1.16 1996/04/15 18:27:05 cgd Exp $ */ /* * 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. 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. */ #include #include #include #include #include #include #include #include #include #ifdef BFD #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "keywords.h" #include "show.h" const struct if_status_description if_status_descriptions[] = LINK_STATE_DESCRIPTIONS; union sockunion so_dst, so_gate, so_mask, so_ifa, so_ifp, so_src, so_label, so_source; typedef union sockunion *sup; pid_t pid; int rtm_addrs, s; int forcehost, forcenet, Fflag, nflag, qflag, tflag, Tflag; int iflag, verbose, aflen = sizeof(struct sockaddr_in); int locking, lockrest, debugonly; u_long mpls_flags = MPLS_OP_LOCAL; u_long rtm_inits; uid_t uid; u_int tableid; struct rt_metrics rt_metrics; int flushroutes(int, char **); int newroute(int, char **); int setsource(int, char **); int pushsrc(int, char *, int); int show(int, char *[]); int keycmp(const void *, const void *); int keyword(char *); void monitor(int, char *[]); int nameserver(int, char **); int prefixlen(int, char *); void sockaddr(char *, struct sockaddr *); void sodump(sup, char *); char *priorityname(uint8_t); uint8_t getpriority(char *); void print_getmsg(struct rt_msghdr *, int); #ifdef BFD const char *bfd_state(unsigned int); const char *bfd_diag(unsigned int); const char *bfd_calc_uptime(time_t); void print_bfdmsg(struct rt_msghdr *); void print_sabfd(struct sockaddr_bfd *, int); #endif const char *get_linkstate(int, int); void print_rtmsg(struct rt_msghdr *, int); void pmsg_common(struct rt_msghdr *); void pmsg_addrs(char *, int); void bprintf(FILE *, int, char *); int getaddr(int, int, char *, struct hostent **); void getmplslabel(char *, int); int rtmsg(int, int, int, uint8_t); __dead void usage(char *); void set_metric(char *, int); void inet_makenetandmask(u_int32_t, struct sockaddr_in *, int); void getlabel(char *); int gettable(const char *); int rdomain(int, char **); void print_rtdns(struct sockaddr_rtdns *); void print_rtstatic(struct sockaddr_rtstatic *); void print_rtsearch(struct sockaddr_rtsearch *); void print_80211info(struct if_ieee80211_msghdr *); __dead void usage(char *cp) { extern char *__progname; if (cp) warnx("botched keyword: %s", cp); fprintf(stderr, #ifndef SMALL "usage: %s [-dnqtv] [-T rtable] command [[modifier ...] arg ...]\n", #else "usage: %s [-dnqtv] command [[modifier ...] arg ...]\n", #endif __progname); exit(1); } #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) int main(int argc, char **argv) { unsigned int filter = 0; int ch; int rval = 0; int kw; int af = AF_UNSPEC; #ifndef SMALL int Terr = 0; u_int rtable_any = RTABLE_ANY; #endif if (argc < 2) usage(NULL); #ifndef SMALL tableid = getrtable(); #endif while ((ch = getopt(argc, argv, "dnqtT:v")) != -1) switch (ch) { case 'n': nflag = 1; break; case 'q': qflag = 1; break; case 'v': verbose = 1; break; case 't': tflag = 1; break; #ifndef SMALL case 'T': Terr = gettable(optarg); Tflag = 1; break; #endif case 'd': debugonly = 1; break; default: usage(NULL); /* NOTREACHED */ } argc -= optind; argv += optind; pid = getpid(); uid = geteuid(); if (*argv == NULL) usage(NULL); kw = keyword(*argv); #ifndef SMALL if (Tflag && Terr != 0 && kw != K_ADD) { errno = Terr; err(1, "routing table %u", tableid); } if (kw == K_EXEC) exit(rdomain(argc - 1, argv + 1)); #endif if (kw == K_MONITOR) { while (--argc > 0) { if (**(++argv)== '-') switch (keyword(*argv + 1)) { case K_INET: af = AF_INET; break; case K_INET6: af = AF_INET6; break; case K_MPLS: af = AF_MPLS; break; case K_IFACE: case K_INTERFACE: filter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE) | ROUTE_FILTER(RTM_80211INFO); break; default: usage(*argv); /* NOTREACHED */ } else usage(*argv); } } if (tflag) s = open(_PATH_DEVNULL, O_WRONLY); else s = socket(AF_ROUTE, SOCK_RAW, af); if (s == -1) err(1, "socket"); if (filter != 0) { if (setsockopt(s, AF_ROUTE, ROUTE_MSGFILTER, &filter, sizeof(filter)) == -1) err(1, "setsockopt(ROUTE_MSGFILTER)"); } #ifndef SMALL if (!tflag) { /* force socket onto table user requested */ if (Tflag == 1 && Terr == 0) { if (setsockopt(s, AF_ROUTE, ROUTE_TABLEFILTER, &tableid, sizeof(tableid)) == -1) err(1, "setsockopt(ROUTE_TABLEFILTER)"); } else { if (setsockopt(s, AF_ROUTE, ROUTE_TABLEFILTER, &rtable_any, sizeof(tableid)) == -1) err(1, "setsockopt(ROUTE_TABLEFILTER)"); } } #endif if (pledge("stdio dns route", NULL) == -1) err(1, "pledge"); switch (kw) { case K_SHOW: uid = 0; exit(show(argc, argv)); break; case K_FLUSH: exit(flushroutes(argc, argv)); break; case K_SOURCEADDR: nflag = 1; exit(setsource(argc, argv)); break; } if (pledge("stdio dns", NULL) == -1) err(1, "pledge"); switch (kw) { case K_GET: uid = 0; /* FALLTHROUGH */ case K_CHANGE: case K_ADD: case K_DEL: case K_DELETE: rval = newroute(argc, argv); break; case K_MONITOR: monitor(argc, argv); break; case K_NAMESERVER: rval = nameserver(argc, argv); break; default: usage(*argv); /* NOTREACHED */ } exit(rval); } /* * Purge all entries in the routing tables not * associated with network interfaces. */ int flushroutes(int argc, char **argv) { size_t needed; int mib[7], mcnt, rlen, seqno, af = AF_UNSPEC; char *buf = NULL, *next, *lim = NULL; struct rt_msghdr *rtm; struct sockaddr *sa; uint8_t prio = 0; unsigned int ifindex = 0; if (uid) errx(1, "must be root to alter routing table"); shutdown(s, SHUT_RD); /* Don't want to read back our messages */ while (--argc > 0) { if (**(++argv) == '-') switch (keyword(*argv + 1)) { case K_INET: af = AF_INET; break; case K_INET6: af = AF_INET6; break; case K_LINK: af = AF_LINK; break; case K_MPLS: af = AF_MPLS; break; case K_IFACE: case K_INTERFACE: if (!--argc) usage(1+*argv); ifindex = if_nametoindex(*++argv); if (ifindex == 0) errx(1, "no such interface %s", *argv); break; case K_PRIORITY: if (!--argc) usage(1+*argv); prio = getpriority(*++argv); break; default: usage(*argv); /* NOTREACHED */ } else usage(*argv); } mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = af; mib[4] = NET_RT_DUMP; mib[5] = prio; mib[6] = tableid; mcnt = 7; needed = get_sysctl(mib, mcnt, &buf); lim = buf + needed; if (pledge("stdio dns", NULL) == -1) err(1, "pledge"); if (verbose) { printf("Examining routing table from sysctl\n"); if (af) printf("(address family %s)\n", (*argv + 1)); } if (buf == NULL) return (1); seqno = 0; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; if (verbose) print_rtmsg(rtm, rtm->rtm_msglen); if ((rtm->rtm_flags & (RTF_GATEWAY|RTF_STATIC|RTF_LLINFO)) == 0) continue; if ((rtm->rtm_flags & (RTF_LOCAL|RTF_BROADCAST)) != 0) continue; sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); if (ifindex && rtm->rtm_index != ifindex) continue; if (sa->sa_family == AF_KEY) continue; /* Don't flush SPD */ if (debugonly) continue; rtm->rtm_type = RTM_DELETE; rtm->rtm_seq = seqno; rtm->rtm_tableid = tableid; rlen = write(s, next, rtm->rtm_msglen); if (rlen < (int)rtm->rtm_msglen) { warn("write to routing socket"); printf("got only %d for rlen\n", rlen); break; } seqno++; if (qflag) continue; if (verbose) print_rtmsg(rtm, rlen); else { struct sockaddr *mask, *rti_info[RTAX_MAX]; sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); sa = rti_info[RTAX_DST]; mask = rti_info[RTAX_NETMASK]; p_sockaddr(sa, mask, rtm->rtm_flags, 20); p_sockaddr(rti_info[RTAX_GATEWAY], NULL, RTF_HOST, 20); printf("done\n"); } } free(buf); return (0); } void set_metric(char *value, int key) { long long relative_expire; const char *errstr; int flag = 0; switch (key) { case K_MTU: rt_metrics.rmx_mtu = strtonum(value, 0, UINT_MAX, &errstr); if (errstr) errx(1, "set_metric mtu: %s is %s", value, errstr); flag = RTV_MTU; break; case K_EXPIRE: relative_expire = strtonum(value, 0, INT_MAX, &errstr); if (errstr) errx(1, "set_metric expire: %s is %s", value, errstr); rt_metrics.rmx_expire = relative_expire ? relative_expire + time(NULL) : 0; flag = RTV_EXPIRE; break; case K_HOPCOUNT: case K_RECVPIPE: case K_SENDPIPE: case K_SSTHRESH: case K_RTT: case K_RTTVAR: /* no longer used, only for compatibility */ return; default: errx(1, "king bula sez: set_metric with invalid key"); } rtm_inits |= flag; if (lockrest || locking) rt_metrics.rmx_locks |= flag; if (locking) locking = 0; } int setsource(int argc, char **argv) { struct ifaddrs *ifap, *ifa = NULL; char *cmd; int af = AF_UNSPEC, ret = 0, key; unsigned int ifindex = 0; cmd = argv[0]; if (argc == 1) printsource(AF_UNSPEC, tableid); while (--argc > 0) { if (**(++argv)== '-') { switch (key = keyword(1 + *argv)) { case K_INET: af = AF_INET; aflen = sizeof(struct sockaddr_in); break; case K_INET6: af = AF_INET6; aflen = sizeof(struct sockaddr_in6); break; case K_IFP: if (!--argc) usage(1+*argv); ifindex = if_nametoindex(*++argv); if (ifindex == 0) errx(1, "no such interface %s", *argv); break; default: usage(NULL); } } else break; } if (!(argc == 1 && ifindex == 0) && !(argc == 0 && ifindex != 0)) usage(NULL); if (uid) errx(1, "must be root to alter source address"); if (ifindex) { if (getifaddrs(&ifap) == -1) err(1, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (if_nametoindex(ifa->ifa_name) != ifindex) continue; if (ifa->ifa_addr == NULL || !(ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)) continue; if ((af != AF_UNSPEC) && (ifa->ifa_addr->sa_family != af)) continue; if (ifa->ifa_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) continue; } if (pushsrc(*cmd, routename(ifa->ifa_addr), ifa->ifa_addr->sa_family)) break; } freeifaddrs(ifap); } else { ret = pushsrc(*cmd, *argv, af); } return (ret != 0); } int pushsrc(int cmd, char *src, int af) { int ret = 0; getaddr(RTA_IFA, af, src, NULL); errno = 0; ret = rtmsg(cmd, 0, 0, 0); if (!qflag && ret != 0) printf("sourceaddr %s: %s\n", src, strerror(errno)); return (ret); } int newroute(int argc, char **argv) { char *cmd, *dest = "", *gateway = "", *error; int ishost = 0, ret = 0, attempts, oerrno, flags = RTF_STATIC; int fmask = 0, af = AF_UNSPEC; int key; uint8_t prio = 0; struct hostent *hp = NULL; int sawdest = 0; if (uid) errx(1, "must be root to alter routing table"); cmd = argv[0]; if (*cmd != 'g') shutdown(s, SHUT_RD); /* Don't want to read back our messages */ while (--argc > 0) { if (**(++argv) == '-') { switch (key = keyword(1 + *argv)) { case K_LINK: af = AF_LINK; aflen = sizeof(struct sockaddr_dl); break; case K_INET: af = AF_INET; aflen = sizeof(struct sockaddr_in); break; case K_INET6: af = AF_INET6; aflen = sizeof(struct sockaddr_in6); break; case K_SA: af = PF_ROUTE; aflen = sizeof(struct sockaddr_storage) - 1; break; case K_MPLS: af = AF_MPLS; aflen = sizeof(struct sockaddr_mpls); fmask |= RTF_MPLS; break; case K_MPLSLABEL: if (!--argc) usage(1+*argv); if (af != AF_INET && af != AF_INET6) errx(1, "-mplslabel requires " "-inet or -inet6"); getmplslabel(*++argv, 0); mpls_flags = MPLS_OP_PUSH; flags |= RTF_MPLS; break; case K_IN: if (!--argc) usage(1+*argv); if (af != AF_MPLS) errx(1, "-in requires -mpls"); getmplslabel(*++argv, 1); break; case K_OUT: if (!--argc) usage(1+*argv); if (af != AF_MPLS) errx(1, "-out requires -mpls"); if (mpls_flags == MPLS_OP_LOCAL) errx(1, "-out requires -push, -pop, " "-swap"); getmplslabel(*++argv, 0); flags |= RTF_MPLS; break; case K_POP: if (af != AF_MPLS) errx(1, "-pop requires -mpls"); mpls_flags = MPLS_OP_POP; break; case K_PUSH: if (af != AF_MPLS) errx(1, "-push requires -mpls"); mpls_flags = MPLS_OP_PUSH; break; case K_SWAP: if (af != AF_MPLS) errx(1, "-swap requires -mpls"); mpls_flags = MPLS_OP_SWAP; break; case K_IFACE: case K_INTERFACE: iflag++; break; case K_NOSTATIC: flags &= ~RTF_STATIC; break; case K_LLINFO: flags |= RTF_LLINFO; break; case K_LOCK: locking = 1; break; case K_LOCKREST: lockrest = 1; break; case K_HOST: forcehost++; break; case K_REJECT: flags |= RTF_REJECT; break; case K_BLACKHOLE: flags |= RTF_BLACKHOLE; break; case K_PROTO1: flags |= RTF_PROTO1; break; case K_PROTO2: flags |= RTF_PROTO2; break; case K_CLONING: flags |= RTF_CLONING; break; case K_STATIC: flags |= RTF_STATIC; break; case K_IFA: if (!--argc) usage(1+*argv); getaddr(RTA_IFA, af, *++argv, NULL); break; case K_IFP: if (!--argc) usage(1+*argv); getaddr(RTA_IFP, AF_LINK, *++argv, NULL); break; case K_GATEWAY: if (!--argc) usage(1+*argv); getaddr(RTA_GATEWAY, af, *++argv, NULL); gateway = *argv; break; case K_DST: if (!--argc) usage(1+*argv); ishost = getaddr(RTA_DST, af, *++argv, &hp); dest = *argv; sawdest = 1; break; case K_LABEL: if (!--argc) usage(1+*argv); getlabel(*++argv); break; case K_NETMASK: if (!sawdest) errx(1, "-netmask must follow " "destination parameter"); if (!--argc) usage(1+*argv); getaddr(RTA_NETMASK, af, *++argv, NULL); /* FALLTHROUGH */ case K_NET: forcenet++; break; case K_PREFIXLEN: if (!sawdest) errx(1, "-prefixlen must follow " "destination parameter"); if (!--argc) usage(1+*argv); ishost = prefixlen(af, *++argv); break; case K_MPATH: flags |= RTF_MPATH; 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 (!--argc) usage(1+*argv); set_metric(*++argv, key); break; case K_PRIORITY: if (!--argc) usage(1+*argv); prio = getpriority(*++argv); break; case K_BFD: flags |= RTF_BFD; fmask |= RTF_BFD; break; case K_NOBFD: flags &= ~RTF_BFD; fmask |= RTF_BFD; break; default: usage(1+*argv); /* NOTREACHED */ } } else { if ((rtm_addrs & RTA_DST) == 0) { dest = *argv; sawdest = 1; ishost = getaddr(RTA_DST, af, *argv, &hp); } else if ((rtm_addrs & RTA_GATEWAY) == 0) { gateway = *argv; getaddr(RTA_GATEWAY, af, *argv, &hp); } else usage(NULL); } } if (forcehost) ishost = 1; if (forcenet) ishost = 0; if (forcenet && !(rtm_addrs & RTA_NETMASK)) errx(1, "netmask missing"); flags |= RTF_UP; if (ishost) flags |= RTF_HOST; if (iflag == 0) flags |= RTF_GATEWAY; for (attempts = 1; ; attempts++) { errno = 0; if ((ret = rtmsg(*cmd, flags, fmask, prio)) == 0) break; if (errno != ENETUNREACH && errno != ESRCH) break; if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { hp->h_addr_list++; memcpy(&so_gate.sin.sin_addr, hp->h_addr_list[0], hp->h_length); } else break; } oerrno = errno; if (!qflag && (*cmd != 'g' || ret != 0)) { printf("%s %s %s", cmd, ishost ? "host" : "net", dest); if (*gateway) { printf(": gateway %s", gateway); if (attempts > 1 && ret == 0 && af == AF_INET) printf(" (%s)", inet_ntoa(so_gate.sin.sin_addr)); } if (ret == 0) printf("\n"); if (ret != 0) { switch (oerrno) { case ESRCH: error = "not in table"; break; case EBUSY: error = "entry in use"; break; case ENOBUFS: error = "routing table overflow"; break; default: error = strerror(oerrno); break; } printf(": %s\n", error); } } return (ret != 0); } int show(int argc, char *argv[]) { int af = AF_UNSPEC; char prio = 0; while (--argc > 0) { if (**(++argv)== '-') switch (keyword(*argv + 1)) { case K_INET: af = AF_INET; break; case K_INET6: af = AF_INET6; break; case K_LINK: af = AF_LINK; break; case K_MPLS: af = AF_MPLS; break; case K_GATEWAY: Fflag = 1; break; case K_LABEL: if (!--argc) usage(1+*argv); getlabel(*++argv); break; case K_PRIORITY: if (!--argc) usage(1+*argv); prio = getpriority(*++argv); break; default: usage(*argv); /* NOTREACHED */ } else usage(*argv); } p_rttables(af, tableid, prio); return (0); } void inet_makenetandmask(u_int32_t net, struct sockaddr_in *sin, int bits) { u_int32_t mask; rtm_addrs |= RTA_NETMASK; if (bits == 0 && net == 0) mask = 0; else { if (bits == 0) bits = 32; mask = 0xffffffff << (32 - bits); net &= mask; } sin->sin_addr.s_addr = htonl(net); sin = &so_mask.sin; sin->sin_addr.s_addr = htonl(mask); sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); } /* * Interpret an argument as a network address of some kind, * returning 1 if a host address, 0 if a network address. */ int getaddr(int which, int af, char *s, struct hostent **hpp) { sup su = NULL; struct hostent *hp; int aflength, afamily, bits; if (af == AF_UNSPEC) { if (strchr(s, ':') != NULL) { af = AF_INET6; aflen = sizeof(struct sockaddr_in6); } else { af = AF_INET; aflen = sizeof(struct sockaddr_in); } } /* local copy of len and af so we can change it */ aflength = aflen; afamily = af; rtm_addrs |= which; switch (which) { case RTA_DST: su = &so_dst; break; case RTA_GATEWAY: su = &so_gate; break; case RTA_NETMASK: su = &so_mask; break; case RTA_IFP: su = &so_ifp; aflength = sizeof(struct sockaddr_dl); afamily = AF_LINK; break; case RTA_IFA: su = &so_ifa; break; default: errx(1, "internal error"); /* NOTREACHED */ } memset(su, 0, sizeof(union sockunion)); su->sa.sa_len = aflength; su->sa.sa_family = afamily; if (strcmp(s, "default") == 0) { switch (which) { case RTA_DST: forcenet++; getaddr(RTA_NETMASK, af, s, NULL); break; case RTA_NETMASK: su->sa.sa_len = 0; } return (0); } switch (afamily) { case AF_INET6: { struct addrinfo hints, *res; char buf[ sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128") ]; char *sep; int error; if (strlcpy(buf, s, sizeof buf) >= sizeof buf) { errx(1, "%s: bad value", s); } sep = strchr(buf, '/'); if (sep != NULL) *sep++ = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_family = afamily; /*AF_INET6*/ hints.ai_flags = AI_NUMERICHOST; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ error = getaddrinfo(buf, "0", &hints, &res); if (error) { hints.ai_flags = 0; error = getaddrinfo(buf, "0", &hints, &res); if (error) errx(1, "%s: %s", s, gai_strerror(error)); } if (res->ai_next) errx(1, "%s: resolved to multiple values", s); memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6)); freeaddrinfo(res); #ifdef __KAME__ if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr) || IN6_IS_ADDR_MC_INTFACELOCAL(&su->sin6.sin6_addr)) && su->sin6.sin6_scope_id) { *(u_int16_t *)&su->sin6.sin6_addr.s6_addr[2] = htons(su->sin6.sin6_scope_id); su->sin6.sin6_scope_id = 0; } #endif if (hints.ai_flags == AI_NUMERICHOST) { if (which == RTA_DST) { if (sep == NULL && su->sin6.sin6_scope_id == 0 && IN6_IS_ADDR_UNSPECIFIED(&su->sin6.sin6_addr)) sep = "0"; if (sep == NULL || prefixlen(AF_INET6, sep)) return (1); } return (0); } else return (1); } case AF_LINK: su->sdl.sdl_index = if_nametoindex(s); memset(&su->sdl.sdl_data, 0, sizeof(su->sdl.sdl_data)); return (1); case AF_MPLS: errx(1, "mpls labels require -in or -out switch"); case PF_ROUTE: su->sa.sa_len = sizeof(struct sockaddr_storage) - 1; sockaddr(s, &su->sa); return (1); case AF_INET: if (hpp != NULL) *hpp = NULL; if (which == RTA_DST && !forcehost) { bits = inet_net_pton(AF_INET, s, &su->sin.sin_addr, sizeof(su->sin.sin_addr)); if (bits == 32) return (1); if (bits >= 0) { inet_makenetandmask(ntohl( su->sin.sin_addr.s_addr), &su->sin, bits); return (0); } } else if (which != RTA_DST || !forcenet) if (inet_pton(AF_INET, s, &su->sin.sin_addr) == 1) return (1); hp = gethostbyname(s); if (hp == NULL) errx(1, "%s: bad address", s); if (hpp != NULL) *hpp = hp; su->sin.sin_addr = *(struct in_addr *)hp->h_addr; return (1); default: errx(1, "%d: bad address family", afamily); /* NOTREACHED */ } } void getmplslabel(char *s, int in) { sup su = NULL; const char *errstr; u_int32_t label; label = strtonum(s, 0, MPLS_LABEL_MAX, &errstr); if (errstr) errx(1, "bad label: %s is %s", s, errstr); if (in) { rtm_addrs |= RTA_DST; su = &so_dst; su->smpls.smpls_label = htonl(label << MPLS_LABEL_OFFSET); } else { rtm_addrs |= RTA_SRC; su = &so_src; su->smpls.smpls_label = htonl(label << MPLS_LABEL_OFFSET); } su->sa.sa_len = sizeof(struct sockaddr_mpls); su->sa.sa_family = AF_MPLS; } int prefixlen(int af, char *s) { const char *errstr; int len, q, r; int max; switch (af) { case AF_INET: max = sizeof(struct in_addr) * 8; break; case AF_INET6: max = sizeof(struct in6_addr) * 8; break; default: errx(1, "prefixlen is not supported with af %d", af); /* NOTREACHED */ } rtm_addrs |= RTA_NETMASK; len = strtonum(s, 0, max, &errstr); if (errstr) errx(1, "prefixlen %s is %s", s, errstr); q = len >> 3; r = len & 7; switch (af) { case AF_INET: memset(&so_mask, 0, sizeof(so_mask)); so_mask.sin.sin_family = AF_INET; so_mask.sin.sin_len = sizeof(struct sockaddr_in); if (len != 0) so_mask.sin.sin_addr.s_addr = htonl(0xffffffff << (32 - len)); break; case AF_INET6: so_mask.sin6.sin6_family = AF_INET6; so_mask.sin6.sin6_len = sizeof(struct sockaddr_in6); memset((void *)&so_mask.sin6.sin6_addr, 0, sizeof(so_mask.sin6.sin6_addr)); if (q > 0) memset((void *)&so_mask.sin6.sin6_addr, 0xff, q); if (r > 0) *((u_char *)&so_mask.sin6.sin6_addr + q) = (0xff00 >> r) & 0xff; break; } return (len == max); } void monitor(int argc, char *argv[]) { int n; char msg[2048]; time_t now; char *ct; verbose = 1; for (;;) { if ((n = read(s, msg, sizeof(msg))) == -1) { if (errno == EINTR) continue; err(1, "read"); } now = time(NULL); ct = ctime(&now); if (ct) printf("got message of size %d on %s", n, ct); else printf("got message of size %d on %lld\n", n, now); print_rtmsg((struct rt_msghdr *)msg, n); } } int nameserver(int argc, char *argv[]) { struct rt_msghdr rtm; struct sockaddr_rtdns rtdns; struct iovec iov[3]; struct addrinfo hints, *res; struct in_addr ns4[5]; struct in6_addr ns6[5]; size_t ns4_count = 0, ns6_count = 0; long pad = 0; unsigned int if_index; int error = 0, iovcnt = 0, padlen, i; char *if_name, buf[INET6_ADDRSTRLEN]; argc--; argv++; if (argc == 0) usage(NULL); if_name = *argv; argc--; argv++; if ((if_index = if_nametoindex(if_name)) == 0) errx(1, "unknown interface: %s", if_name); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif for (; argc > 0 && ns4_count + ns6_count < 5; argc--, argv++) { error = getaddrinfo(*argv, NULL, &hints, &res); if (error) { errx(1, "%s", gai_strerror(error)); } if (res == NULL) { errx(1, "%s: unknown", *argv); } switch (res->ai_addr->sa_family) { case AF_INET: memcpy(&ns4[ns4_count++], &((struct sockaddr_in *)res->ai_addr)->sin_addr, sizeof(struct in_addr)); break; case AF_INET6: memcpy(&ns6[ns6_count++], &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); break; default: errx(1, "unknown address family"); } freeaddrinfo(res); } if (argc > 0) warnx("ignoring additional nameservers"); if (verbose) { for (i = 0; i < ns4_count; i++) warnx("v4: %s", inet_ntop(AF_INET, &ns4[i], buf, sizeof(buf))); for (i = 0; i < ns6_count; i++) warnx("v6: %s", inet_ntop(AF_INET6, &ns6[i], buf, sizeof(buf))); } memset(&rtm, 0, sizeof(rtm)); rtm.rtm_version = RTM_VERSION; rtm.rtm_type = RTM_PROPOSAL; rtm.rtm_msglen = sizeof(rtm); rtm.rtm_tableid = tableid; rtm.rtm_index = if_index; rtm.rtm_seq = 1; rtm.rtm_priority = RTP_PROPOSAL_STATIC; rtm.rtm_addrs = RTA_DNS; rtm.rtm_flags = RTF_UP; iov[iovcnt].iov_base = &rtm; iov[iovcnt++].iov_len = sizeof(rtm); iov[iovcnt].iov_base = &rtdns; iov[iovcnt++].iov_len = sizeof(rtdns); rtm.rtm_msglen += sizeof(rtdns); padlen = ROUNDUP(sizeof(rtdns)) - sizeof(rtdns); if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; rtm.rtm_msglen += padlen; } memset(&rtdns, 0, sizeof(rtdns)); rtdns.sr_family = AF_INET; rtdns.sr_len = 2 + ns4_count * sizeof(struct in_addr); memcpy(rtdns.sr_dns, ns4, rtdns.sr_len - 2); if (debugonly) return (0); if (writev(s, iov, iovcnt) == -1) { warn("failed to send route message"); error = 1; } rtm.rtm_seq++; memset(&rtdns, 0, sizeof(rtdns)); rtdns.sr_family = AF_INET6; rtdns.sr_len = 2 + ns6_count * sizeof(struct in6_addr); memcpy(rtdns.sr_dns, ns6, rtdns.sr_len - 2); if (writev(s, iov, iovcnt) == -1) { warn("failed to send route message"); error = 1; } return (error); } struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; int rtmsg(int cmd, int flags, int fmask, uint8_t prio) { static int seq; char *cp = m_rtmsg.m_space; int l; #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ l = ROUNDUP(u.sa.sa_len); \ memcpy(cp, &(u), l); \ cp += l; \ if (verbose) \ sodump(&(u), #u); \ } errno = 0; memset(&m_rtmsg, 0, sizeof(m_rtmsg)); if (cmd == 'a') cmd = RTM_ADD; else if (cmd == 'c') cmd = RTM_CHANGE; else if (cmd == 'g') { cmd = RTM_GET; if (so_ifp.sa.sa_family == AF_UNSPEC) { so_ifp.sa.sa_family = AF_LINK; so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); rtm_addrs |= RTA_IFP; } } else if (cmd == 's') { cmd = RTM_SOURCE; } else cmd = RTM_DELETE; #define rtm m_rtmsg.m_rtm rtm.rtm_type = cmd; rtm.rtm_flags = flags; rtm.rtm_fmask = fmask; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = ++seq; rtm.rtm_addrs = rtm_addrs; rtm.rtm_rmx = rt_metrics; rtm.rtm_inits = rtm_inits; rtm.rtm_tableid = tableid; rtm.rtm_priority = prio; rtm.rtm_mpls = mpls_flags; rtm.rtm_hdrlen = sizeof(rtm); /* store addresses in ascending order of RTA values */ NEXTADDR(RTA_DST, so_dst); NEXTADDR(RTA_GATEWAY, so_gate); NEXTADDR(RTA_NETMASK, so_mask); NEXTADDR(RTA_IFP, so_ifp); NEXTADDR(RTA_IFA, so_ifa); NEXTADDR(RTA_SRC, so_src); NEXTADDR(RTA_LABEL, so_label); rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; if (verbose) print_rtmsg(&rtm, l); if (debugonly) return (0); if (write(s, &m_rtmsg, l) != l) { return (-1); } if (cmd == RTM_GET) { do { l = read(s, &m_rtmsg, sizeof(m_rtmsg)); } while (l > 0 && (rtm.rtm_version != RTM_VERSION || rtm.rtm_seq != seq || rtm.rtm_pid != pid)); if (l == -1) warn("read from routing socket"); else print_getmsg(&rtm, l); } #undef rtm return (0); } 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_RESOLVE: Route created by cloning", "RTM_NEWADDR: address being added to iface", "RTM_DELADDR: address being removed from iface", "RTM_IFINFO: iface status change", "RTM_IFANNOUNCE: iface arrival/departure", "RTM_DESYNC: route socket overflow", "RTM_INVALIDATE: invalidate cache of L2 route", "RTM_BFD: bidirectional forwarding detection", "RTM_PROPOSAL: config proposal", "RTM_CHGADDRATTR: address attributes being changed", "RTM_80211INFO: 802.11 iface status change" }; char metricnames[] = "\011priority\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; char routeflags[] = "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010XMASK_PRESENT" "\011CLONING\012MULTICAST\013LLINFO\014STATIC\015BLACKHOLE\016PROTO3\017PROTO2" "\020PROTO1\021CLONED\022CACHED\023MPATH\025MPLS\026LOCAL\027BROADCAST" "\030CONNECTED\031BFD"; char ifnetflags[] = "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6STATICARP\7RUNNING\010NOARP\011PPROMISC" "\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST" "\23AUTOCONF6TEMP\24MPLS\25WOL\26AUTOCONF6\27INET6_NOSOII\30AUTOCONF4"; char addrnames[] = "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD\011SRC\012SRCMASK\013LABEL\014BFD\015DNS\016STATIC\017SEARCH"; char ieee80211flags[] = "\1ASCAN\2SIBSS\011WEPON\012IBSSON\013PMGTON\014DESBSSID\016ROAMING" "\020TXPOW_FIXED\021TXPOW_AUTO\022SHSLOT\023SHPREAMBLE\024QOS" "\025USEPROT\026RSNON\027PSK\030COUNTERM\031MFPR\032HTON\033PBAR" "\034BGSCAN\035AUTO_JOIN\036VHTON"; char ieee80211xflags[] = "\1TX_MGMT_ONLY"; const char * get_linkstate(int mt, int link_state) { const struct if_status_description *p; static char buf[8]; for (p = if_status_descriptions; p->ifs_string != NULL; p++) { if (LINK_STATE_DESC_MATCH(p, mt, link_state)) return (p->ifs_string); } snprintf(buf, sizeof(buf), "[#%d]", link_state); return buf; } void print_rtmsg(struct rt_msghdr *rtm, int msglen) { long long relative_expire; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct if_announcemsghdr *ifan; char ifname[IF_NAMESIZE]; if (verbose == 0) return; if (rtm->rtm_version != RTM_VERSION) { warnx("routing message version %u not understood", rtm->rtm_version); return; } if (rtm->rtm_type > 0 && rtm->rtm_type < sizeof(msgtypes)/sizeof(msgtypes[0])) printf("%s", msgtypes[rtm->rtm_type]); else printf("[rtm_type %u out of range]", rtm->rtm_type); printf(": len %u", rtm->rtm_msglen); switch (rtm->rtm_type) { case RTM_DESYNC: printf("\n"); break; case RTM_IFINFO: ifm = (struct if_msghdr *)rtm; printf(", if# %u, ", ifm->ifm_index); if (if_indextoname(ifm->ifm_index, ifname) != NULL) printf("name %s, ", ifname); printf("link: %s, mtu: %u, flags:", get_linkstate(ifm->ifm_data.ifi_type, ifm->ifm_data.ifi_link_state), ifm->ifm_data.ifi_mtu); bprintf(stdout, ifm->ifm_flags | (ifm->ifm_xflags << 16), ifnetflags); pmsg_addrs((char *)ifm + ifm->ifm_hdrlen, ifm->ifm_addrs); break; case RTM_80211INFO: printf(", if# %u, ", rtm->rtm_index); if (if_indextoname(rtm->rtm_index, ifname) != NULL) printf("name %s, ", ifname); print_80211info((struct if_ieee80211_msghdr *)rtm); break; case RTM_NEWADDR: case RTM_DELADDR: case RTM_CHGADDRATTR: ifam = (struct ifa_msghdr *)rtm; printf(", if# %u, ", ifam->ifam_index); if (if_indextoname(ifam->ifam_index, ifname) != NULL) printf("name %s, ", ifname); printf("metric %d, flags:", ifam->ifam_metric); bprintf(stdout, ifam->ifam_flags, routeflags); pmsg_addrs((char *)ifam + ifam->ifam_hdrlen, ifam->ifam_addrs); break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)rtm; printf(", if# %u, name %s, what: ", ifan->ifan_index, ifan->ifan_name); switch (ifan->ifan_what) { case IFAN_ARRIVAL: printf("arrival"); break; case IFAN_DEPARTURE: printf("departure"); break; default: printf("#%u", ifan->ifan_what); break; } printf("\n"); break; #ifdef BFD case RTM_BFD: print_bfdmsg(rtm); break; #endif case RTM_PROPOSAL: printf(", source "); switch (rtm->rtm_priority) { case RTP_PROPOSAL_STATIC: printf("static"); break; case RTP_PROPOSAL_DHCLIENT: printf("dhcp"); break; case RTP_PROPOSAL_SLAAC: printf("slaac"); break; case RTP_PROPOSAL_UMB: printf("umb"); break; case RTP_PROPOSAL_PPP: printf("ppp"); break; case RTP_PROPOSAL_SOLICIT: printf("solicit"); break; default: printf("unknown"); break; } printf(", table %u, if# %u, ", rtm->rtm_tableid, rtm->rtm_index); if (if_indextoname(rtm->rtm_index, ifname) != NULL) printf("name %s, ", ifname); printf("pid: %ld, seq %d, errno %d\nflags:", (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); bprintf(stdout, rtm->rtm_flags, routeflags); printf("\nfmask:"); bprintf(stdout, rtm->rtm_fmask, routeflags); if (verbose) { #define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') relative_expire = rtm->rtm_rmx.rmx_expire ? rtm->rtm_rmx.rmx_expire - time(NULL) : 0; printf("\nuse: %8llu mtu: %8u%c expire: %8lld%c", rtm->rtm_rmx.rmx_pksent, rtm->rtm_rmx.rmx_mtu, lock(MTU), relative_expire, lock(EXPIRE)); #undef lock } printf("\nlocks: "); bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); printf(" inits: "); bprintf(stdout, rtm->rtm_inits, metricnames); pmsg_addrs(((char *)rtm + rtm->rtm_hdrlen), rtm->rtm_addrs & ~(RTA_STATIC | RTA_SEARCH | RTA_DNS)); if(!(rtm->rtm_addrs & (RTA_STATIC | RTA_SEARCH | RTA_DNS))) break; printf("proposals: "); bprintf(stdout, rtm->rtm_addrs & (RTA_STATIC | RTA_SEARCH | RTA_DNS), addrnames); putchar('\n'); if (rtm->rtm_addrs & RTA_STATIC) { char *next = (char *)rtm + rtm->rtm_hdrlen; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_rtstatic *rtstatic; sa = (struct sockaddr *)next; get_rtaddrs(rtm->rtm_addrs, sa, rti_info); rtstatic = (struct sockaddr_rtstatic *) rti_info[RTAX_STATIC]; if (rtstatic != NULL) print_rtstatic(rtstatic); } if (rtm->rtm_addrs & RTA_SEARCH) { char *next = (char *)rtm + rtm->rtm_hdrlen; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_rtsearch *rtsearch; sa = (struct sockaddr *)next; get_rtaddrs(rtm->rtm_addrs, sa, rti_info); rtsearch = (struct sockaddr_rtsearch *) rti_info[RTAX_SEARCH]; if (rtsearch != NULL) print_rtsearch(rtsearch); } if (rtm->rtm_addrs & RTA_DNS) { char *next = (char *)rtm + rtm->rtm_hdrlen; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_rtdns *rtdns; sa = (struct sockaddr *)next; get_rtaddrs(rtm->rtm_addrs, sa, rti_info); rtdns = (struct sockaddr_rtdns *)rti_info[RTAX_DNS]; if (rtdns != NULL) print_rtdns(rtdns); } putchar('\n'); break; default: printf(", priority %u, table %u, if# %u, ", rtm->rtm_priority, rtm->rtm_tableid, rtm->rtm_index); if (if_indextoname(rtm->rtm_index, ifname) != NULL) printf("name %s, ", ifname); printf("pid: %ld, seq %d, errno %d\nflags:", (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); bprintf(stdout, rtm->rtm_flags, routeflags); printf("\nfmask:"); bprintf(stdout, rtm->rtm_fmask, routeflags); if (verbose) { #define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') relative_expire = rtm->rtm_rmx.rmx_expire ? rtm->rtm_rmx.rmx_expire - time(NULL) : 0; printf("\nuse: %8llu mtu: %8u%c expire: %8lld%c", rtm->rtm_rmx.rmx_pksent, rtm->rtm_rmx.rmx_mtu, lock(MTU), relative_expire, lock(EXPIRE)); #undef lock } pmsg_common(rtm); } } char * priorityname(uint8_t prio) { switch (prio) { case RTP_NONE: return ("none"); case RTP_LOCAL: return ("local"); case RTP_CONNECTED: return ("connected"); case RTP_STATIC: return ("static"); case RTP_OSPF: return ("ospf"); case RTP_ISIS: return ("is-is"); case RTP_RIP: return ("rip"); case RTP_BGP: return ("bgp"); case RTP_DEFAULT: return ("default"); default: return (""); } } uint8_t getpriority(char *priostr) { const char *errstr; uint8_t prio; switch (keyword(priostr)) { case K_LOCAL: prio = RTP_LOCAL; break; case K_CONNECTED: prio = RTP_CONNECTED; break; case K_STATIC: prio = RTP_STATIC; break; case K_OSPF: prio = RTP_OSPF; break; case K_RIP: prio = RTP_RIP; break; case K_BGP: prio = RTP_BGP; break; default: prio = strtonum(priostr, -RTP_MAX, RTP_MAX, &errstr); if (errstr) errx(1, "priority is %s: %s", errstr, priostr); } return (prio); } void print_getmsg(struct rt_msghdr *rtm, int msglen) { long long relative_expire; struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *ifa = NULL; struct sockaddr_dl *ifp = NULL; struct sockaddr_rtlabel *sa_rl = NULL; #ifdef BFD struct sockaddr_bfd *sa_bfd = NULL; #endif struct sockaddr *mpls = NULL; struct sockaddr *sa; char *cp; int i; printf(" route to: %s\n", routename(&so_dst.sa)); if (rtm->rtm_version != RTM_VERSION) { warnx("routing message version %u not understood", rtm->rtm_version); return; } if (rtm->rtm_msglen > msglen) warnx("message length mismatch, in packet %u, returned %d", rtm->rtm_msglen, msglen); if (rtm->rtm_errno) { warnx("RTM_GET: %s (errno %d)", strerror(rtm->rtm_errno), rtm->rtm_errno); return; } cp = ((char *)rtm + rtm->rtm_hdrlen); if (rtm->rtm_addrs) for (i = 1; i; i <<= 1) if (i & rtm->rtm_addrs) { 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_IFA: ifa = sa; break; case RTA_IFP: if (sa->sa_family == AF_LINK && ((struct sockaddr_dl *)sa)->sdl_nlen) ifp = (struct sockaddr_dl *)sa; break; case RTA_SRC: mpls = sa; break; case RTA_LABEL: sa_rl = (struct sockaddr_rtlabel *)sa; break; #ifdef BFD case RTA_BFD: sa_bfd = (struct sockaddr_bfd *)sa; break; #endif } ADVANCE(cp, sa); } if (dst && mask) mask->sa_family = dst->sa_family; /* XXX */ if (dst) printf("destination: %s\n", routename(dst)); if (mask) { int savenflag = nflag; nflag = 1; printf(" mask: %s\n", routename(mask)); nflag = savenflag; } if (gate && rtm->rtm_flags & RTF_GATEWAY) printf(" gateway: %s\n", routename(gate)); if (ifp) printf(" interface: %.*s\n", ifp->sdl_nlen, ifp->sdl_data); if (ifa) printf(" if address: %s\n", routename(ifa)); if (mpls) { printf(" mpls label: %s %s\n", mpls_op(rtm->rtm_mpls), routename(mpls)); } printf(" priority: %u (%s)\n", rtm->rtm_priority, priorityname(rtm->rtm_priority)); printf(" flags: "); bprintf(stdout, rtm->rtm_flags, routeflags); printf("\n"); if (sa_rl != NULL) printf(" label: %s\n", sa_rl->sr_label); #ifdef BFD if (sa_bfd) print_sabfd(sa_bfd, rtm->rtm_fmask); #endif #define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') relative_expire = rtm->rtm_rmx.rmx_expire ? rtm->rtm_rmx.rmx_expire - time(NULL) : 0; printf(" use mtu expire\n"); printf("%8llu %8u%c %8lld%c\n", rtm->rtm_rmx.rmx_pksent, rtm->rtm_rmx.rmx_mtu, lock(MTU), relative_expire, lock(EXPIRE)); #undef lock #define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) if (verbose) pmsg_common(rtm); else if (rtm->rtm_addrs &~ RTA_IGN) { printf("sockaddrs: "); bprintf(stdout, rtm->rtm_addrs, addrnames); putchar('\n'); } #undef RTA_IGN } #ifdef BFD const char * bfd_state(unsigned int state) { switch (state) { case BFD_STATE_ADMINDOWN: return("admindown"); break; case BFD_STATE_DOWN: return("down"); break; case BFD_STATE_INIT: return("init"); break; case BFD_STATE_UP: return("up"); break; } return "invalid"; } const char * bfd_diag(unsigned int diag) { switch (diag) { case BFD_DIAG_NONE: return("none"); break; case BFD_DIAG_EXPIRED: return("expired"); break; case BFD_DIAG_ECHO_FAILED: return("echo-failed"); break; case BFD_DIAG_NEIGHBOR_SIGDOWN: return("neighbor-down"); break; case BFD_DIAG_FIB_RESET: return("fib-reset"); break; case BFD_DIAG_PATH_DOWN: return("path-down"); break; case BFD_DIAG_CONCAT_PATH_DOWN: return("concat-path-down"); break; case BFD_DIAG_ADMIN_DOWN: return("admindown"); break; case BFD_DIAG_CONCAT_REVERSE_DOWN: return("concat-reverse-down"); break; } return "invalid"; } const char * bfd_calc_uptime(time_t time) { static char buf[256]; struct tm *tp; const char *fmt; if (time > 2*86400) fmt = "%dd%kh%Mm%Ss"; else if (time > 2*3600) fmt = "%kh%Mm%Ss"; else if (time > 2*60) fmt = "%Mm%Ss"; else fmt = "%Ss"; tp = localtime(&time); if (tp) (void)strftime(buf, sizeof(buf), fmt, tp); else buf[0] = '\0'; return (buf); } void print_bfdmsg(struct rt_msghdr *rtm) { struct bfd_msghdr *bfdm = (struct bfd_msghdr *)rtm; printf("\n"); print_sabfd(&bfdm->bm_sa, rtm->rtm_fmask); pmsg_addrs(((char *)rtm + rtm->rtm_hdrlen), rtm->rtm_addrs); } void print_sabfd(struct sockaddr_bfd *sa_bfd, int fmask) { struct timeval tv; gettimeofday(&tv, NULL); printf(" BFD:"); /* only show the state, unless verbose or -bfd */ if (!verbose && ((fmask & RTF_BFD) != RTF_BFD)) { printf(" %s\n", bfd_state(sa_bfd->bs_state)); return; } switch (sa_bfd->bs_mode) { case BFD_MODE_ASYNC: printf(" async"); break; case BFD_MODE_DEMAND: printf(" demand"); break; default: printf(" unknown %u", sa_bfd->bs_mode); break; } printf(" state %s", bfd_state(sa_bfd->bs_state)); printf(" remote %s", bfd_state(sa_bfd->bs_remotestate)); printf(" laststate %s", bfd_state(sa_bfd->bs_laststate)); printf(" error %u", sa_bfd->bs_error); printf("\n "); printf(" diag %s", bfd_diag(sa_bfd->bs_localdiag)); printf(" remote %s", bfd_diag(sa_bfd->bs_remotediag)); printf("\n "); printf(" discr %u", sa_bfd->bs_localdiscr); printf(" remote %u", sa_bfd->bs_remotediscr); printf("\n "); printf(" uptime %s", bfd_calc_uptime(tv.tv_sec - sa_bfd->bs_uptime)); if (sa_bfd->bs_lastuptime) printf(" last state time %s", bfd_calc_uptime(sa_bfd->bs_lastuptime)); printf("\n "); printf(" mintx %u", sa_bfd->bs_mintx); printf(" minrx %u", sa_bfd->bs_minrx); printf(" minecho %u", sa_bfd->bs_minecho); printf(" multiplier %u", sa_bfd->bs_multiplier); printf("\n"); } #endif /* BFD */ void pmsg_common(struct rt_msghdr *rtm) { printf("\nlocks: "); bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); printf(" inits: "); bprintf(stdout, rtm->rtm_inits, metricnames); pmsg_addrs(((char *)rtm + rtm->rtm_hdrlen), rtm->rtm_addrs); } void pmsg_addrs(char *cp, int addrs) { struct sockaddr *sa; int family = AF_UNSPEC; int i; char *p; if (addrs != 0) { printf("\nsockaddrs: "); bprintf(stdout, addrs, addrnames); putchar('\n'); /* first run, search for address family */ p = cp; for (i = 1; i; i <<= 1) if (i & addrs) { sa = (struct sockaddr *)p; if (family == AF_UNSPEC) switch (i) { case RTA_DST: case RTA_IFA: family = sa->sa_family; } ADVANCE(p, sa); } /* second run, set address family for mask and print */ p = cp; for (i = 1; i; i <<= 1) if (i & addrs) { sa = (struct sockaddr *)p; if (family != AF_UNSPEC) switch (i) { case RTA_NETMASK: sa->sa_family = family; } printf(" %s", routename(sa)); ADVANCE(p, sa); } } putchar('\n'); fflush(stdout); } void bprintf(FILE *fp, int b, char *s) { int i; int gotsome = 0; if (b == 0) return; while ((i = *s++)) { if ((b & (1 << (i-1)))) { if (gotsome == 0) i = '<'; else i = ','; putc(i, fp); gotsome = 1; for (; (i = *s) > 32; s++) putc(i, fp); } else while (*s > 32) s++; } if (gotsome) putc('>', fp); } int keycmp(const void *key, const void *kt) { return (strcmp(key, ((struct keytab *)kt)->kt_cp)); } int keyword(char *cp) { struct keytab *kt; kt = bsearch(cp, keywords, sizeof(keywords)/sizeof(keywords[0]), sizeof(keywords[0]), keycmp); if (!kt) return (0); return (kt->kt_i); } void sodump(sup su, char *which) { switch (su->sa.sa_family) { case AF_LINK: printf("%s: link %s; ", which, link_ntoa(&su->sdl)); break; case AF_INET: printf("%s: inet %s; ", which, inet_ntoa(su->sin.sin_addr)); break; case AF_INET6: { char ntop_buf[NI_MAXHOST]; printf("%s: inet6 %s; ", which, inet_ntop(AF_INET6, &su->sin6.sin6_addr, ntop_buf, sizeof(ntop_buf))); break; } } fflush(stdout); } /* States*/ #define VIRGIN 0 #define GOTONE 1 #define GOTTWO 2 /* Inputs */ #define DIGIT (4*0) #define END (4*1) #define DELIM (4*2) void sockaddr(char *addr, struct sockaddr *sa) { char *cp = (char *)sa; int size = sa->sa_len; char *cplim = cp + size; int byte = 0, state = VIRGIN, new = 0; memset(cp, 0, size); cp++; do { if ((*addr >= '0') && (*addr <= '9')) { new = *addr - '0'; } else if ((*addr >= 'a') && (*addr <= 'f')) { new = *addr - 'a' + 10; } else if ((*addr >= 'A') && (*addr <= 'F')) { new = *addr - 'A' + 10; } else if (*addr == '\0') state |= END; else state |= DELIM; addr++; switch (state /* | INPUT */) { case GOTTWO | DIGIT: *cp++ = byte; /*FALLTHROUGH*/ case VIRGIN | DIGIT: state = GOTONE; byte = new; continue; case GOTONE | DIGIT: state = GOTTWO; byte = new + (byte << 4); continue; default: /* | DELIM */ state = VIRGIN; *cp++ = byte; byte = 0; continue; case GOTONE | END: case GOTTWO | END: *cp++ = byte; /* FALLTHROUGH */ case VIRGIN | END: break; } break; } while (cp < cplim); sa->sa_len = cp - (char *)sa; } void getlabel(char *name) { so_label.rtlabel.sr_len = sizeof(so_label.rtlabel); so_label.rtlabel.sr_family = AF_UNSPEC; if (strlcpy(so_label.rtlabel.sr_label, name, sizeof(so_label.rtlabel.sr_label)) >= sizeof(so_label.rtlabel.sr_label)) errx(1, "label too long"); rtm_addrs |= RTA_LABEL; } #ifndef SMALL int gettable(const char *s) { const char *errstr; struct rt_tableinfo info; int mib[6]; size_t len; tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr); if (errstr) errx(1, "invalid table id: %s", errstr); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = 0; mib[4] = NET_RT_TABLE; mib[5] = tableid; len = sizeof(info); if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) return (errno); else return (0); } int rdomain(int argc, char **argv) { if (!argc) usage(NULL); if (setrtable(tableid) == -1) err(1, "setrtable"); execvp(*argv, argv); warn("%s", argv[0]); return (errno == ENOENT ? 127 : 126); } #endif /* SMALL */ /* * Print RTM_PROPOSAL DNS server addresses. */ void print_rtdns(struct sockaddr_rtdns *rtdns) { struct in_addr server; struct in6_addr in6; size_t srclen, offset; unsigned int servercnt; int i; char *src = rtdns->sr_dns; char ntopbuf[INET6_ADDRSTRLEN]; offset = offsetof(struct sockaddr_rtdns, sr_dns); if (rtdns->sr_len < offset) { printf("\n", rtdns->sr_len, offset); return; } srclen = rtdns->sr_len - offset; if (srclen > sizeof(rtdns->sr_dns)) { printf(" %zu)>\n", srclen, sizeof(rtdns->sr_dns)); return; } switch (rtdns->sr_family) { case AF_INET: printf(" INET ["); /* An array of IPv4 addresses. */ servercnt = srclen / sizeof(struct in_addr); if (servercnt * sizeof(struct in_addr) != srclen) { printf("\n"); return; } for (i = 0; i < servercnt; i++) { memcpy(&server.s_addr, src, sizeof(server.s_addr)); printf("%s%s", inet_ntoa(server), i == servercnt - 1 ? "": ", "); src += sizeof(struct in_addr); } break; case AF_INET6: printf(" INET6 ["); servercnt = srclen / sizeof(struct in6_addr); if (servercnt * sizeof(struct in6_addr) != srclen) { printf("\n"); return; } for (i = 0; i < servercnt; i++) { memcpy(&in6, src, sizeof(in6)); src += sizeof(in6); printf("%s%s", inet_ntop(AF_INET6, &in6, ntopbuf, INET6_ADDRSTRLEN), i == servercnt - 1 ? "": ", "); } break; default: printf(" UNKNOWN ["); break; } printf("]"); } /* * Print RTM_PROPOSAL static routes. */ void print_rtstatic(struct sockaddr_rtstatic *rtstatic) { struct sockaddr_in6 gateway6; struct in6_addr prefix; struct in_addr dest, gateway; size_t srclen, offset; int bits, bytes, error, first = 1; uint8_t prefixlen; unsigned char *src = rtstatic->sr_static; char ntoabuf[INET_ADDRSTRLEN]; char hbuf[NI_MAXHOST]; char ntopbuf[INET6_ADDRSTRLEN]; offset = offsetof(struct sockaddr_rtstatic, sr_static); if (rtstatic->sr_len <= offset) { printf("\n", rtstatic->sr_len, offset); return; } srclen = rtstatic->sr_len - offset; if (srclen > sizeof(rtstatic->sr_static)) { printf(" %zu)>\n", srclen, sizeof(rtstatic->sr_static)); return; } printf(" ["); switch (rtstatic->sr_family) { case AF_INET: /* AF_INET -> RFC 3442 encoded static routes. */ while (srclen) { bits = *src; src++; srclen--; bytes = (bits + 7) / 8; if (srclen < bytes || bytes > sizeof(dest.s_addr)) break; memset(&dest, 0, sizeof(dest)); memcpy(&dest.s_addr, src, bytes); src += bytes; srclen -= bytes; strlcpy(ntoabuf, inet_ntoa(dest), sizeof(ntoabuf)); if (srclen < sizeof(gateway.s_addr)) break; memcpy(&gateway.s_addr, src, sizeof(gateway.s_addr)); src += sizeof(gateway.s_addr); srclen -= sizeof(gateway.s_addr); printf("%s%s/%u %s ", first ? "" : ", ", ntoabuf, bits, inet_ntoa(gateway)); first = 0; } break; case AF_INET6: while (srclen >= sizeof(prefixlen) + sizeof(prefix) + sizeof(gateway6)) { memcpy(&prefixlen, src, sizeof(prefixlen)); srclen -= sizeof(prefixlen); src += sizeof(prefixlen); memcpy(&prefix, src, sizeof(prefix)); srclen -= sizeof(prefix); src += sizeof(prefix); memcpy(&gateway6, src, sizeof(gateway6)); srclen -= sizeof(gateway6); src += sizeof(gateway6); if ((error = getnameinfo((struct sockaddr *)&gateway6, gateway6.sin6_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))) { warnx("cannot get gateway address: %s", gai_strerror(error)); return; } printf("%s%s/%u %s ", first ? "" : ", ", inet_ntop(AF_INET6, &prefix, ntopbuf, INET6_ADDRSTRLEN), prefixlen, hbuf); first = 0; } break; default: printf("", rtstatic->sr_family); break; } printf("]"); } /* * Print RTM_PROPOSAL domain search list. */ void print_rtsearch(struct sockaddr_rtsearch *rtsearch) { char *src = rtsearch->sr_search; size_t srclen, offset; offset = offsetof(struct sockaddr_rtsearch, sr_search); if (rtsearch->sr_len <= offset) { printf("\n", rtsearch->sr_len, offset); return; } srclen = rtsearch->sr_len - offset; if (srclen > sizeof(rtsearch->sr_search)) { printf(" %zu)>\n", srclen, sizeof(rtsearch->sr_search)); return; } printf(" [%.*s]", (int)srclen, src); } /* * Print RTM_80211INFO info. */ void print_80211info(struct if_ieee80211_msghdr *ifim) { unsigned int ascii, nwidlen, i; u_int8_t *nwid, *bssid; ascii = 1; nwid = ifim->ifim_ifie.ifie_nwid; nwidlen = ifim->ifim_ifie.ifie_nwid_len; for (i = 0; i < nwidlen; i++) { if (i == 0) printf("nwid "); else printf(":"); printf("%02x", nwid[i]); if (!isprint((unsigned int)nwid[i])) ascii = 0; } if (i > 0) { if (ascii == 1) printf(" (%.*s)", nwidlen, nwid); printf(", "); } printf("channel %u, ", ifim->ifim_ifie.ifie_channel); bssid = ifim->ifim_ifie.ifie_addr; printf("bssid %02x:%02x:%02x:%02x:%02x:%02x\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); printf("flags:"); bprintf(stdout, ifim->ifim_ifie.ifie_flags, ieee80211flags); printf("\nxflags:"); bprintf(stdout, ifim->ifim_ifie.ifie_xflags, ieee80211xflags); printf("\n"); }