Index: head/contrib/traceroute/findsaddr-socket.c =================================================================== --- head/contrib/traceroute/findsaddr-socket.c (revision 317034) +++ head/contrib/traceroute/findsaddr-socket.c (revision 317035) @@ -1,220 +1,221 @@ /* * Copyright (c) 2000 * 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 Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory 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. * * $FreeBSD$ */ /* XXX Yes this is WAY too complicated */ #ifndef lint static const char rcsid[] = "@(#) $Id: findsaddr-socket.c,v 1.1 2000/11/23 20:17:12 leres Exp $ (LBL)"; #endif #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include /* concession to AIX */ #if __STDC__ struct mbuf; struct rtentry; #endif #include #include #include #include #include #include #include #include #include #include "gnuc.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif #include "findsaddr.h" #ifdef HAVE_SOCKADDR_SA_LEN #define SALEN(sa) ((sa)->sa_len) #else #define SALEN(sa) salen(sa) #endif #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #endif struct rtmsg { struct rt_msghdr rtmsg; u_char data[512]; }; static struct rtmsg rtmsg = { { 0, RTM_VERSION, RTM_GET, 0, RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC, RTA_DST | RTA_IFA, 0, 0, 0, 0, 0, { 0 } }, { 0 } }; #ifndef HAVE_SOCKADDR_SA_LEN static int salen(struct sockaddr *); #endif /* * Return the source address for the given destination address */ const char * findsaddr(register const struct sockaddr_in *to, register struct sockaddr_in *from) { register struct rt_msghdr *rp; register u_char *cp; register struct sockaddr_in *sp, *ifa; register struct sockaddr *sa; register int s, size, cc, seq, i; register pid_t pid; static char errbuf[512]; s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); if (s < 0) { sprintf(errbuf, "socket: %.128s", strerror(errno)); return (errbuf); } seq = 0; pid = getpid(); rp = &rtmsg.rtmsg; rp->rtm_seq = ++seq; cp = (u_char *)(rp + 1); sp = (struct sockaddr_in *)cp; *sp = *to; cp += roundup(SALEN((struct sockaddr *)sp), sizeof(u_int32_t)); size = cp - (u_char *)rp; rp->rtm_msglen = size; cc = write(s, (char *)rp, size); if (cc < 0) { sprintf(errbuf, "write: %.128s", strerror(errno)); close(s); return (errbuf); } if (cc != size) { sprintf(errbuf, "short write (%d != %d)", cc, size); close(s); return (errbuf); } size = sizeof(rtmsg); do { memset(rp, 0, size); cc = read(s, (char *)rp, size); if (cc < 0) { sprintf(errbuf, "read: %.128s", strerror(errno)); close(s); return (errbuf); } - } while (rp->rtm_seq != seq || rp->rtm_pid != pid); + } while (rp->rtm_type != RTM_GET || rp->rtm_seq != seq || + rp->rtm_pid != pid); close(s); if (rp->rtm_version != RTM_VERSION) { sprintf(errbuf, "bad version %d", rp->rtm_version); return (errbuf); } if (rp->rtm_msglen > cc) { sprintf(errbuf, "bad msglen %d > %d", rp->rtm_msglen, cc); return (errbuf); } if (rp->rtm_errno != 0) { sprintf(errbuf, "rtm_errno: %.128s", strerror(rp->rtm_errno)); return (errbuf); } /* Find the interface sockaddr */ cp = (u_char *)(rp + 1); for (i = 1; i != 0; i <<= 1) if ((i & rp->rtm_addrs) != 0) { sa = (struct sockaddr *)cp; switch (i) { case RTA_IFA: if (sa->sa_family == AF_INET) { ifa = (struct sockaddr_in *)cp; if (ifa->sin_addr.s_addr != 0) { *from = *ifa; return (NULL); } } break; } if (SALEN(sa) == 0) cp += sizeof(long); else cp += roundup(SALEN(sa), sizeof(long)); } return ("failed!"); } #ifndef HAVE_SOCKADDR_SA_LEN static int salen(struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: return (sizeof(struct sockaddr_in)); case AF_LINK: return (sizeof(struct sockaddr_dl)); default: return (sizeof(struct sockaddr)); } } #endif Index: head/sbin/route/route.c =================================================================== --- head/sbin/route/route.c (revision 317034) +++ head/sbin/route/route.c (revision 317035) @@ -1,1958 +1,1959 @@ /* * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct fibl { TAILQ_ENTRY(fibl) fl_next; int fl_num; int fl_error; int fl_errno; }; static struct keytab { const char *kt_cp; int kt_i; } const keywords[] = { #include "keywords.h" {0, 0} }; static struct sockaddr_storage so[RTAX_MAX]; static int pid, rtm_addrs; static int s; static int nflag, af, qflag, tflag; static int verbose, aflen; static int locking, lockrest, debugonly; static struct rt_metrics rt_metrics; static u_long rtm_inits; static uid_t uid; static int defaultfib; static int numfibs; static char domain[MAXHOSTNAMELEN + 1]; static bool domain_initialized; static int rtm_seq; static char rt_line[NI_MAXHOST]; static char net_line[MAXHOSTNAMELEN + 1]; static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; static TAILQ_HEAD(fibl_head_t, fibl) fibl_head; static void printb(int, const char *); static void flushroutes(int argc, char *argv[]); static int flushroutes_fib(int); static int getaddr(int, char *, struct hostent **, int); static int keyword(const char *); #ifdef INET static void inet_makenetandmask(u_long, struct sockaddr_in *, struct sockaddr_in *, u_long); #endif #ifdef INET6 static int inet6_makenetandmask(struct sockaddr_in6 *, const char *); #endif static void interfaces(void); static void monitor(int, char*[]); static const char *netname(struct sockaddr *); static void newroute(int, char **); static int newroute_fib(int, char *, int); static void pmsg_addrs(char *, int, size_t); static void pmsg_common(struct rt_msghdr *, size_t); static int prefixlen(const char *); static void print_getmsg(struct rt_msghdr *, int, int); static void print_rtmsg(struct rt_msghdr *, size_t); static const char *routename(struct sockaddr *); static int rtmsg(int, int, int); static void set_metric(char *, int); static int set_sofib(int); static void sockaddr(char *, struct sockaddr *, size_t); static void sodump(struct sockaddr *, const char *); static int fiboptlist_csv(const char *, struct fibl_head_t *); static int fiboptlist_range(const char *, struct fibl_head_t *); static void usage(const char *) __dead2; #define READ_TIMEOUT 10 static volatile sig_atomic_t stop_read; static void stopit(int sig __unused) { stop_read = 1; } static void usage(const char *cp) { if (cp != NULL) warnx("bad keyword: %s", cp); errx(EX_USAGE, "usage: route [-46dnqtv] command [[modifiers] args]"); /* NOTREACHED */ } int main(int argc, char **argv) { int ch; size_t len; if (argc < 2) usage(NULL); while ((ch = getopt(argc, argv, "46nqdtv")) != -1) switch(ch) { case '4': #ifdef INET af = AF_INET; aflen = sizeof(struct sockaddr_in); #else errx(1, "IPv4 support is not compiled in"); #endif break; case '6': #ifdef INET6 af = AF_INET6; aflen = sizeof(struct sockaddr_in6); #else errx(1, "IPv6 support is not compiled in"); #endif break; case 'n': nflag = 1; break; case 'q': qflag = 1; break; case 'v': verbose = 1; break; case 't': tflag = 1; break; case 'd': debugonly = 1; break; case '?': default: usage(NULL); } argc -= optind; argv += optind; pid = getpid(); uid = geteuid(); if (tflag) s = open(_PATH_DEVNULL, O_WRONLY, 0); else s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) err(EX_OSERR, "socket"); len = sizeof(numfibs); if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) == -1) numfibs = -1; len = sizeof(defaultfib); if (numfibs != -1 && sysctlbyname("net.my_fibnum", (void *)&defaultfib, &len, NULL, 0) == -1) defaultfib = -1; if (*argv != NULL) switch (keyword(*argv)) { case K_GET: case K_SHOW: uid = 0; /* FALLTHROUGH */ case K_CHANGE: case K_ADD: case K_DEL: case K_DELETE: newroute(argc, argv); /* NOTREACHED */ case K_MONITOR: monitor(argc, argv); /* NOTREACHED */ case K_FLUSH: flushroutes(argc, argv); exit(0); /* NOTREACHED */ } usage(*argv); /* NOTREACHED */ } static int set_sofib(int fib) { if (fib < 0) return (0); return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib, sizeof(fib))); } static int fiboptlist_range(const char *arg, struct fibl_head_t *flh) { struct fibl *fl; char *str0, *str, *token, *endptr; int fib[2], i, error; str0 = str = strdup(arg); error = 0; i = 0; while ((token = strsep(&str, "-")) != NULL) { switch (i) { case 0: case 1: errno = 0; fib[i] = strtol(token, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib[i] < 0 || (numfibs != -1 && fib[i] > numfibs - 1)) errno = EINVAL; } if (errno) error = 1; break; default: error = 1; } if (error) goto fiboptlist_range_ret; i++; } if (fib[0] >= fib[1]) { error = 1; goto fiboptlist_range_ret; } for (i = fib[0]; i <= fib[1]; i++) { fl = calloc(1, sizeof(*fl)); if (fl == NULL) { error = 1; goto fiboptlist_range_ret; } fl->fl_num = i; TAILQ_INSERT_TAIL(flh, fl, fl_next); } fiboptlist_range_ret: free(str0); return (error); } #define ALLSTRLEN 64 static int fiboptlist_csv(const char *arg, struct fibl_head_t *flh) { struct fibl *fl; char *str0, *str, *token, *endptr; int fib, error; str0 = str = NULL; if (strcmp("all", arg) == 0) { str = calloc(1, ALLSTRLEN); if (str == NULL) { error = 1; goto fiboptlist_csv_ret; } if (numfibs > 1) snprintf(str, ALLSTRLEN - 1, "%d-%d", 0, numfibs - 1); else snprintf(str, ALLSTRLEN - 1, "%d", 0); } else if (strcmp("default", arg) == 0) { str0 = str = calloc(1, ALLSTRLEN); if (str == NULL) { error = 1; goto fiboptlist_csv_ret; } snprintf(str, ALLSTRLEN - 1, "%d", defaultfib); } else str0 = str = strdup(arg); error = 0; while ((token = strsep(&str, ",")) != NULL) { if (*token != '-' && strchr(token, '-') != NULL) { error = fiboptlist_range(token, flh); if (error) goto fiboptlist_csv_ret; } else { errno = 0; fib = strtol(token, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib < 0 || (numfibs != -1 && fib > numfibs - 1)) errno = EINVAL; } if (errno) { error = 1; goto fiboptlist_csv_ret; } fl = calloc(1, sizeof(*fl)); if (fl == NULL) { error = 1; goto fiboptlist_csv_ret; } fl->fl_num = fib; TAILQ_INSERT_TAIL(flh, fl, fl_next); } } fiboptlist_csv_ret: if (str0 != NULL) free(str0); return (error); } /* * Purge all entries in the routing tables not * associated with network interfaces. */ static void flushroutes(int argc, char *argv[]) { struct fibl *fl; int error; if (uid != 0 && !debugonly && !tflag) errx(EX_NOPERM, "must be root to alter routing table"); shutdown(s, SHUT_RD); /* Don't want to read back our messages */ TAILQ_INIT(&fibl_head); while (argc > 1) { argc--; argv++; if (**argv != '-') usage(*argv); switch (keyword(*argv + 1)) { #ifdef INET case K_4: case K_INET: af = AF_INET; break; #endif #ifdef INET6 case K_6: case K_INET6: af = AF_INET6; break; #endif case K_LINK: af = AF_LINK; break; case K_FIB: if (!--argc) usage(*argv); error = fiboptlist_csv(*++argv, &fibl_head); if (error) errx(EX_USAGE, "invalid fib number: %s", *argv); break; default: usage(*argv); } } if (TAILQ_EMPTY(&fibl_head)) { error = fiboptlist_csv("default", &fibl_head); if (error) errx(EX_OSERR, "fiboptlist_csv failed."); } TAILQ_FOREACH(fl, &fibl_head, fl_next) flushroutes_fib(fl->fl_num); } static int flushroutes_fib(int fib) { struct rt_msghdr *rtm; size_t needed; char *buf, *next, *lim; int mib[7], rlen, seqno, count = 0; int error; error = set_sofib(fib); if (error) { warn("fib number %d is ignored", fib); return (error); } retry: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = AF_UNSPEC; mib[4] = NET_RT_DUMP; mib[5] = 0; /* no flags */ mib[6] = fib; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "route-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(EX_OSERR, "malloc failed"); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { if (errno == ENOMEM && count++ < 10) { warnx("Routing table grew, retrying"); sleep(1); free(buf); goto retry; } err(EX_OSERR, "route-sysctl-get"); } lim = buf + needed; if (verbose) (void)printf("Examining routing table from sysctl\n"); seqno = 0; /* ??? */ for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; if (verbose) print_rtmsg(rtm, rtm->rtm_msglen); if ((rtm->rtm_flags & RTF_GATEWAY) == 0) continue; if (af != 0) { struct sockaddr *sa = (struct sockaddr *)(rtm + 1); if (sa->sa_family != af) continue; } if (debugonly) continue; rtm->rtm_type = RTM_DELETE; rtm->rtm_seq = seqno; rlen = write(s, next, rtm->rtm_msglen); if (rlen < 0 && errno == EPERM) err(1, "write to routing socket"); if (rlen < (int)rtm->rtm_msglen) { warn("write to routing socket"); (void)printf("got only %d for rlen\n", rlen); free(buf); goto retry; break; } seqno++; if (qflag) continue; if (verbose) print_rtmsg(rtm, rlen); else { struct sockaddr *sa = (struct sockaddr *)(rtm + 1); printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? routename(sa) : netname(sa)); sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa); printf("%-20.20s ", routename(sa)); if (fib >= 0) printf("-fib %-3d ", fib); printf("done\n"); } } return (error); } static const char * routename(struct sockaddr *sa) { struct sockaddr_dl *sdl; const char *cp; int n; if (!domain_initialized) { domain_initialized = true; if (gethostname(domain, MAXHOSTNAMELEN) == 0 && (cp = strchr(domain, '.'))) { domain[MAXHOSTNAMELEN] = '\0'; (void)strcpy(domain, cp + 1); } else domain[0] = '\0'; } /* If the address is zero-filled, use "default". */ if (sa->sa_len == 0 && nflag == 0) return ("default"); #if defined(INET) || defined(INET6) switch (sa->sa_family) { #ifdef INET case AF_INET: /* If the address is zero-filled, use "default". */ if (nflag == 0 && ((struct sockaddr_in *)(void *)sa)->sin_addr.s_addr == INADDR_ANY) return("default"); break; #endif #ifdef INET6 case AF_INET6: /* If the address is zero-filled, use "default". */ if (nflag == 0 && IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)sa)->sin6_addr)) return("default"); break; #endif } #endif switch (sa->sa_family) { #if defined(INET) || defined(INET6) #ifdef INET case AF_INET: #endif #ifdef INET6 case AF_INET6: #endif { struct sockaddr_storage ss; int error; char *p; memset(&ss, 0, sizeof(ss)); if (sa->sa_len == 0) ss.ss_family = sa->sa_family; else memcpy(&ss, sa, sa->sa_len); /* Expand sa->sa_len because it could be shortened. */ if (sa->sa_family == AF_INET) ss.ss_len = sizeof(struct sockaddr_in); else if (sa->sa_family == AF_INET6) ss.ss_len = sizeof(struct sockaddr_in6); error = getnameinfo((struct sockaddr *)&ss, ss.ss_len, rt_line, sizeof(rt_line), NULL, 0, (nflag == 0) ? 0 : NI_NUMERICHOST); if (error) { warnx("getnameinfo(): %s", gai_strerror(error)); strncpy(rt_line, "invalid", sizeof(rt_line)); } /* Remove the domain part if any. */ p = strchr(rt_line, '.'); if (p != NULL && strcmp(p + 1, domain) == 0) *p = '\0'; return (rt_line); break; } #endif case AF_LINK: sdl = (struct sockaddr_dl *)(void *)sa; if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { n = snprintf(rt_line, sizeof(rt_line), "link#%d", sdl->sdl_index); if (n > (int)sizeof(rt_line)) rt_line[0] = '\0'; return (rt_line); } else return (link_ntoa(sdl)); break; default: { u_short *sp = (u_short *)(void *)sa; u_short *splim = sp + ((sa->sa_len + 1) >> 1); char *cps = rt_line + sprintf(rt_line, "(%d)", sa->sa_family); char *cpe = rt_line + sizeof(rt_line); while (++sp < splim && cps < cpe) /* start with sa->sa_data */ if ((n = snprintf(cps, cpe - cps, " %x", *sp)) > 0) cps += n; else *cps = '\0'; break; } } return (rt_line); } /* * Return the name of the network whose address is given. * The address is assumed to be that of a net, not a host. */ static const char * netname(struct sockaddr *sa) { struct sockaddr_dl *sdl; int n; #ifdef INET struct netent *np = NULL; const char *cp = NULL; u_long i; #endif switch (sa->sa_family) { #ifdef INET case AF_INET: { struct in_addr in; in = ((struct sockaddr_in *)(void *)sa)->sin_addr; i = in.s_addr = ntohl(in.s_addr); if (in.s_addr == 0) cp = "default"; else if (!nflag) { np = getnetbyaddr(i, AF_INET); if (np != NULL) cp = np->n_name; } #define C(x) (unsigned)((x) & 0xff) if (cp != NULL) strncpy(net_line, cp, sizeof(net_line)); else if ((in.s_addr & 0xffffff) == 0) (void)sprintf(net_line, "%u", C(in.s_addr >> 24)); else if ((in.s_addr & 0xffff) == 0) (void)sprintf(net_line, "%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16)); else if ((in.s_addr & 0xff) == 0) (void)sprintf(net_line, "%u.%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16), C(in.s_addr >> 8)); else (void)sprintf(net_line, "%u.%u.%u.%u", C(in.s_addr >> 24), C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); #undef C break; } #endif #ifdef INET6 case AF_INET6: { struct sockaddr_in6 sin6; int niflags = 0; memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6, sa, sa->sa_len); sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; if (nflag) niflags |= NI_NUMERICHOST; if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, net_line, sizeof(net_line), NULL, 0, niflags) != 0) strncpy(net_line, "invalid", sizeof(net_line)); return(net_line); } #endif case AF_LINK: sdl = (struct sockaddr_dl *)(void *)sa; if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 && sdl->sdl_slen == 0) { n = snprintf(net_line, sizeof(net_line), "link#%d", sdl->sdl_index); if (n > (int)sizeof(net_line)) net_line[0] = '\0'; return (net_line); } else return (link_ntoa(sdl)); break; default: { u_short *sp = (u_short *)(void *)sa->sa_data; u_short *splim = sp + ((sa->sa_len + 1)>>1); char *cps = net_line + sprintf(net_line, "af %d:", sa->sa_family); char *cpe = net_line + sizeof(net_line); while (sp < splim && cps < cpe) if ((n = snprintf(cps, cpe - cps, " %x", *sp++)) > 0) cps += n; else *cps = '\0'; break; } } return (net_line); } static void set_metric(char *value, int key) { int flag = 0; char *endptr; u_long noval, *valp = &noval; switch (key) { #define caseof(x, y, z) case x: valp = &rt_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); caseof(K_WEIGHT, RTV_WEIGHT, rmx_weight); } rtm_inits |= flag; if (lockrest || locking) rt_metrics.rmx_locks |= flag; if (locking) locking = 0; errno = 0; *valp = strtol(value, &endptr, 0); if (errno == 0 && *endptr != '\0') errno = EINVAL; if (errno) err(EX_USAGE, "%s", value); if (flag & RTV_EXPIRE && (value[0] == '+' || value[0] == '-')) { struct timespec ts; clock_gettime(CLOCK_REALTIME_FAST, &ts); *valp += ts.tv_sec; } } #define F_ISHOST 0x01 #define F_FORCENET 0x02 #define F_FORCEHOST 0x04 #define F_PROXY 0x08 #define F_INTERFACE 0x10 static void newroute(int argc, char **argv) { struct sigaction sa; struct hostent *hp; struct fibl *fl; char *cmd; const char *dest, *gateway, *errmsg; int key, error, flags, nrflags, fibnum; if (uid != 0 && !debugonly && !tflag) errx(EX_NOPERM, "must be root to alter routing table"); dest = NULL; gateway = NULL; flags = RTF_STATIC; nrflags = 0; hp = NULL; TAILQ_INIT(&fibl_head); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = stopit; if (sigaction(SIGALRM, &sa, 0) == -1) warn("sigaction SIGALRM"); cmd = argv[0]; if (*cmd != 'g' && *cmd != 's') 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; #ifdef INET case K_4: case K_INET: af = AF_INET; aflen = sizeof(struct sockaddr_in); break; #endif #ifdef INET6 case K_6: case K_INET6: af = AF_INET6; aflen = sizeof(struct sockaddr_in6); break; #endif case K_SA: af = PF_ROUTE; aflen = sizeof(struct sockaddr_storage); break; case K_IFACE: case K_INTERFACE: nrflags |= F_INTERFACE; break; case K_NOSTATIC: flags &= ~RTF_STATIC; break; case K_LOCK: locking = 1; break; case K_LOCKREST: lockrest = 1; break; case K_HOST: nrflags |= F_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_PROXY: nrflags |= F_PROXY; break; case K_XRESOLVE: flags |= RTF_XRESOLVE; break; case K_STATIC: flags |= RTF_STATIC; break; case K_STICKY: flags |= RTF_STICKY; break; case K_NOSTICK: flags &= ~RTF_STICKY; break; case K_FIB: if (!--argc) usage(NULL); error = fiboptlist_csv(*++argv, &fibl_head); if (error) errx(EX_USAGE, "invalid fib number: %s", *argv); break; case K_IFA: if (!--argc) usage(NULL); getaddr(RTAX_IFA, *++argv, 0, nrflags); break; case K_IFP: if (!--argc) usage(NULL); getaddr(RTAX_IFP, *++argv, 0, nrflags); break; case K_GENMASK: if (!--argc) usage(NULL); getaddr(RTAX_GENMASK, *++argv, 0, nrflags); break; case K_GATEWAY: if (!--argc) usage(NULL); getaddr(RTAX_GATEWAY, *++argv, 0, nrflags); gateway = *argv; break; case K_DST: if (!--argc) usage(NULL); if (getaddr(RTAX_DST, *++argv, &hp, nrflags)) nrflags |= F_ISHOST; dest = *argv; break; case K_NETMASK: if (!--argc) usage(NULL); getaddr(RTAX_NETMASK, *++argv, 0, nrflags); /* FALLTHROUGH */ case K_NET: nrflags |= F_FORCENET; break; case K_PREFIXLEN: if (!--argc) usage(NULL); if (prefixlen(*++argv) == -1) { nrflags &= ~F_FORCENET; nrflags |= F_ISHOST; } else { nrflags |= F_FORCENET; nrflags &= ~F_ISHOST; } 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: case K_WEIGHT: if (!--argc) usage(NULL); set_metric(*++argv, key); break; default: usage(1+*argv); } } else { if ((rtm_addrs & RTA_DST) == 0) { dest = *argv; if (getaddr(RTAX_DST, *argv, &hp, nrflags)) nrflags |= F_ISHOST; } else if ((rtm_addrs & RTA_GATEWAY) == 0) { gateway = *argv; getaddr(RTAX_GATEWAY, *argv, &hp, nrflags); } else { getaddr(RTAX_NETMASK, *argv, 0, nrflags); nrflags |= F_FORCENET; } } } /* Do some sanity checks on resulting request */ if (so[RTAX_DST].ss_len == 0) { warnx("destination parameter required"); usage(NULL); } if (so[RTAX_NETMASK].ss_len != 0 && so[RTAX_DST].ss_family != so[RTAX_NETMASK].ss_family) { warnx("destination and netmask family need to be the same"); usage(NULL); } if (nrflags & F_FORCEHOST) { nrflags |= F_ISHOST; #ifdef INET6 if (af == AF_INET6) { rtm_addrs &= ~RTA_NETMASK; memset(&so[RTAX_NETMASK], 0, sizeof(so[RTAX_NETMASK])); } #endif } if (nrflags & F_FORCENET) nrflags &= ~F_ISHOST; flags |= RTF_UP; if (nrflags & F_ISHOST) flags |= RTF_HOST; if ((nrflags & F_INTERFACE) == 0) flags |= RTF_GATEWAY; if (nrflags & F_PROXY) flags |= RTF_ANNOUNCE; if (dest == NULL) dest = ""; if (gateway == NULL) gateway = ""; if (TAILQ_EMPTY(&fibl_head)) { error = fiboptlist_csv("default", &fibl_head); if (error) errx(EX_OSERR, "fiboptlist_csv failed."); } error = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { fl->fl_error = newroute_fib(fl->fl_num, cmd, flags); if (fl->fl_error) fl->fl_errno = errno; error += fl->fl_error; } if (*cmd == 'g' || *cmd == 's') exit(error); error = 0; if (!qflag) { fibnum = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error == 0) fibnum++; } if (fibnum > 0) { int firstfib = 1; printf("%s %s %s", cmd, (nrflags & F_ISHOST) ? "host" : "net", dest); if (*gateway) printf(": gateway %s", gateway); if (numfibs > 1) { TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error == 0 && fl->fl_num >= 0) { if (firstfib) { printf(" fib "); firstfib = 0; } printf("%d", fl->fl_num); if (fibnum-- > 1) printf(","); } } } printf("\n"); } fibnum = 0; TAILQ_FOREACH(fl, &fibl_head, fl_next) { if (fl->fl_error != 0) { printf("%s %s %s", cmd, (nrflags & F_ISHOST) ? "host" : "net", dest); if (*gateway) printf(": gateway %s", gateway); if (fl->fl_num >= 0) printf(" fib %d", fl->fl_num); switch (fl->fl_errno) { case ESRCH: errmsg = "not in table"; break; case EBUSY: errmsg = "entry in use"; break; case ENOBUFS: errmsg = "not enough memory"; break; case EADDRINUSE: /* * handle recursion avoidance * in rt_setgate() */ errmsg = "gateway uses the same route"; break; case EEXIST: errmsg = "route already in table"; break; default: errmsg = strerror(fl->fl_errno); break; } printf(": %s\n", errmsg); error = 1; } } } exit(error); } static int newroute_fib(int fib, char *cmd, int flags) { int error; error = set_sofib(fib); if (error) { warn("fib number %d is ignored", fib); return (error); } error = rtmsg(*cmd, flags, fib); return (error); } #ifdef INET static void inet_makenetandmask(u_long net, struct sockaddr_in *sin, struct sockaddr_in *sin_mask, u_long bits) { u_long mask = 0; rtm_addrs |= RTA_NETMASK; /* * MSB of net should be meaningful. 0/0 is exception. */ if (net > 0) while ((net & 0xff000000) == 0) net <<= 8; /* * If no /xx was specified we must calculate the * CIDR address. */ if ((bits == 0) && (net != 0)) { u_long i, j; for(i = 0, j = 0xff; i < 4; i++) { if (net & j) { break; } j <<= 8; } /* i holds the first non zero bit */ bits = 32 - (i*8); } if (bits != 0) mask = 0xffffffff << (32 - bits); sin->sin_addr.s_addr = htonl(net); sin_mask->sin_addr.s_addr = htonl(mask); sin_mask->sin_len = sizeof(struct sockaddr_in); sin_mask->sin_family = AF_INET; } #endif #ifdef INET6 /* * XXX the function may need more improvement... */ static int inet6_makenetandmask(struct sockaddr_in6 *sin6, const char *plen) { if (plen == NULL) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && sin6->sin6_scope_id == 0) plen = "0"; } if (plen == NULL || strcmp(plen, "128") == 0) return (1); rtm_addrs |= RTA_NETMASK; prefixlen(plen); return (0); } #endif /* * Interpret an argument as a network address of some kind, * returning 1 if a host address, 0 if a network address. */ static int getaddr(int idx, char *str, struct hostent **hpp, int nrflags) { struct sockaddr *sa; #if defined(INET) struct sockaddr_in *sin; struct hostent *hp; struct netent *np; u_long val; char *q; #elif defined(INET6) char *q; #endif if (idx < 0 || idx >= RTAX_MAX) usage("internal error"); if (af == 0) { #if defined(INET) af = AF_INET; aflen = sizeof(struct sockaddr_in); #elif defined(INET6) af = AF_INET6; aflen = sizeof(struct sockaddr_in6); #else af = AF_LINK; aflen = sizeof(struct sockaddr_dl); #endif } #ifndef INET hpp = NULL; #endif rtm_addrs |= (1 << idx); sa = (struct sockaddr *)&so[idx]; sa->sa_family = af; sa->sa_len = aflen; switch (idx) { case RTAX_GATEWAY: if (nrflags & F_INTERFACE) { struct ifaddrs *ifap, *ifa; struct sockaddr_dl *sdl0 = (struct sockaddr_dl *)(void *)sa; struct sockaddr_dl *sdl = NULL; if (getifaddrs(&ifap)) err(EX_OSERR, "getifaddrs"); for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; if (strcmp(str, ifa->ifa_name) != 0) continue; sdl = (struct sockaddr_dl *)(void *)ifa->ifa_addr; } /* If we found it, then use it */ if (sdl != NULL) { /* * Note that we need to copy before calling * freeifaddrs(). */ memcpy(sdl0, sdl, sdl->sdl_len); } freeifaddrs(ifap); if (sdl != NULL) return(1); else errx(EX_DATAERR, "interface '%s' does not exist", str); } break; case RTAX_IFP: sa->sa_family = AF_LINK; break; } if (strcmp(str, "default") == 0) { /* * Default is net 0.0.0.0/0 */ switch (idx) { case RTAX_DST: nrflags |= F_FORCENET; getaddr(RTAX_NETMASK, str, 0, nrflags); break; } return (0); } switch (sa->sa_family) { #ifdef INET6 case AF_INET6: { struct addrinfo hints, *res; int ecode; q = NULL; if (idx == RTAX_DST && (q = strchr(str, '/')) != NULL) *q = '\0'; memset(&hints, 0, sizeof(hints)); hints.ai_family = sa->sa_family; hints.ai_socktype = SOCK_DGRAM; ecode = getaddrinfo(str, NULL, &hints, &res); if (ecode != 0 || res->ai_family != AF_INET6 || res->ai_addrlen != sizeof(struct sockaddr_in6)) errx(EX_OSERR, "%s: %s", str, gai_strerror(ecode)); memcpy(sa, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); if (q != NULL) *q++ = '/'; if (idx == RTAX_DST) return (inet6_makenetandmask((struct sockaddr_in6 *)(void *)sa, q)); return (0); } #endif /* INET6 */ case AF_LINK: link_addr(str, (struct sockaddr_dl *)(void *)sa); return (1); case PF_ROUTE: sockaddr(str, sa, sizeof(struct sockaddr_storage)); return (1); #ifdef INET case AF_INET: #endif default: break; } #ifdef INET sin = (struct sockaddr_in *)(void *)sa; if (hpp == NULL) hpp = &hp; *hpp = NULL; q = strchr(str,'/'); if (q != NULL && idx == RTAX_DST) { *q = '\0'; if ((val = inet_network(str)) != INADDR_NONE) { inet_makenetandmask(val, sin, (struct sockaddr_in *)&so[RTAX_NETMASK], strtoul(q+1, 0, 0)); return (0); } *q = '/'; } if ((idx != RTAX_DST || (nrflags & F_FORCENET) == 0) && inet_aton(str, &sin->sin_addr)) { val = sin->sin_addr.s_addr; if (idx != RTAX_DST || nrflags & F_FORCEHOST || inet_lnaof(sin->sin_addr) != INADDR_ANY) return (1); else { val = ntohl(val); goto netdone; } } if (idx == RTAX_DST && (nrflags & F_FORCEHOST) == 0 && ((val = inet_network(str)) != INADDR_NONE || ((np = getnetbyname(str)) != NULL && (val = np->n_net) != 0))) { netdone: inet_makenetandmask(val, sin, (struct sockaddr_in *)&so[RTAX_NETMASK], 0); return (0); } hp = gethostbyname(str); if (hp != NULL) { *hpp = hp; sin->sin_family = hp->h_addrtype; memmove((char *)&sin->sin_addr, hp->h_addr, MIN((size_t)hp->h_length, sizeof(sin->sin_addr))); return (1); } #endif errx(EX_NOHOST, "bad address: %s", str); } static int prefixlen(const char *str) { int len = atoi(str), q, r; int max; char *p; rtm_addrs |= RTA_NETMASK; switch (af) { #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&so[RTAX_NETMASK]; max = 128; p = (char *)&sin6->sin6_addr; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); break; } #endif #ifdef INET case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)&so[RTAX_NETMASK]; max = 32; p = (char *)&sin->sin_addr; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); break; } #endif default: errx(EX_OSERR, "prefixlen not supported in this af"); } if (len < 0 || max < len) errx(EX_USAGE, "%s: invalid prefixlen", str); q = len >> 3; r = len & 7; memset((void *)p, 0, max / 8); if (q > 0) memset((void *)p, 0xff, q); if (r > 0) *((u_char *)p + q) = (0xff00 >> r) & 0xff; if (len == max) return (-1); else return (len); } static void interfaces(void) { size_t needed; int mib[6]; char *buf, *lim, *next, count = 0; struct rt_msghdr *rtm; retry2: mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = AF_UNSPEC; mib[4] = NET_RT_IFLIST; mib[5] = 0; /* no flags */ if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "route-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) errx(EX_OSERR, "malloc failed"); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) { if (errno == ENOMEM && count++ < 10) { warnx("Routing table grew, retrying"); sleep(1); free(buf); goto retry2; } err(EX_OSERR, "actual retrieval of interface table"); } lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)next; print_rtmsg(rtm, rtm->rtm_msglen); } } static void monitor(int argc, char *argv[]) { int n, fib, error; char msg[2048], *endptr; fib = defaultfib; while (argc > 1) { argc--; argv++; if (**argv != '-') usage(*argv); switch (keyword(*argv + 1)) { case K_FIB: if (!--argc) usage(*argv); errno = 0; fib = strtol(*++argv, &endptr, 0); if (errno == 0) { if (*endptr != '\0' || fib < 0 || (numfibs != -1 && fib > numfibs - 1)) errno = EINVAL; } if (errno) errx(EX_USAGE, "invalid fib number: %s", *argv); break; default: usage(*argv); } } error = set_sofib(fib); if (error) errx(EX_USAGE, "invalid fib number: %d", fib); verbose = 1; if (debugonly) { interfaces(); exit(0); } for (;;) { time_t now; n = read(s, msg, 2048); now = time(NULL); (void)printf("\ngot message of size %d on %s", n, ctime(&now)); print_rtmsg((struct rt_msghdr *)(void *)msg, n); } } static int rtmsg(int cmd, int flags, int fib) { int rlen; char *cp = m_rtmsg.m_space; int l; #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ - l = (((struct sockaddr *)&(u))->sa_len == 0) ? \ - sizeof(long) : \ - 1 + ((((struct sockaddr *)&(u))->sa_len - 1) \ - | (sizeof(long) - 1)); \ + l = SA_SIZE(&(u)); \ memmove(cp, (char *)&(u), l); \ cp += l; \ if (verbose) \ sodump((struct sockaddr *)&(u), #w); \ } 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 == 's') { cmd = RTM_GET; if (so[RTAX_IFP].ss_family == 0) { so[RTAX_IFP].ss_family = AF_LINK; so[RTAX_IFP].ss_len = sizeof(struct sockaddr_dl); rtm_addrs |= RTA_IFP; } } else cmd = RTM_DELETE; #define rtm m_rtmsg.m_rtm rtm.rtm_type = cmd; rtm.rtm_flags = flags; rtm.rtm_version = RTM_VERSION; rtm.rtm_seq = ++rtm_seq; rtm.rtm_addrs = rtm_addrs; rtm.rtm_rmx = rt_metrics; rtm.rtm_inits = rtm_inits; NEXTADDR(RTA_DST, so[RTAX_DST]); NEXTADDR(RTA_GATEWAY, so[RTAX_GATEWAY]); NEXTADDR(RTA_NETMASK, so[RTAX_NETMASK]); NEXTADDR(RTA_GENMASK, so[RTAX_GENMASK]); NEXTADDR(RTA_IFP, so[RTAX_IFP]); NEXTADDR(RTA_IFA, so[RTAX_IFA]); 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 EPERM: err(1, "writing to routing socket"); break; case ESRCH: warnx("route has not been found"); break; case EEXIST: /* Handled by newroute() */ break; default: warn("writing to routing socket"); } return (-1); } if (cmd == RTM_GET) { stop_read = 0; alarm(READ_TIMEOUT); do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); } while (l > 0 && stop_read == 0 && - (rtm.rtm_seq != rtm_seq || rtm.rtm_pid != pid)); + (rtm.rtm_type != RTM_GET || rtm.rtm_seq != rtm_seq || + rtm.rtm_pid != pid)); if (stop_read != 0) { warnx("read from routing socket timed out"); return (-1); } else alarm(0); if (l < 0) warn("read from routing socket"); else print_getmsg(&rtm, l, fib); } #undef rtm return (0); } static const char *const 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", "RTM_NEWMADDR: new multicast group membership on iface", "RTM_DELMADDR: multicast group membership removed from iface", "RTM_IFANNOUNCE: interface arrival/departure", "RTM_IEEE80211: IEEE 802.11 wireless event", }; static const char metricnames[] = "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" "\1mtu"; static const char routeflags[] = "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY"; static const char ifnetflags[] = "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" "\017LINK2\020MULTICAST"; static const char addrnames[] = "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; static const char errfmt[] = "\n%s: truncated route message, only %zu bytes left\n"; static void print_rtmsg(struct rt_msghdr *rtm, size_t msglen) { struct if_msghdr *ifm; struct ifa_msghdr *ifam; #ifdef RTM_NEWMADDR struct ifma_msghdr *ifmam; #endif struct if_announcemsghdr *ifan; const char *state; if (verbose == 0) return; if (rtm->rtm_version != RTM_VERSION) { (void)printf("routing message version %d not understood\n", rtm->rtm_version); return; } if (rtm->rtm_type < nitems(msgtypes)) (void)printf("%s: ", msgtypes[rtm->rtm_type]); else (void)printf("unknown type %d: ", rtm->rtm_type); (void)printf("len %d, ", rtm->rtm_msglen); #define REQUIRE(x) do { \ if (msglen < sizeof(x)) \ goto badlen; \ else \ msglen -= sizeof(x); \ } while (0) switch (rtm->rtm_type) { case RTM_IFINFO: REQUIRE(struct if_msghdr); ifm = (struct if_msghdr *)rtm; (void)printf("if# %d, ", ifm->ifm_index); switch (ifm->ifm_data.ifi_link_state) { case LINK_STATE_DOWN: state = "down"; break; case LINK_STATE_UP: state = "up"; break; default: state = "unknown"; break; } (void)printf("link: %s, flags:", state); printb(ifm->ifm_flags, ifnetflags); pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs, msglen); break; case RTM_NEWADDR: case RTM_DELADDR: REQUIRE(struct ifa_msghdr); ifam = (struct ifa_msghdr *)rtm; (void)printf("metric %d, flags:", ifam->ifam_metric); printb(ifam->ifam_flags, routeflags); pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs, msglen); break; #ifdef RTM_NEWMADDR case RTM_NEWMADDR: case RTM_DELMADDR: REQUIRE(struct ifma_msghdr); ifmam = (struct ifma_msghdr *)rtm; pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs, msglen); break; #endif case RTM_IFANNOUNCE: REQUIRE(struct if_announcemsghdr); ifan = (struct if_announcemsghdr *)rtm; (void)printf("if# %d, what: ", ifan->ifan_index); switch (ifan->ifan_what) { case IFAN_ARRIVAL: (void)printf("arrival"); break; case IFAN_DEPARTURE: printf("departure"); break; default: printf("#%d", ifan->ifan_what); break; } printf("\n"); fflush(stdout); break; default: - printf("pid: %ld, seq %d, errno %d, flags:", - (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); - printb(rtm->rtm_flags, routeflags); - pmsg_common(rtm, msglen); + if (rtm->rtm_type <= RTM_RESOLVE) { + printf("pid: %ld, seq %d, errno %d, flags:", + (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + printb(rtm->rtm_flags, routeflags); + pmsg_common(rtm, msglen); + } else + printf("type: %u, len: %zu\n", rtm->rtm_type, msglen); } return; badlen: (void)printf(errfmt, __func__, msglen); #undef REQUIRE } static void print_getmsg(struct rt_msghdr *rtm, int msglen, int fib) { struct sockaddr *sp[RTAX_MAX]; struct timespec ts; char *cp; int i; memset(sp, 0, sizeof(sp)); (void)printf(" route to: %s\n", routename((struct sockaddr *)&so[RTAX_DST])); if (rtm->rtm_version != RTM_VERSION) { warnx("routing message version %d not understood", rtm->rtm_version); return; } if (rtm->rtm_msglen > msglen) { warnx("message length mismatch, in packet %d, returned %d", rtm->rtm_msglen, msglen); return; } if (rtm->rtm_errno) { errno = rtm->rtm_errno; warn("message indicates error %d", errno); return; } cp = ((char *)(rtm + 1)); for (i = 0; i < RTAX_MAX; i++) if (rtm->rtm_addrs & (1 << i)) { sp[i] = (struct sockaddr *)cp; cp += SA_SIZE((struct sockaddr *)cp); } if ((rtm->rtm_addrs & RTA_IFP) && (sp[RTAX_IFP]->sa_family != AF_LINK || ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen == 0)) sp[RTAX_IFP] = NULL; if (sp[RTAX_DST]) (void)printf("destination: %s\n", routename(sp[RTAX_DST])); if (sp[RTAX_NETMASK]) (void)printf(" mask: %s\n", routename(sp[RTAX_NETMASK])); if (sp[RTAX_GATEWAY] && (rtm->rtm_flags & RTF_GATEWAY)) (void)printf(" gateway: %s\n", routename(sp[RTAX_GATEWAY])); if (fib >= 0) (void)printf(" fib: %u\n", (unsigned int)fib); if (sp[RTAX_IFP]) (void)printf(" interface: %.*s\n", ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_nlen, ((struct sockaddr_dl *)(void *)sp[RTAX_IFP])->sdl_data); (void)printf(" flags: "); printb(rtm->rtm_flags, routeflags); #define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') #define msec(u) (((u) + 500) / 1000) /* usec to msec */ printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe", "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire"); printf("%8lu%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); printf("%8lu%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); printf("%8lu%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); printf("%8lu%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); printf("%8lu%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); printf("%8lu%c ", rtm->rtm_rmx.rmx_weight, lock(WEIGHT)); if (rtm->rtm_rmx.rmx_expire > 0) clock_gettime(CLOCK_REALTIME_FAST, &ts); else ts.tv_sec = 0; printf("%8ld%c\n", (long)(rtm->rtm_rmx.rmx_expire - ts.tv_sec), lock(EXPIRE)); #undef lock #undef msec #define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) if (verbose) pmsg_common(rtm, msglen); else if (rtm->rtm_addrs &~ RTA_IGN) { (void)printf("sockaddrs: "); printb(rtm->rtm_addrs, addrnames); putchar('\n'); } #undef RTA_IGN } static void pmsg_common(struct rt_msghdr *rtm, size_t msglen) { (void)printf("\nlocks: "); printb(rtm->rtm_rmx.rmx_locks, metricnames); (void)printf(" inits: "); printb(rtm->rtm_inits, metricnames); if (msglen > sizeof(struct rt_msghdr)) pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs, msglen - sizeof(struct rt_msghdr)); else (void)fflush(stdout); } static void pmsg_addrs(char *cp, int addrs, size_t len) { struct sockaddr *sa; int i; if (addrs == 0) { (void)putchar('\n'); return; } (void)printf("\nsockaddrs: "); printb(addrs, addrnames); putchar('\n'); for (i = 0; i < RTAX_MAX; i++) if (addrs & (1 << i)) { sa = (struct sockaddr *)cp; if (len == 0 || len < SA_SIZE(sa)) { (void)printf(errfmt, __func__, len); break; } (void)printf(" %s", routename(sa)); len -= SA_SIZE(sa); cp += SA_SIZE(sa); } (void)putchar('\n'); (void)fflush(stdout); } static void printb(int b, const char *str) { int i; int gotsome = 0; if (b == 0) return; while ((i = *str++) != 0) { if (b & (1 << (i-1))) { if (gotsome == 0) i = '<'; else i = ','; putchar(i); gotsome = 1; for (; (i = *str) > 32; str++) putchar(i); } else while (*str > 32) str++; } if (gotsome) putchar('>'); } int keyword(const char *cp) { const struct keytab *kt = keywords; while (kt->kt_cp != NULL && strcmp(kt->kt_cp, cp) != 0) kt++; return (kt->kt_i); } static void sodump(struct sockaddr *sa, const char *which) { #ifdef INET6 char nbuf[INET6_ADDRSTRLEN]; #endif switch (sa->sa_family) { case AF_LINK: (void)printf("%s: link %s; ", which, link_ntoa((struct sockaddr_dl *)(void *)sa)); break; #ifdef INET case AF_INET: (void)printf("%s: inet %s; ", which, inet_ntoa(((struct sockaddr_in *)(void *)sa)->sin_addr)); break; #endif #ifdef INET6 case AF_INET6: (void)printf("%s: inet6 %s; ", which, inet_ntop(sa->sa_family, &((struct sockaddr_in6 *)(void *)sa)->sin6_addr, nbuf, sizeof(nbuf))); break; #endif } (void)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) static void sockaddr(char *addr, struct sockaddr *sa, size_t size) { char *cp = (char *)sa; char *cplim = cp + size; int byte = 0, state = VIRGIN, new = 0 /* foil gcc */; 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; } Index: head/sbin/routed/table.c =================================================================== --- head/sbin/routed/table.c (revision 317034) +++ head/sbin/routed/table.c (revision 317035) @@ -1,2155 +1,2164 @@ /* * Copyright (c) 1983, 1988, 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif static struct rt_spare *rts_better(struct rt_entry *); static struct rt_spare rts_empty = {0,0,0,HOPCNT_INFINITY,0,0,0}; static void set_need_flash(void); #ifdef _HAVE_SIN_LEN static void masktrim(struct sockaddr_in *ap); #else static void masktrim(struct sockaddr_in_new *ap); #endif static void rtbad(struct rt_entry *); struct radix_node_head *rhead; /* root of the radix tree */ int need_flash = 1; /* flash update needed * start =1 to suppress the 1st */ struct timeval age_timer; /* next check of old routes */ struct timeval need_kern = { /* need to update kernel table */ EPOCH+MIN_WAITTIME-1, 0 }; int stopint; int total_routes; /* zap any old routes through this gateway */ static naddr age_bad_gate; /* It is desirable to "aggregate" routes, to combine differing routes of * the same metric and next hop into a common route with a smaller netmask * or to suppress redundant routes, routes that add no information to * routes with smaller netmasks. * * A route is redundant if and only if any and all routes with smaller * but matching netmasks and nets are the same. Since routes are * kept sorted in the radix tree, redundant routes always come second. * * There are two kinds of aggregations. First, two routes of the same bit * mask and differing only in the least significant bit of the network * number can be combined into a single route with a coarser mask. * * Second, a route can be suppressed in favor of another route with a more * coarse mask provided no incompatible routes with intermediate masks * are present. The second kind of aggregation involves suppressing routes. * A route must not be suppressed if an incompatible route exists with * an intermediate mask, since the suppressed route would be covered * by the intermediate. * * This code relies on the radix tree walk encountering routes * sorted first by address, with the smallest address first. */ static struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest; /* #define DEBUG_AG */ #ifdef DEBUG_AG #define CHECK_AG() {int acnt = 0; struct ag_info *cag; \ for (cag = ag_avail; cag != NULL; cag = cag->ag_fine) \ acnt++; \ for (cag = ag_corsest; cag != NULL; cag = cag->ag_fine) \ acnt++; \ if (acnt != NUM_AG_SLOTS) { \ (void)fflush(stderr); \ abort(); \ } \ } #else #define CHECK_AG() #endif /* Output the contents of an aggregation table slot. * This function must always be immediately followed with the deletion * of the target slot. */ static void ag_out(struct ag_info *ag, void (*out)(struct ag_info *)) { struct ag_info *ag_cors; naddr bit; /* Forget it if this route should not be output for split-horizon. */ if (ag->ag_state & AGS_SPLIT_HZ) return; /* If we output both the even and odd twins, then the immediate parent, * if it is present, is redundant, unless the parent manages to * aggregate into something coarser. * On successive calls, this code detects the even and odd twins, * and marks the parent. * * Note that the order in which the radix tree code emits routes * ensures that the twins are seen before the parent is emitted. */ ag_cors = ag->ag_cors; if (ag_cors != NULL && ag_cors->ag_mask == ag->ag_mask<<1 && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) { ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) ? AGS_REDUN0 : AGS_REDUN1); } /* Skip it if this route is itself redundant. * * It is ok to change the contents of the slot here, since it is * always deleted next. */ if (ag->ag_state & AGS_REDUN0) { if (ag->ag_state & AGS_REDUN1) return; /* quit if fully redundant */ /* make it finer if it is half-redundant */ bit = (-ag->ag_mask) >> 1; ag->ag_dst_h |= bit; ag->ag_mask |= bit; } else if (ag->ag_state & AGS_REDUN1) { /* make it finer if it is half-redundant */ bit = (-ag->ag_mask) >> 1; ag->ag_mask |= bit; } out(ag); } static void ag_del(struct ag_info *ag) { CHECK_AG(); if (ag->ag_cors == NULL) ag_corsest = ag->ag_fine; else ag->ag_cors->ag_fine = ag->ag_fine; if (ag->ag_fine == NULL) ag_finest = ag->ag_cors; else ag->ag_fine->ag_cors = ag->ag_cors; ag->ag_fine = ag_avail; ag_avail = ag; CHECK_AG(); } /* Flush routes waiting for aggregation. * This must not suppress a route unless it is known that among all * routes with coarser masks that match it, the one with the longest * mask is appropriate. This is ensured by scanning the routes * in lexical order, and with the most restrictive mask first * among routes to the same destination. */ void ag_flush(naddr lim_dst_h, /* flush routes to here */ naddr lim_mask, /* matching this mask */ void (*out)(struct ag_info *)) { struct ag_info *ag, *ag_cors; naddr dst_h; for (ag = ag_finest; ag != NULL && ag->ag_mask >= lim_mask; ag = ag_cors) { ag_cors = ag->ag_cors; /* work on only the specified routes */ dst_h = ag->ag_dst_h; if ((dst_h & lim_mask) != lim_dst_h) continue; if (!(ag->ag_state & AGS_SUPPRESS)) ag_out(ag, out); else for ( ; ; ag_cors = ag_cors->ag_cors) { /* Look for a route that can suppress the * current route */ if (ag_cors == NULL) { /* failed, so output it and look for * another route to work on */ ag_out(ag, out); break; } if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) { /* We found a route with a coarser mask that * aggregates the current target. * * If it has a different next hop, it * cannot replace the target, so output * the target. */ if (ag->ag_gate != ag_cors->ag_gate && !(ag->ag_state & AGS_FINE_GATE) && !(ag_cors->ag_state & AGS_CORS_GATE)) { ag_out(ag, out); break; } /* If the coarse route has a good enough * metric, it suppresses the target. * If the suppressed target was redundant, * then mark the suppressor redundant. */ if (ag_cors->ag_pref <= ag->ag_pref) { if (AG_IS_REDUN(ag->ag_state) && ag_cors->ag_mask==ag->ag_mask<<1) { if (ag_cors->ag_dst_h == dst_h) ag_cors->ag_state |= AGS_REDUN0; else ag_cors->ag_state |= AGS_REDUN1; } if (ag->ag_tag != ag_cors->ag_tag) ag_cors->ag_tag = 0; if (ag->ag_nhop != ag_cors->ag_nhop) ag_cors->ag_nhop = 0; break; } } } /* That route has either been output or suppressed */ ag_cors = ag->ag_cors; ag_del(ag); } CHECK_AG(); } /* Try to aggregate a route with previous routes. */ void ag_check(naddr dst, naddr mask, naddr gate, naddr nhop, char metric, char pref, u_int new_seqno, u_short tag, u_short state, void (*out)(struct ag_info *)) /* output using this */ { struct ag_info *ag, *nag, *ag_cors; naddr xaddr; int x; dst = ntohl(dst); /* Punt non-contiguous subnet masks. * * (X & -X) contains a single bit if and only if X is a power of 2. * (X + (X & -X)) == 0 if and only if X is a power of 2. */ if ((mask & -mask) + mask != 0) { struct ag_info nc_ag; nc_ag.ag_dst_h = dst; nc_ag.ag_mask = mask; nc_ag.ag_gate = gate; nc_ag.ag_nhop = nhop; nc_ag.ag_metric = metric; nc_ag.ag_pref = pref; nc_ag.ag_tag = tag; nc_ag.ag_state = state; nc_ag.ag_seqno = new_seqno; out(&nc_ag); return; } /* Search for the right slot in the aggregation table. */ ag_cors = NULL; ag = ag_corsest; while (ag != NULL) { if (ag->ag_mask >= mask) break; /* Suppress old routes (i.e. combine with compatible routes * with coarser masks) as we look for the right slot in the * aggregation table for the new route. * A route to an address less than the current destination * will not be affected by the current route or any route * seen hereafter. That means it is safe to suppress it. * This check keeps poor routes (e.g. with large hop counts) * from preventing suppression of finer routes. */ if (ag_cors != NULL && ag->ag_dst_h < dst && (ag->ag_state & AGS_SUPPRESS) && ag_cors->ag_pref <= ag->ag_pref && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h && (ag_cors->ag_gate == ag->ag_gate || (ag->ag_state & AGS_FINE_GATE) || (ag_cors->ag_state & AGS_CORS_GATE))) { /* If the suppressed target was redundant, * then mark the suppressor redundant. */ if (AG_IS_REDUN(ag->ag_state) && ag_cors->ag_mask == ag->ag_mask<<1) { if (ag_cors->ag_dst_h == dst) ag_cors->ag_state |= AGS_REDUN0; else ag_cors->ag_state |= AGS_REDUN1; } if (ag->ag_tag != ag_cors->ag_tag) ag_cors->ag_tag = 0; if (ag->ag_nhop != ag_cors->ag_nhop) ag_cors->ag_nhop = 0; ag_del(ag); CHECK_AG(); } else { ag_cors = ag; } ag = ag_cors->ag_fine; } /* If we find the even/odd twin of the new route, and if the * masks and so forth are equal, we can aggregate them. * We can probably promote one of the pair. * * Since the routes are encountered in lexical order, * the new route must be odd. However, the second or later * times around this loop, it could be the even twin promoted * from the even/odd pair of twins of the finer route. */ while (ag != NULL && ag->ag_mask == mask && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { /* Here we know the target route and the route in the current * slot have the same netmasks and differ by at most the * last bit. They are either for the same destination, or * for an even/odd pair of destinations. */ if (ag->ag_dst_h == dst) { /* We have two routes to the same destination. * Routes are encountered in lexical order, so a * route is never promoted until the parent route is * already present. So we know that the new route is * a promoted (or aggregated) pair and the route * already in the slot is the explicit route. * * Prefer the best route if their metrics differ, * or the aggregated one if not, following a sort * of longest-match rule. */ if (pref <= ag->ag_pref) { ag->ag_gate = gate; ag->ag_nhop = nhop; ag->ag_tag = tag; ag->ag_metric = metric; ag->ag_pref = pref; if (ag->ag_seqno < new_seqno) ag->ag_seqno = new_seqno; x = ag->ag_state; ag->ag_state = state; state = x; } /* Some bits are set if they are set on either route, * except when the route is for an interface. */ if (!(ag->ag_state & AGS_IF)) ag->ag_state |= (state & (AGS_AGGREGATE_EITHER | AGS_REDUN0 | AGS_REDUN1)); return; } /* If one of the routes can be promoted and the other can * be suppressed, it may be possible to combine them or * worthwhile to promote one. * * Any route that can be promoted is always * marked to be eligible to be suppressed. */ if (!((state & AGS_AGGREGATE) && (ag->ag_state & AGS_SUPPRESS)) && !((ag->ag_state & AGS_AGGREGATE) && (state & AGS_SUPPRESS))) break; /* A pair of even/odd twin routes can be combined * if either is redundant, or if they are via the * same gateway and have the same metric. */ if (AG_IS_REDUN(ag->ag_state) || AG_IS_REDUN(state) || (ag->ag_gate == gate && ag->ag_pref == pref && (state & ag->ag_state & AGS_AGGREGATE) != 0)) { /* We have both the even and odd pairs. * Since the routes are encountered in order, * the route in the slot must be the even twin. * * Combine and promote (aggregate) the pair of routes. */ if (new_seqno < ag->ag_seqno) new_seqno = ag->ag_seqno; if (!AG_IS_REDUN(state)) state &= ~AGS_REDUN1; if (AG_IS_REDUN(ag->ag_state)) state |= AGS_REDUN0; else state &= ~AGS_REDUN0; state |= (ag->ag_state & AGS_AGGREGATE_EITHER); if (ag->ag_tag != tag) tag = 0; if (ag->ag_nhop != nhop) nhop = 0; /* Get rid of the even twin that was already * in the slot. */ ag_del(ag); } else if (ag->ag_pref >= pref && (ag->ag_state & AGS_AGGREGATE)) { /* If we cannot combine the pair, maybe the route * with the worse metric can be promoted. * * Promote the old, even twin, by giving its slot * in the table to the new, odd twin. */ ag->ag_dst_h = dst; xaddr = ag->ag_gate; ag->ag_gate = gate; gate = xaddr; xaddr = ag->ag_nhop; ag->ag_nhop = nhop; nhop = xaddr; x = ag->ag_tag; ag->ag_tag = tag; tag = x; /* The promoted route is even-redundant only if the * even twin was fully redundant. It is not * odd-redundant because the odd-twin will still be * in the table. */ x = ag->ag_state; if (!AG_IS_REDUN(x)) x &= ~AGS_REDUN0; x &= ~AGS_REDUN1; ag->ag_state = state; state = x; x = ag->ag_metric; ag->ag_metric = metric; metric = x; x = ag->ag_pref; ag->ag_pref = pref; pref = x; /* take the newest sequence number */ if (new_seqno <= ag->ag_seqno) new_seqno = ag->ag_seqno; else ag->ag_seqno = new_seqno; } else { if (!(state & AGS_AGGREGATE)) break; /* cannot promote either twin */ /* Promote the new, odd twin by shaving its * mask and address. * The promoted route is odd-redundant only if the * odd twin was fully redundant. It is not * even-redundant because the even twin is still in * the table. */ if (!AG_IS_REDUN(state)) state &= ~AGS_REDUN1; state &= ~AGS_REDUN0; if (new_seqno < ag->ag_seqno) new_seqno = ag->ag_seqno; else ag->ag_seqno = new_seqno; } mask <<= 1; dst &= mask; if (ag_cors == NULL) { ag = ag_corsest; break; } ag = ag_cors; ag_cors = ag->ag_cors; } /* When we can no longer promote and combine routes, * flush the old route in the target slot. Also flush * any finer routes that we know will never be aggregated by * the new route. * * In case we moved toward coarser masks, * get back where we belong */ if (ag != NULL && ag->ag_mask < mask) { ag_cors = ag; ag = ag->ag_fine; } /* Empty the target slot */ if (ag != NULL && ag->ag_mask == mask) { ag_flush(ag->ag_dst_h, ag->ag_mask, out); ag = (ag_cors == NULL) ? ag_corsest : ag_cors->ag_fine; } #ifdef DEBUG_AG (void)fflush(stderr); if (ag == NULL && ag_cors != ag_finest) abort(); if (ag_cors == NULL && ag != ag_corsest) abort(); if (ag != NULL && ag->ag_cors != ag_cors) abort(); if (ag_cors != NULL && ag_cors->ag_fine != ag) abort(); CHECK_AG(); #endif /* Save the new route on the end of the table. */ nag = ag_avail; ag_avail = nag->ag_fine; nag->ag_dst_h = dst; nag->ag_mask = mask; nag->ag_gate = gate; nag->ag_nhop = nhop; nag->ag_metric = metric; nag->ag_pref = pref; nag->ag_tag = tag; nag->ag_state = state; nag->ag_seqno = new_seqno; nag->ag_fine = ag; if (ag != NULL) ag->ag_cors = nag; else ag_finest = nag; nag->ag_cors = ag_cors; if (ag_cors == NULL) ag_corsest = nag; else ag_cors->ag_fine = nag; CHECK_AG(); } static const char * rtm_type_name(u_char type) { static const char * const rtm_types[] = { "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING", "RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", "RTM_NEWADDR", "RTM_DELADDR", #ifdef RTM_OIFINFO "RTM_OIFINFO", #endif "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" }; #define NEW_RTM_PAT "RTM type %#x" static char name0[sizeof(NEW_RTM_PAT)+2]; if (type > sizeof(rtm_types)/sizeof(rtm_types[0]) || type == 0) { snprintf(name0, sizeof(name0), NEW_RTM_PAT, type); return name0; } else { return rtm_types[type-1]; } #undef NEW_RTM_PAT } /* Trim a mask in a sockaddr * Produce a length of 0 for an address of 0. * Otherwise produce the index of the first zero byte. */ void #ifdef _HAVE_SIN_LEN masktrim(struct sockaddr_in *ap) #else masktrim(struct sockaddr_in_new *ap) #endif { char *cp; if (ap->sin_addr.s_addr == 0) { ap->sin_len = 0; return; } cp = (char *)(&ap->sin_addr.s_addr+1); while (*--cp == 0) continue; ap->sin_len = cp - (char*)ap + 1; } /* Tell the kernel to add, delete or change a route */ static void rtioctl(int action, /* RTM_DELETE, etc */ naddr dst, naddr gate, naddr mask, int metric, int flags) { struct { struct rt_msghdr w_rtm; struct sockaddr_in w_dst; struct sockaddr_in w_gate; #ifdef _HAVE_SA_LEN struct sockaddr_in w_mask; #else struct sockaddr_in_new w_mask; #endif } w; long cc; # define PAT " %-10s %s metric=%d flags=%#x" # define ARGS rtm_type_name(action), rtname(dst,mask,gate), metric, flags again: memset(&w, 0, sizeof(w)); w.w_rtm.rtm_msglen = sizeof(w); w.w_rtm.rtm_version = RTM_VERSION; w.w_rtm.rtm_type = action; w.w_rtm.rtm_flags = flags; w.w_rtm.rtm_seq = ++rt_sock_seqno; w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; if (metric != 0 || action == RTM_CHANGE) { w.w_rtm.rtm_rmx.rmx_hopcount = metric; w.w_rtm.rtm_inits |= RTV_HOPCOUNT; } w.w_dst.sin_family = AF_INET; w.w_dst.sin_addr.s_addr = dst; w.w_gate.sin_family = AF_INET; w.w_gate.sin_addr.s_addr = gate; #ifdef _HAVE_SA_LEN w.w_dst.sin_len = sizeof(w.w_dst); w.w_gate.sin_len = sizeof(w.w_gate); #endif if (mask == HOST_MASK) { w.w_rtm.rtm_flags |= RTF_HOST; w.w_rtm.rtm_msglen -= sizeof(w.w_mask); } else { w.w_rtm.rtm_addrs |= RTA_NETMASK; w.w_mask.sin_addr.s_addr = htonl(mask); #ifdef _HAVE_SA_LEN masktrim(&w.w_mask); if (w.w_mask.sin_len == 0) w.w_mask.sin_len = sizeof(long); w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len); #endif } #ifndef NO_INSTALL cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); if (cc < 0) { if (errno == ESRCH && (action == RTM_CHANGE || action == RTM_DELETE)) { trace_act("route disappeared before" PAT, ARGS); if (action == RTM_CHANGE) { action = RTM_ADD; goto again; } return; } msglog("write(rt_sock)" PAT ": %s", ARGS, strerror(errno)); return; } else if (cc != w.w_rtm.rtm_msglen) { msglog("write(rt_sock) wrote %ld instead of %d for" PAT, cc, w.w_rtm.rtm_msglen, ARGS); return; } #endif if (TRACEKERNEL) trace_misc("write kernel" PAT, ARGS); #undef PAT #undef ARGS } #define KHASH_SIZE 71 /* should be prime */ #define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE] static struct khash { struct khash *k_next; naddr k_dst; naddr k_mask; naddr k_gate; short k_metric; u_short k_state; #define KS_NEW 0x001 #define KS_DELETE 0x002 /* need to delete the route */ #define KS_ADD 0x004 /* add to the kernel */ #define KS_CHANGE 0x008 /* tell kernel to change the route */ #define KS_DEL_ADD 0x010 /* delete & add to change the kernel */ #define KS_STATIC 0x020 /* Static flag in kernel */ #define KS_GATEWAY 0x040 /* G flag in kernel */ #define KS_DYNAMIC 0x080 /* result of redirect */ #define KS_DELETED 0x100 /* already deleted from kernel */ #define KS_CHECK 0x200 time_t k_keep; #define K_KEEP_LIM 30 time_t k_redirect_time; /* when redirected route 1st seen */ } *khash_bins[KHASH_SIZE]; static struct khash* kern_find(naddr dst, naddr mask, struct khash ***ppk) { struct khash *k, **pk; for (pk = &KHASH(dst,mask); (k = *pk) != NULL; pk = &k->k_next) { if (k->k_dst == dst && k->k_mask == mask) break; } if (ppk != NULL) *ppk = pk; return k; } static struct khash* kern_add(naddr dst, naddr mask) { struct khash *k, **pk; k = kern_find(dst, mask, &pk); if (k != NULL) return k; k = (struct khash *)rtmalloc(sizeof(*k), "kern_add"); memset(k, 0, sizeof(*k)); k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; k->k_keep = now.tv_sec; *pk = k; return k; } /* If a kernel route has a non-zero metric, check that it is still in the * daemon table, and not deleted by interfaces coming and going. */ static void kern_check_static(struct khash *k, struct interface *ifp) { struct rt_entry *rt; struct rt_spare new; if (k->k_metric == 0) return; memset(&new, 0, sizeof(new)); new.rts_ifp = ifp; new.rts_gate = k->k_gate; new.rts_router = (ifp != NULL) ? ifp->int_addr : loopaddr; new.rts_metric = k->k_metric; new.rts_time = now.tv_sec; rt = rtget(k->k_dst, k->k_mask); if (rt != NULL) { if (!(rt->rt_state & RS_STATIC)) rtchange(rt, rt->rt_state | RS_STATIC, &new, 0); } else { rtadd(k->k_dst, k->k_mask, RS_STATIC, &new); } } /* operate on a kernel entry */ static void kern_ioctl(struct khash *k, int action, /* RTM_DELETE, etc */ int flags) { switch (action) { case RTM_DELETE: k->k_state &= ~KS_DYNAMIC; if (k->k_state & KS_DELETED) return; k->k_state |= KS_DELETED; break; case RTM_ADD: k->k_state &= ~KS_DELETED; break; case RTM_CHANGE: if (k->k_state & KS_DELETED) { action = RTM_ADD; k->k_state &= ~KS_DELETED; } break; } rtioctl(action, k->k_dst, k->k_gate, k->k_mask, k->k_metric, flags); } /* add a route the kernel told us */ static void rtm_add(struct rt_msghdr *rtm, struct rt_addrinfo *info, time_t keep) { struct khash *k; struct interface *ifp; naddr mask; if (rtm->rtm_flags & RTF_HOST) { mask = HOST_MASK; } else if (INFO_MASK(info) != 0) { mask = ntohl(S_ADDR(INFO_MASK(info))); } else { msglog("ignore %s without mask", rtm_type_name(rtm->rtm_type)); return; } k = kern_add(S_ADDR(INFO_DST(info)), mask); if (k->k_state & KS_NEW) k->k_keep = now.tv_sec+keep; if (INFO_GATE(info) == 0) { trace_act("note %s without gateway", rtm_type_name(rtm->rtm_type)); k->k_metric = HOPCNT_INFINITY; } else if (INFO_GATE(info)->sa_family != AF_INET) { trace_act("note %s with gateway AF=%d", rtm_type_name(rtm->rtm_type), INFO_GATE(info)->sa_family); k->k_metric = HOPCNT_INFINITY; } else { k->k_gate = S_ADDR(INFO_GATE(info)); k->k_metric = rtm->rtm_rmx.rmx_hopcount; if (k->k_metric < 0) k->k_metric = 0; else if (k->k_metric > HOPCNT_INFINITY-1) k->k_metric = HOPCNT_INFINITY-1; } k->k_state &= ~(KS_DELETE | KS_ADD | KS_CHANGE | KS_DEL_ADD | KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW | KS_CHECK); if (rtm->rtm_flags & RTF_GATEWAY) k->k_state |= KS_GATEWAY; if (rtm->rtm_flags & RTF_STATIC) k->k_state |= KS_STATIC; if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { if (INFO_AUTHOR(info) != 0 && INFO_AUTHOR(info)->sa_family == AF_INET) ifp = iflookup(S_ADDR(INFO_AUTHOR(info))); else ifp = NULL; if (supplier && (ifp == NULL || !(ifp->int_state & IS_REDIRECT_OK))) { /* Routers are not supposed to listen to redirects, * so delete it if it came via an unknown interface * or the interface does not have special permission. */ k->k_state &= ~KS_DYNAMIC; k->k_state |= KS_DELETE; LIM_SEC(need_kern, 0); trace_act("mark for deletion redirected %s --> %s" " via %s", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate), ifp ? ifp->int_name : "unknown interface"); } else { k->k_state |= KS_DYNAMIC; k->k_redirect_time = now.tv_sec; trace_act("accept redirected %s --> %s via %s", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate), ifp ? ifp->int_name : "unknown interface"); } return; } /* If it is not a static route, quit until the next comparison * between the kernel and daemon tables, when it will be deleted. */ if (!(k->k_state & KS_STATIC)) { k->k_state |= KS_DELETE; LIM_SEC(need_kern, k->k_keep); return; } /* Put static routes with real metrics into the daemon table so * they can be advertised. * * Find the interface toward the gateway. */ ifp = iflookup(k->k_gate); if (ifp == NULL) msglog("static route %s --> %s impossibly lacks ifp", addrname(S_ADDR(INFO_DST(info)), mask, 0), naddr_ntoa(k->k_gate)); kern_check_static(k, ifp); } /* deal with packet loss */ static void rtm_lose(struct rt_msghdr *rtm, struct rt_addrinfo *info) { if (INFO_GATE(info) == 0 || INFO_GATE(info)->sa_family != AF_INET) { trace_act("ignore %s without gateway", rtm_type_name(rtm->rtm_type)); return; } if (rdisc_ok) rdisc_age(S_ADDR(INFO_GATE(info))); age(S_ADDR(INFO_GATE(info))); } /* Make the gateway slot of an info structure point to something * useful. If it is not already useful, but it specifies an interface, * then fill in the sockaddr_in provided and point it there. */ static int get_info_gate(struct sockaddr **sap, struct sockaddr_in *rsin) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)*sap; struct interface *ifp; if (sdl == NULL) return 0; if ((sdl)->sdl_family == AF_INET) return 1; if ((sdl)->sdl_family != AF_LINK) return 0; ifp = ifwithindex(sdl->sdl_index, 1); if (ifp == NULL) return 0; rsin->sin_addr.s_addr = ifp->int_addr; #ifdef _HAVE_SA_LEN rsin->sin_len = sizeof(*rsin); #endif rsin->sin_family = AF_INET; *sap = (struct sockaddr*)rsin; return 1; } /* Clean the kernel table by copying it to the daemon image. * Eventually the daemon will delete any extra routes. */ void flush_kern(void) { static char *sysctl_buf; static size_t sysctl_buf_size = 0; size_t needed; int mib[6]; char *next, *lim; struct rt_msghdr *rtm; struct sockaddr_in gate_sin; struct rt_addrinfo info; int i; struct khash *k; for (i = 0; i < KHASH_SIZE; i++) { for (k = khash_bins[i]; k != NULL; k = k->k_next) { k->k_state |= KS_CHECK; } } mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; /* protocol */ mib[3] = 0; /* wildcard address family */ mib[4] = NET_RT_DUMP; mib[5] = 0; /* no flags */ for (;;) { if ((needed = sysctl_buf_size) != 0) { if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) break; if (errno != ENOMEM && errno != EFAULT) BADERR(1,"flush_kern: sysctl(RT_DUMP)"); free(sysctl_buf); needed = 0; } if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) BADERR(1,"flush_kern: sysctl(RT_DUMP) estimate"); /* Kludge around the habit of some systems, such as * BSD/OS 3.1, to not admit how many routes are in the * kernel, or at least to be quite wrong. */ needed += 50*(sizeof(*rtm)+5*sizeof(struct sockaddr)); sysctl_buf = rtmalloc(sysctl_buf_size = needed, "flush_kern sysctl(RT_DUMP)"); } lim = sysctl_buf + needed; for (next = sysctl_buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_msglen == 0) { msglog("zero length kernel route at " " %#lx in buffer %#lx before %#lx", (u_long)rtm, (u_long)sysctl_buf, (u_long)lim); break; } rt_xaddrs(&info, (struct sockaddr *)(rtm+1), (struct sockaddr *)(next + rtm->rtm_msglen), rtm->rtm_addrs); if (INFO_DST(&info) == 0 || INFO_DST(&info)->sa_family != AF_INET) continue; #if defined (RTF_LLINFO) /* ignore ARP table entries on systems with a merged route * and ARP table. */ if (rtm->rtm_flags & RTF_LLINFO) continue; #endif #if defined(RTF_WASCLONED) && defined(__FreeBSD__) /* ignore cloned routes */ if (rtm->rtm_flags & RTF_WASCLONED) continue; #endif /* ignore multicast addresses */ if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) continue; if (!get_info_gate(&INFO_GATE(&info), &gate_sin)) continue; /* Note static routes and interface routes, and also * preload the image of the kernel table so that * we can later clean it, as well as avoid making * unneeded changes. Keep the old kernel routes for a * few seconds to allow a RIP or router-discovery * response to be heard. */ rtm_add(rtm,&info,MIN_WAITTIME); } for (i = 0; i < KHASH_SIZE; i++) { for (k = khash_bins[i]; k != NULL; k = k->k_next) { if (k->k_state & KS_CHECK) { msglog("%s --> %s disappeared from kernel", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate)); del_static(k->k_dst, k->k_mask, k->k_gate, 1); } } } } /* Listen to announcements from the kernel */ void read_rt(void) { long cc; struct interface *ifp; struct sockaddr_in gate_sin; naddr mask, gate; union { struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; struct if_msghdr ifm; } m; char str[100], *strp; struct rt_addrinfo info; for (;;) { cc = read(rt_sock, &m, sizeof(m)); if (cc <= 0) { if (cc < 0 && errno != EWOULDBLOCK) LOGERR("read(rt_sock)"); return; } if (m.r.rtm.rtm_version != RTM_VERSION) { msglog("bogus routing message version %d", m.r.rtm.rtm_version); continue; } /* Ignore our own results. */ if (m.r.rtm.rtm_type <= RTM_CHANGE && m.r.rtm.rtm_pid == mypid) { static int complained = 0; if (!complained) { msglog("receiving our own change messages"); complained = 1; } continue; } if (m.r.rtm.rtm_type == RTM_IFINFO || m.r.rtm.rtm_type == RTM_NEWADDR || m.r.rtm.rtm_type == RTM_DELADDR) { ifp = ifwithindex(m.ifm.ifm_index, m.r.rtm.rtm_type != RTM_DELADDR); if (ifp == NULL) trace_act("note %s with flags %#x" " for unknown interface index #%d", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, m.ifm.ifm_index); else trace_act("note %s with flags %#x for %s", rtm_type_name(m.r.rtm.rtm_type), m.ifm.ifm_flags, ifp->int_name); /* After being informed of a change to an interface, * check them all now if the check would otherwise * be a long time from now, if the interface is * not known, or if the interface has been turned * off or on. */ if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL || ifp == NULL || ((ifp->int_if_flags ^ m.ifm.ifm_flags) & IFF_UP) != 0) ifinit_timer.tv_sec = now.tv_sec; continue; } #ifdef RTM_OIFINFO if (m.r.rtm.rtm_type == RTM_OIFINFO) continue; /* ignore compat message */ #endif strlcpy(str, rtm_type_name(m.r.rtm.rtm_type), sizeof(str)); strp = &str[strlen(str)]; if (m.r.rtm.rtm_type <= RTM_CHANGE) strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); + /* + * Only messages that use the struct rt_msghdr format are + * allowed beyond this point. + */ + if (m.r.rtm.rtm_type > RTM_RESOLVE) { + trace_act("ignore %s", str); + continue; + } + rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], m.r.rtm.rtm_addrs); if (INFO_DST(&info) == 0) { trace_act("ignore %s without dst", str); continue; } if (INFO_DST(&info)->sa_family != AF_INET) { trace_act("ignore %s for AF %d", str, INFO_DST(&info)->sa_family); continue; } mask = ((INFO_MASK(&info) != 0) ? ntohl(S_ADDR(INFO_MASK(&info))) : (m.r.rtm.rtm_flags & RTF_HOST) ? HOST_MASK : std_mask(S_ADDR(INFO_DST(&info)))); strp += sprintf(strp, ": %s", addrname(S_ADDR(INFO_DST(&info)), mask, 0)); if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { trace_act("ignore multicast %s", str); continue; } #if defined(RTF_LLINFO) if (m.r.rtm.rtm_flags & RTF_LLINFO) { trace_act("ignore ARP %s", str); continue; } #endif #if defined(RTF_WASCLONED) && defined(__FreeBSD__) if (m.r.rtm.rtm_flags & RTF_WASCLONED) { trace_act("ignore cloned %s", str); continue; } #endif if (get_info_gate(&INFO_GATE(&info), &gate_sin)) { gate = S_ADDR(INFO_GATE(&info)); strp += sprintf(strp, " --> %s", naddr_ntoa(gate)); } else { gate = 0; } if (INFO_AUTHOR(&info) != 0) strp += sprintf(strp, " by authority of %s", saddr_ntoa(INFO_AUTHOR(&info))); switch (m.r.rtm.rtm_type) { case RTM_ADD: case RTM_CHANGE: case RTM_REDIRECT: if (m.r.rtm.rtm_errno != 0) { trace_act("ignore %s with \"%s\" error", str, strerror(m.r.rtm.rtm_errno)); } else { trace_act("%s", str); rtm_add(&m.r.rtm,&info,0); } break; case RTM_DELETE: if (m.r.rtm.rtm_errno != 0 && m.r.rtm.rtm_errno != ESRCH) { trace_act("ignore %s with \"%s\" error", str, strerror(m.r.rtm.rtm_errno)); } else { trace_act("%s", str); del_static(S_ADDR(INFO_DST(&info)), mask, gate, 1); } break; case RTM_LOSING: trace_act("%s", str); rtm_lose(&m.r.rtm,&info); break; default: trace_act("ignore %s", str); break; } } } /* after aggregating, note routes that belong in the kernel */ static void kern_out(struct ag_info *ag) { struct khash *k; /* Do not install bad routes if they are not already present. * This includes routes that had RS_NET_SYN for interfaces that * recently died. */ if (ag->ag_metric == HOPCNT_INFINITY) { k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0); if (k == NULL) return; } else { k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); } if (k->k_state & KS_NEW) { /* will need to add new entry to the kernel table */ k->k_state = KS_ADD; if (ag->ag_state & AGS_GATEWAY) k->k_state |= KS_GATEWAY; k->k_gate = ag->ag_gate; k->k_metric = ag->ag_metric; return; } if (k->k_state & KS_STATIC) return; /* modify existing kernel entry if necessary */ if (k->k_gate != ag->ag_gate || k->k_metric != ag->ag_metric) { /* Must delete bad interface routes etc. to change them. */ if (k->k_metric == HOPCNT_INFINITY) k->k_state |= KS_DEL_ADD; k->k_gate = ag->ag_gate; k->k_metric = ag->ag_metric; k->k_state |= KS_CHANGE; } /* If the daemon thinks the route should exist, forget * about any redirections. * If the daemon thinks the route should exist, eventually * override manual intervention by the operator. */ if ((k->k_state & (KS_DYNAMIC | KS_DELETED)) != 0) { k->k_state &= ~KS_DYNAMIC; k->k_state |= (KS_ADD | KS_DEL_ADD); } if ((k->k_state & KS_GATEWAY) && !(ag->ag_state & AGS_GATEWAY)) { k->k_state &= ~KS_GATEWAY; k->k_state |= (KS_ADD | KS_DEL_ADD); } else if (!(k->k_state & KS_GATEWAY) && (ag->ag_state & AGS_GATEWAY)) { k->k_state |= KS_GATEWAY; k->k_state |= (KS_ADD | KS_DEL_ADD); } /* Deleting-and-adding is necessary to change aspects of a route. * Just delete instead of deleting and then adding a bad route. * Otherwise, we want to keep the route in the kernel. */ if (k->k_metric == HOPCNT_INFINITY && (k->k_state & KS_DEL_ADD)) k->k_state |= KS_DELETE; else k->k_state &= ~KS_DELETE; #undef RT } /* ARGSUSED */ static int walk_kern(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) char metric, pref; u_int ags = 0; /* Do not install synthetic routes */ if (RT->rt_state & RS_NET_SYN) return 0; if (!(RT->rt_state & RS_IF)) { /* This is an ordinary route, not for an interface. */ /* aggregate, ordinary good routes without regard to * their metric */ pref = 1; ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE); /* Do not install host routes directly to hosts, to avoid * interfering with ARP entries in the kernel table. */ if (RT_ISHOST(RT) && ntohl(RT->rt_dst) == RT->rt_gate) return 0; } else { /* This is an interface route. * Do not install routes for "external" remote interfaces. */ if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL)) return 0; /* Interfaces should override received routes. */ pref = 0; ags |= (AGS_IF | AGS_CORS_GATE); /* If it is not an interface, or an alias for an interface, * it must be a "gateway." * * If it is a "remote" interface, it is also a "gateway" to * the kernel if is not an alias. */ if (RT->rt_ifp == 0 || (RT->rt_ifp->int_state & IS_REMOTE)) ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_AGGREGATE); } /* If RIP is off and IRDP is on, let the route to the discovered * route suppress any RIP routes. Eventually the RIP routes * will time-out and be deleted. This reaches the steady-state * quicker. */ if ((RT->rt_state & RS_RDISC) && rip_sock < 0) ags |= AGS_CORS_GATE; metric = RT->rt_metric; if (metric == HOPCNT_INFINITY) { /* if the route is dead, so try hard to aggregate. */ pref = HOPCNT_INFINITY; ags |= (AGS_FINE_GATE | AGS_SUPPRESS); ags &= ~(AGS_IF | AGS_CORS_GATE); } ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, metric,pref, 0, 0, ags, kern_out); return 0; #undef RT } /* Update the kernel table to match the daemon table. */ static void fix_kern(void) { int i; struct khash *k, **pk; need_kern = age_timer; /* Walk daemon table, updating the copy of the kernel table. */ (void)rn_walktree(rhead, walk_kern, 0); ag_flush(0,0,kern_out); for (i = 0; i < KHASH_SIZE; i++) { for (pk = &khash_bins[i]; (k = *pk) != NULL; ) { /* Do not touch static routes */ if (k->k_state & KS_STATIC) { kern_check_static(k,0); pk = &k->k_next; continue; } /* check hold on routes deleted by the operator */ if (k->k_keep > now.tv_sec) { /* ensure we check when the hold is over */ LIM_SEC(need_kern, k->k_keep); /* mark for the next cycle */ k->k_state |= KS_DELETE; pk = &k->k_next; continue; } if ((k->k_state & KS_DELETE) && !(k->k_state & KS_DYNAMIC)) { kern_ioctl(k, RTM_DELETE, 0); *pk = k->k_next; free(k); continue; } if (k->k_state & KS_DEL_ADD) kern_ioctl(k, RTM_DELETE, 0); if (k->k_state & KS_ADD) { kern_ioctl(k, RTM_ADD, ((0 != (k->k_state & (KS_GATEWAY | KS_DYNAMIC))) ? RTF_GATEWAY : 0)); } else if (k->k_state & KS_CHANGE) { kern_ioctl(k, RTM_CHANGE, ((0 != (k->k_state & (KS_GATEWAY | KS_DYNAMIC))) ? RTF_GATEWAY : 0)); } k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); /* Mark this route to be deleted in the next cycle. * This deletes routes that disappear from the * daemon table, since the normal aging code * will clear the bit for routes that have not * disappeared from the daemon table. */ k->k_state |= KS_DELETE; pk = &k->k_next; } } } /* Delete a static route in the image of the kernel table. */ void del_static(naddr dst, naddr mask, naddr gate, int gone) { struct khash *k; struct rt_entry *rt; /* Just mark it in the table to be deleted next time the kernel * table is updated. * If it has already been deleted, mark it as such, and set its * keep-timer so that it will not be deleted again for a while. * This lets the operator delete a route added by the daemon * and add a replacement. */ k = kern_find(dst, mask, 0); if (k != NULL && (gate == 0 || k->k_gate == gate)) { k->k_state &= ~(KS_STATIC | KS_DYNAMIC | KS_CHECK); k->k_state |= KS_DELETE; if (gone) { k->k_state |= KS_DELETED; k->k_keep = now.tv_sec + K_KEEP_LIM; } } rt = rtget(dst, mask); if (rt != NULL && (rt->rt_state & RS_STATIC)) rtbad(rt); } /* Delete all routes generated from ICMP Redirects that use a given gateway, * as well as old redirected routes. */ void del_redirects(naddr bad_gate, time_t old) { int i; struct khash *k; for (i = 0; i < KHASH_SIZE; i++) { for (k = khash_bins[i]; k != NULL; k = k->k_next) { if (!(k->k_state & KS_DYNAMIC) || (k->k_state & KS_STATIC)) continue; if (k->k_gate != bad_gate && k->k_redirect_time > old && !supplier) continue; k->k_state |= KS_DELETE; k->k_state &= ~KS_DYNAMIC; need_kern.tv_sec = now.tv_sec; trace_act("mark redirected %s --> %s for deletion", addrname(k->k_dst, k->k_mask, 0), naddr_ntoa(k->k_gate)); } } } /* Start the daemon tables. */ extern int max_keylen; void rtinit(void) { int i; struct ag_info *ag; /* Initialize the radix trees */ max_keylen = sizeof(struct sockaddr_in); rn_init(); rn_inithead(&rhead, 32); /* mark all of the slots in the table free */ ag_avail = ag_slots; for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) { ag->ag_fine = ag+1; ag++; } } #ifdef _HAVE_SIN_LEN static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET, 0, {0}, {0}}; static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET, 0, {0}, {0}}; #else static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET}; static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; #endif static void set_need_flash(void) { if (!need_flash) { need_flash = 1; /* Do not send the flash update immediately. Wait a little * while to hear from other routers. */ no_flash.tv_sec = now.tv_sec + MIN_WAITTIME; } } /* Get a particular routing table entry */ struct rt_entry * rtget(naddr dst, naddr mask) { struct rt_entry *rt; dst_sock.sin_addr.s_addr = dst; mask_sock.sin_addr.s_addr = htonl(mask); masktrim(&mask_sock); rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead); if (!rt || rt->rt_dst != dst || rt->rt_mask != mask) return 0; return rt; } /* Find a route to dst as the kernel would. */ struct rt_entry * rtfind(naddr dst) { dst_sock.sin_addr.s_addr = dst; return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead); } /* add a route to the table */ void rtadd(naddr dst, naddr mask, u_int state, /* rt_state for the entry */ struct rt_spare *new) { struct rt_entry *rt; naddr smask; int i; struct rt_spare *rts; rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); memset(rt, 0, sizeof(*rt)); for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) rts->rts_metric = HOPCNT_INFINITY; rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock; rt->rt_dst = dst; rt->rt_dst_sock.sin_family = AF_INET; #ifdef _HAVE_SIN_LEN rt->rt_dst_sock.sin_len = dst_sock.sin_len; #endif if (mask != HOST_MASK) { smask = std_mask(dst); if ((smask & ~mask) == 0 && mask > smask) state |= RS_SUBNET; } mask_sock.sin_addr.s_addr = htonl(mask); masktrim(&mask_sock); rt->rt_mask = mask; rt->rt_state = state; rt->rt_spares[0] = *new; rt->rt_time = now.tv_sec; rt->rt_poison_metric = HOPCNT_INFINITY; rt->rt_seqno = update_seqno; if (++total_routes == MAX_ROUTES) msglog("have maximum (%d) routes", total_routes); if (TRACEACTIONS) trace_add_del("Add", rt); need_kern.tv_sec = now.tv_sec; set_need_flash(); if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, rhead, rt->rt_nodes)) { msglog("rnh_addaddr() failed for %s mask=%#lx", naddr_ntoa(dst), (u_long)mask); free(rt); } } /* notice a changed route */ void rtchange(struct rt_entry *rt, u_int state, /* new state bits */ struct rt_spare *new, char *label) { if (rt->rt_metric != new->rts_metric) { /* Fix the kernel immediately if it seems the route * has gone bad, since there may be a working route that * aggregates this route. */ if (new->rts_metric == HOPCNT_INFINITY) { need_kern.tv_sec = now.tv_sec; if (new->rts_time >= now.tv_sec - EXPIRE_TIME) new->rts_time = now.tv_sec - EXPIRE_TIME; } rt->rt_seqno = update_seqno; set_need_flash(); } if (rt->rt_gate != new->rts_gate) { need_kern.tv_sec = now.tv_sec; rt->rt_seqno = update_seqno; set_need_flash(); } state |= (rt->rt_state & RS_SUBNET); /* Keep various things from deciding ageless routes are stale. */ if (!AGE_RT(state, new->rts_ifp)) new->rts_time = now.tv_sec; if (TRACEACTIONS) trace_change(rt, state, new, label ? label : "Chg "); rt->rt_state = state; rt->rt_spares[0] = *new; } /* check for a better route among the spares */ static struct rt_spare * rts_better(struct rt_entry *rt) { struct rt_spare *rts, *rts1; int i; /* find the best alternative among the spares */ rts = rt->rt_spares+1; for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { if (BETTER_LINK(rt,rts1,rts)) rts = rts1; } return rts; } /* switch to a backup route */ void rtswitch(struct rt_entry *rt, struct rt_spare *rts) { struct rt_spare swap; char label[10]; /* Do not change permanent routes */ if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC | RS_NET_SYN | RS_IF))) return; /* find the best alternative among the spares */ if (rts == NULL) rts = rts_better(rt); /* Do not bother if it is not worthwhile. */ if (!BETTER_LINK(rt, rts, rt->rt_spares)) return; swap = rt->rt_spares[0]; (void)sprintf(label, "Use #%d", (int)(rts - rt->rt_spares)); rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), rts, label); if (swap.rts_metric == HOPCNT_INFINITY) { *rts = rts_empty; } else { *rts = swap; } } void rtdelete(struct rt_entry *rt) { struct khash *k; if (TRACEACTIONS) trace_add_del("Del", rt); k = kern_find(rt->rt_dst, rt->rt_mask, 0); if (k != NULL) { k->k_state |= KS_DELETE; need_kern.tv_sec = now.tv_sec; } dst_sock.sin_addr.s_addr = rt->rt_dst; mask_sock.sin_addr.s_addr = htonl(rt->rt_mask); masktrim(&mask_sock); if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock, rhead)) { msglog("rnh_deladdr() failed"); } else { free(rt); total_routes--; } } void rts_delete(struct rt_entry *rt, struct rt_spare *rts) { trace_upslot(rt, rts, &rts_empty); *rts = rts_empty; } /* Get rid of a bad route, and try to switch to a replacement. */ static void rtbad(struct rt_entry *rt) { struct rt_spare new; /* Poison the route */ new = rt->rt_spares[0]; new.rts_metric = HOPCNT_INFINITY; rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), &new, 0); rtswitch(rt, 0); } /* Junk a RS_NET_SYN or RS_LOCAL route, * unless it is needed by another interface. */ void rtbad_sub(struct rt_entry *rt) { struct interface *ifp, *ifp1; struct intnet *intnetp; u_int state; ifp1 = NULL; state = 0; if (rt->rt_state & RS_LOCAL) { /* Is this the route through loopback for the interface? * If so, see if it is used by any other interfaces, such * as a point-to-point interface with the same local address. */ LIST_FOREACH(ifp, &ifnet, int_list) { /* Retain it if another interface needs it. */ if (ifp->int_addr == rt->rt_ifp->int_addr) { state |= RS_LOCAL; ifp1 = ifp; break; } } } if (!(state & RS_LOCAL)) { /* Retain RIPv1 logical network route if there is another * interface that justifies it. */ if (rt->rt_state & RS_NET_SYN) { LIST_FOREACH(ifp, &ifnet, int_list) { if ((ifp->int_state & IS_NEED_NET_SYN) && rt->rt_mask == ifp->int_std_mask && rt->rt_dst == ifp->int_std_addr) { state |= RS_NET_SYN; ifp1 = ifp; break; } } } /* or if there is an authority route that needs it. */ for (intnetp = intnets; intnetp != NULL; intnetp = intnetp->intnet_next) { if (intnetp->intnet_addr == rt->rt_dst && intnetp->intnet_mask == rt->rt_mask) { state |= (RS_NET_SYN | RS_NET_INT); break; } } } if (ifp1 != NULL || (state & RS_NET_SYN)) { struct rt_spare new = rt->rt_spares[0]; new.rts_ifp = ifp1; rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN|RS_LOCAL)) | state), &new, 0); } else { rtbad(rt); } } /* Called while walking the table looking for sick interfaces * or after a time change. */ /* ARGSUSED */ int walk_bad(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct rt_spare *rts; int i; /* fix any spare routes through the interface */ rts = RT->rt_spares; for (i = NUM_SPARES; i != 1; i--) { rts++; if (rts->rts_metric < HOPCNT_INFINITY && (rts->rts_ifp == NULL || (rts->rts_ifp->int_state & IS_BROKE))) rts_delete(RT, rts); } /* Deal with the main route */ /* finished if it has been handled before or if its interface is ok */ if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE)) return 0; /* Bad routes for other than interfaces are easy. */ if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { rtbad(RT); return 0; } rtbad_sub(RT); return 0; #undef RT } /* Check the age of an individual route. */ /* ARGSUSED */ static int walk_age(struct radix_node *rn, struct walkarg *argp UNUSED) { #define RT ((struct rt_entry *)rn) struct interface *ifp; struct rt_spare *rts; int i; /* age all of the spare routes, including the primary route * currently in use */ rts = RT->rt_spares; for (i = NUM_SPARES; i != 0; i--, rts++) { ifp = rts->rts_ifp; if (i == NUM_SPARES) { if (!AGE_RT(RT->rt_state, ifp)) { /* Keep various things from deciding ageless * routes are stale */ rts->rts_time = now.tv_sec; continue; } /* forget RIP routes after RIP has been turned off. */ if (rip_sock < 0) { rtdelete(RT); return 0; } } /* age failing routes */ if (age_bad_gate == rts->rts_gate && rts->rts_time >= now_stale) { rts->rts_time -= SUPPLY_INTERVAL; } /* trash the spare routes when they go bad */ if (rts->rts_metric < HOPCNT_INFINITY && now_garbage > rts->rts_time && i != NUM_SPARES) rts_delete(RT, rts); } /* finished if the active route is still fresh */ if (now_stale <= RT->rt_time) return 0; /* try to switch to an alternative */ rtswitch(RT, 0); /* Delete a dead route after it has been publicly mourned. */ if (now_garbage > RT->rt_time) { rtdelete(RT); return 0; } /* Start poisoning a bad route before deleting it. */ if (now.tv_sec - RT->rt_time > EXPIRE_TIME) { struct rt_spare new = RT->rt_spares[0]; new.rts_metric = HOPCNT_INFINITY; rtchange(RT, RT->rt_state, &new, 0); } return 0; } /* Watch for dead routes and interfaces. */ void age(naddr bad_gate) { struct interface *ifp; int need_query = 0; /* If not listening to RIP, there is no need to age the routes in * the table. */ age_timer.tv_sec = (now.tv_sec + ((rip_sock < 0) ? NEVER : SUPPLY_INTERVAL)); /* Check for dead IS_REMOTE interfaces by timing their * transmissions. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (!(ifp->int_state & IS_REMOTE)) continue; /* ignore unreachable remote interfaces */ if (!check_remote(ifp)) continue; /* Restore remote interface that has become reachable */ if (ifp->int_state & IS_BROKE) if_ok(ifp, "remote "); if (ifp->int_act_time != NEVER && now.tv_sec - ifp->int_act_time > EXPIRE_TIME) { msglog("remote interface %s to %s timed out after" " %ld:%ld", ifp->int_name, naddr_ntoa(ifp->int_dstaddr), (long)(now.tv_sec - ifp->int_act_time)/60, (long)(now.tv_sec - ifp->int_act_time)%60); if_sick(ifp); } /* If we have not heard from the other router * recently, ask it. */ if (now.tv_sec >= ifp->int_query_time) { ifp->int_query_time = NEVER; need_query = 1; } } /* Age routes. */ age_bad_gate = bad_gate; (void)rn_walktree(rhead, walk_age, 0); /* delete old redirected routes to keep the kernel table small * and prevent blackholes */ del_redirects(bad_gate, now.tv_sec-STALE_TIME); /* Update the kernel routing table. */ fix_kern(); /* poke reticent remote gateways */ if (need_query) rip_query(); } Index: head/sys/net/route.h =================================================================== --- head/sys/net/route.h (revision 317034) +++ head/sys/net/route.h (revision 317035) @@ -1,498 +1,507 @@ /*- * Copyright (c) 1980, 1986, 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. * * @(#)route.h 8.4 (Berkeley) 1/9/95 * $FreeBSD$ */ #ifndef _NET_ROUTE_H_ #define _NET_ROUTE_H_ #include #include /* * Kernel resident routing tables. * * The routing tables are initialized when interface addresses * are set by making entries for all directly connected interfaces. */ /* * Struct route consiste of a destination address, * a route entry pointer, link-layer prepend data pointer along * with its length. */ struct route { struct rtentry *ro_rt; struct llentry *ro_lle; /* * ro_prepend and ro_plen are only used for bpf to pass in a * preformed header. They are not cacheable. */ char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; uint16_t ro_mtu; /* saved ro_rt mtu */ uint16_t spare; struct sockaddr ro_dst; }; #define RT_L2_ME_BIT 2 /* dst L2 addr is our address */ #define RT_MAY_LOOP_BIT 3 /* dst may require loop copy */ #define RT_HAS_HEADER_BIT 4 /* mbuf already have its header prepended */ #define RT_CACHING_CONTEXT 0x1 /* XXX: not used anywhere */ #define RT_NORTREF 0x2 /* doesn't hold reference on ro_rt */ #define RT_L2_ME (1 << RT_L2_ME_BIT) /* 0x0004 */ #define RT_MAY_LOOP (1 << RT_MAY_LOOP_BIT) /* 0x0008 */ #define RT_HAS_HEADER (1 << RT_HAS_HEADER_BIT) /* 0x0010 */ #define RT_REJECT 0x0020 /* Destination is reject */ #define RT_BLACKHOLE 0x0040 /* Destination is blackhole */ #define RT_HAS_GW 0x0080 /* Destination has GW */ #define RT_LLE_CACHE 0x0100 /* Cache link layer */ struct rt_metrics { u_long rmx_locks; /* Kernel must leave these values alone */ u_long rmx_mtu; /* MTU for this path */ u_long rmx_hopcount; /* max hops expected */ u_long rmx_expire; /* lifetime for route, e.g. redirect */ u_long rmx_recvpipe; /* inbound delay-bandwidth product */ u_long rmx_sendpipe; /* outbound delay-bandwidth product */ u_long rmx_ssthresh; /* outbound gateway buffer limit */ u_long rmx_rtt; /* estimated round trip time */ u_long rmx_rttvar; /* estimated rtt variance */ u_long rmx_pksent; /* packets sent using this route */ u_long rmx_weight; /* route weight */ u_long rmx_filler[3]; /* will be used for T/TCP later */ }; /* * rmx_rtt and rmx_rttvar are stored as microseconds; * RTTTOPRHZ(rtt) converts to a value suitable for use * by a protocol slowtimo counter. */ #define RTM_RTTUNIT 1000000 /* units for rtt, rttvar, as units per sec */ #define RTTTOPRHZ(r) ((r) / (RTM_RTTUNIT / PR_SLOWHZ)) /* lle state is exported in rmx_state rt_metrics field */ #define rmx_state rmx_weight /* * Keep a generation count of routing table, incremented on route addition, * so we can invalidate caches. This is accessed without a lock, as precision * is not required. */ typedef volatile u_int rt_gen_t; /* tree generation (for adds) */ #define RT_GEN(fibnum, af) rt_tables_get_gen(fibnum, af) #define RT_DEFAULT_FIB 0 /* Explicitly mark fib=0 restricted cases */ #define RT_ALL_FIBS -1 /* Announce event for every fib */ #ifdef _KERNEL extern u_int rt_numfibs; /* number of usable routing tables */ VNET_DECLARE(u_int, rt_add_addr_allfibs); /* Announce interfaces to all fibs */ #define V_rt_add_addr_allfibs VNET(rt_add_addr_allfibs) #endif /* * We distinguish between routes to hosts and routes to networks, * preferring the former if available. For each route we infer * the interface to use from the gateway address supplied when * the route was entered. Routes that forward packets through * gateways are marked so that the output routines know to address the * gateway rather than the ultimate destination. */ #ifndef RNF_NORMAL #include #ifdef RADIX_MPATH #include #endif #endif #if defined(_KERNEL) struct rtentry { struct radix_node rt_nodes[2]; /* tree glue, and other values */ /* * XXX struct rtentry must begin with a struct radix_node (or two!) * because the code does some casts of a 'struct radix_node *' * to a 'struct rtentry *' */ #define rt_key(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_key))) #define rt_mask(r) (*((struct sockaddr **)(&(r)->rt_nodes->rn_mask))) struct sockaddr *rt_gateway; /* value */ struct ifnet *rt_ifp; /* the answer: interface to use */ struct ifaddr *rt_ifa; /* the answer: interface address to use */ int rt_flags; /* up/down?, host/net */ int rt_refcnt; /* # held references */ u_int rt_fibnum; /* which FIB */ u_long rt_mtu; /* MTU for this path */ u_long rt_weight; /* absolute weight */ u_long rt_expire; /* lifetime for route, e.g. redirect */ #define rt_endzero rt_pksent counter_u64_t rt_pksent; /* packets sent using this route */ struct mtx rt_mtx; /* mutex for routing entry */ struct rtentry *rt_chain; /* pointer to next rtentry to delete */ }; #endif /* _KERNEL */ #define RTF_UP 0x1 /* route usable */ #define RTF_GATEWAY 0x2 /* destination is a gateway */ #define RTF_HOST 0x4 /* host entry (net otherwise) */ #define RTF_REJECT 0x8 /* host or net unreachable */ #define RTF_DYNAMIC 0x10 /* created dynamically (by redirect) */ #define RTF_MODIFIED 0x20 /* modified dynamically (by redirect) */ #define RTF_DONE 0x40 /* message confirmed */ /* 0x80 unused, was RTF_DELCLONE */ /* 0x100 unused, was RTF_CLONING */ #define RTF_XRESOLVE 0x200 /* external daemon resolves name */ #define RTF_LLINFO 0x400 /* DEPRECATED - exists ONLY for backward compatibility */ #define RTF_LLDATA 0x400 /* used by apps to add/del L2 entries */ #define RTF_STATIC 0x800 /* manually added */ #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ /* 0x10000 unused, was RTF_PRCLONING */ /* 0x20000 unused, was RTF_WASCLONED */ #define RTF_PROTO3 0x40000 /* protocol specific routing flag */ #define RTF_FIXEDMTU 0x80000 /* MTU was explicitly specified */ #define RTF_PINNED 0x100000 /* route is immutable */ #define RTF_LOCAL 0x200000 /* route represents a local address */ #define RTF_BROADCAST 0x400000 /* route represents a bcast address */ #define RTF_MULTICAST 0x800000 /* route represents a mcast address */ /* 0x8000000 and up unassigned */ #define RTF_STICKY 0x10000000 /* always route dst->src */ #define RTF_RNH_LOCKED 0x40000000 /* unused */ #define RTF_GWFLAG_COMPAT 0x80000000 /* a compatibility bit for interacting with existing routing apps */ /* Mask of RTF flags that are allowed to be modified by RTM_CHANGE. */ #define RTF_FMASK \ (RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_BLACKHOLE | \ RTF_REJECT | RTF_STATIC | RTF_STICKY) /* * fib_ nexthop API flags. */ /* Consumer-visible nexthop info flags */ #define NHF_REJECT 0x0010 /* RTF_REJECT */ #define NHF_BLACKHOLE 0x0020 /* RTF_BLACKHOLE */ #define NHF_REDIRECT 0x0040 /* RTF_DYNAMIC|RTF_MODIFIED */ #define NHF_DEFAULT 0x0080 /* Default route */ #define NHF_BROADCAST 0x0100 /* RTF_BROADCAST */ #define NHF_GATEWAY 0x0200 /* RTF_GATEWAY */ /* Nexthop request flags */ #define NHR_IFAIF 0x01 /* Return ifa_ifp interface */ #define NHR_REF 0x02 /* For future use */ /* Control plane route request flags */ #define NHR_COPY 0x100 /* Copy rte data */ #ifdef _KERNEL /* rte<>ro_flags translation */ static inline void rt_update_ro_flags(struct route *ro) { int rt_flags = ro->ro_rt->rt_flags; ro->ro_flags &= ~ (RT_REJECT|RT_BLACKHOLE|RT_HAS_GW); ro->ro_flags |= (rt_flags & RTF_REJECT) ? RT_REJECT : 0; ro->ro_flags |= (rt_flags & RTF_BLACKHOLE) ? RT_BLACKHOLE : 0; ro->ro_flags |= (rt_flags & RTF_GATEWAY) ? RT_HAS_GW : 0; } #endif /* * Routing statistics. */ struct rtstat { short rts_badredirect; /* bogus redirect calls */ short rts_dynamic; /* routes created by redirects */ short rts_newgateway; /* routes modified by redirects */ short rts_unreach; /* lookups which failed */ short rts_wildcard; /* lookups satisfied by a wildcard */ }; /* * Structures for routing messages. */ struct rt_msghdr { u_short rtm_msglen; /* to skip over non-understood messages */ u_char rtm_version; /* future binary compatibility */ u_char rtm_type; /* message type */ u_short rtm_index; /* index for associated ifp */ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ int rtm_addrs; /* bitmask identifying sockaddrs in msg */ pid_t rtm_pid; /* identify sender */ int rtm_seq; /* for sender to identify action */ int rtm_errno; /* why failed */ int rtm_fmask; /* bitmask used in RTM_CHANGE message */ u_long rtm_inits; /* which metrics we are initializing */ struct rt_metrics rtm_rmx; /* metrics themselves */ }; #define RTM_VERSION 5 /* Up the ante and ignore older versions */ /* * Message types. + * + * The format for each message is annotated below using the following + * identifiers: + * + * (1) struct rt_msghdr + * (2) struct ifa_msghdr + * (3) struct if_msghdr + * (4) struct ifma_msghdr + * (5) struct if_announcemsghdr + * */ -#define RTM_ADD 0x1 /* Add Route */ -#define RTM_DELETE 0x2 /* Delete Route */ -#define RTM_CHANGE 0x3 /* Change Metrics or flags */ -#define RTM_GET 0x4 /* Report Metrics */ -#define RTM_LOSING 0x5 /* Kernel Suspects Partitioning */ -#define RTM_REDIRECT 0x6 /* Told to use different route */ -#define RTM_MISS 0x7 /* Lookup failed on this address */ -#define RTM_LOCK 0x8 /* fix specified metrics */ +#define RTM_ADD 0x1 /* (1) Add Route */ +#define RTM_DELETE 0x2 /* (1) Delete Route */ +#define RTM_CHANGE 0x3 /* (1) Change Metrics or flags */ +#define RTM_GET 0x4 /* (1) Report Metrics */ +#define RTM_LOSING 0x5 /* (1) Kernel Suspects Partitioning */ +#define RTM_REDIRECT 0x6 /* (1) Told to use different route */ +#define RTM_MISS 0x7 /* (1) Lookup failed on this address */ +#define RTM_LOCK 0x8 /* (1) fix specified metrics */ /* 0x9 */ /* 0xa */ -#define RTM_RESOLVE 0xb /* req to resolve dst to LL addr */ -#define RTM_NEWADDR 0xc /* address being added to iface */ -#define RTM_DELADDR 0xd /* address being removed from iface */ -#define RTM_IFINFO 0xe /* iface going up/down etc. */ -#define RTM_NEWMADDR 0xf /* mcast group membership being added to if */ -#define RTM_DELMADDR 0x10 /* mcast group membership being deleted */ -#define RTM_IFANNOUNCE 0x11 /* iface arrival/departure */ -#define RTM_IEEE80211 0x12 /* IEEE80211 wireless event */ +#define RTM_RESOLVE 0xb /* (1) req to resolve dst to LL addr */ +#define RTM_NEWADDR 0xc /* (2) address being added to iface */ +#define RTM_DELADDR 0xd /* (2) address being removed from iface */ +#define RTM_IFINFO 0xe /* (3) iface going up/down etc. */ +#define RTM_NEWMADDR 0xf /* (4) mcast group membership being added to if */ +#define RTM_DELMADDR 0x10 /* (4) mcast group membership being deleted */ +#define RTM_IFANNOUNCE 0x11 /* (5) iface arrival/departure */ +#define RTM_IEEE80211 0x12 /* (5) IEEE80211 wireless event */ /* * Bitmask values for rtm_inits and rmx_locks. */ #define RTV_MTU 0x1 /* init or lock _mtu */ #define RTV_HOPCOUNT 0x2 /* init or lock _hopcount */ #define RTV_EXPIRE 0x4 /* init or lock _expire */ #define RTV_RPIPE 0x8 /* init or lock _recvpipe */ #define RTV_SPIPE 0x10 /* init or lock _sendpipe */ #define RTV_SSTHRESH 0x20 /* init or lock _ssthresh */ #define RTV_RTT 0x40 /* init or lock _rtt */ #define RTV_RTTVAR 0x80 /* init or lock _rttvar */ #define RTV_WEIGHT 0x100 /* init or lock _weight */ /* * Bitmask values for rtm_addrs. */ #define RTA_DST 0x1 /* destination sockaddr present */ #define RTA_GATEWAY 0x2 /* gateway sockaddr present */ #define RTA_NETMASK 0x4 /* netmask sockaddr present */ #define RTA_GENMASK 0x8 /* cloning mask sockaddr present */ #define RTA_IFP 0x10 /* interface name sockaddr present */ #define RTA_IFA 0x20 /* interface addr sockaddr present */ #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ /* * Index offsets for sockaddr array for alternate internal encoding. */ #define RTAX_DST 0 /* destination sockaddr present */ #define RTAX_GATEWAY 1 /* gateway sockaddr present */ #define RTAX_NETMASK 2 /* netmask sockaddr present */ #define RTAX_GENMASK 3 /* cloning mask sockaddr present */ #define RTAX_IFP 4 /* interface name sockaddr present */ #define RTAX_IFA 5 /* interface addr sockaddr present */ #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ #define RTAX_MAX 8 /* size of array to allocate */ typedef int rt_filter_f_t(const struct rtentry *, void *); struct rt_addrinfo { int rti_addrs; /* Route RTF_ flags */ int rti_flags; /* Route RTF_ flags */ struct sockaddr *rti_info[RTAX_MAX]; /* Sockaddr data */ struct ifaddr *rti_ifa; /* value of rt_ifa addr */ struct ifnet *rti_ifp; /* route interface */ rt_filter_f_t *rti_filter; /* filter function */ void *rti_filterdata; /* filter paramenters */ u_long rti_mflags; /* metrics RTV_ flags */ u_long rti_spare; /* Will be used for fib */ struct rt_metrics *rti_rmx; /* Pointer to route metrics */ }; /* * This macro returns the size of a struct sockaddr when passed * through a routing socket. Basically we round up sa_len to * a multiple of sizeof(long), with a minimum of sizeof(long). - * The check for a NULL pointer is just a convenience, probably never used. * The case sa_len == 0 should only apply to empty structures. */ #define SA_SIZE(sa) \ - ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ + ( (((struct sockaddr *)(sa))->sa_len == 0) ? \ sizeof(long) : \ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) #define sa_equal(a, b) ( \ (((const struct sockaddr *)(a))->sa_len == ((const struct sockaddr *)(b))->sa_len) && \ (bcmp((a), (b), ((const struct sockaddr *)(b))->sa_len) == 0)) #ifdef _KERNEL #define RT_LINK_IS_UP(ifp) (!((ifp)->if_capabilities & IFCAP_LINKSTATE) \ || (ifp)->if_link_state == LINK_STATE_UP) #define RT_LOCK_INIT(_rt) \ mtx_init(&(_rt)->rt_mtx, "rtentry", NULL, MTX_DEF | MTX_DUPOK | MTX_NEW) #define RT_LOCK(_rt) mtx_lock(&(_rt)->rt_mtx) #define RT_UNLOCK(_rt) mtx_unlock(&(_rt)->rt_mtx) #define RT_LOCK_DESTROY(_rt) mtx_destroy(&(_rt)->rt_mtx) #define RT_LOCK_ASSERT(_rt) mtx_assert(&(_rt)->rt_mtx, MA_OWNED) #define RT_UNLOCK_COND(_rt) do { \ if (mtx_owned(&(_rt)->rt_mtx)) \ mtx_unlock(&(_rt)->rt_mtx); \ } while (0) #define RT_ADDREF(_rt) do { \ RT_LOCK_ASSERT(_rt); \ KASSERT((_rt)->rt_refcnt >= 0, \ ("negative refcnt %d", (_rt)->rt_refcnt)); \ (_rt)->rt_refcnt++; \ } while (0) #define RT_REMREF(_rt) do { \ RT_LOCK_ASSERT(_rt); \ KASSERT((_rt)->rt_refcnt > 0, \ ("bogus refcnt %d", (_rt)->rt_refcnt)); \ (_rt)->rt_refcnt--; \ } while (0) #define RTFREE_LOCKED(_rt) do { \ if ((_rt)->rt_refcnt <= 1) \ rtfree(_rt); \ else { \ RT_REMREF(_rt); \ RT_UNLOCK(_rt); \ } \ /* guard against invalid refs */ \ _rt = 0; \ } while (0) #define RTFREE(_rt) do { \ RT_LOCK(_rt); \ RTFREE_LOCKED(_rt); \ } while (0) #define RO_RTFREE(_ro) do { \ if ((_ro)->ro_rt) { \ if ((_ro)->ro_flags & RT_NORTREF) { \ (_ro)->ro_flags &= ~RT_NORTREF; \ (_ro)->ro_rt = NULL; \ (_ro)->ro_lle = NULL; \ } else { \ RT_LOCK((_ro)->ro_rt); \ RTFREE_LOCKED((_ro)->ro_rt); \ } \ } \ } while (0) /* * Validate a cached route based on a supplied cookie. If there is an * out-of-date cache, simply free it. Update the generation number * for the new allocation */ #define RT_VALIDATE(ro, cookiep, fibnum) do { \ rt_gen_t cookie = RT_GEN(fibnum, (ro)->ro_dst.sa_family); \ if (*(cookiep) != cookie) { \ if ((ro)->ro_rt != NULL) { \ RTFREE((ro)->ro_rt); \ (ro)->ro_rt = NULL; \ } \ *(cookiep) = cookie; \ } \ } while (0) struct ifmultiaddr; struct rib_head; void rt_ieee80211msg(struct ifnet *, int, void *, size_t); void rt_ifannouncemsg(struct ifnet *, int); void rt_ifmsg(struct ifnet *); void rt_missmsg(int, struct rt_addrinfo *, int, int); void rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int); void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *); void rt_newaddrmsg_fib(int, struct ifaddr *, int, struct rtentry *, int); int rt_addrmsg(int, struct ifaddr *, int); int rt_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); void rt_newmaddrmsg(int, struct ifmultiaddr *); int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); struct rib_head *rt_table_init(int); void rt_table_destroy(struct rib_head *); u_int rt_tables_get_gen(int table, int fam); int rtsock_addrmsg(int, struct ifaddr *, int); int rtsock_routemsg(int, struct ifnet *ifp, int, struct rtentry *, int); /* * Note the following locking behavior: * * rtalloc1() returns a locked rtentry * * rtfree() and RTFREE_LOCKED() require a locked rtentry * * RTFREE() uses an unlocked entry. */ void rtfree(struct rtentry *); void rt_updatemtu(struct ifnet *); typedef int rt_walktree_f_t(struct rtentry *, void *); typedef void rt_setwarg_t(struct rib_head *, uint32_t, int, void *); void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *); void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg); void rt_flushifroutes_af(struct ifnet *, int); void rt_flushifroutes(struct ifnet *ifp); /* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ /* Thes are used by old code not yet converted to use multiple FIBS */ struct rtentry *rtalloc1(struct sockaddr *, int, u_long); int rtinit(struct ifaddr *, int, int); /* XXX MRT NEW VERSIONS THAT USE FIBs * For now the protocol indepedent versions are the same as the AF_INET ones * but this will change.. */ int rt_getifa_fib(struct rt_addrinfo *, u_int fibnum); void rtalloc_ign_fib(struct route *ro, u_long ignflags, u_int fibnum); struct rtentry *rtalloc1_fib(struct sockaddr *, int, u_long, u_int); int rtioctl_fib(u_long, caddr_t, u_int); void rtredirect_fib(struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct sockaddr *, u_int); int rtrequest_fib(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **, u_int); int rtrequest1_fib(int, struct rt_addrinfo *, struct rtentry **, u_int); int rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t, struct rt_addrinfo *); void rib_free_info(struct rt_addrinfo *info); #endif #endif Index: head/usr.bin/netstat/route.c =================================================================== --- head/usr.bin/netstat/route.c (revision 317034) +++ head/usr.bin/netstat/route.c (revision 317035) @@ -1,799 +1,800 @@ /*- * Copyright (c) 1983, 1988, 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. */ #if 0 #ifndef lint static char sccsid[] = "From: @(#)route.c 8.6 (Berkeley) 4/28/95"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netstat.h" #include "nl_defs.h" /* * Definitions for showing gateway flags. */ static struct bits { u_long b_mask; char b_val; const char *b_name; } bits[] = { { RTF_UP, 'U', "up" }, { RTF_GATEWAY, 'G', "gateway" }, { RTF_HOST, 'H', "host" }, { RTF_REJECT, 'R', "reject" }, { RTF_DYNAMIC, 'D', "dynamic" }, { RTF_MODIFIED, 'M', "modified" }, { RTF_DONE, 'd', "done" }, /* Completed -- for routing msgs only */ { RTF_XRESOLVE, 'X', "xresolve" }, { RTF_STATIC, 'S', "static" }, { RTF_PROTO1, '1', "proto1" }, { RTF_PROTO2, '2', "proto2" }, { RTF_PROTO3, '3', "proto3" }, { RTF_BLACKHOLE,'B', "blackhole" }, { RTF_BROADCAST,'b', "broadcast" }, #ifdef RTF_LLINFO { RTF_LLINFO, 'L', "llinfo" }, #endif { 0 , 0, NULL } }; struct ifmap_entry { char ifname[IFNAMSIZ]; }; static struct ifmap_entry *ifmap; static int ifmap_size; static struct timespec uptime; static const char *netname4(in_addr_t, in_addr_t); #ifdef INET6 static const char *netname6(struct sockaddr_in6 *, struct sockaddr_in6 *); #endif static void p_rtable_sysctl(int, int); static void p_rtentry_sysctl(const char *name, struct rt_msghdr *); static int p_sockaddr(const char *name, struct sockaddr *, struct sockaddr *, int, int); static const char *fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags); static void p_flags(int, const char *); static const char *fmt_flags(int f); static void domask(char *, size_t, u_long); /* * Print routing tables. */ void routepr(int fibnum, int af) { size_t intsize; int numfibs; if (live == 0) return; intsize = sizeof(int); if (fibnum == -1 && sysctlbyname("net.my_fibnum", &fibnum, &intsize, NULL, 0) == -1) fibnum = 0; if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) numfibs = 1; if (fibnum < 0 || fibnum > numfibs - 1) errx(EX_USAGE, "%d: invalid fib", fibnum); /* * Since kernel & userland use different timebase * (time_uptime vs time_second) and we are reading kernel memory * directly we should do rt_expire --> expire_time conversion. */ if (clock_gettime(CLOCK_UPTIME, &uptime) < 0) err(EX_OSERR, "clock_gettime() failed"); xo_open_container("route-information"); xo_emit("{T:Routing tables}"); if (fibnum) xo_emit(" ({L:fib}: {:fib/%d})", fibnum); xo_emit("\n"); p_rtable_sysctl(fibnum, af); xo_close_container("route-information"); } /* * Print address family header before a section of the routing table. */ void pr_family(int af1) { const char *afname; switch (af1) { case AF_INET: afname = "Internet"; break; #ifdef INET6 case AF_INET6: afname = "Internet6"; break; #endif /*INET6*/ case AF_ISO: afname = "ISO"; break; case AF_CCITT: afname = "X.25"; break; case AF_NETGRAPH: afname = "Netgraph"; break; default: afname = NULL; break; } if (afname) xo_emit("\n{k:address-family/%s}:\n", afname); else xo_emit("\n{L:Protocol Family} {k:address-family/%d}:\n", af1); } /* column widths; each followed by one space */ #ifndef INET6 #define WID_DST_DEFAULT(af) 18 /* width of destination column */ #define WID_GW_DEFAULT(af) 18 /* width of gateway column */ #define WID_IF_DEFAULT(af) (Wflag ? 10 : 8) /* width of netif column */ #else #define WID_DST_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 33: 18) : 18) #define WID_GW_DEFAULT(af) \ ((af) == AF_INET6 ? (numeric_addr ? 29 : 18) : 18) #define WID_IF_DEFAULT(af) ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 8)) #endif /*INET6*/ static int wid_dst; static int wid_gw; static int wid_flags; static int wid_pksent; static int wid_mtu; static int wid_if; static int wid_expire; /* * Print header for routing table columns. */ static void pr_rthdr(int af1 __unused) { if (Wflag) { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n", wid_dst, wid_dst, "Destination", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_pksent, wid_pksent, "Use", wid_mtu, wid_mtu, "Mtu", wid_if, wid_if, "Netif", wid_expire, "Expire"); } else { xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} " "{T:/%*s}\n", wid_dst, wid_dst, "Destination", wid_gw, wid_gw, "Gateway", wid_flags, wid_flags, "Flags", wid_if, wid_if, "Netif", wid_expire, "Expire"); } } static void p_rtable_sysctl(int fibnum, int af) { size_t needed; int mib[7]; char *buf, *next, *lim; struct rt_msghdr *rtm; struct sockaddr *sa; int fam = AF_UNSPEC, ifindex = 0, size; int need_table_close = false; struct ifaddrs *ifap, *ifa; struct sockaddr_dl *sdl; /* * Retrieve interface list at first * since we need #ifindex -> if_xname match */ if (getifaddrs(&ifap) != 0) err(EX_OSERR, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; sdl = (struct sockaddr_dl *)ifa->ifa_addr; ifindex = sdl->sdl_index; if (ifindex >= ifmap_size) { size = roundup(ifindex + 1, 32) * sizeof(struct ifmap_entry); if ((ifmap = realloc(ifmap, size)) == NULL) errx(2, "realloc(%d) failed", size); memset(&ifmap[ifmap_size], 0, size - ifmap_size * sizeof(struct ifmap_entry)); ifmap_size = roundup(ifindex + 1, 32); } if (*ifmap[ifindex].ifname != '\0') continue; strlcpy(ifmap[ifindex].ifname, ifa->ifa_name, IFNAMSIZ); } freeifaddrs(ifap); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = af; mib[4] = NET_RT_DUMP; mib[5] = 0; mib[6] = fibnum; if (sysctl(mib, nitems(mib), NULL, &needed, NULL, 0) < 0) err(EX_OSERR, "sysctl: net.route.0.%d.dump.%d estimate", af, fibnum); if ((buf = malloc(needed)) == NULL) errx(2, "malloc(%lu)", (unsigned long)needed); if (sysctl(mib, nitems(mib), buf, &needed, NULL, 0) < 0) err(1, "sysctl: net.route.0.%d.dump.%d", af, fibnum); lim = buf + needed; xo_open_container("route-table"); xo_open_list("rt-family"); for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; /* * Peek inside header to determine AF */ sa = (struct sockaddr *)(rtm + 1); /* Only print family first time. */ if (fam != sa->sa_family) { if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } need_table_close = true; fam = sa->sa_family; wid_dst = WID_DST_DEFAULT(fam); wid_gw = WID_GW_DEFAULT(fam); wid_flags = 6; wid_pksent = 8; wid_mtu = 6; wid_if = WID_IF_DEFAULT(fam); wid_expire = 6; xo_open_instance("rt-family"); pr_family(fam); xo_open_list("rt-entry"); pr_rthdr(fam); } p_rtentry_sysctl("rt-entry", rtm); } if (need_table_close) { xo_close_list("rt-entry"); xo_close_instance("rt-family"); } xo_close_list("rt-family"); xo_close_container("route-table"); free(buf); } static void p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm) { struct sockaddr *sa, *addr[RTAX_MAX]; char buffer[128]; char prettyname[128]; int i, protrusion; xo_open_instance(name); sa = (struct sockaddr *)(rtm + 1); for (i = 0; i < RTAX_MAX; i++) { - if (rtm->rtm_addrs & (1 << i)) + if (rtm->rtm_addrs & (1 << i)) { addr[i] = sa; - sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + } } protrusion = p_sockaddr("destination", addr[RTAX_DST], addr[RTAX_NETMASK], rtm->rtm_flags, wid_dst); protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST, wid_gw - protrusion); snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ", wid_flags - protrusion); p_flags(rtm->rtm_flags, buffer); if (Wflag) { xo_emit("{t:use/%*lu} ", wid_pksent, rtm->rtm_rmx.rmx_pksent); if (rtm->rtm_rmx.rmx_mtu != 0) xo_emit("{t:mtu/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_mtu); else xo_emit("{P:/%*s} ", wid_mtu, ""); } memset(prettyname, 0, sizeof(prettyname)); if (rtm->rtm_index < ifmap_size) { strlcpy(prettyname, ifmap[rtm->rtm_index].ifname, sizeof(prettyname)); if (*prettyname == '\0') strlcpy(prettyname, "---", sizeof(prettyname)); } if (Wflag) xo_emit("{t:interface-name/%*s}", wid_if, prettyname); else xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if, prettyname); if (rtm->rtm_rmx.rmx_expire) { time_t expire_time; if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0) xo_emit(" {:expire-time/%*d}", wid_expire, (int)expire_time); } xo_emit("\n"); xo_close_instance(name); } static int p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask, int flags, int width) { const char *cp; char buf[128]; int protrusion; cp = fmt_sockaddr(sa, mask, flags); if (width < 0) { snprintf(buf, sizeof(buf), "{:%s/%%s} ", name); xo_emit(buf, cp); protrusion = 0; } else { if (Wflag != 0 || numeric_addr) { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%s}{]:} ", -width, name); xo_emit(buf, cp); protrusion = strlen(cp) - width; if (protrusion < 0) protrusion = 0; } else { snprintf(buf, sizeof(buf), "{[:%d}{:%s/%%-.*s}{]:} ", -width, name); xo_emit(buf, width, cp); protrusion = 0; } } return (protrusion); } static const char * fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, int flags) { static char buf[128]; const char *cp; if (sa == NULL) return ("null"); switch(sa->sa_family) { #ifdef INET6 case AF_INET6: /* * The sa6->sin6_scope_id must be filled here because * this sockaddr is extracted from kmem(4) directly * and has KAME-specific embedded scope id in * sa6->sin6_addr.s6_addr[2]. */ in6_fillscopeid(satosin6(sa)); /* FALLTHROUGH */ #endif /*INET6*/ case AF_INET: if (flags & RTF_HOST) cp = routename(sa, numeric_addr); else if (mask) cp = netname(sa, mask); else cp = netname(sa, NULL); break; case AF_NETGRAPH: { strlcpy(buf, ((struct sockaddr_ng *)sa)->sg_data, sizeof(buf)); cp = buf; break; } case AF_LINK: { #if 0 struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; /* Interface route. */ if (sdl->sdl_nlen) cp = sdl->sdl_data; else #endif cp = routename(sa, 1); break; } default: { u_char *s = (u_char *)sa->sa_data, *slim; char *cq, *cqlim; cq = buf; slim = sa->sa_len + (u_char *) sa; cqlim = cq + sizeof(buf) - sizeof(" ffff"); snprintf(cq, sizeof(buf), "(%d)", sa->sa_family); cq += strlen(cq); while (s < slim && cq < cqlim) { snprintf(cq, sizeof(" ff"), " %02x", *s++); cq += strlen(cq); if (s < slim) { snprintf(cq, sizeof("ff"), "%02x", *s++); cq += strlen(cq); } } cp = buf; } } return (cp); } static void p_flags(int f, const char *format) { struct bits *p; xo_emit(format, fmt_flags(f)); xo_open_list("flags_pretty"); for (p = bits; p->b_mask; p++) if (p->b_mask & f) xo_emit("{le:flags_pretty/%s}", p->b_name); xo_close_list("flags_pretty"); } static const char * fmt_flags(int f) { static char name[33]; char *flags; struct bits *p = bits; for (flags = name; p->b_mask; p++) if (p->b_mask & f) *flags++ = p->b_val; *flags = '\0'; return (name); } char * routename(struct sockaddr *sa, int flags) { static char line[NI_MAXHOST]; int error, f; f = (flags) ? NI_NUMERICHOST : 0; error = getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, f); if (error) { const void *src; switch (sa->sa_family) { #ifdef INET case AF_INET: src = &satosin(sa)->sin_addr; break; #endif /* INET */ #ifdef INET6 case AF_INET6: src = &satosin6(sa)->sin6_addr; break; #endif /* INET6 */ default: return(line); } inet_ntop(sa->sa_family, src, line, sizeof(line) - 1); return (line); } trimdomain(line, strlen(line)); return (line); } #define NSHIFT(m) ( \ (m) == IN_CLASSA_NET ? IN_CLASSA_NSHIFT : \ (m) == IN_CLASSB_NET ? IN_CLASSB_NSHIFT : \ (m) == IN_CLASSC_NET ? IN_CLASSC_NSHIFT : \ 0) static void domask(char *dst, size_t buflen, u_long mask) { int b, i; if (mask == 0) { *dst = '\0'; return; } i = 0; for (b = 0; b < 32; b++) if (mask & (1 << b)) { int bb; i = b; for (bb = b+1; bb < 32; bb++) if (!(mask & (1 << bb))) { i = -1; /* noncontig */ break; } break; } if (i == -1) snprintf(dst, buflen, "&0x%lx", mask); else snprintf(dst, buflen, "/%d", 32-i); } /* * Return the name of the network whose address is given. */ const char * netname(struct sockaddr *sa, struct sockaddr *mask) { switch (sa->sa_family) { case AF_INET: if (mask != NULL) return (netname4(satosin(sa)->sin_addr.s_addr, satosin(mask)->sin_addr.s_addr)); else return (netname4(satosin(sa)->sin_addr.s_addr, INADDR_ANY)); break; #ifdef INET6 case AF_INET6: return (netname6(satosin6(sa), satosin6(mask))); #endif /* INET6 */ default: return (NULL); } } static const char * netname4(in_addr_t in, in_addr_t mask) { char *cp = 0; static char line[MAXHOSTNAMELEN + sizeof("&0xffffffff")]; char nline[INET_ADDRSTRLEN]; struct netent *np = 0; in_addr_t i; if (in == INADDR_ANY && mask == 0) { strlcpy(line, "default", sizeof(line)); return (line); } /* It is ok to supply host address. */ in &= mask; i = ntohl(in); if (!numeric_addr && i) { np = getnetbyaddr(i >> NSHIFT(ntohl(mask)), AF_INET); if (np != NULL) { cp = np->n_name; trimdomain(cp, strlen(cp)); } } if (cp != NULL) strlcpy(line, cp, sizeof(line)); else { inet_ntop(AF_INET, &in, nline, sizeof(nline)); strlcpy(line, nline, sizeof(line)); domask(line + strlen(line), sizeof(line) - strlen(line), ntohl(mask)); } return (line); } #undef NSHIFT #ifdef INET6 void in6_fillscopeid(struct sockaddr_in6 *sa6) { #if defined(__KAME__) /* * XXX: This is a special workaround for KAME kernels. * sin6_scope_id field of SA should be set in the future. */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_NODELOCAL(&sa6->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr)) { if (sa6->sin6_scope_id == 0) sa6->sin6_scope_id = ntohs(*(u_int16_t *)&sa6->sin6_addr.s6_addr[2]); sa6->sin6_addr.s6_addr[2] = sa6->sin6_addr.s6_addr[3] = 0; } #endif } /* Mask to length table. To check an invalid value, (length + 1) is used. */ static const u_char masktolen[256] = { [0xff] = 8 + 1, [0xfe] = 7 + 1, [0xfc] = 6 + 1, [0xf8] = 5 + 1, [0xf0] = 4 + 1, [0xe0] = 3 + 1, [0xc0] = 2 + 1, [0x80] = 1 + 1, [0x00] = 0 + 1, }; static const char * netname6(struct sockaddr_in6 *sa6, struct sockaddr_in6 *mask) { static char line[NI_MAXHOST + sizeof("/xxx") - 1]; struct sockaddr_in6 addr; char nline[NI_MAXHOST]; char maskbuf[sizeof("/xxx")]; u_char *p, *lim; u_char masklen; int i; bool illegal = false; if (mask) { p = (u_char *)&mask->sin6_addr; for (masklen = 0, lim = p + 16; p < lim; p++) { if (masktolen[*p] > 0) { /* -1 is required. */ masklen += (masktolen[*p] - 1); } else illegal = true; } if (illegal) xo_error("illegal prefixlen\n"); memcpy(&addr, sa6, sizeof(addr)); for (i = 0; i < 16; ++i) addr.sin6_addr.s6_addr[i] &= mask->sin6_addr.s6_addr[i]; sa6 = &addr; } else masklen = 128; if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&sa6->sin6_addr)) return("default"); getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, nline, sizeof(nline), NULL, 0, NI_NUMERICHOST); if (numeric_addr) strlcpy(line, nline, sizeof(line)); else getnameinfo((struct sockaddr *)sa6, sa6->sin6_len, line, sizeof(line), NULL, 0, 0); if (numeric_addr || strcmp(line, nline) == 0) { snprintf(maskbuf, sizeof(maskbuf), "/%d", masklen); strlcat(line, maskbuf, sizeof(line)); } return (line); } #endif /*INET6*/ /* * Print routing statistics */ void rt_stats(void) { struct rtstat rtstat; u_long rtsaddr, rttaddr; int rttrash; if ((rtsaddr = nl[N_RTSTAT].n_value) == 0) { xo_emit("{W:rtstat: symbol not in namelist}\n"); return; } if ((rttaddr = nl[N_RTTRASH].n_value) == 0) { xo_emit("{W:rttrash: symbol not in namelist}\n"); return; } kread(rtsaddr, (char *)&rtstat, sizeof (rtstat)); kread(rttaddr, (char *)&rttrash, sizeof (rttrash)); xo_emit("{T:routing}:\n"); #define p(f, m) if (rtstat.f || sflag <= 1) \ xo_emit(m, rtstat.f, plural(rtstat.f)) p(rts_badredirect, "\t{:bad-redirects/%hu} " "{N:/bad routing redirect%s}\n"); p(rts_dynamic, "\t{:dynamically-created/%hu} " "{N:/dynamically created route%s}\n"); p(rts_newgateway, "\t{:new-gateways/%hu} " "{N:/new gateway%s due to redirects}\n"); p(rts_unreach, "\t{:unreachable-destination/%hu} " "{N:/destination%s found unreachable}\n"); p(rts_wildcard, "\t{:wildcard-uses/%hu} " "{N:/use%s of a wildcard route}\n"); #undef p if (rttrash || sflag <= 1) xo_emit("\t{:unused-but-not-freed/%u} " "{N:/route%s not in table but not freed}\n", rttrash, plural(rttrash)); } Index: head/usr.sbin/arp/arp.c =================================================================== --- head/usr.sbin/arp/arp.c (revision 317034) +++ head/usr.sbin/arp/arp.c (revision 317035) @@ -1,914 +1,905 @@ /* * Copyright (c) 1984, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Sun Microsystems, Inc. * * 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. */ #if 0 #ifndef lint static char const copyright[] = "@(#) Copyright (c) 1984, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * arp - display, set, and delete arp table entries */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in, struct rt_msghdr *rtm); static int search(u_long addr, action_fn *action); static action_fn print_entry; static action_fn nuke_entry; static int delete(char *host); static void usage(void); static int set(int argc, char **argv); static int get(char *host); static int file(char *name); static struct rt_msghdr *rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl); static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); static struct sockaddr_in *getaddr(char *host); static int valid_type(int type); static int nflag; /* no reverse dns lookups */ static char *rifname; static time_t expire_time; static int flags, doing_proxy; struct if_nameindex *ifnameindex; /* which function we're supposed to do */ #define F_GET 1 #define F_SET 2 #define F_FILESET 3 #define F_REPLACE 4 #define F_DELETE 5 #define SETFUNC(f) { if (func) usage(); func = (f); } #define ARP_XO_VERSION "1" int main(int argc, char *argv[]) { int ch, func = 0; int rtn = 0; int aflag = 0; /* do it for all entries */ argc = xo_parse_args(argc, argv); if (argc < 0) exit(1); while ((ch = getopt(argc, argv, "andfsSi:")) != -1) switch(ch) { case 'a': aflag = 1; break; case 'd': SETFUNC(F_DELETE); break; case 'n': nflag = 1; break; case 'S': SETFUNC(F_REPLACE); break; case 's': SETFUNC(F_SET); break; case 'f' : SETFUNC(F_FILESET); break; case 'i': rifname = optarg; break; case '?': default: usage(); } argc -= optind; argv += optind; if (!func) func = F_GET; if (rifname) { if (func != F_GET && !(func == F_DELETE && aflag)) xo_errx(1, "-i not applicable to this operation"); if (if_nametoindex(rifname) == 0) { if (errno == ENXIO) xo_errx(1, "interface %s does not exist", rifname); else xo_err(1, "if_nametoindex(%s)", rifname); } } switch (func) { case F_GET: if (aflag) { if (argc != 0) usage(); xo_set_version(ARP_XO_VERSION); xo_open_container("arp"); xo_open_list("arp-cache"); search(0, print_entry); xo_close_list("arp-cache"); xo_close_container("arp"); xo_finish(); } else { if (argc != 1) usage(); rtn = get(argv[0]); } break; case F_SET: case F_REPLACE: if (argc < 2 || argc > 6) usage(); if (func == F_REPLACE) (void)delete(argv[0]); rtn = set(argc, argv) ? 1 : 0; break; case F_DELETE: if (aflag) { if (argc != 0) usage(); search(0, nuke_entry); } else { if (argc != 1) usage(); rtn = delete(argv[0]); } break; case F_FILESET: if (argc != 1) usage(); rtn = file(argv[0]); break; } if (ifnameindex != NULL) if_freenameindex(ifnameindex); return (rtn); } /* * Process a file to set standard arp entries */ static int file(char *name) { FILE *fp; int i, retval; char line[100], arg[5][50], *args[5], *p; if ((fp = fopen(name, "r")) == NULL) xo_err(1, "cannot open %s", name); args[0] = &arg[0][0]; args[1] = &arg[1][0]; args[2] = &arg[2][0]; args[3] = &arg[3][0]; args[4] = &arg[4][0]; retval = 0; while(fgets(line, sizeof(line), fp) != NULL) { if ((p = strchr(line, '#')) != NULL) *p = '\0'; for (p = line; isblank(*p); p++); if (*p == '\n' || *p == '\0') continue; i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1], arg[2], arg[3], arg[4]); if (i < 2) { xo_warnx("bad line: %s", line); retval = 1; continue; } if (set(i, args)) retval = 1; } fclose(fp); return (retval); } /* * Given a hostname, fills up a (static) struct sockaddr_in with * the address of the host and returns a pointer to the * structure. */ static struct sockaddr_in * getaddr(char *host) { struct hostent *hp; static struct sockaddr_in reply; bzero(&reply, sizeof(reply)); reply.sin_len = sizeof(reply); reply.sin_family = AF_INET; reply.sin_addr.s_addr = inet_addr(host); if (reply.sin_addr.s_addr == INADDR_NONE) { if (!(hp = gethostbyname(host))) { xo_warnx("%s: %s", host, hstrerror(h_errno)); return (NULL); } bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, sizeof reply.sin_addr); } return (&reply); } /* * Returns true if the type is a valid one for ARP. */ static int valid_type(int type) { switch (type) { case IFT_ETHER: case IFT_FDDI: case IFT_INFINIBAND: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN: case IFT_BRIDGE: return (1); default: return (0); } } /* * Set an individual arp entry */ static int set(int argc, char **argv) { struct sockaddr_in *addr; struct sockaddr_in *dst; /* what are we looking for */ struct sockaddr_dl *sdl; struct rt_msghdr *rtm; struct ether_addr *ea; char *host = argv[0], *eaddr = argv[1]; struct sockaddr_dl sdl_m; argc -= 2; argv += 2; bzero(&sdl_m, sizeof(sdl_m)); sdl_m.sdl_len = sizeof(sdl_m); sdl_m.sdl_family = AF_LINK; dst = getaddr(host); if (dst == NULL) return (1); doing_proxy = flags = expire_time = 0; while (argc-- > 0) { if (strcmp(argv[0], "temp") == 0) { struct timespec tp; int max_age; size_t len = sizeof(max_age); clock_gettime(CLOCK_MONOTONIC, &tp); if (sysctlbyname("net.link.ether.inet.max_age", &max_age, &len, NULL, 0) != 0) xo_err(1, "sysctlbyname"); expire_time = tp.tv_sec + max_age; } else if (strcmp(argv[0], "pub") == 0) { flags |= RTF_ANNOUNCE; doing_proxy = 1; if (argc && strcmp(argv[1], "only") == 0) { /* * Compatibility: in pre FreeBSD 8 times * the "only" keyword used to mean that * an ARP entry should be announced, but * not installed into routing table. */ argc--; argv++; } } else if (strcmp(argv[0], "blackhole") == 0) { if (flags & RTF_REJECT) { xo_errx(1, "Choose one of blackhole or reject, " "not both."); } flags |= RTF_BLACKHOLE; } else if (strcmp(argv[0], "reject") == 0) { if (flags & RTF_BLACKHOLE) { xo_errx(1, "Choose one of blackhole or reject, " "not both."); } flags |= RTF_REJECT; } else { xo_warnx("Invalid parameter '%s'", argv[0]); usage(); } argv++; } ea = (struct ether_addr *)LLADDR(&sdl_m); if (doing_proxy && !strcmp(eaddr, "auto")) { if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { xo_warnx("no interface found for %s", inet_ntoa(dst->sin_addr)); return (1); } sdl_m.sdl_alen = ETHER_ADDR_LEN; } else { struct ether_addr *ea1 = ether_aton(eaddr); if (ea1 == NULL) { xo_warnx("invalid Ethernet address '%s'", eaddr); return (1); } else { *ea = *ea1; sdl_m.sdl_alen = ETHER_ADDR_LEN; } } /* * In the case a proxy-arp entry is being added for * a remote end point, the RTF_ANNOUNCE flag in the * RTM_GET command is an indication to the kernel * routing code that the interface associated with * the prefix route covering the local end of the * PPP link should be returned, on which ARP applies. */ - rtm = rtmsg(RTM_GET, dst, &sdl_m); + rtm = rtmsg(RTM_GET, dst, NULL); if (rtm == NULL) { xo_warn("%s", host); return (1); } addr = (struct sockaddr_in *)(rtm + 1); sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); if ((sdl->sdl_family != AF_LINK) || (rtm->rtm_flags & RTF_GATEWAY) || !valid_type(sdl->sdl_type)) { xo_warnx("cannot intuit interface index and type for %s", host); return (1); } sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); } /* * Display an individual arp entry */ static int get(char *host) { struct sockaddr_in *addr; int found; addr = getaddr(host); if (addr == NULL) return (1); xo_set_version(ARP_XO_VERSION); xo_open_container("arp"); xo_open_list("arp-cache"); found = search(addr->sin_addr.s_addr, print_entry); if (found == 0) { xo_emit("{d:hostname/%s} ({d:ip-address/%s}) -- no entry", host, inet_ntoa(addr->sin_addr)); if (rifname) xo_emit(" on {d:interface/%s}", rifname); xo_emit("\n"); } xo_close_list("arp-cache"); xo_close_container("arp"); xo_finish(); return (found == 0); } /* * Delete an arp entry */ static int delete(char *host) { struct sockaddr_in *addr, *dst; struct rt_msghdr *rtm; struct sockaddr_dl *sdl; - struct sockaddr_dl sdl_m; dst = getaddr(host); if (dst == NULL) return (1); /* * Perform a regular entry delete first. */ flags &= ~RTF_ANNOUNCE; - /* - * setup the data structure to notify the kernel - * it is the ARP entry the RTM_GET is interested - * in - */ - bzero(&sdl_m, sizeof(sdl_m)); - sdl_m.sdl_len = sizeof(sdl_m); - sdl_m.sdl_family = AF_LINK; - for (;;) { /* try twice */ - rtm = rtmsg(RTM_GET, dst, &sdl_m); + rtm = rtmsg(RTM_GET, dst, NULL); if (rtm == NULL) { xo_warn("%s", host); return (1); } addr = (struct sockaddr_in *)(rtm + 1); sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); /* * With the new L2/L3 restructure, the route * returned is a prefix route. The important * piece of information from the previous * RTM_GET is the interface index. In the * case of ECMP, the kernel will traverse * the route group for the given entry. */ if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY) && valid_type(sdl->sdl_type) ) { addr->sin_addr.s_addr = dst->sin_addr.s_addr; break; } /* - * Regualar entry delete failed, now check if there + * Regular entry delete failed, now check if there * is a proxy-arp entry to remove. */ if (flags & RTF_ANNOUNCE) { xo_warnx("delete: cannot locate %s", host); return (1); } flags |= RTF_ANNOUNCE; } rtm->rtm_flags |= RTF_LLDATA; if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); return (0); } return (1); } /* * Search the arp table and do some action on matching entries */ static int search(u_long addr, action_fn *action) { int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_in *sin2; struct sockaddr_dl *sdl; char ifname[IF_NAMESIZE]; int st, found_entry = 0; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) xo_err(1, "route-sysctl-estimate"); if (needed == 0) /* empty table */ return 0; buf = NULL; for (;;) { buf = reallocf(buf, needed); if (buf == NULL) xo_errx(1, "could not reallocate memory"); st = sysctl(mib, 6, buf, &needed, NULL, 0); if (st == 0 || errno != ENOMEM) break; needed += needed / 8; } if (st == -1) xo_err(1, "actual retrieval of routing table"); lim = buf + needed; for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; sin2 = (struct sockaddr_in *)(rtm + 1); sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); if (rifname && if_indextoname(sdl->sdl_index, ifname) && strcmp(ifname, rifname)) continue; if (addr) { if (addr != sin2->sin_addr.s_addr) continue; found_entry = 1; } (*action)(sdl, sin2, rtm); } free(buf); return (found_entry); } /* * Display an arp entry */ static void print_entry(struct sockaddr_dl *sdl, struct sockaddr_in *addr, struct rt_msghdr *rtm) { const char *host; struct hostent *hp; struct iso88025_sockaddr_dl_data *trld; struct if_nameindex *p; int seg; if (ifnameindex == NULL) if ((ifnameindex = if_nameindex()) == NULL) xo_err(1, "cannot retrieve interface names"); xo_open_instance("arp-cache"); if (nflag == 0) hp = gethostbyaddr((caddr_t)&(addr->sin_addr), sizeof addr->sin_addr, AF_INET); else hp = 0; if (hp) host = hp->h_name; else { host = "?"; if (h_errno == TRY_AGAIN) nflag = 1; } xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host, inet_ntoa(addr->sin_addr)); if (sdl->sdl_alen) { if ((sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_L2VLAN || sdl->sdl_type == IFT_BRIDGE) && sdl->sdl_alen == ETHER_ADDR_LEN) xo_emit("{:mac-address/%s}", ether_ntoa((struct ether_addr *)LLADDR(sdl))); else { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; xo_emit("{:mac-address/%s}", link_ntoa(sdl) + n); } } else xo_emit("{d:/(incomplete)}{en:incomplete/true}"); for (p = ifnameindex; p && ifnameindex->if_index && ifnameindex->if_name; p++) { if (p->if_index == sdl->sdl_index) { xo_emit(" on {:interface/%s}", p->if_name); break; } } if (rtm->rtm_rmx.rmx_expire == 0) xo_emit("{d:/ permanent}{en:permanent/true}"); else { static struct timespec tp; if (tp.tv_sec == 0) clock_gettime(CLOCK_MONOTONIC, &tp); if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0) xo_emit(" expires in {:expires/%d} seconds", (int)expire_time); else xo_emit("{d:/ expired}{en:expired/true}"); } if (rtm->rtm_flags & RTF_ANNOUNCE) xo_emit("{d:/ published}{en:published/true}"); switch(sdl->sdl_type) { case IFT_ETHER: xo_emit(" [{:type/ethernet}]"); break; case IFT_ISO88025: xo_emit(" [{:type/token-ring}]"); trld = SDL_ISO88025(sdl); if (trld->trld_rcf != 0) { xo_emit(" rt=%x", ntohs(trld->trld_rcf)); for (seg = 0; seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); seg++) xo_emit(":%x", ntohs(*(trld->trld_route[seg]))); } break; case IFT_FDDI: xo_emit(" [{:type/fddi}]"); break; case IFT_ATM: xo_emit(" [{:type/atm}]"); break; case IFT_L2VLAN: xo_emit(" [{:type/vlan}]"); break; case IFT_IEEE1394: xo_emit(" [{:type/firewire}]"); break; case IFT_BRIDGE: xo_emit(" [{:type/bridge}]"); break; case IFT_INFINIBAND: xo_emit(" [{:type/infiniband}]"); break; default: break; } xo_emit("\n"); xo_close_instance("arp-cache"); } /* * Nuke an arp entry */ static void nuke_entry(struct sockaddr_dl *sdl __unused, struct sockaddr_in *addr, struct rt_msghdr *rtm) { char ip[20]; if (rtm->rtm_flags & RTF_PINNED) return; snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); delete(ip); } static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: arp [-n] [-i interface] hostname", " arp [-n] [-i interface] -a", " arp -d hostname [pub]", " arp -d [-i interface] -a", " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]", " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]", " arp -f filename"); exit(1); } static struct rt_msghdr * rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl) { static int seq; int rlen; int l; struct sockaddr_in so_mask, *som = &so_mask; static int s = -1; static pid_t pid; static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; struct rt_msghdr *rtm = &m_rtmsg.m_rtm; char *cp = m_rtmsg.m_space; if (s < 0) { /* first time: open socket, get pid */ s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) xo_err(1, "socket"); pid = getpid(); } bzero(&so_mask, sizeof(so_mask)); so_mask.sin_len = 8; so_mask.sin_addr.s_addr = 0xffffffff; errno = 0; /* * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer * appropriately. */ if (cmd == RTM_DELETE) goto doit; bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); rtm->rtm_flags = flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { default: xo_errx(1, "internal wrong cmd"); case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; rtm->rtm_rmx.rmx_expire = expire_time; rtm->rtm_inits = RTV_EXPIRE; rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); if (doing_proxy) { rtm->rtm_addrs |= RTA_NETMASK; rtm->rtm_flags &= ~RTF_HOST; } /* FALLTHROUGH */ case RTM_GET: rtm->rtm_addrs |= RTA_DST; } #define NEXTADDR(w, s) \ do { \ if ((s) != NULL && rtm->rtm_addrs & (w)) { \ bcopy((s), cp, sizeof(*(s))); \ cp += SA_SIZE(s); \ } \ } while (0) NEXTADDR(RTA_DST, dst); NEXTADDR(RTA_GATEWAY, sdl); NEXTADDR(RTA_NETMASK, som); rtm->rtm_msglen = cp - (char *)&m_rtmsg; doit: l = rtm->rtm_msglen; rtm->rtm_seq = ++seq; rtm->rtm_type = cmd; if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { if (errno != ESRCH || cmd != RTM_DELETE) { xo_warn("writing to routing socket"); return (NULL); } } do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); - } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); + } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || + rtm->rtm_pid != pid)); if (l < 0) xo_warn("read from routing socket"); return (rtm); } /* * get_ether_addr - get the hardware address of an interface on the * the same subnet as ipaddr. */ #define MAX_IFS 32 static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) { struct ifreq *ifr, *ifend, *ifp; in_addr_t ina, mask; struct sockaddr_dl *dla; struct ifreq ifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; int sock; int retval = 0; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) xo_err(1, "socket"); ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { xo_warnx("ioctl(SIOCGIFCONF)"); goto done; } #define NEXTIFR(i) \ ((struct ifreq *)((char *)&(i)->ifr_addr \ + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) /* * Scan through looking for an interface with an Internet * address on the same subnet as `ipaddr'. */ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { if (ifr->ifr_addr.sa_family != AF_INET) continue; strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); ifreq.ifr_addr = ifr->ifr_addr; /* * Check that the interface is up, * and not point-to-point or loopback. */ if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) continue; if ((ifreq.ifr_flags & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST)) continue; /* Get its netmask and check that it's on the right subnet. */ if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) continue; mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr; ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; if ((ipaddr & mask) == (ina & mask)) break; /* ok, we got it! */ } if (ifr >= ifend) goto done; /* * Now scan through again looking for a link-level address * for this interface. */ ifp = ifr; for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && ifr->ifr_addr.sa_family == AF_LINK) break; if (ifr >= ifend) goto done; /* * Found the link-level address - copy it out */ dla = (struct sockaddr_dl *) &ifr->ifr_addr; memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); printf("using interface %s for proxy with address %s\n", ifp->ifr_name, ether_ntoa(hwaddr)); retval = dla->sdl_alen; done: close(sock); return (retval); } Index: head/usr.sbin/ndp/ndp.c =================================================================== --- head/usr.sbin/ndp/ndp.c (revision 317034) +++ head/usr.sbin/ndp/ndp.c (revision 317035) @@ -1,1375 +1,1376 @@ /* $FreeBSD$ */ /* $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ /* * Copyright (c) 1984, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Sun Microsystems, Inc. * * 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. */ /* * Based on: * "@(#) Copyright (c) 1984, 1993\n\ * The Regents of the University of California. All rights reserved.\n"; * * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; */ /* * ndp - display, set, delete and flush neighbor cache */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gmt2local.h" #define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ bcopy((char *)&s, cp, sizeof(s)); \ cp += SA_SIZE(&s); \ } static pid_t pid; static int nflag; static int tflag; static int32_t thiszone; /* time difference with gmt */ static int s = -1; static int repeat = 0; static char host_buf[NI_MAXHOST]; /* getnameinfo() */ static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ static int file(char *); static void getsocket(void); static int set(int, char **); static void get(char *); static int delete(char *); static void dump(struct sockaddr_in6 *, int); static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); static char *ether_str(struct sockaddr_dl *); static int ndp_ether_aton(char *, u_char *); static void usage(void); static int rtmsg(int); static void ifinfo(char *, int, char **); static void rtrlist(void); static void plist(void); static void pfx_flush(void); static void rtr_flush(void); static void harmonize_rtr(void); #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void getdefif(void); static void setdefif(char *); #endif static char *sec2str(time_t); static void ts_print(const struct timeval *); static char *rtpref_str[] = { "medium", /* 00 */ "high", /* 01 */ "rsv", /* 10 */ "low" /* 11 */ }; int main(int argc, char **argv) { int ch, mode = 0; char *arg = NULL; pid = getpid(); thiszone = gmt2local(0); while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1) switch (ch) { case 'a': case 'c': case 'p': case 'r': case 'H': case 'P': case 'R': case 's': case 'I': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = NULL; break; case 'f': exit(file(optarg) ? 1 : 0); case 'd': case 'i': if (mode) { usage(); /*NOTREACHED*/ } mode = ch; arg = optarg; break; case 'n': nflag = 1; break; case 't': tflag = 1; break; case 'A': if (mode) { usage(); /*NOTREACHED*/ } mode = 'a'; repeat = atoi(optarg); if (repeat < 0) { usage(); /*NOTREACHED*/ } break; default: usage(); } argc -= optind; argv += optind; switch (mode) { case 'a': case 'c': if (argc != 0) { usage(); /*NOTREACHED*/ } dump(0, mode == 'c'); break; case 'd': if (argc != 0) { usage(); /*NOTREACHED*/ } delete(arg); break; case 'I': #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ if (argc > 1) { usage(); /*NOTREACHED*/ } else if (argc == 1) { if (strcmp(*argv, "delete") == 0 || if_nametoindex(*argv)) setdefif(*argv); else errx(1, "invalid interface %s", *argv); } getdefif(); /* always call it to print the result */ break; #else errx(1, "not supported yet"); /*NOTREACHED*/ #endif case 'p': if (argc != 0) { usage(); /*NOTREACHED*/ } plist(); break; case 'i': ifinfo(arg, argc, argv); break; case 'r': if (argc != 0) { usage(); /*NOTREACHED*/ } rtrlist(); break; case 's': if (argc < 2 || argc > 4) usage(); exit(set(argc, argv) ? 1 : 0); case 'H': if (argc != 0) { usage(); /*NOTREACHED*/ } harmonize_rtr(); break; case 'P': if (argc != 0) { usage(); /*NOTREACHED*/ } pfx_flush(); break; case 'R': if (argc != 0) { usage(); /*NOTREACHED*/ } rtr_flush(); break; case 0: if (argc != 1) { usage(); /*NOTREACHED*/ } get(argv[0]); break; } exit(0); } /* * Process a file to set standard ndp entries */ static int file(char *name) { FILE *fp; int i, retval; char line[100], arg[5][50], *args[5], *p; if ((fp = fopen(name, "r")) == NULL) err(1, "cannot open %s", name); args[0] = &arg[0][0]; args[1] = &arg[1][0]; args[2] = &arg[2][0]; args[3] = &arg[3][0]; args[4] = &arg[4][0]; retval = 0; while (fgets(line, sizeof(line), fp) != NULL) { if ((p = strchr(line, '#')) != NULL) *p = '\0'; for (p = line; isblank(*p); p++); if (*p == '\n' || *p == '\0') continue; i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], arg[2], arg[3], arg[4]); if (i < 2) { warnx("bad line: %s", line); retval = 1; continue; } if (set(i, args)) retval = 1; } fclose(fp); return (retval); } static void getsocket() { if (s < 0) { s = socket(PF_ROUTE, SOCK_RAW, 0); if (s < 0) { err(1, "socket"); /* NOTREACHED */ } } } static struct sockaddr_in6 so_mask = { .sin6_len = sizeof(so_mask), .sin6_family = AF_INET6 }; static struct sockaddr_in6 blank_sin = { .sin6_len = sizeof(blank_sin), .sin6_family = AF_INET6 }; static struct sockaddr_in6 sin_m; static struct sockaddr_dl blank_sdl = { .sdl_len = sizeof(blank_sdl), .sdl_family = AF_LINK }; static struct sockaddr_dl sdl_m; static time_t expire_time; static int flags, found_entry; static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; /* * Set an individual neighbor cache entry */ static int set(int argc, char **argv) { register struct sockaddr_in6 *sin = &sin_m; register struct sockaddr_dl *sdl; register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); struct addrinfo hints, *res; int gai_error; u_char *ea; char *host = argv[0], *eaddr = argv[1]; getsocket(); argc -= 2; argv += 2; sdl_m = blank_sdl; sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return 1; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; ea = (u_char *)LLADDR(&sdl_m); if (ndp_ether_aton(eaddr, ea) == 0) sdl_m.sdl_alen = 6; flags = expire_time = 0; while (argc-- > 0) { if (strncmp(argv[0], "temp", 4) == 0) { struct timeval now; gettimeofday(&now, 0); expire_time = now.tv_sec + 20 * 60; } else if (strncmp(argv[0], "proxy", 5) == 0) flags |= RTF_ANNOUNCE; argv++; } if (rtmsg(RTM_GET) < 0) { errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { switch (sdl->sdl_type) { case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: case IFT_ISO88024: case IFT_ISO88025: case IFT_L2VLAN: case IFT_BRIDGE: goto overwrite; } } fprintf(stderr, "set: cannot configure a new entry\n"); return 1; } overwrite: if (sdl->sdl_family != AF_LINK) { printf("cannot intuit interface index and type for %s\n", host); return (1); } sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (rtmsg(RTM_ADD)); } /* * Display an individual neighbor cache entry */ static void get(char *host) { struct sockaddr_in6 *sin = &sin_m; struct addrinfo hints, *res; int gai_error; sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; dump(sin, 0); if (found_entry == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL ,0, (nflag ? NI_NUMERICHOST : 0)); printf("%s (%s) -- no entry\n", host, host_buf); exit(1); } } /* * Delete a neighbor cache entry */ static int delete(char *host) { struct sockaddr_in6 *sin = &sin_m; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; struct sockaddr_dl *sdl; struct addrinfo hints, *res; int gai_error; getsocket(); sin_m = blank_sin; bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET6; gai_error = getaddrinfo(host, NULL, &hints, &res); if (gai_error) { fprintf(stderr, "ndp: %s: %s\n", host, gai_strerror(gai_error)); return 1; } sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; sin->sin6_scope_id = ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; if (rtmsg(RTM_GET) < 0) { errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ } sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin); if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { goto delete; } fprintf(stderr, "delete: cannot delete non-NDP entry\n"); return 1; } delete: if (sdl->sdl_family != AF_LINK) { printf("cannot locate %s\n", host); return (1); } /* * need to reinit the field because it has rt_key * but we want the actual address */ NEXTADDR(RTA_DST, sin_m); rtm->rtm_flags |= RTF_LLDATA; if (rtmsg(RTM_DELETE) == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); printf("%s (%s) deleted\n", host, host_buf); } return 0; } #define W_ADDR 36 #define W_LL 17 #define W_IF 6 /* * Dump the entire neighbor cache */ static void dump(struct sockaddr_in6 *addr, int cflag) { int mib[6]; size_t needed; char *lim, *buf, *next; struct rt_msghdr *rtm; struct sockaddr_in6 *sin; struct sockaddr_dl *sdl; extern int h_errno; struct timeval now; u_long expire; int addrwidth; int llwidth; int ifwidth; char flgbuf[8]; char *ifname; /* Print header */ if (!tflag && !cflag) printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n", W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address", W_IF, W_IF, "Netif", "Expire", "S", "Flags"); again:; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; #else mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) err(1, "sysctl(PF_ROUTE estimate)"); if (needed > 0) { if ((buf = malloc(needed)) == NULL) err(1, "malloc"); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); lim = buf + needed; } else buf = lim = NULL; for (next = buf; next && next < lim; next += rtm->rtm_msglen) { int isrouter = 0, prbs = 0; rtm = (struct rt_msghdr *)next; sin = (struct sockaddr_in6 *)(rtm + 1); sdl = (struct sockaddr_dl *)((char *)sin + ALIGN(sin->sin6_len)); /* * Some OSes can produce a route that has the LINK flag but * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD * and BSD/OS, where xx is not the interface identifier on * lo0). Such routes entry would annoy getnbrinfo() below, * so we skip them. * XXX: such routes should have the GATEWAY flag, not the * LINK flag. However, there is rotten routing software * that advertises all routes that have the GATEWAY flag. * Thus, KAME kernel intentionally does not set the LINK flag. * What is to be fixed is not ndp, but such routing software * (and the kernel workaround)... */ if (sdl->sdl_family != AF_LINK) continue; if (!(rtm->rtm_flags & RTF_HOST)) continue; if (addr) { if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &sin->sin6_addr) == 0 || addr->sin6_scope_id != sin->sin6_scope_id) continue; found_entry = 1; } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) continue; if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { /* XXX: should scope id be filled in the kernel? */ if (sin->sin6_scope_id == 0) sin->sin6_scope_id = sdl->sdl_index; } getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); if (cflag) { #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) delete(host_buf); #elif defined(RTF_CLONED) if (rtm->rtm_flags & RTF_CLONED) delete(host_buf); #else if (rtm->rtm_flags & RTF_PINNED) continue; delete(host_buf); #endif continue; } gettimeofday(&now, 0); if (tflag) ts_print(&now); addrwidth = strlen(host_buf); if (addrwidth < W_ADDR) addrwidth = W_ADDR; llwidth = strlen(ether_str(sdl)); if (W_ADDR + W_LL - addrwidth > llwidth) llwidth = W_ADDR + W_LL - addrwidth; ifname = if_indextoname(sdl->sdl_index, ifix_buf); if (!ifname) ifname = "?"; ifwidth = strlen(ifname); if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); /* Print neighbor discovery specific information */ expire = rtm->rtm_rmx.rmx_expire; if (expire > now.tv_sec) printf(" %-9.9s", sec2str(expire - now.tv_sec)); else if (expire == 0) printf(" %-9.9s", "permanent"); else printf(" %-9.9s", "expired"); switch (rtm->rtm_rmx.rmx_state) { case ND6_LLINFO_NOSTATE: printf(" N"); break; #ifdef ND6_LLINFO_WAITDELETE case ND6_LLINFO_WAITDELETE: printf(" W"); break; #endif case ND6_LLINFO_INCOMPLETE: printf(" I"); break; case ND6_LLINFO_REACHABLE: printf(" R"); break; case ND6_LLINFO_STALE: printf(" S"); break; case ND6_LLINFO_DELAY: printf(" D"); break; case ND6_LLINFO_PROBE: printf(" P"); break; default: printf(" ?"); break; } isrouter = rtm->rtm_flags & RTF_GATEWAY; prbs = rtm->rtm_rmx.rmx_pksent; /* * other flags. R: router, P: proxy, W: ?? */ if ((rtm->rtm_addrs & RTA_NETMASK) == 0) { snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); } else { #if 0 /* W and P are mystery even for us */ sin = (struct sockaddr_in6 *) (sdl->sdl_len + (char *)sdl); snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s", isrouter ? "R" : "", !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "", (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #else snprintf(flgbuf, sizeof(flgbuf), "%s%s", isrouter ? "R" : "", (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); #endif } printf(" %s", flgbuf); if (prbs) printf(" %d", prbs); printf("\n"); } if (buf != NULL) free(buf); if (repeat) { printf("\n"); fflush(stdout); sleep(repeat); goto again; } } static struct in6_nbrinfo * getnbrinfo(struct in6_addr *addr, int ifindex, int warning) { static struct in6_nbrinfo nbi; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); bzero(&nbi, sizeof(nbi)); if_indextoname(ifindex, nbi.ifname); nbi.addr = *addr; if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) { if (warning) warn("ioctl(SIOCGNBRINFO_IN6)"); close(s); return(NULL); } close(s); return(&nbi); } static char * ether_str(struct sockaddr_dl *sdl) { static char hbuf[NI_MAXHOST]; if (sdl->sdl_alen == ETHER_ADDR_LEN) { strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)), sizeof(hbuf)); } else if (sdl->sdl_alen) { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n); } else snprintf(hbuf, sizeof(hbuf), "(incomplete)"); return(hbuf); } static int ndp_ether_aton(char *a, u_char *n) { int i, o[6]; i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], &o[3], &o[4], &o[5]); if (i != 6) { fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a); return (1); } for (i = 0; i < 6; i++) n[i] = o[i]; return (0); } static void usage() { printf("usage: ndp [-nt] hostname\n"); printf(" ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n"); printf(" ndp [-nt] -A wait\n"); printf(" ndp [-nt] -d hostname\n"); printf(" ndp [-nt] -f filename\n"); printf(" ndp [-nt] -i interface [flags...]\n"); #ifdef SIOCSDEFIFACE_IN6 printf(" ndp [-nt] -I [interface|delete]\n"); #endif printf(" ndp [-nt] -s nodename etheraddr [temp] [proxy]\n"); exit(1); } static int rtmsg(int cmd) { static int seq; int rlen; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; register int l; errno = 0; if (cmd == RTM_DELETE) goto doit; bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); rtm->rtm_flags = flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { default: fprintf(stderr, "ndp: internal wrong cmd\n"); exit(1); case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; if (expire_time) { rtm->rtm_rmx.rmx_expire = expire_time; rtm->rtm_inits = RTV_EXPIRE; } rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); #if 0 /* we don't support ipv6addr/128 type proxying */ if (rtm->rtm_flags & RTF_ANNOUNCE) { rtm->rtm_flags &= ~RTF_HOST; rtm->rtm_addrs |= RTA_NETMASK; } #endif /* FALLTHROUGH */ case RTM_GET: rtm->rtm_addrs |= RTA_DST; } NEXTADDR(RTA_DST, sin_m); NEXTADDR(RTA_GATEWAY, sdl_m); #if 0 /* we don't support ipv6addr/128 type proxying */ memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); NEXTADDR(RTA_NETMASK, so_mask); #endif rtm->rtm_msglen = cp - (char *)&m_rtmsg; doit: l = rtm->rtm_msglen; rtm->rtm_seq = ++seq; rtm->rtm_type = cmd; if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { if (errno != ESRCH || cmd != RTM_DELETE) { err(1, "writing to routing socket"); /* NOTREACHED */ } } do { l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); - } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); + } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || + rtm->rtm_pid != pid)); if (l < 0) (void) fprintf(stderr, "ndp: read from routing socket: %s\n", strerror(errno)); return (0); } static void ifinfo(char *ifname, int argc, char **argv) { struct in6_ndireq nd; int i, s; u_int32_t newflags; #ifdef IPV6CTL_USETEMPADDR u_int8_t nullbuf[8]; #endif if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { err(1, "socket"); /* NOTREACHED */ } bzero(&nd, sizeof(nd)); strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } #define ND nd.ndi newflags = ND.flags; for (i = 0; i < argc; i++) { int clear = 0; char *cp = argv[i]; if (*cp == '-') { clear = 1; cp++; } #define SETFLAG(s, f) do { \ if (strcmp(cp, (s)) == 0) { \ if (clear) \ newflags &= ~(f); \ else \ newflags |= (f); \ } \ } while (0) /* * XXX: this macro is not 100% correct, in that it matches "nud" against * "nudbogus". But we just let it go since this is minor. */ #define SETVALUE(f, v) do { \ char *valptr; \ unsigned long newval; \ v = 0; /* unspecified */ \ if (strncmp(cp, f, strlen(f)) == 0) { \ valptr = strchr(cp, '='); \ if (valptr == NULL) \ err(1, "syntax error in %s field", (f)); \ errno = 0; \ newval = strtoul(++valptr, NULL, 0); \ if (errno) \ err(1, "syntax error in %s's value", (f)); \ v = newval; \ } \ } while (0) SETFLAG("disabled", ND6_IFF_IFDISABLED); SETFLAG("nud", ND6_IFF_PERFORMNUD); #ifdef ND6_IFF_ACCEPT_RTADV SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL); #endif #ifdef ND6_IFF_NO_PREFER_IFACE SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE); #endif SETVALUE("basereachable", ND.basereachable); SETVALUE("retrans", ND.retrans); SETVALUE("curhlim", ND.chlim); ND.flags = newflags; if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCSIFINFO_IN6)"); /* NOTREACHED */ } #undef SETFLAG #undef SETVALUE } if (!ND.initialized) { errx(1, "%s: not initialized yet", ifname); /* NOTREACHED */ } if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { err(1, "ioctl(SIOCGIFINFO_IN6)"); /* NOTREACHED */ } printf("linkmtu=%d", ND.linkmtu); printf(", maxmtu=%d", ND.maxmtu); printf(", curhlim=%d", ND.chlim); printf(", basereachable=%ds%dms", ND.basereachable / 1000, ND.basereachable % 1000); printf(", reachable=%ds", ND.reachable); printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000); #ifdef IPV6CTL_USETEMPADDR memset(nullbuf, 0, sizeof(nullbuf)); if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) { int j; u_int8_t *rbuf; for (i = 0; i < 3; i++) { switch (i) { case 0: printf("\nRandom seed(0): "); rbuf = ND.randomseed0; break; case 1: printf("\nRandom seed(1): "); rbuf = ND.randomseed1; break; case 2: printf("\nRandom ID: "); rbuf = ND.randomid; break; default: errx(1, "impossible case for tempaddr display"); } for (j = 0; j < 8; j++) printf("%02x", rbuf[j]); } } #endif /* IPV6CTL_USETEMPADDR */ if (ND.flags) { printf("\nFlags: "); #ifdef ND6_IFF_IFDISABLED if ((ND.flags & ND6_IFF_IFDISABLED)) printf("disabled "); #endif if ((ND.flags & ND6_IFF_PERFORMNUD)) printf("nud "); #ifdef ND6_IFF_ACCEPT_RTADV if ((ND.flags & ND6_IFF_ACCEPT_RTADV)) printf("accept_rtadv "); #endif #ifdef ND6_IFF_AUTO_LINKLOCAL if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL)) printf("auto_linklocal "); #endif #ifdef ND6_IFF_NO_PREFER_IFACE if ((ND.flags & ND6_IFF_NO_PREFER_IFACE)) printf("no_prefer_iface "); #endif } putc('\n', stdout); #undef ND close(s); } #ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */ #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #endif static void rtrlist() { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST }; char *buf; struct in6_defrouter *p, *ep; size_t l; struct timeval now; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } if (l == 0) return; buf = malloc(l); if (!buf) { err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)"); /*NOTREACHED*/ } ep = (struct in6_defrouter *)(buf + l); for (p = (struct in6_defrouter *)buf; p < ep; p++) { int rtpref; if (getnameinfo((struct sockaddr *)&p->rtaddr, p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)) != 0) strlcpy(host_buf, "?", sizeof(host_buf)); printf("%s if=%s", host_buf, if_indextoname(p->if_index, ifix_buf)); printf(", flags=%s%s", p->flags & ND_RA_FLAG_MANAGED ? "M" : "", p->flags & ND_RA_FLAG_OTHER ? "O" : ""); rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff; printf(", pref=%s", rtpref_str[rtpref]); gettimeofday(&now, 0); if (p->expire == 0) printf(", expire=Never\n"); else printf(", expire=%s\n", sec2str(p->expire - now.tv_sec)); } free(buf); } static void plist() { int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST }; char *buf; struct in6_prefix *p, *ep, *n; struct sockaddr_in6 *advrtr; size_t l; struct timeval now; const int niflags = NI_NUMERICHOST; int ninflags = nflag ? NI_NUMERICHOST : 0; char namebuf[NI_MAXHOST]; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } buf = malloc(l); if (!buf) { err(1, "malloc"); /*NOTREACHED*/ } if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)"); /*NOTREACHED*/ } ep = (struct in6_prefix *)(buf + l); for (p = (struct in6_prefix *)buf; p < ep; p = n) { advrtr = (struct sockaddr_in6 *)(p + 1); n = (struct in6_prefix *)&advrtr[p->advrtrs]; if (getnameinfo((struct sockaddr *)&p->prefix, p->prefix.sin6_len, namebuf, sizeof(namebuf), NULL, 0, niflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); printf("%s/%d if=%s\n", namebuf, p->prefixlen, if_indextoname(p->if_index, ifix_buf)); gettimeofday(&now, 0); /* * meaning of fields, especially flags, is very different * by origin. notify the difference to the users. */ printf("flags=%s%s%s%s%s", p->raflags.onlink ? "L" : "", p->raflags.autonomous ? "A" : "", (p->flags & NDPRF_ONLINK) != 0 ? "O" : "", (p->flags & NDPRF_DETACHED) != 0 ? "D" : "", #ifdef NDPRF_HOME (p->flags & NDPRF_HOME) != 0 ? "H" : "" #else "" #endif ); if (p->vltime == ND6_INFINITE_LIFETIME) printf(" vltime=infinity"); else printf(" vltime=%lu", (unsigned long)p->vltime); if (p->pltime == ND6_INFINITE_LIFETIME) printf(", pltime=infinity"); else printf(", pltime=%lu", (unsigned long)p->pltime); if (p->expire == 0) printf(", expire=Never"); else if (p->expire >= now.tv_sec) printf(", expire=%s", sec2str(p->expire - now.tv_sec)); else printf(", expired"); printf(", ref=%d", p->refcnt); printf("\n"); /* * "advertising router" list is meaningful only if the prefix * information is from RA. */ if (p->advrtrs) { int j; struct sockaddr_in6 *sin6; sin6 = advrtr; printf(" advertised by\n"); for (j = 0; j < p->advrtrs; j++) { struct in6_nbrinfo *nbi; if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, namebuf, sizeof(namebuf), NULL, 0, ninflags) != 0) strlcpy(namebuf, "?", sizeof(namebuf)); printf(" %s", namebuf); nbi = getnbrinfo(&sin6->sin6_addr, p->if_index, 0); if (nbi) { switch (nbi->state) { case ND6_LLINFO_REACHABLE: case ND6_LLINFO_STALE: case ND6_LLINFO_DELAY: case ND6_LLINFO_PROBE: printf(" (reachable)\n"); break; default: printf(" (unreachable)\n"); } } else printf(" (no neighbor state)\n"); sin6++; } } else printf(" No advertising router\n"); } free(buf); } static void pfx_flush() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSPFXFLUSH_IN6)"); } static void rtr_flush() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSRTRFLUSH_IN6)"); close(s); } static void harmonize_rtr() { char dummyif[IFNAMSIZ+8]; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */ if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) err(1, "ioctl(SIOCSNDFLUSH_IN6)"); close(s); } #ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */ static void setdefif(char *ifname) { struct in6_ndifreq ndifreq; unsigned int ifindex; if (strcasecmp(ifname, "delete") == 0) ifindex = 0; else { if ((ifindex = if_nametoindex(ifname)) == 0) err(1, "failed to resolve i/f index for %s", ifname); } if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ ndifreq.ifindex = ifindex; if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) err(1, "ioctl(SIOCSDEFIFACE_IN6)"); close(s); } static void getdefif() { struct in6_ndifreq ndifreq; char ifname[IFNAMSIZ+8]; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) err(1, "socket"); memset(&ndifreq, 0, sizeof(ndifreq)); strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */ if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0) err(1, "ioctl(SIOCGDEFIFACE_IN6)"); if (ndifreq.ifindex == 0) printf("No default interface.\n"); else { if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL) err(1, "failed to resolve ifname for index %lu", ndifreq.ifindex); printf("ND default interface = %s\n", ifname); } close(s); } #endif /* SIOCSDEFIFACE_IN6 */ static char * sec2str(time_t total) { static char result[256]; int days, hours, mins, secs; int first = 1; char *p = result; char *ep = &result[sizeof(result)]; int n; days = total / 3600 / 24; hours = (total / 3600) % 24; mins = (total / 60) % 60; secs = total % 60; if (days) { first = 0; n = snprintf(p, ep - p, "%dd", days); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || hours) { first = 0; n = snprintf(p, ep - p, "%dh", hours); if (n < 0 || n >= ep - p) return "?"; p += n; } if (!first || mins) { first = 0; n = snprintf(p, ep - p, "%dm", mins); if (n < 0 || n >= ep - p) return "?"; p += n; } snprintf(p, ep - p, "%ds", secs); return(result); } /* * Print the timestamp * from tcpdump/util.c */ static void ts_print(const struct timeval *tvp) { int s; /* Default */ s = (tvp->tv_sec + thiszone) % 86400; (void)printf("%02d:%02d:%02d.%06u ", s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec); } #undef NEXTADDR Index: head/usr.sbin/rarpd/rarpd.c =================================================================== --- head/usr.sbin/rarpd/rarpd.c (revision 317034) +++ head/usr.sbin/rarpd/rarpd.c (revision 317035) @@ -1,1003 +1,1005 @@ /* * Copyright (c) 1990, 1991, 1992, 1993, 1996 * 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: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1990, 1991, 1992, 1993, 1996\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * rarpd - Reverse ARP Daemon * * Usage: rarpd -a [-dfsv] [-t directory] [-P pidfile] [hostname] * rarpd [-dfsv] [-t directory] [-P pidfile] interface [hostname] * * 'hostname' is optional solely for backwards compatibility with Sun's rarpd. * Currently, the argument is ignored. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Cast a struct sockaddr to a struct sockaddr_in */ #define SATOSIN(sa) ((struct sockaddr_in *)(sa)) #ifndef TFTP_DIR #define TFTP_DIR "/tftpboot" #endif #define ARPSECS (20 * 60) /* as per code in netinet/if_ether.c */ #define REVARP_REQUEST ARPOP_REVREQUEST #define REVARP_REPLY ARPOP_REVREPLY /* * The structure for each interface. */ struct if_info { struct if_info *ii_next; int ii_fd; /* BPF file descriptor */ in_addr_t ii_ipaddr; /* IP address */ in_addr_t ii_netmask; /* subnet or net mask */ u_char ii_eaddr[ETHER_ADDR_LEN]; /* ethernet address */ char ii_ifname[IF_NAMESIZE]; }; /* * The list of all interfaces that are being listened to. rarp_loop() * "selects" on the descriptors in this list. */ static struct if_info *iflist; static int verbose; /* verbose messages */ static const char *tftp_dir = TFTP_DIR; /* tftp directory */ static int dflag; /* messages to stdout/stderr, not syslog(3) */ static int sflag; /* ignore /tftpboot */ static u_char zero[6]; static char pidfile_buf[PATH_MAX]; static char *pidfile; #define RARPD_PIDFILE "/var/run/rarpd.%s.pid" static struct pidfh *pidfile_fh; static int bpf_open(void); static in_addr_t choose_ipaddr(in_addr_t **, in_addr_t, in_addr_t); static char *eatoa(u_char *); static int expand_syslog_m(const char *fmt, char **newfmt); static void init(char *); static void init_one(struct ifaddrs *, char *, int); static char *intoa(in_addr_t); static in_addr_t ipaddrtonetmask(in_addr_t); static void logmsg(int, const char *, ...) __printflike(2, 3); static int rarp_bootable(in_addr_t); static int rarp_check(u_char *, u_int); static void rarp_loop(void); static int rarp_open(char *); static void rarp_process(struct if_info *, u_char *, u_int); static void rarp_reply(struct if_info *, struct ether_header *, in_addr_t, u_int); static void update_arptab(u_char *, in_addr_t); static void usage(void); int main(int argc, char *argv[]) { int op; char *ifname, *name; int aflag = 0; /* listen on "all" interfaces */ int fflag = 0; /* don't fork */ if ((name = strrchr(argv[0], '/')) != NULL) ++name; else name = argv[0]; if (*name == '-') ++name; /* * All error reporting is done through syslog, unless -d is specified */ openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON); opterr = 0; while ((op = getopt(argc, argv, "adfsP:t:v")) != -1) switch (op) { case 'a': ++aflag; break; case 'd': ++dflag; break; case 'f': ++fflag; break; case 's': ++sflag; break; case 'P': strncpy(pidfile_buf, optarg, sizeof(pidfile_buf) - 1); pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; pidfile = pidfile_buf; break; case 't': tftp_dir = optarg; break; case 'v': ++verbose; break; default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; ifname = (aflag == 0) ? argv[0] : NULL; if ((aflag && ifname) || (!aflag && ifname == NULL)) usage(); init(ifname); if (!fflag) { if (pidfile == NULL && ifname != NULL && aflag == 0) { snprintf(pidfile_buf, sizeof(pidfile_buf) - 1, RARPD_PIDFILE, ifname); pidfile_buf[sizeof(pidfile_buf) - 1] = '\0'; pidfile = pidfile_buf; } /* If pidfile == NULL, /var/run/.pid will be used. */ pidfile_fh = pidfile_open(pidfile, 0600, NULL); if (pidfile_fh == NULL) logmsg(LOG_ERR, "Cannot open or create pidfile: %s", (pidfile == NULL) ? "/var/run/rarpd.pid" : pidfile); if (daemon(0,0)) { logmsg(LOG_ERR, "cannot fork"); pidfile_remove(pidfile_fh); exit(1); } pidfile_write(pidfile_fh); } rarp_loop(); return(0); } /* * Add to the interface list. */ static void init_one(struct ifaddrs *ifa, char *target, int pass1) { struct if_info *ii, *ii2; struct sockaddr_dl *ll; int family; family = ifa->ifa_addr->sa_family; switch (family) { case AF_INET: if (pass1) /* Consider only AF_LINK during pass1. */ return; /* FALLTHROUGH */ case AF_LINK: if (!(ifa->ifa_flags & IFF_UP) || (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) return; break; default: return; } /* Don't bother going any further if not the target interface */ if (target != NULL && strcmp(ifa->ifa_name, target) != 0) return; /* Look for interface in list */ for (ii = iflist; ii != NULL; ii = ii->ii_next) if (strcmp(ifa->ifa_name, ii->ii_ifname) == 0) break; if (pass1 && ii != NULL) /* We've already seen that interface once. */ return; /* Allocate a new one if not found */ if (ii == NULL) { ii = (struct if_info *)malloc(sizeof(*ii)); if (ii == NULL) { logmsg(LOG_ERR, "malloc: %m"); pidfile_remove(pidfile_fh); exit(1); } bzero(ii, sizeof(*ii)); ii->ii_fd = -1; strlcpy(ii->ii_ifname, ifa->ifa_name, sizeof(ii->ii_ifname)); ii->ii_next = iflist; iflist = ii; } else if (!pass1 && ii->ii_ipaddr != 0) { /* * Second AF_INET definition for that interface: clone * the existing one, and work on that cloned one. * This must be another IP address for this interface, * so avoid killing the previous configuration. */ ii2 = (struct if_info *)malloc(sizeof(*ii2)); if (ii2 == NULL) { logmsg(LOG_ERR, "malloc: %m"); pidfile_remove(pidfile_fh); exit(1); } memcpy(ii2, ii, sizeof(*ii2)); ii2->ii_fd = -1; ii2->ii_next = iflist; iflist = ii2; ii = ii2; } switch (family) { case AF_INET: ii->ii_ipaddr = SATOSIN(ifa->ifa_addr)->sin_addr.s_addr; ii->ii_netmask = SATOSIN(ifa->ifa_netmask)->sin_addr.s_addr; if (ii->ii_netmask == 0) ii->ii_netmask = ipaddrtonetmask(ii->ii_ipaddr); if (ii->ii_fd < 0) ii->ii_fd = rarp_open(ii->ii_ifname); break; case AF_LINK: ll = (struct sockaddr_dl *)ifa->ifa_addr; switch (ll->sdl_type) { case IFT_ETHER: case IFT_L2VLAN: bcopy(LLADDR(ll), ii->ii_eaddr, 6); } break; } } /* * Initialize all "candidate" interfaces that are in the system * configuration list. A "candidate" is up, not loopback and not * point to point. */ static void init(char *target) { struct if_info *ii, *nii, *lii; struct ifaddrs *ifhead, *ifa; int error; error = getifaddrs(&ifhead); if (error) { logmsg(LOG_ERR, "getifaddrs: %m"); pidfile_remove(pidfile_fh); exit(1); } /* * We make two passes over the list we have got. In the first * one, we only collect AF_LINK interfaces, and initialize our * list of interfaces from them. In the second pass, we * collect the actual IP addresses from the AF_INET * interfaces, and allow for the same interface name to appear * multiple times (in case of more than one IP address). */ for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next) init_one(ifa, target, 1); for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next) init_one(ifa, target, 0); freeifaddrs(ifhead); /* Throw away incomplete interfaces */ lii = NULL; for (ii = iflist; ii != NULL; ii = nii) { nii = ii->ii_next; if (ii->ii_ipaddr == 0 || bcmp(ii->ii_eaddr, zero, 6) == 0) { if (lii == NULL) iflist = nii; else lii->ii_next = nii; if (ii->ii_fd >= 0) close(ii->ii_fd); free(ii); continue; } lii = ii; } /* Verbose stuff */ if (verbose) for (ii = iflist; ii != NULL; ii = ii->ii_next) logmsg(LOG_DEBUG, "%s %s 0x%08x %s", ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)), (in_addr_t)ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr)); } static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: rarpd -a [-dfsv] [-t directory] [-P pidfile]", " rarpd [-dfsv] [-t directory] [-P pidfile] interface"); exit(1); } static int bpf_open(void) { int fd; int n = 0; char device[sizeof "/dev/bpf000"]; /* * Go through all the minors and find one that isn't in use. */ do { (void)sprintf(device, "/dev/bpf%d", n++); fd = open(device, O_RDWR); } while ((fd == -1) && (errno == EBUSY)); if (fd == -1) { logmsg(LOG_ERR, "%s: %m", device); pidfile_remove(pidfile_fh); exit(1); } return fd; } /* * Open a BPF file and attach it to the interface named 'device'. * Set immediate mode, and set a filter that accepts only RARP requests. */ static int rarp_open(char *device) { int fd; struct ifreq ifr; u_int dlt; int immediate; static struct bpf_insn insns[] = { BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3), BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1), BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) + sizeof(struct ether_header)), BPF_STMT(BPF_RET|BPF_K, 0), }; static struct bpf_program filter = { sizeof insns / sizeof(insns[0]), insns }; fd = bpf_open(); /* * Set immediate mode so packets are processed as they arrive. */ immediate = 1; if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) { logmsg(LOG_ERR, "BIOCIMMEDIATE: %m"); goto rarp_open_err; } strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) { logmsg(LOG_ERR, "BIOCSETIF: %m"); goto rarp_open_err; } /* * Check that the data link layer is an Ethernet; this code won't * work with anything else. */ if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) { logmsg(LOG_ERR, "BIOCGDLT: %m"); goto rarp_open_err; } if (dlt != DLT_EN10MB) { logmsg(LOG_ERR, "%s is not an ethernet", device); goto rarp_open_err; } /* * Set filter program. */ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) { logmsg(LOG_ERR, "BIOCSETF: %m"); goto rarp_open_err; } return fd; rarp_open_err: pidfile_remove(pidfile_fh); exit(1); } /* * Perform various sanity checks on the RARP request packet. Return * false on failure and log the reason. */ static int rarp_check(u_char *p, u_int len) { struct ether_header *ep = (struct ether_header *)p; struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep)); if (len < sizeof(*ep) + sizeof(*ap)) { logmsg(LOG_ERR, "truncated request, got %u, expected %lu", len, (u_long)(sizeof(*ep) + sizeof(*ap))); return 0; } /* * XXX This test might be better off broken out... */ if (ntohs(ep->ether_type) != ETHERTYPE_REVARP || ntohs(ap->arp_hrd) != ARPHRD_ETHER || ntohs(ap->arp_op) != REVARP_REQUEST || ntohs(ap->arp_pro) != ETHERTYPE_IP || ap->arp_hln != 6 || ap->arp_pln != 4) { logmsg(LOG_DEBUG, "request fails sanity check"); return 0; } if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) { logmsg(LOG_DEBUG, "ether/arp sender address mismatch"); return 0; } if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) { logmsg(LOG_DEBUG, "ether/arp target address mismatch"); return 0; } return 1; } /* * Loop indefinitely listening for RARP requests on the * interfaces in 'iflist'. */ static void rarp_loop(void) { u_char *buf, *bp, *ep; int cc, fd; fd_set fds, listeners; int bufsize, maxfd = 0; struct if_info *ii; if (iflist == NULL) { logmsg(LOG_ERR, "no interfaces"); goto rarpd_loop_err; } if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) { logmsg(LOG_ERR, "BIOCGBLEN: %m"); goto rarpd_loop_err; } buf = malloc(bufsize); if (buf == NULL) { logmsg(LOG_ERR, "malloc: %m"); goto rarpd_loop_err; } while (1) { /* * Find the highest numbered file descriptor for select(). * Initialize the set of descriptors to listen to. */ FD_ZERO(&fds); for (ii = iflist; ii != NULL; ii = ii->ii_next) { FD_SET(ii->ii_fd, &fds); if (ii->ii_fd > maxfd) maxfd = ii->ii_fd; } listeners = fds; if (select(maxfd + 1, &listeners, NULL, NULL, NULL) == -1) { /* Don't choke when we get ptraced */ if (errno == EINTR) continue; logmsg(LOG_ERR, "select: %m"); goto rarpd_loop_err; } for (ii = iflist; ii != NULL; ii = ii->ii_next) { fd = ii->ii_fd; if (!FD_ISSET(fd, &listeners)) continue; again: cc = read(fd, (char *)buf, bufsize); /* Don't choke when we get ptraced */ if ((cc == -1) && (errno == EINTR)) goto again; /* Loop through the packet(s) */ #define bhp ((struct bpf_hdr *)bp) bp = buf; ep = bp + cc; while (bp < ep) { u_int caplen, hdrlen; caplen = bhp->bh_caplen; hdrlen = bhp->bh_hdrlen; if (rarp_check(bp + hdrlen, caplen)) rarp_process(ii, bp + hdrlen, caplen); bp += BPF_WORDALIGN(hdrlen + caplen); } } } #undef bhp return; rarpd_loop_err: pidfile_remove(pidfile_fh); exit(1); } /* * True if this server can boot the host whose IP address is 'addr'. * This check is made by looking in the tftp directory for the * configuration file. */ static int rarp_bootable(in_addr_t addr) { struct dirent *dent; DIR *d; char ipname[9]; static DIR *dd = NULL; (void)sprintf(ipname, "%08X", (in_addr_t)ntohl(addr)); /* * If directory is already open, rewind it. Otherwise, open it. */ if ((d = dd) != NULL) rewinddir(d); else { if (chdir(tftp_dir) == -1) { logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir); goto rarp_bootable_err; } d = opendir("."); if (d == NULL) { logmsg(LOG_ERR, "opendir: %m"); goto rarp_bootable_err; } dd = d; } while ((dent = readdir(d)) != NULL) if (strncmp(dent->d_name, ipname, 8) == 0) return 1; return 0; rarp_bootable_err: pidfile_remove(pidfile_fh); exit(1); } /* * Given a list of IP addresses, 'alist', return the first address that * is on network 'net'; 'netmask' is a mask indicating the network portion * of the address. */ static in_addr_t choose_ipaddr(in_addr_t **alist, in_addr_t net, in_addr_t netmask) { for (; *alist; ++alist) if ((**alist & netmask) == net) return **alist; return 0; } /* * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has * already been checked for validity. The reply is overlaid on the request. */ static void rarp_process(struct if_info *ii, u_char *pkt, u_int len) { struct ether_header *ep; struct hostent *hp; in_addr_t target_ipaddr; char ename[256]; ep = (struct ether_header *)pkt; /* should this be arp_tha? */ if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0) { logmsg(LOG_ERR, "cannot map %s to name", eatoa(ep->ether_shost)); return; } if ((hp = gethostbyname(ename)) == NULL) { logmsg(LOG_ERR, "cannot map %s to IP address", ename); return; } /* * Choose correct address from list. */ if (hp->h_addrtype != AF_INET) { logmsg(LOG_ERR, "cannot handle non IP addresses for %s", ename); return; } target_ipaddr = choose_ipaddr((in_addr_t **)hp->h_addr_list, ii->ii_ipaddr & ii->ii_netmask, ii->ii_netmask); if (target_ipaddr == 0) { logmsg(LOG_ERR, "cannot find %s on net %s", ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask))); return; } if (sflag || rarp_bootable(target_ipaddr)) rarp_reply(ii, ep, target_ipaddr, len); else if (verbose > 1) logmsg(LOG_INFO, "%s %s at %s DENIED (not bootable)", ii->ii_ifname, eatoa(ep->ether_shost), intoa(ntohl(target_ipaddr))); } /* * Poke the kernel arp tables with the ethernet/ip address combinataion * given. When processing a reply, we must do this so that the booting * host (i.e. the guy running rarpd), won't try to ARP for the hardware * address of the guy being booted (he cannot answer the ARP). */ static struct sockaddr_in sin_inarp = { sizeof(struct sockaddr_in), AF_INET, 0, {0}, {0}, }; static struct sockaddr_dl sin_dl = { sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6, 0, "" }; static struct { struct rt_msghdr rthdr; char rtspace[512]; } rtmsg; static void update_arptab(u_char *ep, in_addr_t ipaddr) { struct timespec tp; int cc; struct sockaddr_in *ar, *ar2; struct sockaddr_dl *ll, *ll2; struct rt_msghdr *rt; int xtype, xindex; static pid_t pid; int r; static int seq; r = socket(PF_ROUTE, SOCK_RAW, 0); if (r == -1) { logmsg(LOG_ERR, "raw route socket: %m"); pidfile_remove(pidfile_fh); exit(1); } pid = getpid(); ar = &sin_inarp; ar->sin_addr.s_addr = ipaddr; ll = &sin_dl; bcopy(ep, LLADDR(ll), 6); /* Get the type and interface index */ rt = &rtmsg.rthdr; bzero(&rtmsg, sizeof(rtmsg)); rt->rtm_version = RTM_VERSION; rt->rtm_addrs = RTA_DST; rt->rtm_type = RTM_GET; rt->rtm_seq = ++seq; ar2 = (struct sockaddr_in *)rtmsg.rtspace; bcopy(ar, ar2, sizeof(*ar)); rt->rtm_msglen = sizeof(*rt) + sizeof(*ar); errno = 0; if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != ESRCH)) { logmsg(LOG_ERR, "rtmsg get write: %m"); close(r); return; } do { cc = read(r, rt, sizeof(rtmsg)); - } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid)); + } while (cc > 0 && (rt->rtm_type != RTM_GET || rt->rtm_seq != seq || + rt->rtm_pid != pid)); if (cc == -1) { logmsg(LOG_ERR, "rtmsg get read: %m"); close(r); return; } ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len); if (ll2->sdl_family != AF_LINK) { /* * XXX I think this means the ip address is not on a * directly connected network (the family is AF_INET in * this case). */ logmsg(LOG_ERR, "bogus link family (%d) wrong net for %08X?\n", ll2->sdl_family, ipaddr); close(r); return; } xtype = ll2->sdl_type; xindex = ll2->sdl_index; /* Set the new arp entry */ bzero(rt, sizeof(rtmsg)); rt->rtm_version = RTM_VERSION; rt->rtm_addrs = RTA_DST | RTA_GATEWAY; rt->rtm_inits = RTV_EXPIRE; clock_gettime(CLOCK_MONOTONIC, &tp); rt->rtm_rmx.rmx_expire = tp.tv_sec + ARPSECS; rt->rtm_flags = RTF_HOST | RTF_STATIC; rt->rtm_type = RTM_ADD; rt->rtm_seq = ++seq; bcopy(ar, ar2, sizeof(*ar)); ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2)); bcopy(ll, ll2, sizeof(*ll)); ll2->sdl_type = xtype; ll2->sdl_index = xindex; rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2); errno = 0; if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != EEXIST)) { logmsg(LOG_ERR, "rtmsg add write: %m"); close(r); return; } do { cc = read(r, rt, sizeof(rtmsg)); - } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid)); + } while (cc > 0 && (rt->rtm_type != RTM_ADD || rt->rtm_seq != seq || + rt->rtm_pid != pid)); close(r); if (cc == -1) { logmsg(LOG_ERR, "rtmsg add read: %m"); return; } } /* * Build a reverse ARP packet and sent it out on the interface. * 'ep' points to a valid REVARP_REQUEST. The REVARP_REPLY is built * on top of the request, then written to the network. * * RFC 903 defines the ether_arp fields as follows. The following comments * are taken (more or less) straight from this document. * * REVARP_REQUEST * * arp_sha is the hardware address of the sender of the packet. * arp_spa is undefined. * arp_tha is the 'target' hardware address. * In the case where the sender wishes to determine his own * protocol address, this, like arp_sha, will be the hardware * address of the sender. * arp_tpa is undefined. * * REVARP_REPLY * * arp_sha is the hardware address of the responder (the sender of the * reply packet). * arp_spa is the protocol address of the responder (see the note below). * arp_tha is the hardware address of the target, and should be the same as * that which was given in the request. * arp_tpa is the protocol address of the target, that is, the desired address. * * Note that the requirement that arp_spa be filled in with the responder's * protocol is purely for convenience. For instance, if a system were to use * both ARP and RARP, then the inclusion of the valid protocol-hardware * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent * ARP request. */ static void rarp_reply(struct if_info *ii, struct ether_header *ep, in_addr_t ipaddr, u_int len) { u_int n; struct ether_arp *ap = (struct ether_arp *)(ep + 1); update_arptab((u_char *)&ap->arp_sha, ipaddr); /* * Build the rarp reply by modifying the rarp request in place. */ ap->arp_op = htons(REVARP_REPLY); #ifdef BROKEN_BPF ep->ether_type = ETHERTYPE_REVARP; #endif bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6); bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6); bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6); bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4); /* Target hardware is unchanged. */ bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4); /* Zero possible garbage after packet. */ bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)), len - (sizeof(*ep) + sizeof(*ap))); n = write(ii->ii_fd, (char *)ep, len); if (n != len) logmsg(LOG_ERR, "write: only %d of %d bytes written", n, len); if (verbose) logmsg(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname, eatoa(ap->arp_tha), intoa(ntohl(ipaddr))); } /* * Get the netmask of an IP address. This routine is used if * SIOCGIFNETMASK doesn't work. */ static in_addr_t ipaddrtonetmask(in_addr_t addr) { addr = ntohl(addr); if (IN_CLASSA(addr)) return htonl(IN_CLASSA_NET); if (IN_CLASSB(addr)) return htonl(IN_CLASSB_NET); if (IN_CLASSC(addr)) return htonl(IN_CLASSC_NET); logmsg(LOG_DEBUG, "unknown IP address class: %08X", addr); return htonl(0xffffffff); } /* * A faster replacement for inet_ntoa(). */ static char * intoa(in_addr_t addr) { char *cp; u_int byte; int n; static char buf[sizeof(".xxx.xxx.xxx.xxx")]; cp = &buf[sizeof buf]; *--cp = '\0'; n = 4; do { byte = addr & 0xff; *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) { *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) *--cp = byte + '0'; } *--cp = '.'; addr >>= 8; } while (--n > 0); return cp + 1; } static char * eatoa(u_char *ea) { static char buf[sizeof("xx:xx:xx:xx:xx:xx")]; (void)sprintf(buf, "%x:%x:%x:%x:%x:%x", ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); return (buf); } static void logmsg(int pri, const char *fmt, ...) { va_list v; FILE *fp; char *newfmt; va_start(v, fmt); if (dflag) { if (pri == LOG_ERR) fp = stderr; else fp = stdout; if (expand_syslog_m(fmt, &newfmt) == -1) { vfprintf(fp, fmt, v); } else { vfprintf(fp, newfmt, v); free(newfmt); } fputs("\n", fp); fflush(fp); } else { vsyslog(pri, fmt, v); } va_end(v); } static int expand_syslog_m(const char *fmt, char **newfmt) { const char *str, *m; char *p, *np; p = strdup(""); str = fmt; while ((m = strstr(str, "%m")) != NULL) { asprintf(&np, "%s%.*s%s", p, (int)(m - str), str, strerror(errno)); free(p); if (np == NULL) { errno = ENOMEM; return (-1); } p = np; str = m + 2; } if (*str != '\0') { asprintf(&np, "%s%s", p, str); free(p); if (np == NULL) { errno = ENOMEM; return (-1); } p = np; } *newfmt = p; return (0); } Index: head/usr.sbin/route6d/route6d.c =================================================================== --- head/usr.sbin/route6d/route6d.c (revision 317034) +++ head/usr.sbin/route6d/route6d.c (revision 317035) @@ -1,3569 +1,3579 @@ /* $FreeBSD$ */ /* $KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ #ifndef lint static const char _rcsid[] = "$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include #include #include #include #include "route6d.h" #define MAXFILTER 40 #define RT_DUMP_MAXRETRY 15 #ifdef DEBUG #define INIT_INTERVAL6 6 #else #define INIT_INTERVAL6 10 /* Wait to submit an initial riprequest */ #endif /* alignment constraint for routing socket */ #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) struct ifc { /* Configuration of an interface */ TAILQ_ENTRY(ifc) ifc_next; char ifc_name[IFNAMSIZ]; /* if name */ int ifc_index; /* if index */ int ifc_mtu; /* if mtu */ int ifc_metric; /* if metric */ u_int ifc_flags; /* flags */ short ifc_cflags; /* IFC_XXX */ struct in6_addr ifc_mylladdr; /* my link-local address */ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */ TAILQ_HEAD(, ifac) ifc_ifac_head; /* list of AF_INET6 addrs */ TAILQ_HEAD(, iff) ifc_iff_head; /* list of filters */ int ifc_joined; /* joined to ff02::9 */ }; static TAILQ_HEAD(, ifc) ifc_head = TAILQ_HEAD_INITIALIZER(ifc_head); struct ifac { /* Adddress associated to an interface */ TAILQ_ENTRY(ifac) ifac_next; struct ifc *ifac_ifc; /* back pointer */ struct in6_addr ifac_addr; /* address */ struct in6_addr ifac_raddr; /* remote address, valid in p2p */ int ifac_scope_id; /* scope id */ int ifac_plen; /* prefix length */ }; struct iff { /* Filters for an interface */ TAILQ_ENTRY(iff) iff_next; int iff_type; struct in6_addr iff_addr; int iff_plen; }; static struct ifc **index2ifc; static unsigned int nindex2ifc; static struct ifc *loopifcp = NULL; /* pointing to loopback */ #ifdef HAVE_POLL_H static struct pollfd set[2]; #else static fd_set *sockvecp; /* vector to select() for receiving */ static fd_set *recvecp; static int fdmasks; static int maxfd; /* maximum fd for select() */ #endif static int rtsock; /* the routing socket */ static int ripsock; /* socket to send/receive RIP datagram */ static struct rip6 *ripbuf; /* packet buffer for sending */ /* * Maintain the routes in a linked list. When the number of the routes * grows, somebody would like to introduce a hash based or a radix tree * based structure. I believe the number of routes handled by RIP is * limited and I don't have to manage a complex data structure, however. * * One of the major drawbacks of the linear linked list is the difficulty * of representing the relationship between a couple of routes. This may * be a significant problem when we have to support route aggregation with * suppressing the specifics covered by the aggregate. */ struct riprt { TAILQ_ENTRY(riprt) rrt_next; /* next destination */ struct riprt *rrt_same; /* same destination - future use */ struct netinfo6 rrt_info; /* network info */ struct in6_addr rrt_gw; /* gateway */ u_long rrt_flags; /* kernel routing table flags */ u_long rrt_rflags; /* route6d routing table flags */ time_t rrt_t; /* when the route validated */ int rrt_index; /* ifindex from which this route got */ }; static TAILQ_HEAD(, riprt) riprt_head = TAILQ_HEAD_INITIALIZER(riprt_head); static int dflag = 0; /* debug flag */ static int qflag = 0; /* quiet flag */ static int nflag = 0; /* don't update kernel routing table */ static int aflag = 0; /* age out even the statically defined routes */ static int hflag = 0; /* don't split horizon */ static int lflag = 0; /* exchange site local routes */ static int Pflag = 0; /* don't age out routes with RTF_PROTO[123] */ static int Qflag = RTF_PROTO2; /* set RTF_PROTO[123] flag to routes by RIPng */ static int sflag = 0; /* announce static routes w/ split horizon */ static int Sflag = 0; /* announce static routes to every interface */ static unsigned long routetag = 0; /* route tag attached on originating case */ static char *filter[MAXFILTER]; static int filtertype[MAXFILTER]; static int nfilter = 0; static pid_t pid; static struct sockaddr_storage ripsin; static int interval = 1; static time_t nextalarm = 0; #if 0 static time_t sup_trig_update = 0; #endif static FILE *rtlog = NULL; static int logopened = 0; static int seq = 0; static volatile sig_atomic_t seenalrm; static volatile sig_atomic_t seenquit; static volatile sig_atomic_t seenusr1; #define RRTF_AGGREGATE 0x08000000 #define RRTF_NOADVERTISE 0x10000000 #define RRTF_NH_NOT_LLADDR 0x20000000 #define RRTF_SENDANYWAY 0x40000000 #define RRTF_CHANGED 0x80000000 static void sighandler(int); static void ripalarm(void); static void riprecv(void); static void ripsend(struct ifc *, struct sockaddr_in6 *, int); static int out_filter(struct riprt *, struct ifc *); static void init(void); static void ifconfig(void); static int ifconfig1(const char *, const struct sockaddr *, struct ifc *, int); static void rtrecv(void); static int rt_del(const struct sockaddr_in6 *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); static int rt_deladdr(struct ifc *, const struct sockaddr_in6 *, const struct sockaddr_in6 *); static void filterconfig(void); static int getifmtu(int); static const char *rttypes(struct rt_msghdr *); static const char *rtflags(struct rt_msghdr *); static const char *ifflags(int); static int ifrt(struct ifc *, int); static void ifrt_p2p(struct ifc *, int); static void applyplen(struct in6_addr *, int); static void ifrtdump(int); static void ifdump(int); static void ifdump0(FILE *, const struct ifc *); static void ifremove(int); static void rtdump(int); static void rt_entry(struct rt_msghdr *, int); static void rtdexit(void); static void riprequest(struct ifc *, struct netinfo6 *, int, struct sockaddr_in6 *); static void ripflush(struct ifc *, struct sockaddr_in6 *, int, struct netinfo6 *np); static void sendrequest(struct ifc *); static int sin6mask2len(const struct sockaddr_in6 *); static int mask2len(const struct in6_addr *, int); static int sendpacket(struct sockaddr_in6 *, int); static int addroute(struct riprt *, const struct in6_addr *, struct ifc *); static int delroute(struct netinfo6 *, struct in6_addr *); #if 0 static struct in6_addr *getroute(struct netinfo6 *, struct in6_addr *); #endif static void krtread(int); static int tobeadv(struct riprt *, struct ifc *); static char *allocopy(char *); static char *hms(void); static const char *inet6_n2p(const struct in6_addr *); static struct ifac *ifa_match(const struct ifc *, const struct in6_addr *, int); static struct in6_addr *plen2mask(int); static struct riprt *rtsearch(struct netinfo6 *); static int ripinterval(int); #if 0 static time_t ripsuptrig(void); #endif static void fatal(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); static void trace(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); static void tracet(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); static struct ifc *ifc_find(char *); static struct iff *iff_find(struct ifc *, int); static void setindex2ifc(int, struct ifc *); #define MALLOC(type) ((type *)malloc(sizeof(type))) #define IFIL_TYPE_ANY 0x0 #define IFIL_TYPE_A 'A' #define IFIL_TYPE_N 'N' #define IFIL_TYPE_T 'T' #define IFIL_TYPE_O 'O' #define IFIL_TYPE_L 'L' int main(int argc, char *argv[]) { int ch; int error = 0; unsigned long proto; struct ifc *ifcp; sigset_t mask, omask; const char *pidfile = ROUTE6D_PID; FILE *pidfh; char *progname; char *ep; progname = strrchr(*argv, '/'); if (progname) progname++; else progname = *argv; pid = getpid(); while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnp:P:Q:qsS")) != -1) { switch (ch) { case 'A': case 'N': case 'O': case 'T': case 'L': if (nfilter >= MAXFILTER) { fatal("Exceeds MAXFILTER"); /*NOTREACHED*/ } filtertype[nfilter] = ch; filter[nfilter++] = allocopy(optarg); break; case 't': ep = NULL; routetag = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) { fatal("invalid route tag"); /*NOTREACHED*/ } break; case 'p': pidfile = optarg; break; case 'P': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid P flag"); /*NOTREACHED*/ } if (proto == 0) Pflag = 0; if (proto == 1) Pflag |= RTF_PROTO1; if (proto == 2) Pflag |= RTF_PROTO2; if (proto == 3) Pflag |= RTF_PROTO3; break; case 'Q': ep = NULL; proto = strtoul(optarg, &ep, 0); if (!ep || *ep != '\0' || 3 < proto) { fatal("invalid Q flag"); /*NOTREACHED*/ } if (proto == 0) Qflag = 0; if (proto == 1) Qflag |= RTF_PROTO1; if (proto == 2) Qflag |= RTF_PROTO2; if (proto == 3) Qflag |= RTF_PROTO3; break; case 'R': if ((rtlog = fopen(optarg, "w")) == NULL) { fatal("Can not write to routelog"); /*NOTREACHED*/ } break; #define FLAG(c, flag, n) case c: do { flag = n; break; } while(0) FLAG('a', aflag, 1); break; FLAG('d', dflag, 1); break; FLAG('D', dflag, 2); break; FLAG('h', hflag, 1); break; FLAG('l', lflag, 1); break; FLAG('n', nflag, 1); break; FLAG('q', qflag, 1); break; FLAG('s', sflag, 1); break; FLAG('S', Sflag, 1); break; #undef FLAG default: fatal("Invalid option specified, terminating"); /*NOTREACHED*/ } } argc -= optind; argv += optind; if (argc > 0) { fatal("bogus extra arguments"); /*NOTREACHED*/ } if (geteuid()) { nflag = 1; fprintf(stderr, "No kernel update is allowed\n"); } if (dflag == 0) { if (daemon(0, 0) < 0) { fatal("daemon"); /*NOTREACHED*/ } } openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON); logopened++; if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) fatal("malloc"); memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; init(); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index < 0) { fprintf(stderr, "No ifindex found at %s " "(no link-local address?)\n", ifcp->ifc_name); error++; } } if (error) exit(1); if (loopifcp == NULL) { fatal("No loopback found"); /*NOTREACHED*/ } TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { ifrt(ifcp, 0); } filterconfig(); krtread(0); if (dflag) ifrtdump(0); pid = getpid(); if ((pidfh = fopen(pidfile, "w")) != NULL) { fprintf(pidfh, "%d\n", pid); fclose(pidfh); } if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(ripbuf, 0, RIP6_MAXMTU); ripbuf->rip6_cmd = RIP6_RESPONSE; ripbuf->rip6_vers = RIP6_VERSION; ripbuf->rip6_res1[0] = 0; ripbuf->rip6_res1[1] = 0; if (signal(SIGALRM, sighandler) == SIG_ERR || signal(SIGQUIT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR || signal(SIGUSR1, sighandler) == SIG_ERR || signal(SIGHUP, sighandler) == SIG_ERR || signal(SIGINT, sighandler) == SIG_ERR) { fatal("signal"); /*NOTREACHED*/ } /* * To avoid rip packet congestion (not on a cable but in this * process), wait for a moment to send the first RIP6_RESPONSE * packets. */ alarm(ripinterval(INIT_INTERVAL6)); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) sendrequest(ifcp); } syslog(LOG_INFO, "**** Started ****"); sigemptyset(&mask); sigaddset(&mask, SIGALRM); while (1) { if (seenalrm) { ripalarm(); seenalrm = 0; continue; } if (seenquit) { rtdexit(); seenquit = 0; continue; } if (seenusr1) { ifrtdump(SIGUSR1); seenusr1 = 0; continue; } #ifdef HAVE_POLL_H switch (poll(set, 2, INFTIM)) #else memcpy(recvecp, sockvecp, fdmasks); switch (select(maxfd + 1, recvecp, 0, 0, 0)) #endif { case -1: if (errno != EINTR) { fatal("select"); /*NOTREACHED*/ } continue; case 0: continue; default: #ifdef HAVE_POLL_H if (set[0].revents & POLLIN) #else if (FD_ISSET(ripsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); riprecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } #ifdef HAVE_POLL_H if (set[1].revents & POLLIN) #else if (FD_ISSET(rtsock, recvecp)) #endif { sigprocmask(SIG_BLOCK, &mask, &omask); rtrecv(); sigprocmask(SIG_SETMASK, &omask, NULL); } } } } static void sighandler(int signo) { switch (signo) { case SIGALRM: seenalrm++; break; case SIGQUIT: case SIGTERM: seenquit++; break; case SIGUSR1: case SIGHUP: case SIGINT: seenusr1++; break; } } /* * gracefully exits after resetting sockopts. */ /* ARGSUSED */ static void rtdexit(void) { struct riprt *rrt; alarm(0); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_AGGREGATE) { delroute(&rrt->rrt_info, &rrt->rrt_gw); } } close(ripsock); close(rtsock); syslog(LOG_INFO, "**** Terminated ****"); closelog(); exit(1); } /* * Called periodically: * 1. age out the learned route. remove it if necessary. * 2. submit RIP6_RESPONSE packets. * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have * to invoke this function in every 1 or 5 or 10 seconds only to age the * routes more precisely. */ /* ARGSUSED */ static void ripalarm(void) { struct ifc *ifcp; struct riprt *rrt, *rrt_tmp; time_t t_lifetime, t_holddown; /* age the RIP routes */ t_lifetime = time(NULL) - RIP_LIFETIME; t_holddown = t_lifetime - RIP_HOLDDOWN; TAILQ_FOREACH_SAFE(rrt, &riprt_head, rrt_next, rrt_tmp) { if (rrt->rrt_t == 0) continue; else if (rrt->rrt_t < t_holddown) { TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); free(rrt); } else if (rrt->rrt_t < t_lifetime) rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } /* Supply updates */ TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP)) ripsend(ifcp, &ifcp->ifc_ripsin, 0); } alarm(ripinterval(SUPPLY_INTERVAL6)); } static void init(void) { int error; const int int0 = 0, int1 = 1, int255 = 255; struct addrinfo hints, *res; char port[NI_MAXSERV]; TAILQ_INIT(&ifc_head); nindex2ifc = 0; /*initial guess*/ index2ifc = NULL; snprintf(port, sizeof(port), "%u", RIP6_PORT); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal(":: resolved to multiple address"); /*NOTREACHED*/ } ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (ripsock < 0) { fatal("rip socket"); /*NOTREACHED*/ } #ifdef IPV6_V6ONLY if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_V6ONLY, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_V6ONLY"); /*NOTREACHED*/ } #endif if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) { fatal("rip bind"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &int255, sizeof(int255)) < 0) { fatal("rip IPV6_MULTICAST_HOPS"); /*NOTREACHED*/ } if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &int0, sizeof(int0)) < 0) { fatal("rip IPV6_MULTICAST_LOOP"); /*NOTREACHED*/ } #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVPKTINFO"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_PKTINFO"); /*NOTREACHED*/ } #endif #ifdef IPV6_RECVPKTINFO if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_RECVHOPLIMIT"); /*NOTREACHED*/ } #else /* old adv. API */ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &int1, sizeof(int1)) < 0) { fatal("rip IPV6_HOPLIMIT"); /*NOTREACHED*/ } #endif freeaddrinfo(res); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(RIP6_DEST, port, &hints, &res); if (error) { fatal("%s", gai_strerror(error)); /*NOTREACHED*/ } if (res->ai_next) { fatal("%s resolved to multiple address", RIP6_DEST); /*NOTREACHED*/ } memcpy(&ripsin, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); #ifdef HAVE_POLL_H set[0].fd = ripsock; set[0].events = POLLIN; #else maxfd = ripsock; #endif if (nflag == 0) { if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { fatal("route socket"); /*NOTREACHED*/ } #ifdef HAVE_POLL_H set[1].fd = rtsock; set[1].events = POLLIN; #else if (rtsock > maxfd) maxfd = rtsock; #endif } else { #ifdef HAVE_POLL_H set[1].fd = -1; #else rtsock = -1; /*just for safety */ #endif } #ifndef HAVE_POLL_H fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask); if ((sockvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if ((recvecp = malloc(fdmasks)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(sockvecp, 0, fdmasks); FD_SET(ripsock, sockvecp); if (rtsock >= 0) FD_SET(rtsock, sockvecp); #endif } #define RIPSIZE(n) \ (sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6)) /* * ripflush flushes the rip datagram stored in the rip buffer */ static void ripflush(struct ifc *ifcp, struct sockaddr_in6 *sin6, int nrt, struct netinfo6 *np) { int i; int error; if (ifcp) tracet(1, "Send(%s): info(%d) to %s.%d\n", ifcp->ifc_name, nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); else tracet(1, "Send: info(%d) to %s.%d\n", nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port)); if (dflag >= 2) { np = ripbuf->rip6_nets; for (i = 0; i < nrt; i++, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) trace(2, " NextHop reset"); else { trace(2, " NextHop %s", inet6_n2p(&np->rip6_dest)); } } else { trace(2, " %s/%d[%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); } if (np->rip6_tag) { trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); } trace(2, "\n"); } } error = sendpacket(sin6, RIPSIZE(nrt)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ if (ifcp != NULL) { tracet(1, "Could not send info to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); /* As if down for AF_INET6 */ ifcp->ifc_flags &= ~IFF_UP; } else { tracet(1, "Could not send info to %s\n", inet6_n2p(&sin6->sin6_addr)); } } } /* * Generate RIP6_RESPONSE packets and send them. */ static void ripsend(struct ifc *ifcp, struct sockaddr_in6 *sin6, int flag) { struct riprt *rrt; struct in6_addr *nh; /* next hop */ struct netinfo6 *np; int maxrte; int nrt; if (qflag) return; if (ifcp == NULL) { /* * Request from non-link local address is not * a regular route6d update. */ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(NULL, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(NULL, sin6, nrt, np); return; } if ((flag & RRTF_SENDANYWAY) == 0 && (qflag || (ifcp->ifc_flags & IFF_LOOPBACK))) return; /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; /* -T: generate default route only */ if (iff_find(ifcp, IFIL_TYPE_T) != NULL) { struct netinfo6 rrt_info; memset(&rrt_info, 0, sizeof(struct netinfo6)); rrt_info.rip6_dest = in6addr_any; rrt_info.rip6_plen = 0; rrt_info.rip6_metric = 1; rrt_info.rip6_metric += ifcp->ifc_metric; rrt_info.rip6_tag = htons(routetag & 0xffff); np = ripbuf->rip6_nets; *np = rrt_info; nrt = 1; ripflush(ifcp, sin6, nrt, np); return; } maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); nrt = 0; np = ripbuf->rip6_nets; nh = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_rflags & RRTF_NOADVERTISE) continue; /* Need to check filter here */ if (out_filter(rrt, ifcp) == 0) continue; /* Check split horizon and other conditions */ if (tobeadv(rrt, ifcp) == 0) continue; /* Only considers the routes with flag if specified */ if ((flag & RRTF_CHANGED) && (rrt->rrt_rflags & RRTF_CHANGED) == 0) continue; /* Check nexthop */ if (rrt->rrt_index == ifcp->ifc_index && !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) && (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) { if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) { if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } np->rip6_dest = rrt->rrt_gw; np->rip6_plen = 0; np->rip6_tag = 0; np->rip6_metric = NEXTHOP_METRIC; nh = &rrt->rrt_gw; np++; nrt++; } } else if (nh && (rrt->rrt_index != ifcp->ifc_index || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) || rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) { /* Reset nexthop */ if (nrt == maxrte - 2) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = NEXTHOP_METRIC; nh = NULL; np++; nrt++; } /* Put the route to the buffer */ *np = rrt->rrt_info; np++; nrt++; if (nrt == maxrte) { ripflush(ifcp, sin6, nrt, np); nh = NULL; nrt = 0; np = ripbuf->rip6_nets; } } if (nrt) /* Send last packet */ ripflush(ifcp, sin6, nrt, np); } /* * outbound filter logic, per-route/interface. */ static int out_filter(struct riprt *rrt, struct ifc *ifcp) { struct iff *iffp; struct in6_addr ia; int ok; /* * -A: filter out less specific routes, if we have aggregated * route configured. */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen <= iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) return 0; } /* * if it is an aggregated route, advertise it only to the * interfaces specified on -A. */ if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'A') continue; if (rrt->rrt_info.rip6_plen == iffp->iff_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* * -O: advertise only if prefix matches the configured prefix. */ if (iff_find(ifcp, IFIL_TYPE_O) != NULL) { ok = 0; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != 'O') continue; if (rrt->rrt_info.rip6_plen < iffp->iff_plen) continue; ia = rrt->rrt_info.rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) return 0; } /* the prefix should be advertised */ return 1; } /* * Determine if the route is to be advertised on the specified interface. * It checks options specified in the arguments and the split horizon rule. */ static int tobeadv(struct riprt *rrt, struct ifc *ifcp) { /* Special care for static routes */ if (rrt->rrt_flags & RTF_STATIC) { /* XXX don't advertise reject/blackhole routes */ if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE)) return 0; if (Sflag) /* Yes, advertise it anyway */ return 1; if (sflag && rrt->rrt_index != ifcp->ifc_index) return 1; return 0; } /* Regular split horizon */ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index) return 0; return 1; } /* * Send a rip packet actually. */ static int sendpacket(struct sockaddr_in6 *sin6, int len) { struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; struct in6_pktinfo *pi; u_char cmsgbuf[256]; int idx; struct sockaddr_in6 sincopy; /* do not overwrite the given sin */ sincopy = *sin6; sin6 = &sincopy; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) idx = sin6->sin6_scope_id; else idx = 0; m.msg_name = (caddr_t)sin6; m.msg_namelen = sizeof(*sin6); iov[0].iov_base = (caddr_t)ripbuf; iov[0].iov_len = len; m.msg_iov = iov; m.msg_iovlen = 1; m.msg_flags = 0; if (!idx) { m.msg_control = NULL; m.msg_controllen = 0; } else { memset(cmsgbuf, 0, sizeof(cmsgbuf)); cm = (struct cmsghdr *)(void *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cm->cmsg_level = IPPROTO_IPV6; cm->cmsg_type = IPV6_PKTINFO; pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/ pi->ipi6_ifindex = idx; } if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) { trace(1, "sendmsg: %s\n", strerror(errno)); return errno; } return 0; } /* * Receive and process RIP packets. Update the routes/kernel forwarding * table if necessary. */ static void riprecv(void) { struct ifc *ifcp, *ic; struct sockaddr_in6 fsock; struct in6_addr nh; /* next hop */ struct rip6 *rp; struct netinfo6 *np, *nq; struct riprt *rrt; ssize_t len, nn; unsigned int need_trigger, idx; char buf[4 * RIP6_MAXMTU]; time_t t; struct msghdr m; struct cmsghdr *cm; struct iovec iov[2]; u_char cmsgbuf[256]; struct in6_pktinfo *pi = NULL; int *hlimp = NULL; struct iff *iffp; struct in6_addr ia; int ok; time_t t_half_lifetime; need_trigger = 0; m.msg_name = (caddr_t)&fsock; m.msg_namelen = sizeof(fsock); iov[0].iov_base = (caddr_t)buf; iov[0].iov_len = sizeof(buf); m.msg_iov = iov; m.msg_iovlen = 1; cm = (struct cmsghdr *)(void *)cmsgbuf; m.msg_control = (caddr_t)cm; m.msg_controllen = sizeof(cmsgbuf); m.msg_flags = 0; if ((len = recvmsg(ripsock, &m, 0)) < 0) { fatal("recvmsg"); /*NOTREACHED*/ } idx = 0; for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { if (cm->cmsg_level != IPPROTO_IPV6) continue; switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(*pi))) { trace(1, "invalid cmsg length for IPV6_PKTINFO\n"); return; } pi = (struct in6_pktinfo *)(void *)CMSG_DATA(cm); idx = pi->ipi6_ifindex; break; case IPV6_HOPLIMIT: if (cm->cmsg_len != CMSG_LEN(sizeof(int))) { trace(1, "invalid cmsg length for IPV6_HOPLIMIT\n"); return; } hlimp = (int *)(void *)CMSG_DATA(cm); break; } } if ((size_t)len < sizeof(struct rip6)) { trace(1, "Packet too short\n"); return; } if (pi == NULL || hlimp == NULL) { /* * This can happen when the kernel failed to allocate memory * for the ancillary data. Although we might be able to handle * some cases without this info, those are minor and not so * important, so it's better to discard the packet for safer * operation. */ trace(1, "IPv6 packet information cannot be retrieved\n"); return; } nh = fsock.sin6_addr; nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) / sizeof(struct netinfo6); rp = (struct rip6 *)(void *)buf; np = rp->rip6_nets; if (rp->rip6_vers != RIP6_VERSION) { trace(1, "Incorrect RIP version %d\n", rp->rip6_vers); return; } if (rp->rip6_cmd == RIP6_REQUEST) { if (idx && idx < nindex2ifc) { ifcp = index2ifc[idx]; riprequest(ifcp, np, nn, &fsock); } else { riprequest(NULL, np, nn, &fsock); } return; } if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) { trace(1, "Response from non-ll addr: %s\n", inet6_n2p(&fsock.sin6_addr)); return; /* Ignore packets from non-link-local addr */ } if (ntohs(fsock.sin6_port) != RIP6_PORT) { trace(1, "Response from non-rip port from %s\n", inet6_n2p(&fsock.sin6_addr)); return; } if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet with a smaller hop limit (%d) from %s\n", *hlimp, inet6_n2p(&fsock.sin6_addr)); return; } /* * Further validation: since this program does not send off-link * requests, an incoming response must always come from an on-link * node. Although this is normally ensured by the source address * check above, it may not 100% be safe because there are router * implementations that (invalidly) allow a packet with a link-local * source address to be forwarded to a different link. * So we also check whether the destination address is a link-local * address or the hop limit is 255. Note that RFC2080 does not require * the specific hop limit for a unicast response, so we cannot assume * the limitation. */ if (!IN6_IS_ADDR_LINKLOCAL(&pi->ipi6_addr) && *hlimp != 255) { trace(1, "Response packet possibly from an off-link node: " "from %s to %s hlim=%d\n", inet6_n2p(&fsock.sin6_addr), inet6_n2p(&pi->ipi6_addr), *hlimp); return; } idx = fsock.sin6_scope_id; ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL; if (!ifcp) { trace(1, "Packets to unknown interface index %d\n", idx); return; /* Ignore it */ } if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr)) return; /* The packet is from me; ignore */ if (rp->rip6_cmd != RIP6_RESPONSE) { trace(1, "Invalid command %d\n", rp->rip6_cmd); return; } /* -N: no use */ if (iff_find(ifcp, IFIL_TYPE_N) != NULL) return; tracet(1, "Recv(%s): from %s.%d info(%zd)\n", ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn); t = time(NULL); t_half_lifetime = t - (RIP_LIFETIME/2); for (; nn; nn--, np++) { if (np->rip6_metric == NEXTHOP_METRIC) { /* modify neighbor address */ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { nh = np->rip6_dest; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) { nh = fsock.sin6_addr; trace(1, "\tNexthop: %s\n", inet6_n2p(&nh)); } else { nh = fsock.sin6_addr; trace(1, "\tInvalid Nexthop: %s\n", inet6_n2p(&np->rip6_dest)); } continue; } if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) { trace(1, "\tMulticast netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) { trace(1, "\tLoopback netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) { trace(1, "\tLink Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } /* may need to pass sitelocal prefix in some case, however*/ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) { trace(1, "\tSite Local netinfo6: %s/%d [%d]\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); continue; } trace(2, "\tnetinfo6: %s/%d [%d]", inet6_n2p(&np->rip6_dest), np->rip6_plen, np->rip6_metric); if (np->rip6_tag) trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff); if (dflag >= 2) { ia = np->rip6_dest; applyplen(&ia, np->rip6_plen); if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest)) trace(2, " [junk outside prefix]"); } /* * -L: listen only if the prefix matches the configuration */ ok = 1; /* if there's no L filter, it is ok */ TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (iffp->iff_type != IFIL_TYPE_L) continue; ok = 0; if (np->rip6_plen < iffp->iff_plen) continue; /* special rule: ::/0 means default, not "in /0" */ if (iffp->iff_plen == 0 && np->rip6_plen > 0) continue; ia = np->rip6_dest; applyplen(&ia, iffp->iff_plen); if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) { ok = 1; break; } } if (!ok) { trace(2, " (filtered)\n"); continue; } trace(2, "\n"); np->rip6_metric++; np->rip6_metric += ifcp->ifc_metric; if (np->rip6_metric > HOPCNT_INFINITY6) np->rip6_metric = HOPCNT_INFINITY6; applyplen(&np->rip6_dest, np->rip6_plen); if ((rrt = rtsearch(np)) != NULL) { if (rrt->rrt_t == 0) continue; /* Intf route has priority */ nq = &rrt->rrt_info; if (nq->rip6_metric > np->rip6_metric) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Small metric from the same gateway */ nq->rip6_metric = np->rip6_metric; } else { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); } rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; need_trigger = 1; } else if (nq->rip6_metric < np->rip6_metric && rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* Got worse route from same gw */ nq->rip6_metric = np->rip6_metric; rrt->rrt_t = t; rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } else if (nq->rip6_metric == np->rip6_metric && np->rip6_metric < HOPCNT_INFINITY6) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) { /* same metric, same route from same gw */ rrt->rrt_t = t; } else if (rrt->rrt_t < t_half_lifetime) { /* Better route found */ rrt->rrt_index = ifcp->ifc_index; /* Update routing table */ delroute(nq, &rrt->rrt_gw); rrt->rrt_gw = nh; *nq = *np; addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; rrt->rrt_t = t; } } /* * if nq->rip6_metric == HOPCNT_INFINITY6 then * do not update age value. Do nothing. */ } else if (np->rip6_metric < HOPCNT_INFINITY6) { /* Got a new valid route */ if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); nq = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_flags = RTF_UP|RTF_GATEWAY; rrt->rrt_gw = nh; *nq = *np; applyplen(&nq->rip6_dest, nq->rip6_plen); if (nq->rip6_plen == sizeof(struct in6_addr) * 8) rrt->rrt_flags |= RTF_HOST; /* Update routing table */ addroute(rrt, &nh, ifcp); rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; rrt->rrt_t = t; /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } } /* XXX need to care the interval between triggered updates */ if (need_trigger) { if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } } /* * Send all routes request packet to the specified interface. */ static void sendrequest(struct ifc *ifcp) { struct netinfo6 *np; int error; if (ifcp->ifc_flags & IFF_LOOPBACK) return; ripbuf->rip6_cmd = RIP6_REQUEST; np = ripbuf->rip6_nets; memset(np, 0, sizeof(struct netinfo6)); np->rip6_metric = HOPCNT_INFINITY6; tracet(1, "Send rtdump Request to %s (%s)\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1)); if (error == EAFNOSUPPORT) { /* Protocol not supported */ tracet(1, "Could not send rtdump Request to %s (%s): " "set IFF_UP to 0\n", ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr)); ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */ } ripbuf->rip6_cmd = RIP6_RESPONSE; } /* * Process a RIP6_REQUEST packet. */ static void riprequest(struct ifc *ifcp, struct netinfo6 *np, int nn, struct sockaddr_in6 *sin6) { int i; struct riprt *rrt; if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) && np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) { /* Specific response, don't split-horizon */ trace(1, "\tRIP Request\n"); for (i = 0; i < nn; i++, np++) { rrt = rtsearch(np); if (rrt) np->rip6_metric = rrt->rrt_info.rip6_metric; else np->rip6_metric = HOPCNT_INFINITY6; } (void)sendpacket(sin6, RIPSIZE(nn)); return; } /* Whole routing table dump */ trace(1, "\tRIP Request -- whole routing table\n"); ripsend(ifcp, sin6, RRTF_SENDANYWAY); } /* * Get information of each interface. */ static void ifconfig(void) { struct ifaddrs *ifap, *ifa; struct ifc *ifcp; struct ipv6_mreq mreq; int s; if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { fatal("socket"); /*NOTREACHED*/ } if (getifaddrs(&ifap) != 0) { fatal("getifaddrs"); /*NOTREACHED*/ } for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifcp = ifc_find(ifa->ifa_name); /* we are interested in multicast-capable interfaces */ if ((ifa->ifa_flags & IFF_MULTICAST) == 0) continue; if (!ifcp) { /* new interface */ if ((ifcp = MALLOC(struct ifc)) == NULL) { fatal("malloc: struct ifc"); /*NOTREACHED*/ } memset(ifcp, 0, sizeof(*ifcp)); ifcp->ifc_index = -1; strlcpy(ifcp->ifc_name, ifa->ifa_name, sizeof(ifcp->ifc_name)); TAILQ_INIT(&ifcp->ifc_ifac_head); TAILQ_INIT(&ifcp->ifc_iff_head); ifcp->ifc_flags = ifa->ifa_flags; TAILQ_INSERT_HEAD(&ifc_head, ifcp, ifc_next); trace(1, "newif %s <%s>\n", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); if (!strcmp(ifcp->ifc_name, LOOPBACK_IF)) loopifcp = ifcp; } else { /* update flag, this may be up again */ if (ifcp->ifc_flags != ifa->ifa_flags) { trace(1, "%s: <%s> -> ", ifcp->ifc_name, ifflags(ifcp->ifc_flags)); trace(1, "<%s>\n", ifflags(ifa->ifa_flags)); ifcp->ifc_cflags |= IFC_CHANGED; } ifcp->ifc_flags = ifa->ifa_flags; } if (ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s) < 0) { /* maybe temporary failure */ continue; } if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP && 0 < ifcp->ifc_index && !ifcp->ifc_joined) { mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr; mreq.ipv6mr_interface = ifcp->ifc_index; if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { fatal("IPV6_JOIN_GROUP"); /*NOTREACHED*/ } trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST); ifcp->ifc_joined++; } } close(s); freeifaddrs(ifap); } static int ifconfig1(const char *name, const struct sockaddr *sa, struct ifc *ifcp, int s) { struct in6_ifreq ifr; const struct sockaddr_in6 *sin6; struct ifac *ifac; int plen; char buf[BUFSIZ]; sin6 = (const struct sockaddr_in6 *)(const void *)sa; if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !lflag) return (-1); ifr.ifr_addr = *sin6; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) { syslog(LOG_INFO, "ioctl: SIOCGIFNETMASK_IN6"); return (-1); } plen = sin6mask2len(&ifr.ifr_addr); if ((ifac = ifa_match(ifcp, &sin6->sin6_addr, plen)) != NULL) { /* same interface found */ /* need check if something changed */ /* XXX not yet implemented */ return (-1); } /* * New address is found */ if ((ifac = MALLOC(struct ifac)) == NULL) { fatal("malloc: struct ifac"); /*NOTREACHED*/ } memset(ifac, 0, sizeof(*ifac)); ifac->ifac_ifc = ifcp; ifac->ifac_addr = sin6->sin6_addr; ifac->ifac_plen = plen; ifac->ifac_scope_id = sin6->sin6_scope_id; if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifr.ifr_addr = *sin6; if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFDSTADDR_IN6"); /*NOTREACHED*/ } ifac->ifac_raddr = ifr.ifr_dstaddr.sin6_addr; inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); trace(1, "found address %s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { trace(1, "found address %s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { ifcp->ifc_mylladdr = ifac->ifac_addr; ifcp->ifc_index = ifac->ifac_scope_id; memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len); ifcp->ifc_ripsin.sin6_scope_id = ifcp->ifc_index; setindex2ifc(ifcp->ifc_index, ifcp); ifcp->ifc_mtu = getifmtu(ifcp->ifc_index); if (ifcp->ifc_mtu > RIP6_MAXMTU) ifcp->ifc_mtu = RIP6_MAXMTU; if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) { fatal("ioctl: SIOCGIFMETRIC"); /*NOTREACHED*/ } ifcp->ifc_metric = ifr.ifr_metric; trace(1, "\tindex: %d, mtu: %d, metric: %d\n", ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric); } else ifcp->ifc_cflags |= IFC_CHANGED; TAILQ_INSERT_HEAD(&ifcp->ifc_ifac_head, ifac, ifac_next); return 0; } static void ifremove(int ifindex) { struct ifc *ifcp; struct riprt *rrt; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_index == ifindex) break; } if (ifcp == NULL) return; tracet(1, "ifremove: %s is departed.\n", ifcp->ifc_name); TAILQ_REMOVE(&ifc_head, ifcp, ifc_next); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_index == ifcp->ifc_index && rrt->rrt_rflags & RRTF_AGGREGATE) delroute(&rrt->rrt_info, &rrt->rrt_gw); } free(ifcp); } /* * Receive and process routing messages. * Update interface information as necesssary. */ static void rtrecv(void) { char buf[BUFSIZ]; char *p, *q = NULL; struct rt_msghdr *rtm; struct ifa_msghdr *ifam; struct if_msghdr *ifm; struct if_announcemsghdr *ifan; int len; struct ifc *ifcp, *ic; int iface = 0, rtable = 0; struct sockaddr_in6 *rta[RTAX_MAX]; struct sockaddr_in6 mask; int i, addrs = 0; struct riprt *rrt; if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } if (len == 0) return; #if 0 if (len < sizeof(*rtm)) { trace(1, "short read from rtsock: %d (should be > %lu)\n", len, (u_long)sizeof(*rtm)); return; } #endif if (dflag >= 2) { fprintf(stderr, "rtmsg:\n"); for (i = 0; i < len; i++) { fprintf(stderr, "%02x ", buf[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } for (p = buf; p - buf < len; p += ((struct rt_msghdr *)(void *)p)->rtm_msglen) { if (((struct rt_msghdr *)(void *)p)->rtm_version != RTM_VERSION) continue; /* safety against bogus message */ if (((struct rt_msghdr *)(void *)p)->rtm_msglen <= 0) { trace(1, "bogus rtmsg: length=%d\n", ((struct rt_msghdr *)(void *)p)->rtm_msglen); break; } rtm = NULL; ifam = NULL; ifm = NULL; switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: ifam = (struct ifa_msghdr *)(void *)p; addrs = ifam->ifam_addrs; q = (char *)(ifam + 1); break; case RTM_IFINFO: ifm = (struct if_msghdr *)(void *)p; addrs = ifm->ifm_addrs; q = (char *)(ifm + 1); break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)(void *)p; switch (ifan->ifan_what) { case IFAN_ARRIVAL: iface++; break; case IFAN_DEPARTURE: ifremove(ifan->ifan_index); iface++; break; } break; default: rtm = (struct rt_msghdr *)(void *)p; - addrs = rtm->rtm_addrs; - q = (char *)(rtm + 1); if (rtm->rtm_version != RTM_VERSION) { trace(1, "unexpected rtmsg version %d " "(should be %d)\n", rtm->rtm_version, RTM_VERSION); continue; } + /* + * Only messages that use the struct rt_msghdr + * format are allowed beyond this point. + */ + if (rtm->rtm_type > RTM_RESOLVE) { + trace(1, "rtmsg type %d ignored\n", + rtm->rtm_type); + continue; + } + addrs = rtm->rtm_addrs; + q = (char *)(rtm + 1); if (rtm->rtm_pid == pid) { #if 0 trace(1, "rtmsg looped back to me, ignored\n"); #endif continue; } break; } memset(&rta, 0, sizeof(rta)); for (i = 0; i < RTAX_MAX; i++) { if (addrs & (1 << i)) { rta[i] = (struct sockaddr_in6 *)(void *)q; q += ROUNDUP(rta[i]->sin6_len); } } trace(1, "rtsock: %s (addrs=%x)\n", rttypes((struct rt_msghdr *)(void *)p), addrs); if (dflag >= 2) { for (i = 0; i < ((struct rt_msghdr *)(void *)p)->rtm_msglen; i++) { fprintf(stderr, "%02x ", p[i] & 0xff); if (i % 16 == 15) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } /* * Easy ones first. * * We may be able to optimize by using ifm->ifm_index or * ifam->ifam_index. For simplicity we don't do that here. */ switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: iface++; continue; case RTM_ADD: rtable++; continue; case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* nothing to be done here */ trace(1, "\tnothing to be done, ignored\n"); continue; } #if 0 if (rta[RTAX_DST] == NULL) { trace(1, "\tno destination, ignored\n"); continue; } if (rta[RTAX_DST]->sin6_family != AF_INET6) { trace(1, "\taf mismatch, ignored\n"); continue; } if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tlinklocal destination, ignored\n"); continue; } if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) { trace(1, "\tloopback destination, ignored\n"); continue; /* Loopback */ } if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) { trace(1, "\tmulticast destination, ignored\n"); continue; } #endif /* hard ones */ switch (((struct rt_msghdr *)(void *)p)->rtm_type) { case RTM_NEWADDR: case RTM_IFINFO: case RTM_ADD: case RTM_LOSING: case RTM_MISS: case RTM_GET: case RTM_LOCK: /* should already be handled */ fatal("rtrecv: never reach here"); /*NOTREACHED*/ case RTM_DELETE: if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if ((rtm->rtm_flags & RTF_HOST) != 0) { mask.sin6_len = sizeof(mask); memset(&mask.sin6_addr, 0xff, sizeof(mask.sin6_addr)); rta[RTAX_NETMASK] = &mask; } else if (!rta[RTAX_NETMASK]) { trace(1, "\tsome of dst/gw/netamsk are " "unavailable, ignored\n"); break; } if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY], rta[RTAX_NETMASK]) == 0) { rtable++; /*just to be sure*/ } break; case RTM_CHANGE: case RTM_REDIRECT: trace(1, "\tnot supported yet, ignored\n"); break; case RTM_DELADDR: if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) { trace(1, "\tno netmask or ifa given, ignored\n"); break; } if (ifam->ifam_index < nindex2ifc) ifcp = index2ifc[ifam->ifam_index]; else ifcp = NULL; if (!ifcp) { trace(1, "\tinvalid ifam_index %d, ignored\n", ifam->ifam_index); break; } if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK])) iface++; break; } } if (iface) { trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n"); ifconfig(); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (ifcp->ifc_cflags & IFC_CHANGED) { if (ifrt(ifcp, 1)) { TAILQ_FOREACH(ic, &ifc_head, ifc_next) { if (ifcp->ifc_index == ic->ifc_index) continue; if (ic->ifc_flags & IFF_UP) ripsend(ic, &ic->ifc_ripsin, RRTF_CHANGED); } /* Reset the flag */ TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { rrt->rrt_rflags &= ~RRTF_CHANGED; } } ifcp->ifc_cflags &= ~IFC_CHANGED; } } } if (rtable) { trace(1, "rtsock: read routing table again\n"); krtread(1); } } /* * remove specified route from the internal routing table. */ static int rt_del(const struct sockaddr_in6 *sdst, const struct sockaddr_in6 *sgw, const struct sockaddr_in6 *smask) { const struct in6_addr *dst = NULL; const struct in6_addr *gw = NULL; int prefix; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; if (sdst->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr) || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback) || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) { trace(1, "\taddress %s not interesting, ignored\n", inet6_n2p(&sdst->sin6_addr)); return -1; } dst = &sdst->sin6_addr; if (sgw->sin6_family == AF_INET6) { /* easy case */ gw = &sgw->sin6_addr; prefix = sin6mask2len(smask); } else if (sgw->sin6_family == AF_LINK) { /* * Interface route... a hard case. We need to get the prefix * length from the kernel, but we now are parsing rtmsg. * We'll purge matching routes from my list, then get the * fresh list. */ struct riprt *longest; trace(1, "\t%s is an interface route, guessing prefixlen\n", inet6_n2p(dst)); longest = NULL; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &sdst->sin6_addr) && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) { if (!longest || longest->rrt_info.rip6_plen < rrt->rrt_info.rip6_plen) { longest = rrt; } } } rrt = longest; if (!rrt) { trace(1, "\tno matching interface route found\n"); return -1; } gw = &in6addr_loopback; prefix = rrt->rrt_info.rip6_plen; } else { trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family); return -1; } trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix); trace(1, "gw %s\n", inet6_n2p(gw)); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = *dst; ni6.rip6_plen = prefix; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen); if (!rrt && (rrt = rtsearch(&ni6)) == NULL) { trace(1, "\tno route found\n"); return -1; } #if 0 if ((rrt->rrt_flags & RTF_STATIC) == 0) { trace(1, "\tyou can delete static routes only\n"); } else #endif if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) { trace(1, "\tgw mismatch: %s <-> ", inet6_n2p(&rrt->rrt_gw)); trace(1, "%s\n", inet6_n2p(gw)); } else { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } } return 0; } /* * remove specified address from internal interface/routing table. */ static int rt_deladdr(struct ifc *ifcp, const struct sockaddr_in6 *sifa, const struct sockaddr_in6 *smask) { const struct in6_addr *addr = NULL; int prefix; struct ifac *ifac = NULL; struct netinfo6 ni6; struct riprt *rrt = NULL; time_t t_lifetime; int updated = 0; if (sifa->sin6_family != AF_INET6) { trace(1, "\tother AF, ignored\n"); return -1; } addr = &sifa->sin6_addr; prefix = sin6mask2len(smask); trace(1, "\tdeleting %s/%d from %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); ifac = ifa_match(ifcp, addr, prefix); if (!ifac) { trace(1, "\tno matching ifa found for %s/%d on %s\n", inet6_n2p(addr), prefix, ifcp->ifc_name); return -1; } if (ifac->ifac_ifc != ifcp) { trace(1, "\taddress table corrupt: back pointer does not match " "(%s != %s)\n", ifcp->ifc_name, ifac->ifac_ifc->ifc_name); return -1; } TAILQ_REMOVE(&ifcp->ifc_ifac_head, ifac, ifac_next); t_lifetime = time(NULL) - RIP_LIFETIME; /* age route for interface address */ memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_addr; ni6.rip6_plen = ifac->ifac_plen; applyplen(&ni6.rip6_dest, ni6.rip6_plen); trace(1, "\tfind interface route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { struct in6_addr none; memset(&none, 0, sizeof(none)); if (rrt->rrt_index == ifcp->ifc_index && (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) || IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; } updated++; } else { trace(1, "\tnon-interface route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno interface route found\n"); /* age route for p2p destination */ if (ifcp->ifc_flags & IFF_POINTOPOINT) { memset(&ni6, 0, sizeof(ni6)); ni6.rip6_dest = ifac->ifac_raddr; ni6.rip6_plen = 128; applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/ trace(1, "\tfind p2p route %s/%d on %d\n", inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index); if ((rrt = rtsearch(&ni6)) != NULL) { if (rrt->rrt_index == ifcp->ifc_index && IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifac->ifac_addr)) { trace(1, "\troute found, age it\n"); if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) { rrt->rrt_t = t_lifetime; rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; updated++; } } else { trace(1, "\tnon-p2p route found: %s/%d on %d\n", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen, rrt->rrt_index); } } else trace(1, "\tno p2p route found\n"); } free(ifac); return ((updated) ? 0 : -1); } /* * Get each interface address and put those interface routes to the route * list. */ static int ifrt(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt = NULL, *search_rrt, *loop_rrt; struct netinfo6 *np; time_t t_lifetime; int need_trigger = 0; #if 0 if (ifcp->ifc_flags & IFF_LOOPBACK) return 0; /* ignore loopback */ #endif if (ifcp->ifc_flags & IFF_POINTOPOINT) { ifrt_p2p(ifcp, again); return 0; } TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_IS_ADDR_LINKLOCAL(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s on %s: " "skip linklocal interface address\n", inet6_n2p(&ifac->ifac_addr), ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip unspec interface address\n", ifcp->ifc_name); #endif continue; } if (IN6_IS_ADDR_LOOPBACK(&ifac->ifac_addr)) { #if 0 trace(1, "route: %s: skip loopback address\n", ifcp->ifc_name); #endif continue; } if (ifcp->ifc_flags & IFF_UP) { if ((rrt = MALLOC(struct riprt)) == NULL) fatal("malloc: struct riprt"); memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; rrt->rrt_info.rip6_plen = ifac->ifac_plen; rrt->rrt_flags = RTF_HOST; rrt->rrt_rflags |= RRTF_CHANGED; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); rrt->rrt_gw = ifac->ifac_addr; np = &rrt->rrt_info; search_rrt = rtsearch(np); if (search_rrt != NULL) { if (search_rrt->rrt_info.rip6_metric <= rrt->rrt_info.rip6_metric) { /* Already have better route */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); } goto next; } TAILQ_REMOVE(&riprt_head, rrt, rrt_next); delroute(&rrt->rrt_info, &rrt->rrt_gw); } /* Attach the route to the list */ trace(1, "route: %s/%d: register route (%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, ifcp->ifc_name); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); addroute(rrt, &rrt->rrt_gw, ifcp); rrt = NULL; sendrequest(ifcp); ripsend(ifcp, &ifcp->ifc_ripsin, 0); need_trigger = 1; } else { TAILQ_FOREACH(loop_rrt, &riprt_head, rrt_next) { if (loop_rrt->rrt_index == ifcp->ifc_index) { t_lifetime = time(NULL) - RIP_LIFETIME; if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) { loop_rrt->rrt_t = t_lifetime; loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6; loop_rrt->rrt_rflags |= RRTF_CHANGED; need_trigger = 1; } } } } next: if (rrt) free(rrt); } return need_trigger; } /* * there are couple of p2p interface routing models. "behavior" lets * you pick one. it looks that gated behavior fits best with BSDs, * since BSD kernels do not look at prefix length on p2p interfaces. */ static void ifrt_p2p(struct ifc *ifcp, int again) { struct ifac *ifac; struct riprt *rrt, *orrt; struct netinfo6 *np; struct in6_addr addr, dest; int advert, ignore, i; #define P2PADVERT_NETWORK 1 #define P2PADVERT_ADDR 2 #define P2PADVERT_DEST 4 #define P2PADVERT_MAX 4 const enum { CISCO, GATED, ROUTE6D } behavior = GATED; const char *category = ""; const char *noadv; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { addr = ifac->ifac_addr; dest = ifac->ifac_raddr; applyplen(&addr, ifac->ifac_plen); applyplen(&dest, ifac->ifac_plen); advert = ignore = 0; switch (behavior) { case CISCO: /* * honor addr/plen, just like normal shared medium * interface. this may cause trouble if you reuse * addr/plen on other interfaces. * * advertise addr/plen. */ advert |= P2PADVERT_NETWORK; break; case GATED: /* * prefixlen on p2p interface is meaningless. * advertise addr/128 and dest/128. * * do not install network route to route6d routing * table (if we do, it would prevent route installation * for other p2p interface that shares addr/plen). * * XXX what should we do if dest is ::? it will not * get announced anyways (see following filter), * but we need to think. */ advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; break; case ROUTE6D: /* * just for testing. actually the code is redundant * given the current p2p interface address assignment * rule for kame kernel. * * intent: * A/n -> announce A/n * A B/n, A and B share prefix -> A/n (= B/n) * A B/n, do not share prefix -> A/128 and B/128 * actually, A/64 and A B/128 are the only cases * permitted by the kernel: * A/64 -> A/64 * A B/128 -> A/128 and B/128 */ if (!IN6_IS_ADDR_UNSPECIFIED(&ifac->ifac_raddr)) { if (IN6_ARE_ADDR_EQUAL(&addr, &dest)) advert |= P2PADVERT_NETWORK; else { advert |= P2PADVERT_ADDR; advert |= P2PADVERT_DEST; ignore |= P2PADVERT_NETWORK; } } else advert |= P2PADVERT_NETWORK; break; } for (i = 1; i <= P2PADVERT_MAX; i *= 2) { if ((ignore & i) != 0) continue; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); rrt->rrt_same = NULL; rrt->rrt_index = ifcp->ifc_index; rrt->rrt_t = 0; /* don't age */ switch (i) { case P2PADVERT_NETWORK: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = ifac->ifac_plen; applyplen(&rrt->rrt_info.rip6_dest, ifac->ifac_plen); category = "network"; break; case P2PADVERT_ADDR: rrt->rrt_info.rip6_dest = ifac->ifac_addr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = in6addr_loopback; category = "addr"; break; case P2PADVERT_DEST: rrt->rrt_info.rip6_dest = ifac->ifac_raddr; rrt->rrt_info.rip6_plen = 128; rrt->rrt_gw = ifac->ifac_addr; category = "dest"; break; } if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) || IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) { #if 0 trace(1, "route: %s: skip unspec/linklocal " "(%s on %s)\n", category, ifcp->ifc_name); #endif free(rrt); continue; } if ((advert & i) == 0) { rrt->rrt_rflags |= RRTF_NOADVERTISE; noadv = ", NO-ADV"; } else noadv = ""; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric; np = &rrt->rrt_info; orrt = rtsearch(np); if (!orrt) { /* Attach the route to the list */ trace(1, "route: %s/%d: register route " "(%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } else if (rrt->rrt_index != orrt->rrt_index || rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) { /* replace route */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); free(orrt); trace(1, "route: %s/%d: update (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } else { /* Already found */ if (!again) { trace(1, "route: %s/%d: " "already registered (%s on %s%s)\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, category, ifcp->ifc_name, noadv); } free(rrt); } } } #undef P2PADVERT_NETWORK #undef P2PADVERT_ADDR #undef P2PADVERT_DEST #undef P2PADVERT_MAX } static int getifmtu(int ifindex) { int mib[6]; char *buf; size_t msize; struct if_msghdr *ifm; int mtu; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; mib[4] = NET_RT_IFLIST; mib[5] = ifindex; if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { fatal("sysctl estimate NET_RT_IFLIST"); /*NOTREACHED*/ } if ((buf = malloc(msize)) == NULL) { fatal("malloc"); /*NOTREACHED*/ } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { fatal("sysctl NET_RT_IFLIST"); /*NOTREACHED*/ } ifm = (struct if_msghdr *)(void *)buf; mtu = ifm->ifm_data.ifi_mtu; if (ifindex != ifm->ifm_index) { fatal("ifindex does not match with ifm_index"); /*NOTREACHED*/ } free(buf); return mtu; } static const char * rttypes(struct rt_msghdr *rtm) { #define RTTYPE(s, f) \ do { \ if (rtm->rtm_type == (f)) \ return (s); \ } while (0) RTTYPE("ADD", RTM_ADD); RTTYPE("DELETE", RTM_DELETE); RTTYPE("CHANGE", RTM_CHANGE); RTTYPE("GET", RTM_GET); RTTYPE("LOSING", RTM_LOSING); RTTYPE("REDIRECT", RTM_REDIRECT); RTTYPE("MISS", RTM_MISS); RTTYPE("LOCK", RTM_LOCK); RTTYPE("NEWADDR", RTM_NEWADDR); RTTYPE("DELADDR", RTM_DELADDR); RTTYPE("IFINFO", RTM_IFINFO); #ifdef RTM_OIFINFO RTTYPE("OIFINFO", RTM_OIFINFO); #endif #ifdef RTM_IFANNOUNCE RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE); #endif #ifdef RTM_NEWMADDR RTTYPE("NEWMADDR", RTM_NEWMADDR); #endif #ifdef RTM_DELMADDR RTTYPE("DELMADDR", RTM_DELMADDR); #endif #undef RTTYPE return NULL; } static const char * rtflags(struct rt_msghdr *rtm) { static char buf[BUFSIZ]; /* * letter conflict should be okay. painful when *BSD diverges... */ strlcpy(buf, "", sizeof(buf)); #define RTFLAG(s, f) \ do { \ if (rtm->rtm_flags & (f)) \ strlcat(buf, (s), sizeof(buf)); \ } while (0) RTFLAG("U", RTF_UP); RTFLAG("G", RTF_GATEWAY); RTFLAG("H", RTF_HOST); RTFLAG("R", RTF_REJECT); RTFLAG("D", RTF_DYNAMIC); RTFLAG("M", RTF_MODIFIED); RTFLAG("d", RTF_DONE); #ifdef RTF_MASK RTFLAG("m", RTF_MASK); #endif #ifdef RTF_CLONED RTFLAG("c", RTF_CLONED); #endif RTFLAG("X", RTF_XRESOLVE); #ifdef RTF_LLINFO RTFLAG("L", RTF_LLINFO); #endif RTFLAG("S", RTF_STATIC); RTFLAG("B", RTF_BLACKHOLE); #ifdef RTF_PROTO3 RTFLAG("3", RTF_PROTO3); #endif RTFLAG("2", RTF_PROTO2); RTFLAG("1", RTF_PROTO1); #ifdef RTF_BROADCAST RTFLAG("b", RTF_BROADCAST); #endif #ifdef RTF_DEFAULT RTFLAG("d", RTF_DEFAULT); #endif #ifdef RTF_ISAROUTER RTFLAG("r", RTF_ISAROUTER); #endif #ifdef RTF_TUNNEL RTFLAG("T", RTF_TUNNEL); #endif #ifdef RTF_AUTH RTFLAG("A", RTF_AUTH); #endif #ifdef RTF_CRYPT RTFLAG("E", RTF_CRYPT); #endif #undef RTFLAG return buf; } static const char * ifflags(int flags) { static char buf[BUFSIZ]; strlcpy(buf, "", sizeof(buf)); #define IFFLAG(s, f) \ do { \ if (flags & (f)) { \ if (buf[0]) \ strlcat(buf, ",", sizeof(buf)); \ strlcat(buf, (s), sizeof(buf)); \ } \ } while (0) IFFLAG("UP", IFF_UP); IFFLAG("BROADCAST", IFF_BROADCAST); IFFLAG("DEBUG", IFF_DEBUG); IFFLAG("LOOPBACK", IFF_LOOPBACK); IFFLAG("POINTOPOINT", IFF_POINTOPOINT); #ifdef IFF_NOTRAILERS IFFLAG("NOTRAILERS", IFF_NOTRAILERS); #endif IFFLAG("RUNNING", IFF_RUNNING); IFFLAG("NOARP", IFF_NOARP); IFFLAG("PROMISC", IFF_PROMISC); IFFLAG("ALLMULTI", IFF_ALLMULTI); IFFLAG("OACTIVE", IFF_OACTIVE); IFFLAG("SIMPLEX", IFF_SIMPLEX); IFFLAG("LINK0", IFF_LINK0); IFFLAG("LINK1", IFF_LINK1); IFFLAG("LINK2", IFF_LINK2); IFFLAG("MULTICAST", IFF_MULTICAST); #undef IFFLAG return buf; } static void krtread(int again) { int mib[6]; size_t msize; char *buf, *p, *lim; struct rt_msghdr *rtm; int retry; const char *errmsg; retry = 0; buf = NULL; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET6; /* Address family */ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */ mib[5] = 0; /* No flags */ do { if (retry) sleep(1); retry++; errmsg = NULL; if (buf) { free(buf); buf = NULL; } if (sysctl(mib, nitems(mib), NULL, &msize, NULL, 0) < 0) { errmsg = "sysctl estimate"; continue; } if ((buf = malloc(msize)) == NULL) { errmsg = "malloc"; continue; } if (sysctl(mib, nitems(mib), buf, &msize, NULL, 0) < 0) { errmsg = "sysctl NET_RT_DUMP"; continue; } } while (retry < RT_DUMP_MAXRETRY && errmsg != NULL); if (errmsg) { fatal("%s (with %d retries, msize=%lu)", errmsg, retry, (u_long)msize); /*NOTREACHED*/ } else if (1 < retry) syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry); lim = buf + msize; for (p = buf; p < lim; p += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)(void *)p; rt_entry(rtm, again); } free(buf); } static void rt_entry(struct rt_msghdr *rtm, int again) { struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask; struct sockaddr_in6 *sin6_genmask, *sin6_ifp; char *rtmp, *ifname = NULL; struct riprt *rrt, *orrt; struct netinfo6 *np; int ifindex; sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0; if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags & (RTF_XRESOLVE|RTF_BLACKHOLE)) { return; /* not interested in the link route */ } /* do not look at cloned routes */ #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) return; #endif #ifdef RTF_CLONED if (rtm->rtm_flags & RTF_CLONED) return; #endif /* XXX: Ignore connected routes. */ if (!(rtm->rtm_flags & (RTF_GATEWAY|RTF_HOST|RTF_STATIC))) return; /* * do not look at dynamic routes. * netbsd/openbsd cloned routes have UGHD. */ if (rtm->rtm_flags & RTF_DYNAMIC) return; rtmp = (char *)(rtm + 1); /* Destination */ if ((rtm->rtm_addrs & RTA_DST) == 0) return; /* ignore routes without destination address */ sin6_dst = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_dst->sin6_len); if (rtm->rtm_addrs & RTA_GATEWAY) { sin6_gw = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_gw->sin6_len); } if (rtm->rtm_addrs & RTA_NETMASK) { sin6_mask = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_mask->sin6_len); } if (rtm->rtm_addrs & RTA_GENMASK) { sin6_genmask = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_genmask->sin6_len); } if (rtm->rtm_addrs & RTA_IFP) { sin6_ifp = (struct sockaddr_in6 *)(void *)rtmp; rtmp += ROUNDUP(sin6_ifp->sin6_len); } /* Destination */ if (sin6_dst->sin6_family != AF_INET6) return; if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr)) return; /* Link-local */ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback)) return; /* Loopback */ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr)) return; if ((rrt = MALLOC(struct riprt)) == NULL) { fatal("malloc: struct riprt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(*rrt)); np = &rrt->rrt_info; rrt->rrt_same = NULL; rrt->rrt_t = time(NULL); if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC)) rrt->rrt_t = 0; /* Don't age static routes */ if (rtm->rtm_flags & Pflag) rrt->rrt_t = 0; /* Don't age PROTO[123] routes */ if ((rtm->rtm_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST) rrt->rrt_t = 0; /* Don't age non-gateway host routes */ np->rip6_tag = 0; np->rip6_metric = rtm->rtm_rmx.rmx_hopcount; if (np->rip6_metric < 1) np->rip6_metric = 1; rrt->rrt_flags = rtm->rtm_flags; np->rip6_dest = sin6_dst->sin6_addr; /* Mask or plen */ if (rtm->rtm_flags & RTF_HOST) np->rip6_plen = 128; /* Host route */ else if (sin6_mask) np->rip6_plen = sin6mask2len(sin6_mask); else np->rip6_plen = 0; orrt = rtsearch(np); if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) { /* Already found */ if (!again) { trace(1, "route: %s/%d flags %s: already registered\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); } free(rrt); return; } /* Gateway */ if (!sin6_gw) memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); else { if (sin6_gw->sin6_family == AF_INET6) rrt->rrt_gw = sin6_gw->sin6_addr; else if (sin6_gw->sin6_family == AF_LINK) { /* XXX in case ppp link? */ rrt->rrt_gw = in6addr_loopback; } else memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr)); } trace(1, "route: %s/%d flags %s", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw)); /* Interface */ ifindex = rtm->rtm_index; if ((unsigned int)ifindex < nindex2ifc && index2ifc[ifindex]) ifname = index2ifc[ifindex]->ifc_name; else { trace(1, " not configured\n"); free(rrt); return; } trace(1, " if %s sock %d", ifname, ifindex); rrt->rrt_index = ifindex; trace(1, "\n"); /* Check gateway */ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) && !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw) && (rrt->rrt_flags & RTF_LOCAL) == 0) { trace(0, "***** Gateway %s is not a link-local address.\n", inet6_n2p(&rrt->rrt_gw)); trace(0, "***** dest(%s) if(%s) -- Not optimized.\n", inet6_n2p(&rrt->rrt_info.rip6_dest), ifname); rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR; } /* Put it to the route list */ if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) { /* replace route list */ TAILQ_INSERT_BEFORE(orrt, rrt, rrt_next); TAILQ_REMOVE(&riprt_head, orrt, rrt_next); trace(1, "route: %s/%d flags %s: replace new route\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm)); free(orrt); } else TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); } static int addroute(struct riprt *rrt, const struct in6_addr *gw, struct ifc *ifcp) { struct netinfo6 *np; u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; np = &rrt->rrt_info; inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1)); inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2)); tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (rtlog) fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1, np->rip6_metric - 1, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)(void *)buf; rtm->rtm_type = RTM_ADD; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = rrt->rrt_flags; rtm->rtm_flags |= Qflag; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1; rtm->rtm_inits = RTV_HOPCOUNT; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) sin6->sin6_scope_id = ifcp->ifc_index; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) > 0) return 0; if (errno == EEXIST) { trace(0, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); if (rtlog) fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1); } else { trace(0, "Can not write to rtsock (addroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } static int delroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ], buf2[BUFSIZ]; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; int len; inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2)); tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "%s: DEL: %s/%d gw %s\n", hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (nflag) return 0; memset(buf, 0, sizeof(buf)); rtm = (struct rt_msghdr *)(void *)buf; rtm->rtm_type = RTM_DELETE; rtm->rtm_version = RTM_VERSION; rtm->rtm_seq = ++seq; rtm->rtm_pid = pid; rtm->rtm_flags = RTF_UP | RTF_GATEWAY; rtm->rtm_flags |= Qflag; if (np->rip6_plen == sizeof(struct in6_addr) * 8) rtm->rtm_flags |= RTF_HOST; rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; /* Destination */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Gateway */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *gw; sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); /* Netmask */ sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = *(plen2mask(np->rip6_plen)); sin6 = (struct sockaddr_in6 *)(void *)((char *)sin6 + ROUNDUP(sin6->sin6_len)); len = (char *)sin6 - (char *)buf; rtm->rtm_msglen = len; if (write(rtsock, buf, len) >= 0) return 0; if (errno == ESRCH) { trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); if (rtlog) fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2); } else { trace(0, "Can not write to rtsock (delroute): %s\n", strerror(errno)); if (rtlog) fprintf(rtlog, "\tCan not write to rtsock: %s\n", strerror(errno)); } return -1; } #if 0 static struct in6_addr * getroute(struct netinfo6 *np, struct in6_addr *gw) { u_char buf[BUFSIZ]; int myseq; int len; struct rt_msghdr *rtm; struct sockaddr_in6 *sin6; rtm = (struct rt_msghdr *)(void *)buf; len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6); memset(rtm, 0, len); rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION; myseq = ++seq; rtm->rtm_seq = myseq; rtm->rtm_addrs = RTA_DST; rtm->rtm_msglen = len; sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_addr = np->rip6_dest; if (write(rtsock, buf, len) < 0) { if (errno == ESRCH) /* No such route found */ return NULL; perror("write to rtsock"); exit(1); } do { if ((len = read(rtsock, buf, sizeof(buf))) < 0) { perror("read from rtsock"); exit(1); } rtm = (struct rt_msghdr *)(void *)buf; - } while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid); + } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != myseq || + rtm->rtm_pid != pid); sin6 = (struct sockaddr_in6 *)(void *)&buf[sizeof(struct rt_msghdr)]; if (rtm->rtm_addrs & RTA_DST) { sin6 = (struct sockaddr_in6 *)(void *) ((char *)sin6 + ROUNDUP(sin6->sin6_len)); } if (rtm->rtm_addrs & RTA_GATEWAY) { *gw = sin6->sin6_addr; return gw; } return NULL; } #endif static const char * inet6_n2p(const struct in6_addr *p) { static char buf[BUFSIZ]; return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf)); } static void ifrtdump(int sig) { ifdump(sig); rtdump(sig); } static void ifdump(int sig) { struct ifc *ifcp; FILE *dump; int nifc = 0; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; fprintf(dump, "%s: Interface Table Dump\n", hms()); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) nifc++; fprintf(dump, " Number of interfaces: %d\n", nifc); fprintf(dump, " advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) == 0) continue; if (iff_find(ifcp, IFIL_TYPE_N) != NULL) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); fprintf(dump, " non-advertising interfaces:\n"); TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if ((ifcp->ifc_flags & IFF_UP) && (iff_find(ifcp, IFIL_TYPE_N) == NULL)) continue; ifdump0(dump, ifcp); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } static void ifdump0(FILE *dump, const struct ifc *ifcp) { struct ifac *ifac; struct iff *iffp; char buf[BUFSIZ]; const char *ft; int addr; fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n", ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags), inet6_n2p(&ifcp->ifc_mylladdr), ifcp->ifc_mtu, ifcp->ifc_metric); TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (ifcp->ifc_flags & IFF_POINTOPOINT) { inet_ntop(AF_INET6, (void *)&ifac->ifac_raddr, buf, sizeof(buf)); fprintf(dump, "\t%s/%d -- %s\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen, buf); } else { fprintf(dump, "\t%s/%d\n", inet6_n2p(&ifac->ifac_addr), ifac->ifac_plen); } } fprintf(dump, "\tFilter:\n"); TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { addr = 0; switch (iffp->iff_type) { case IFIL_TYPE_A: ft = "Aggregate"; addr++; break; case IFIL_TYPE_N: ft = "No-use"; break; case IFIL_TYPE_O: ft = "Advertise-only"; addr++; break; case IFIL_TYPE_T: ft = "Default-only"; break; case IFIL_TYPE_L: ft = "Listen-only"; addr++; break; default: snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type); ft = buf; addr++; break; } fprintf(dump, "\t\t%s", ft); if (addr) fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr), iffp->iff_plen); fprintf(dump, "\n"); } fprintf(dump, "\n"); } static void rtdump(int sig) { struct riprt *rrt; char buf[BUFSIZ]; FILE *dump; time_t t, age; if (sig == 0) dump = stderr; else if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL) dump = stderr; t = time(NULL); fprintf(dump, "\n%s: Routing Table Dump\n", hms()); TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_t == 0) age = 0; else age = t - rrt->rrt_t; inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest, buf, sizeof(buf)); fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)", buf, rrt->rrt_info.rip6_plen, rrt->rrt_index, index2ifc[rrt->rrt_index]->ifc_name, inet6_n2p(&rrt->rrt_gw), rrt->rrt_info.rip6_metric, (long)age); if (rrt->rrt_info.rip6_tag) { fprintf(dump, " tag(0x%04x)", ntohs(rrt->rrt_info.rip6_tag) & 0xffff); } if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) fprintf(dump, " NOT-LL"); if (rrt->rrt_rflags & RRTF_NOADVERTISE) fprintf(dump, " NO-ADV"); fprintf(dump, "\n"); } fprintf(dump, "\n"); if (dump != stderr) fclose(dump); } /* * Parse the -A (and -O) options and put corresponding filter object to the * specified interface structures. Each of the -A/O option has the following * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate) * -O 5f09:c400::/32,ef0,ef1 (only when match) */ static void filterconfig(void) { int i; char *p, *ap, *iflp, *ifname, *ep; struct iff iff, *iffp; struct ifc *ifcp; struct riprt *rrt; #if 0 struct in6_addr gw; #endif u_long plen; for (i = 0; i < nfilter; i++) { ap = filter[i]; iflp = NULL; iffp = ⇔ memset(iffp, 0, sizeof(*iffp)); if (filtertype[i] == 'N' || filtertype[i] == 'T') { iflp = ap; goto ifonly; } if ((p = strchr(ap, ',')) != NULL) { *p++ = '\0'; iflp = p; } if ((p = strchr(ap, '/')) == NULL) { fatal("no prefixlen specified for '%s'", ap); /*NOTREACHED*/ } *p++ = '\0'; if (inet_pton(AF_INET6, ap, &iffp->iff_addr) != 1) { fatal("invalid prefix specified for '%s'", ap); /*NOTREACHED*/ } errno = 0; ep = NULL; plen = strtoul(p, &ep, 10); if (errno || !*p || *ep || plen > sizeof(iffp->iff_addr) * 8) { fatal("invalid prefix length specified for '%s'", ap); /*NOTREACHED*/ } iffp->iff_plen = plen; applyplen(&iffp->iff_addr, iffp->iff_plen); ifonly: iffp->iff_type = filtertype[i]; if (iflp == NULL || *iflp == '\0') { fatal("no interface specified for '%s'", ap); /*NOTREACHED*/ } /* parse the interface listing portion */ while (iflp) { ifname = iflp; if ((iflp = strchr(iflp, ',')) != NULL) *iflp++ = '\0'; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (fnmatch(ifname, ifcp->ifc_name, 0) != 0) continue; iffp = malloc(sizeof(*iffp)); if (iffp == NULL) { fatal("malloc of iff"); /*NOTREACHED*/ } memcpy(iffp, &iff, sizeof(*iffp)); #if 0 syslog(LOG_INFO, "Add filter: type %d, ifname %s.", iffp->iff_type, ifname); #endif TAILQ_INSERT_HEAD(&ifcp->ifc_iff_head, iffp, iff_next); } } /* * -A: aggregate configuration. */ if (filtertype[i] != IFIL_TYPE_A) continue; /* put the aggregate to the kernel routing table */ rrt = (struct riprt *)malloc(sizeof(struct riprt)); if (rrt == NULL) { fatal("malloc: rrt"); /*NOTREACHED*/ } memset(rrt, 0, sizeof(struct riprt)); rrt->rrt_info.rip6_dest = iff.iff_addr; rrt->rrt_info.rip6_plen = iff.iff_plen; rrt->rrt_info.rip6_metric = 1; rrt->rrt_info.rip6_tag = htons(routetag & 0xffff); rrt->rrt_gw = in6addr_loopback; rrt->rrt_flags = RTF_UP | RTF_REJECT; rrt->rrt_rflags = RRTF_AGGREGATE; rrt->rrt_t = 0; rrt->rrt_index = loopifcp->ifc_index; #if 0 if (getroute(&rrt->rrt_info, &gw)) { #if 0 /* * When the address has already been registered in the * kernel routing table, it should be removed */ delroute(&rrt->rrt_info, &gw); #else /* it is safer behavior */ errno = EINVAL; fatal("%s/%u already in routing table, " "cannot aggregate", inet6_n2p(&rrt->rrt_info.rip6_dest), rrt->rrt_info.rip6_plen); /*NOTREACHED*/ #endif } #endif /* Put the route to the list */ TAILQ_INSERT_HEAD(&riprt_head, rrt, rrt_next); trace(1, "Aggregate: %s/%d for %s\n", inet6_n2p(&iff.iff_addr), iff.iff_plen, loopifcp->ifc_name); /* Add this route to the kernel */ if (nflag) /* do not modify kernel routing table */ continue; addroute(rrt, &in6addr_loopback, loopifcp); } } /***************** utility functions *****************/ /* * Returns a pointer to ifac whose address and prefix length matches * with the address and prefix length specified in the arguments. */ static struct ifac * ifa_match(const struct ifc *ifcp, const struct in6_addr *ia, int plen) { struct ifac *ifac; TAILQ_FOREACH(ifac, &ifcp->ifc_ifac_head, ifac_next) { if (IN6_ARE_ADDR_EQUAL(&ifac->ifac_addr, ia) && ifac->ifac_plen == plen) break; } return (ifac); } /* * Return a pointer to riprt structure whose address and prefix length * matches with the address and prefix length found in the argument. * Note: This is not a rtalloc(). Therefore exact match is necessary. */ static struct riprt * rtsearch(struct netinfo6 *np) { struct riprt *rrt; TAILQ_FOREACH(rrt, &riprt_head, rrt_next) { if (rrt->rrt_info.rip6_plen == np->rip6_plen && IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest, &np->rip6_dest)) break; } return (rrt); } static int sin6mask2len(const struct sockaddr_in6 *sin6) { return mask2len(&sin6->sin6_addr, sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr)); } static int mask2len(const struct in6_addr *addr, int lenlim) { int i = 0, j; const u_char *p = (const u_char *)addr; for (j = 0; j < lenlim; j++, p++) { if (*p != 0xff) break; i += 8; } if (j < lenlim) { switch (*p) { #define MASKLEN(m, l) case m: do { i += l; break; } while (0) MASKLEN(0xfe, 7); break; MASKLEN(0xfc, 6); break; MASKLEN(0xf8, 5); break; MASKLEN(0xf0, 4); break; MASKLEN(0xe0, 3); break; MASKLEN(0xc0, 2); break; MASKLEN(0x80, 1); break; #undef MASKLEN } } return i; } static const u_char plent[8] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe }; static void applyplen(struct in6_addr *ia, int plen) { u_char *p; int i; p = ia->s6_addr; for (i = 0; i < 16; i++) { if (plen <= 0) *p = 0; else if (plen < 8) *p &= plent[plen]; p++, plen -= 8; } } static const int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; static struct in6_addr * plen2mask(int n) { static struct in6_addr ia; u_char *p; int i; memset(&ia, 0, sizeof(struct in6_addr)); p = (u_char *)&ia; for (i = 0; i < 16; i++, p++, n -= 8) { if (n >= 8) { *p = 0xff; continue; } *p = pl2m[n]; break; } return &ia; } static char * allocopy(char *p) { int len = strlen(p) + 1; char *q = (char *)malloc(len); if (!q) { fatal("malloc"); /*NOTREACHED*/ } strlcpy(q, p, len); return q; } static char * hms(void) { static char buf[BUFSIZ]; time_t t; struct tm *tm; t = time(NULL); if ((tm = localtime(&t)) == 0) { fatal("localtime"); /*NOTREACHED*/ } snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); return buf; } #define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */ static int ripinterval(int timer) { double r = rand(); interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5)); nextalarm = time(NULL) + interval; return interval; } #if 0 static time_t ripsuptrig(void) { time_t t; double r = rand(); t = (int)(RIP_TRIG_INT6_MIN + (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX)); sup_trig_update = time(NULL) + t; return t; } #endif static void fatal(const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); perror(buf); if (errno) syslog(LOG_ERR, "%s: %s", buf, strerror(errno)); else syslog(LOG_ERR, "%s", buf); rtdexit(); } static void tracet(int level, const char *fmt, ...) { va_list ap; if (level <= dflag) { va_start(ap, fmt); fprintf(stderr, "%s: ", hms()); vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { va_start(ap, fmt); if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } static void trace(int level, const char *fmt, ...) { va_list ap; if (level <= dflag) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } if (dflag) { va_start(ap, fmt); if (level > 0) vsyslog(LOG_DEBUG, fmt, ap); else vsyslog(LOG_WARNING, fmt, ap); va_end(ap); } } static struct ifc * ifc_find(char *name) { struct ifc *ifcp; TAILQ_FOREACH(ifcp, &ifc_head, ifc_next) { if (strcmp(name, ifcp->ifc_name) == 0) break; } return (ifcp); } static struct iff * iff_find(struct ifc *ifcp, int type) { struct iff *iffp; TAILQ_FOREACH(iffp, &ifcp->ifc_iff_head, iff_next) { if (type == IFIL_TYPE_ANY || type == iffp->iff_type) break; } return (iffp); } static void setindex2ifc(int idx, struct ifc *ifcp) { int n, nsize; struct ifc **p; if (!index2ifc) { nindex2ifc = 5; /*initial guess*/ index2ifc = (struct ifc **) malloc(sizeof(*index2ifc) * nindex2ifc); if (index2ifc == NULL) { fatal("malloc"); /*NOTREACHED*/ } memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc); } n = nindex2ifc; for (nsize = nindex2ifc; nsize <= idx; nsize *= 2) ; if (n != nsize) { p = (struct ifc **)realloc(index2ifc, sizeof(*index2ifc) * nsize); if (p == NULL) { fatal("realloc"); /*NOTREACHED*/ } memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n)); index2ifc = p; nindex2ifc = nsize; } index2ifc[idx] = ifcp; }