Better patch, with validation in the domain name decoder function. Still unsure how to proceed with regards to the objection to "less SMALL".
Chuck Index: dump.c =================================================================== RCS file: /cvs/src/usr.sbin/rtsold/dump.c,v retrieving revision 1.15 diff -u -p -r1.15 dump.c --- dump.c 21 Apr 2013 19:46:31 -0000 1.15 +++ dump.c 21 Sep 2014 02:37:15 -0000 @@ -33,6 +33,7 @@ #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> +#include <sys/queue.h> #include <net/if.h> #include <netinet/in.h> @@ -51,7 +52,6 @@ static FILE *fp; extern struct ifinfo *iflist; static void dump_interface_status(void); -static char *sec2str(time_t); char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"}; static void @@ -108,7 +108,7 @@ rtsold_dump_file(char *dumpfile) fclose(fp); } -static char * +const char * sec2str(time_t total) { static char result[256]; Index: rtsol.c =================================================================== RCS file: /cvs/src/usr.sbin/rtsold/rtsol.c,v retrieving revision 1.19 diff -u -p -r1.19 rtsol.c --- rtsol.c 21 Oct 2013 09:58:14 -0000 1.19 +++ rtsol.c 21 Sep 2014 02:37:15 -0000 @@ -50,6 +50,7 @@ #include <arpa/inet.h> +#include <netdb.h> #include <time.h> #include <unistd.h> #include <stdio.h> @@ -59,6 +60,7 @@ #include <stdlib.h> #include <syslog.h> #include <fcntl.h> +#include <ctype.h> #include "rtsold.h" @@ -71,13 +73,39 @@ static struct iovec sndiov[2]; static struct sockaddr_in6 from; int rssock; +static char rsid[IFNAMSIZ + 1 + sizeof(DNSINFO_ORIGIN_LABEL) + 1 + NI_MAXHOST]; static struct sockaddr_in6 sin6_allrouters = {sizeof(sin6_allrouters), AF_INET6}; -#ifndef SMALL -void call_script(char *, char *); +static void call_script(const int, const char *const *, + struct script_msg_head_t *); int safefile(const char *); -#endif +static size_t dname_labeldec(char *, size_t, const char *); +static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t); +static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *, + struct script_msg_head_t *, struct script_msg_head_t *); +static char *make_rsid(const char *, const char *, struct rainfo *); + +#define _ARGS_OTHER otherconf_script, ifi->ifname +#define _ARGS_RESADD resolvconf_script, "-a", rsid +#define _ARGS_RESDEL resolvconf_script, "-d", rsid + +#define CALL_SCRIPT(name, sm_head) \ + do { \ + const char *const sarg[] = { _ARGS_##name, NULL }; \ + call_script(sizeof(sarg), sarg, sm_head); \ + } while(0) + +#define ELM_MALLOC(p,error_action) \ + do { \ + p = malloc(sizeof(*p)); \ + if (p == NULL) { \ + warnmsg(LOG_ERR, __func__, "malloc failed: %s", \ + strerror(errno)); \ + error_action; \ + } \ + memset(p, 0, sizeof(*p)); \ + } while(0) int sockopen(u_int rdomain) @@ -226,18 +254,31 @@ void rtsol_input(int s) { u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; - int ifindex = 0, *hlimp = NULL; + int l, ifindex = 0, *hlimp = NULL; + ssize_t msglen; struct in6_pktinfo *pi = NULL; struct ifinfo *ifi = NULL; + struct ra_opt *rao = NULL; struct icmp6_hdr *icp; struct cmsghdr *cm; - ssize_t i; -#ifndef SMALL struct nd_router_advert *nd_ra; -#endif + struct rainfo *rai; + char *raoptp; + char *p; + struct in6_addr *addr; + struct nd_opt_hdr *ndo; + struct nd_opt_rdnss *rdnss; + struct nd_opt_dnssl *dnssl; + size_t len; + char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; + char dname[NI_MAXHOST]; + struct timeval now; + struct timeval lifetime; + int newent_rai; + int newent_rao; /* get message */ - if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { + if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } @@ -268,9 +309,9 @@ rtsol_input(int s) return; } - if (i < sizeof(struct nd_router_advert)) { + if (msglen < sizeof(struct nd_router_advert)) { warnmsg(LOG_ERR, __func__, - "packet size(%zd) is too short", i); + "packet size(%zd) is too short", msglen); return; } @@ -329,7 +370,6 @@ rtsol_input(int s) inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN), ifi->ifname, ifi->state); -#ifndef SMALL nd_ra = (struct nd_router_advert *)icp; /* @@ -344,9 +384,183 @@ rtsol_input(int s) warnmsg(LOG_DEBUG, __func__, "OtherConfigFlag on %s is turned on", ifi->ifname); ifi->otherconfig = 1; - call_script(otherconf_script, ifi->ifname); + CALL_SCRIPT(OTHER, NULL); + } + gettimeofday(&now, NULL); + newent_rai = 0; + rai = find_rainfo(ifi, &from); + if (rai == NULL) { + ELM_MALLOC(rai, exit(1)); + rai->rai_ifinfo = ifi; + TAILQ_INIT(&rai->rai_ra_opt); + rai->rai_saddr.sin6_family = AF_INET6; + rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr); + memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr, + sizeof(rai->rai_saddr.sin6_addr)); + newent_rai = 1; + } + +#define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ + (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) + /* Process RA options. */ + warnmsg(LOG_DEBUG, __func__, "Processing RA"); + raoptp = (char *)icp + sizeof(struct nd_router_advert); + while (raoptp < (char *)icp + msglen) { + ndo = (struct nd_opt_hdr *)raoptp; + warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp); + warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d", + ndo->nd_opt_type); + warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d", + ndo->nd_opt_len); + + switch (ndo->nd_opt_type) { + case ND_OPT_RDNSS: + warnmsg(LOG_DEBUG, __func__, "RDNSS option"); + rdnss = (struct nd_opt_rdnss *)raoptp; + + /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ + if (rdnss->nd_opt_rdnss_len < 3) { + warnmsg(LOG_INFO, __func__, + "too short RDNSS option" + "in RA from %s was ignored.", + inet_ntop(AF_INET6, &from.sin6_addr, + ntopbuf, sizeof(ntopbuf))); + break; + } + + addr = (struct in6_addr *)(void *)(raoptp + sizeof(*rdnss)); + while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { + if (inet_ntop(AF_INET6, addr, ntopbuf, + sizeof(ntopbuf)) == NULL) { + warnmsg(LOG_INFO, __func__, + "an invalid address in RDNSS option" + " in RA from %s was ignored.", + inet_ntop(AF_INET6, &from.sin6_addr, + ntopbuf, sizeof(ntopbuf))); + addr++; + continue; + } + if (IN6_IS_ADDR_LINKLOCAL(addr)) + /* XXX: % has to be escaped here */ + l = snprintf(nsbuf, sizeof(nsbuf), + "%s%c%s", ntopbuf, + SCOPE_DELIMITER, + ifi->ifname); + else + l = snprintf(nsbuf, sizeof(nsbuf), + "%s", ntopbuf); + if (l < 0 || (size_t)l >= sizeof(nsbuf)) { + warnmsg(LOG_ERR, __func__, + "address copying error in " + "RDNSS option: %d.", l); + addr++; + continue; + } + warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", + nsbuf); + + newent_rao = 0; + rao = find_raopt(rai, ndo->nd_opt_type, nsbuf, + strlen(nsbuf)); + if (rao == NULL) { + ELM_MALLOC(rao, break); + rao->rao_type = ndo->nd_opt_type; + rao->rao_len = strlen(nsbuf); + rao->rao_msg = strdup(nsbuf); + if (rao->rao_msg == NULL) { + warnmsg(LOG_ERR, __func__, + "strdup failed: %s", + strerror(errno)); + free(rao); + addr++; + continue; + } + newent_rao = 1; + } + /* Set expiration timer */ + memset(&rao->rao_expire, 0, + sizeof(rao->rao_expire)); + memset(&lifetime, 0, sizeof(lifetime)); + lifetime.tv_sec = + ntohl(rdnss->nd_opt_rdnss_lifetime); + timeradd(&now, &lifetime, &rao->rao_expire); + + if (newent_rao) + TAILQ_INSERT_TAIL(&rai->rai_ra_opt, + rao, rao_next); + addr++; + } + break; + case ND_OPT_DNSSL: + warnmsg(LOG_DEBUG, __func__, "DNSSL option"); + dnssl = (struct nd_opt_dnssl *)raoptp; + + /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ + if (dnssl->nd_opt_dnssl_len < 2) { + warnmsg(LOG_INFO, __func__, + "too short DNSSL option" + "in RA from %s was ignored.", + inet_ntop(AF_INET6, &from.sin6_addr, + ntopbuf, sizeof(ntopbuf))); + break; + } + + /* + * Ensure NUL-termination in DNSSL in case of + * malformed field. + */ + p = (char *)RA_OPT_NEXT_HDR(raoptp); + *(p - 1) = '\0'; + + p = raoptp + sizeof(*dnssl); + while (1 < (len = dname_labeldec(dname, sizeof(dname), + p))) { + /* length == 1 means empty string */ + warnmsg(LOG_DEBUG, __func__, "dname = %s", + dname); + + newent_rao = 0; + rao = find_raopt(rai, ndo->nd_opt_type, dname, + strlen(dname)); + if (rao == NULL) { + ELM_MALLOC(rao, break); + rao->rao_type = ndo->nd_opt_type; + rao->rao_len = strlen(dname); + rao->rao_msg = strdup(dname); + if (rao->rao_msg == NULL) { + warnmsg(LOG_ERR, __func__, + "strdup failed: %s", + strerror(errno)); + free(rao); + addr++; + continue; + } + newent_rao = 1; + } + /* Set expiration timer */ + memset(&rao->rao_expire, 0, + sizeof(rao->rao_expire)); + memset(&lifetime, 0, sizeof(lifetime)); + lifetime.tv_sec = + ntohl(dnssl->nd_opt_dnssl_lifetime); + timeradd(&now, &lifetime, &rao->rao_expire); + + if (newent_rao) + TAILQ_INSERT_TAIL(&rai->rai_ra_opt, + rao, rao_next); + p += len; + } + break; + default: + /* nothing to do for other options */ + break; + } + raoptp = (char *)RA_OPT_NEXT_HDR(raoptp); } -#endif + if (newent_rai) + TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next); + + ra_opt_handler(ifi); ifi->racnt++; @@ -362,15 +576,242 @@ rtsol_input(int s) } } -#ifndef SMALL -void -call_script(char *scriptpath, char *ifname) +static char resstr_ns_prefix[] = "nameserver "; +static char resstr_sh_prefix[] = "search "; +static char resstr_nl[] = "\n"; +static char resstr_sp[] = " "; + +int +ra_opt_handler(struct ifinfo *ifi) { + struct ra_opt *rao; + struct rainfo *rai; + struct script_msg *smp1, *smp2, *smp3; + struct timeval now; + struct script_msg_head_t sm_rdnss_head = + TAILQ_HEAD_INITIALIZER(sm_rdnss_head); + struct script_msg_head_t sm_dnssl_head = + TAILQ_HEAD_INITIALIZER(sm_dnssl_head); + + int dcount, dlen; + + dcount = 0; + dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl); + gettimeofday(&now, NULL); + + /* + * All options from multiple RAs with the same or different + * source addresses on a single interface will be gathered and + * handled, not overridden. [RFC 4861 6.3.4] + */ + TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) { + TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { + switch (rao->rao_type) { + case ND_OPT_RDNSS: + if (timercmp(&now, &rao->rao_expire, >)) { + warnmsg(LOG_INFO, __func__, + "expired rdnss entry: %s", + (char *)rao->rao_msg); + break; + } + ELM_MALLOC(smp1, continue); + ELM_MALLOC(smp2, goto free1); + ELM_MALLOC(smp3, goto free2); + smp1->sm_msg = resstr_ns_prefix; + TAILQ_INSERT_TAIL(&sm_rdnss_head, smp1, + sm_next); + smp2->sm_msg = rao->rao_msg; + TAILQ_INSERT_TAIL(&sm_rdnss_head, smp2, + sm_next); + smp3->sm_msg = resstr_nl; + TAILQ_INSERT_TAIL(&sm_rdnss_head, smp3, + sm_next); + ifi->ifi_rdnss = IFI_DNSOPT_STATE_RECEIVED; + + break; + case ND_OPT_DNSSL: + if (timercmp(&now, &rao->rao_expire, >)) { + warnmsg(LOG_INFO, __func__, + "expired dnssl entry: %s", + (char *)rao->rao_msg); + break; + } + dcount++; + /* Check resolv.conf(5) restrictions. */ + if (dcount > 6) { + warnmsg(LOG_INFO, __func__, + "dnssl entry exceeding maximum count (%d>6)" + ": %s", dcount, (char *)rao->rao_msg); + break; + } + if (256 < dlen + strlen(rao->rao_msg) + + strlen(resstr_sp)) { + warnmsg(LOG_INFO, __func__, + "dnssl entry exceeding maximum length " + "(>256): %s", (char *)rao->rao_msg); + break; + } + ELM_MALLOC(smp1, continue); + ELM_MALLOC(smp2, goto free1); + if (TAILQ_EMPTY(&sm_dnssl_head)) { + ELM_MALLOC(smp3, goto free2); + smp3->sm_msg = resstr_sh_prefix; + TAILQ_INSERT_TAIL(&sm_dnssl_head, smp3, + sm_next); + } + smp1->sm_msg = rao->rao_msg; + TAILQ_INSERT_TAIL(&sm_dnssl_head, smp1, + sm_next); + smp2->sm_msg = resstr_sp; + TAILQ_INSERT_TAIL(&sm_dnssl_head, smp2, + sm_next); + dlen += strlen(rao->rao_msg) + + strlen(resstr_sp); + break; + + ifi->ifi_dnssl = IFI_DNSOPT_STATE_RECEIVED; + default: + break; + } + continue; +free2: + free(smp2); +free1: + free(smp1); + } + /* Call the script for each information source. */ + if (uflag) + ra_opt_rdnss_dispatch(ifi, rai, &sm_rdnss_head, + &sm_dnssl_head); + } + /* Call the script for each interface. */ + if (!uflag) + ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head, + &sm_dnssl_head); + return (0); +} + +char * +make_rsid(const char *ifname, const char *origin, struct rainfo *rai) +{ + char hbuf[NI_MAXHOST]; + + if (rai == NULL) + snprintf(rsid, sizeof(rsid), "%s:%s", ifname, origin); + else { + if (!IN6_IS_ADDR_LINKLOCAL(&rai->rai_saddr.sin6_addr)) + return (NULL); + if (getnameinfo((struct sockaddr *)&rai->rai_saddr, + rai->rai_saddr.sin6_len, hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST) != 0) + return (NULL); + snprintf(rsid, sizeof(rsid), "%s:%s:[%s]", ifname, origin, hbuf); + } + warnmsg(LOG_DEBUG, __func__, "rsid = [%s]", rsid); + return (rsid); +} + +int +ra_opt_rdnss_dispatch(struct ifinfo *ifi, + struct rainfo *rai, + struct script_msg_head_t *sm_rdnss_head, + struct script_msg_head_t *sm_dnssl_head) +{ + const char *r; + struct script_msg *smp1, *dnssl, *dnssl_tmp; + int error; + + error = 0; + + /* Add \n for DNSSL list. */ + if (!TAILQ_EMPTY(sm_dnssl_head)) { + ELM_MALLOC(smp1, goto ra_opt_rdnss_freeit); + smp1->sm_msg = resstr_nl; + TAILQ_INSERT_TAIL(sm_dnssl_head, smp1, sm_next); + } + /* Concatenate the search list elements onto the server list + elements. + */ + + TAILQ_FOREACH_SAFE(dnssl, sm_dnssl_head, sm_next, dnssl_tmp) { + TAILQ_REMOVE(sm_dnssl_head, dnssl, sm_next); + TAILQ_INSERT_TAIL(sm_rdnss_head, dnssl, sm_next); + } + + if (rai != NULL && uflag) + r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, rai); + else + r = make_rsid(ifi->ifname, DNSINFO_ORIGIN_LABEL, NULL); + if (r == NULL) { + warnmsg(LOG_ERR, __func__, "make_rsid() failed. " + "Script was not invoked."); + error = 1; + goto ra_opt_rdnss_freeit; + } + if (!TAILQ_EMPTY(sm_rdnss_head)) + CALL_SCRIPT(RESADD, sm_rdnss_head); + else if (ifi->ifi_rdnss == IFI_DNSOPT_STATE_RECEIVED || + ifi->ifi_dnssl == IFI_DNSOPT_STATE_RECEIVED) { + CALL_SCRIPT(RESDEL, NULL); + ifi->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; + ifi->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; + } + +ra_opt_rdnss_freeit: + /* Clear script message queue. */ + if (!TAILQ_EMPTY(sm_rdnss_head)) { + while ((smp1 = TAILQ_FIRST(sm_rdnss_head)) != NULL) { + TAILQ_REMOVE(sm_rdnss_head, smp1, sm_next); + free(smp1); + } + } + if (!TAILQ_EMPTY(sm_dnssl_head)) { + while ((smp1 = TAILQ_FIRST(sm_dnssl_head)) != NULL) { + TAILQ_REMOVE(sm_dnssl_head, smp1, sm_next); + free(smp1); + } + } + + return (error); +} + +static struct ra_opt * +find_raopt(struct rainfo *rai, int type, void *msg, size_t len) +{ + struct ra_opt *rao; + + TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { + if (rao->rao_type == type && + rao->rao_len == strlen(msg) && + memcmp(rao->rao_msg, msg, len) == 0) + break; + } + + return (rao); +} + +static void +call_script(const int argc, const char *const argv[], + struct script_msg_head_t *sm_head) +{ + const char *scriptpath; + int fd[2]; + int error; pid_t pid, wpid; - if (scriptpath == NULL) + if ((scriptpath = argv[0]) == NULL) return; + fd[0] = fd[1] = -1; + if (sm_head != NULL && !TAILQ_EMPTY(sm_head)) { + error = pipe(fd); + if (error) { + warnmsg(LOG_ERR, __func__, + "failed to create a pipe: %s", strerror(errno)); + return; + } + } + /* launch the script */ pid = fork(); if (pid < 0) { @@ -380,6 +821,25 @@ call_script(char *scriptpath, char *ifna } else if (pid) { int wstatus; + if (fd[0] != -1) { /* Send message to the child if any. */ + ssize_t len; + struct script_msg *smp; + + close(fd[0]); + TAILQ_FOREACH(smp, sm_head, sm_next) { + len = strlen(smp->sm_msg); + warnmsg(LOG_DEBUG, __func__, + "write to child = %s(%zd)", + smp->sm_msg, len); + if (write(fd[1], smp->sm_msg, len) != len) { + warnmsg(LOG_ERR, __func__, + "write to child failed: %s", + strerror(errno)); + break; + } + } + close(fd[1]); + } do { wpid = wait(&wstatus); } while (wpid != pid && wpid > 0); @@ -392,12 +852,8 @@ call_script(char *scriptpath, char *ifna "script \"%s\" terminated", scriptpath); } } else { - char *argv[3]; - int fd; - - argv[0] = scriptpath; - argv[1] = ifname; - argv[2] = NULL; + int nullfd; + char **_argv; if (safefile(scriptpath) != 0) { warnmsg(LOG_ERR, __func__, @@ -405,17 +861,39 @@ call_script(char *scriptpath, char *ifna scriptpath); exit(1); } - - if ((fd = open("/dev/null", O_RDWR)) != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); + nullfd = open("/dev/null", O_RDWR); + if (nullfd < 0) { + warnmsg(LOG_ERR, __func__, + "open /dev/null: %s", strerror(errno)); + exit(1); } + if (fd[0] != -1) { /* Receive message from STDIN if any. */ + close(fd[1]); + if (fd[0] != STDIN_FILENO) { + /* Connect a pipe read-end to child's STDIN. */ + if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { + warnmsg(LOG_ERR, __func__, + "dup2 STDIN: %s", strerror(errno)); + exit(1); + } + close(fd[0]); + } + } else + dup2(nullfd, STDIN_FILENO); + + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + if (nullfd > STDERR_FILENO) + close(nullfd); - execv(scriptpath, argv); - + _argv = malloc(sizeof(*_argv) * argc); + if (_argv == NULL) { + warnmsg(LOG_ERR, __func__, + "malloc: %s", strerror(errno)); + exit(1); + } + memcpy(_argv, argv, (size_t)argc); + execv(scriptpath, (char *const *)_argv); warnmsg(LOG_ERR, __func__, "child: exec failed: %s", strerror(errno)); exit(1); @@ -463,4 +941,42 @@ safefile(const char *path) return (0); } -#endif + +/* Decode domain name label encoding in RFC 1035 Section 3.1 */ +static size_t +dname_labeldec(char *dst, size_t dlen, const char *src) +{ + size_t len; + const char *src_origin; + const char *src_last; + const char *dst_origin; + const char *dec; + + src_origin = src; + src_last = strchr(src, '\0'); + dst_origin = dst; + memset(dst, '\0', dlen); + while (src && (len = (uint8_t)(*src++) & 0x3f) && + (src + len) <= src_last) { + if (dst != dst_origin) + *dst++ = '.'; + warnmsg(LOG_DEBUG, __func__, "labellen = %zd", len); + memcpy(dst, src, len); + src += len; + dst += len; + } + *dst = '\0'; + + /* Check for valid domain name characters. On failure, return value + that caller interprets as an empty string. + */ + for (dec = dst_origin; *dec != '\0'; dec++) { + if ( !(isalnum(*dec) || *dec == '.' || *dec == '-' )) { + warnmsg(LOG_WARNING, __func__, + "encountered domain with invalid char"); + return(1); + } + } + + return (src - src_origin); +} Index: rtsold.8 =================================================================== RCS file: /cvs/src/usr.sbin/rtsold/rtsold.8,v retrieving revision 1.33 diff -u -p -r1.33 rtsold.8 --- rtsold.8 27 Aug 2014 14:04:16 -0000 1.33 +++ rtsold.8 21 Sep 2014 02:37:15 -0000 @@ -38,19 +38,25 @@ .\" .Sh SYNOPSIS .Nm rtsold -.Op Fl 1DdFfm +.Op Fl 1DdFfmu .Op Fl O Ar script-name +.Op Fl R Ar script-name .Ar interface ... .Nm rtsold .Op Fl 1DdFfm +.Op Fl O Ar script-name +.Op Fl R Ar script-name .Fl a .Pp .Nm rtsol -.Op Fl DdF +.Op Fl DdFu .Op Fl O Ar script-name +.Op Fl R Ar script-name .Ar interface ... .Nm rtsol .Op Fl DdF +.Op Fl O Ar script-name +.Op Fl R Ar script-name .Fl a .\" .Sh DESCRIPTION @@ -137,6 +143,15 @@ When sending a Router Solicitation on an includes a Source Link-layer address option if the interface has a link-layer address. .Pp +If +.Nm +receives a Router Advertisement with RDNSS (Recursive DNS Server) or +DNSSL (DNS Search List) options, it invokes a script, passing the options +on standard input. By default, the resolvconf(8) script is invoked. This can +be overridden with the +.Fl R +option. +.Pp .Nm is able to do some additional configuration for interfaces where more than setting the host's address is needed. @@ -216,6 +231,30 @@ should be specified as the absolute path and the file itself should be a regular file and owned by the same user running .Nm . +.It Fl R Ar script-name +Specifies a script to run when router advertisement options RDNSS +(Recursive DNS Server) or DNSSL (DNS Search List) are encountered. The +information of DNS servers and DNS search domains will be sent to standard +input of this script. +.Ar script-name +should be specified as the absolute path from root to the script file, +and the file itself should be a regular file and owned by the same user +running +.Nm . +The +resolvconf(8) +script is used by default. +.It Fl u +Adds the source address of Router Advertisement messages to the interface +name in an argument of the RDNSS and DNSSL script. +.Pp +If +.Fl u +is specified, the interface name in the script argument will be +.Ql ifname:slaac:[RA-source-address] . +.Pp +If not, it will be +.Ql ifname:slaac . .El .\" .Sh FILES Index: rtsold.c =================================================================== RCS file: /cvs/src/usr.sbin/rtsold/rtsold.c,v retrieving revision 1.53 diff -u -p -r1.53 rtsold.c --- rtsold.c 27 Aug 2014 14:04:16 -0000 1.53 +++ rtsold.c 21 Sep 2014 02:37:15 -0000 @@ -33,6 +33,7 @@ #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> +#include <sys/queue.h> #include <sys/param.h> #include <net/if.h> @@ -60,11 +61,13 @@ struct ifinfo *iflist; static int log_upto = 999; static int fflag = 0; static int Fflag = 0; /* force setting sysctl parameters */ +int uflag = 0; int aflag = 0; int dflag = 0; char *otherconf_script; +const char *resolvconf_script = "/sbin/resolvconf"; /* protocol constants */ #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ @@ -116,9 +119,9 @@ main(int argc, char *argv[]) if (argv0 && argv0[0] != '\0' && argv0[strlen(argv0) - 1] != 'd') { fflag = 1; once = 1; - opts = "adDFO:"; + opts = "adDFuO:R:"; } else - opts = "adDfFm1O:"; + opts = "adDfFmu1O:R:"; while ((ch = getopt(argc, argv, opts)) != -1) { switch (ch) { @@ -143,11 +146,15 @@ main(int argc, char *argv[]) case '1': once = 1; break; -#ifndef SMALL case 'O': otherconf_script = optarg; break; -#endif + case 'R': + resolvconf_script = optarg; + break; + case 'u': + uflag = 1; + break; default: usage(argv0); /*NOTREACHED*/ @@ -177,12 +184,10 @@ main(int argc, char *argv[]) setlogmask(LOG_UPTO(log_upto)); } -#ifndef SMALL if (otherconf_script && *otherconf_script != '/') { errx(1, "configuration script (%s) must be an absolute path", otherconf_script); } -#endif if (Fflag) setinet6sysctl(IPPROTO_IPV6, IPV6CTL_FORWARDING, 0); @@ -322,8 +327,10 @@ ifconfig(char *ifname) return(-1); } ifinfo->sdl = sdl; - - strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); + ifinfo->ifi_rdnss = IFI_DNSOPT_STATE_NOINFO; + ifinfo->ifi_dnssl = IFI_DNSOPT_STATE_NOINFO; + TAILQ_INIT(&ifinfo->ifi_rainfo); + strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); /* construct a router solicitation message */ if (make_packet(ifinfo)) @@ -402,6 +409,19 @@ ifreconfig(char *ifname) } #endif +struct rainfo * +find_rainfo(struct ifinfo *ifi, struct sockaddr_in6 *sin6) +{ + struct rainfo *rai; + + TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) + if (memcmp(&rai->rai_saddr.sin6_addr, &sin6->sin6_addr, + sizeof(rai->rai_saddr.sin6_addr)) == 0) + return (rai); + + return (NULL); +} + struct ifinfo * find_ifinfo(int ifindex) { @@ -457,6 +477,8 @@ rtsol_check_timer(void) static struct timeval returnval; struct timeval now, rtsol_timer; struct ifinfo *ifinfo; + struct rainfo *rai; + struct ra_opt *rao; int flags, timers; gettimeofday(&now, NULL); @@ -471,6 +493,20 @@ rtsol_check_timer(void) "state = %d", ifinfo->ifname, ifinfo->state); + while((rai = TAILQ_FIRST(&ifinfo->ifi_rainfo)) != NULL) { + /* Remove all RA options. */ + TAILQ_REMOVE(&ifinfo->ifi_rainfo, rai, rai_next); + while ((rao = TAILQ_FIRST(&rai->rai_ra_opt)) != + NULL) { + TAILQ_REMOVE(&rai->rai_ra_opt, rao, + rao_next); + if (rao->rao_msg != NULL) + free(rao->rao_msg); + free(rao); + } + free(rai); + } + switch (ifinfo->state) { case IFS_DOWN: case IFS_TENTATIVE: @@ -508,14 +544,12 @@ rtsol_check_timer(void) ifinfo->state = IFS_PROBE; } -#ifndef SMALL /* * If we need a probe, clear the previous * status wrt the "other" configuration. */ if (probe) ifinfo->otherconfig = 0; -#endif if (probe && mobile_node) defrouter_probe(ifinfo); @@ -538,6 +572,33 @@ rtsol_check_timer(void) break; } rtsol_timer_update(ifinfo); + } else { + /* Expiration check for RA options. */ + int expire = 0; + + TAILQ_FOREACH(rai, &ifinfo->ifi_rainfo, rai_next) { + TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) { + warnmsg(LOG_DEBUG, __func__, + "RA expiration timer: " + "type=%d, msg=%s, expire=%s", + rao->rao_type, (char *)rao->rao_msg, + sec2str(rao->rao_expire.tv_sec)); + if (timercmp(&now, &rao->rao_expire, + >=)) { + warnmsg(LOG_DEBUG, __func__, + "RA expiration timer: " + "expired."); + TAILQ_REMOVE(&rai->rai_ra_opt, + rao, rao_next); + if (rao->rao_msg != NULL) + free(rao->rao_msg); + free(rao); + expire = 1; + } + } + } + if (expire) + ra_opt_handler(ifinfo); } if (!ifinfo->stoptimer) { if (timers == 0) @@ -652,12 +713,14 @@ usage(char *progname) { if (progname && progname[0] != '\0' && progname[strlen(progname) - 1] != 'd') { fprintf(stderr, - "usage: rtsol [-DdF] [-O script-name] interface ...\n" - " rtsol [-DdF] -a\n"); + "usage: rtsol [-DdFu] [-O script-name] [-R script-name] " + "interface ...\n" + " rtsol [-DdFu] [-O script-name] [-R script-name] -a\n"); } else { fprintf(stderr, - "usage: rtsold [-1DdFfm] [-O script-name] interface ...\n" - " rtsold [-1DdFfm] -a\n"); + "usage: rtsold [-1DdFfmu] [-O script-name] [-R script-name] " + "interface ...\n" + " rtsold [-1DdFfmu] [-O script-name] [-R script-name] -a\n"); } exit(1); } Index: rtsold.h =================================================================== RCS file: /cvs/src/usr.sbin/rtsold/rtsold.h,v retrieving revision 1.17 diff -u -p -r1.17 rtsold.h --- rtsold.h 12 Nov 2013 22:27:13 -0000 1.17 +++ rtsold.h 21 Sep 2014 02:37:15 -0000 @@ -30,6 +30,33 @@ * SUCH DAMAGE. */ +struct script_msg { + TAILQ_ENTRY(script_msg) sm_next; + + char *sm_msg; +}; + +TAILQ_HEAD(script_msg_head_t, script_msg); + +struct ra_opt { + TAILQ_ENTRY(ra_opt) rao_next; + + u_int8_t rao_type; + struct timeval rao_expire; + size_t rao_len; + void *rao_msg; +}; + +TAILQ_HEAD(rainfo_head, ra_opt); + +struct rainfo { + TAILQ_ENTRY(rainfo) rai_next; + + struct ifinfo *rai_ifinfo; + struct sockaddr_in6 rai_saddr; + TAILQ_HEAD(, ra_opt) rai_ra_opt; +}; + struct ifinfo { struct ifinfo *next; /* pointer to the next interface */ @@ -49,8 +76,13 @@ struct ifinfo { struct timeval expire; int stoptimer; int errors; /* # of errors we've got - detect wedge */ +#define IFI_DNSOPT_STATE_NOINFO 0 +#define IFI_DNSOPT_STATE_RECEIVED 1 + int ifi_rdnss; /* RDNSS option state */ + int ifi_dnssl; /* DNSSL option state */ int racnt; /* total # of valid RAs it have got */ + TAILQ_HEAD(, rainfo) ifi_rainfo; size_t rs_datalen; u_char *rs_data; @@ -63,14 +95,20 @@ struct ifinfo { #define IFS_DOWN 3 #define IFS_TENTATIVE 4 +#define DNSINFO_ORIGIN_LABEL "slaac" + /* rtsold.c */ extern int dflag; +extern int uflag; extern char *otherconf_script; +extern const char *resolvconf_script; struct ifinfo *find_ifinfo(int ifindex); +struct rainfo *find_rainfo(struct ifinfo *, struct sockaddr_in6 *); void rtsol_timer_update(struct ifinfo *); extern void warnmsg(int, const char *, const char *, ...) __attribute__((__format__(__printf__, 3, 4))); extern char **autoifprobe(u_int); +extern int ra_opt_handler(struct ifinfo *); /* if.c */ extern int ifinit(void); @@ -94,3 +132,4 @@ extern void defrouter_probe(struct ifinf /* dump.c */ extern void rtsold_dump_file(char *); +extern const char *sec2str(time_t);