diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c index 1b48868692ba..c0e442bd70e9 100644 --- a/usr.sbin/rtadvd/config.c +++ b/usr.sbin/rtadvd/config.c @@ -141,6 +141,33 @@ dname_labelenc(char *dst, const char *src) memset(p, 0, sizeof(*p)); \ } while(0) +int +loadconfig(char *ifl_names[], const int ifl_len) +{ + int i; + int idx; + int error; + + for (i = 0; i < ifl_len; i++) { + idx = if_nametoindex(ifl_names[i]); + if (idx == 0) { + syslog(LOG_ERR, + "<%s> interface %s not found. " + "Ignored at this moment.", __func__, ifl_names[i]); + continue; + } + syslog(LOG_INFO, + "<%s> loading config for %s.", __func__, ifl_names[i]); + error = getconfig(idx); + if (error) + syslog(LOG_ERR, + "<%s> invalid configuration for %s. " + "Ignored at this moment.", __func__, ifl_names[i]); + } + + return (0); +} + int rmconfig(int idx) { @@ -207,6 +234,7 @@ getconfig(int idx) int stat, i; char tbuf[BUFSIZ]; struct rainfo *rai; + struct rainfo *rai_old; long val; int64_t val64; char buf[BUFSIZ]; @@ -220,6 +248,10 @@ getconfig(int idx) return (-1); } + TAILQ_FOREACH(rai_old, &railist, rai_next) + if (idx == rai_old->rai_ifindex) + break; + if ((stat = agetent(tbuf, intface)) <= 0) { memset(tbuf, 0, sizeof(tbuf)); syslog(LOG_INFO, @@ -254,7 +286,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> can't get information of %s", __func__, intface); - return (-1); + goto getconfig_free_rai; } rai->rai_ifindex = rai->rai_sdl->sdl_index; } else @@ -280,7 +312,7 @@ getconfig(int idx) "<%s> maxinterval (%ld) on %s is invalid " "(must be between %u and %u)", __func__, val, intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); - return (-1); + goto getconfig_free_rai; } rai->rai_maxinterval = (u_int)val; @@ -292,7 +324,7 @@ getconfig(int idx) "(must be between %d and %d)", __func__, val, intface, MIN_MININTERVAL, (rai->rai_maxinterval * 3) / 4); - return (-1); + goto getconfig_free_rai; } rai->rai_mininterval = (u_int)val; @@ -311,7 +343,7 @@ getconfig(int idx) if ((val & ND_RA_FLAG_RTPREF_HIGH)) { syslog(LOG_ERR, "<%s> the \'h\' and \'l\'" " router flags are exclusive", __func__); - return (-1); + goto getconfig_free_rai; } val |= ND_RA_FLAG_RTPREF_LOW; } @@ -328,7 +360,7 @@ getconfig(int idx) if (rai->rai_rtpref == ND_RA_FLAG_RTPREF_RSV) { syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s", __func__, rai->rai_rtpref, intface); - return (-1); + goto getconfig_free_rai; } MAYHAVE(val, "rltime", rai->rai_maxinterval * 3); @@ -339,7 +371,7 @@ getconfig(int idx) "(must be 0 or between %d and %d)", __func__, val, intface, rai->rai_maxinterval, MAXROUTERLIFETIME); - return (-1); + goto getconfig_free_rai; } rai->rai_lifetime = val & 0xffff; @@ -349,7 +381,7 @@ getconfig(int idx) "<%s> reachable time (%ld) on %s is invalid " "(must be no greater than %d)", __func__, val, intface, MAXREACHABLETIME); - return (-1); + goto getconfig_free_rai; } rai->rai_reachabletime = (u_int32_t)val; @@ -357,7 +389,7 @@ getconfig(int idx) if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range", __func__, (long long)val64, intface); - return (-1); + goto getconfig_free_rai; } rai->rai_retranstimer = (u_int32_t)val64; @@ -365,7 +397,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> mobile-ip6 configuration not supported", __func__); - return (-1); + goto getconfig_free_rai; } /* prefix information */ @@ -395,14 +427,14 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, addr); - return (-1); + goto getconfig_free_pfx; } if (IN6_IS_ADDR_MULTICAST(&pfx->pfx_prefix)) { syslog(LOG_ERR, "<%s> multicast prefix (%s) must " "not be advertised on %s", __func__, addr, intface); - return (-1); + goto getconfig_free_pfx; } if (IN6_IS_ADDR_LINKLOCAL(&pfx->pfx_prefix)) syslog(LOG_NOTICE, @@ -416,7 +448,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s " "on %s out of range", __func__, val, addr, intface); - return (-1); + goto getconfig_free_pfx; } pfx->pfx_prefixlen = (int)val; @@ -441,7 +473,7 @@ getconfig(int idx) "%s/%d on %s is out of range", __func__, (long long)val64, addr, pfx->pfx_prefixlen, intface); - return (-1); + goto getconfig_free_pfx; } pfx->pfx_validlifetime = (u_int32_t)val64; @@ -461,7 +493,7 @@ getconfig(int idx) "is out of range", __func__, (long long)val64, addr, pfx->pfx_prefixlen, intface); - return (-1); + goto getconfig_free_pfx; } pfx->pfx_preflifetime = (u_int32_t)val64; @@ -475,6 +507,9 @@ getconfig(int idx) /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_prefix, pfx, pfx_next); rai->rai_pfxs++; + continue; +getconfig_free_pfx: + free(pfx); } if (rai->rai_advifprefix && rai->rai_pfxs == 0) get_prefix(rai); @@ -484,7 +519,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> mtu (%ld) on %s out of range", __func__, val, intface); - return (-1); + goto getconfig_free_rai; } rai->rai_linkmtu = (u_int32_t)val; if (rai->rai_linkmtu == 0) { @@ -501,7 +536,7 @@ getconfig(int idx) "be between least MTU (%d) and physical link MTU (%d)", __func__, (unsigned long)rai->rai_linkmtu, intface, IPV6_MMTU, rai->rai_phymtu); - return (-1); + goto getconfig_free_rai; } #ifdef SIOCSIFINFO_IN6 @@ -553,14 +588,10 @@ getconfig(int idx) /* allocate memory to store prefix information */ ELM_MALLOC(rti, exit(1)); - /* link into chain */ - TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); - rai->rai_routes++; - if (inet_pton(AF_INET6, addr, &rti->rti_prefix) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, addr); - return (-1); + goto getconfig_free_rti; } #if 0 /* @@ -575,14 +606,14 @@ getconfig(int idx) "<%s> multicast route (%s) must " "not be advertised on %s", __func__, addr, intface); - return (-1); + goto getconfig_free_rti; } if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { syslog(LOG_NOTICE, "<%s> link-local route (%s) will " "be advertised on %s", __func__, addr, intface); - return (-1); + goto getconfig_free_rti; } #endif @@ -602,7 +633,7 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s " "out of range", __func__, val, addr, intface); - return (-1); + goto getconfig_free_rti; } rti->rti_prefixlen = (int)val; @@ -617,7 +648,7 @@ getconfig(int idx) "<%s> the \'h\' and \'l\' route" " preferences are exclusive", __func__); - exit(1); + goto getconfig_free_rti; } val |= ND_RA_FLAG_RTPREF_LOW; } @@ -638,7 +669,7 @@ getconfig(int idx) "for %s/%d on %s", __func__, rti->rti_rtpref, addr, rti->rti_prefixlen, intface); - return (-1); + goto getconfig_free_rti; } /* @@ -665,9 +696,16 @@ getconfig(int idx) syslog(LOG_ERR, "<%s> route lifetime (%lld) for " "%s/%d on %s out of range", __func__, (long long)val64, addr, rti->rti_prefixlen, intface); - return (-1); + goto getconfig_free_rti; } rti->rti_ltime = (u_int32_t)val64; + + /* link into chain */ + TAILQ_INSERT_TAIL(&rai->rai_route, rti, rti_next); + rai->rai_routes++; + continue; +getconfig_free_rti: + free(rti); } #endif /* DNS server and DNS search list information */ @@ -689,12 +727,12 @@ getconfig(int idx) c = strcspn(ap, ","); strncpy(abuf, ap, c); abuf[c] = '\0'; - ELM_MALLOC(rdna, exit(1)); + ELM_MALLOC(rdna, goto getconfig_free_rdn); if (inet_pton(AF_INET6, abuf, &rdna->ra_dns) != 1) { syslog(LOG_ERR, "<%s> inet_pton failed for %s", __func__, abuf); free(rdna); - return (-1); + goto getconfig_free_rdn; } TAILQ_INSERT_TAIL(&rdn->rd_list, rdna, ra_next); } @@ -707,12 +745,19 @@ getconfig(int idx) "(must be between %d and %d)", entbuf, val, intface, rai->rai_maxinterval, rai->rai_maxinterval * 2); - return (-1); + goto getconfig_free_rdn; } rdn->rd_ltime = val; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_rdnss, rdn, rd_next); + continue; +getconfig_free_rdn: + while ((rdna = TAILQ_FIRST(&rdn->rd_list)) != NULL) { + TAILQ_REMOVE(&rdn->rd_list, rdna, ra_next); + free(rdna); + } + free(rdn); } for (i = -1; i < MAXDNSSLENT ; i++) { @@ -734,7 +779,7 @@ getconfig(int idx) c = strcspn(ap, ","); strncpy(abuf, ap, c); abuf[c] = '\0'; - ELM_MALLOC(dnsa, exit(1)); + ELM_MALLOC(dnsa, goto getconfig_free_dns); dnsa->da_len = dname_labelenc(dnsa->da_dom, abuf); syslog(LOG_DEBUG, "<%s>: dnsa->da_len = %d", __func__, dnsa->da_len); @@ -749,15 +794,46 @@ getconfig(int idx) "(must be between %d and %d)", entbuf, val, intface, rai->rai_maxinterval, rai->rai_maxinterval * 2); - return (-1); + goto getconfig_free_dns; } dns->dn_ltime = val; /* link into chain */ TAILQ_INSERT_TAIL(&rai->rai_dnssl, dns, dn_next); + continue; +getconfig_free_dns: + while ((dnsa = TAILQ_FIRST(&dns->dn_list)) != NULL) { + TAILQ_REMOVE(&dns->dn_list, dnsa, da_next); + free(dnsa); + } + free(dns); } /* construct the sending packet */ make_packet(rai); + + /* + * If an entry with the same ifindex exists, remove it first. + * Before the removal, RDNSS and DNSSL options with + * zero-lifetime will be sent. + */ + if (rai_old != NULL) { + const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; + struct rdnss *rdn; + struct dnssl *dns; + + rai_old->rai_lifetime = 0; + TAILQ_FOREACH(rdn, &rai_old->rai_rdnss, rd_next) + rdn->rd_ltime = 0; + TAILQ_FOREACH(dns, &rai_old->rai_dnssl, dn_next) + dns->dn_ltime = 0; + + make_packet(rai_old); + for (i = 0; i < retrans; i++) { + ra_output(rai_old); + sleep(MIN_DELAY_BETWEEN_RAS); + } + rmconfig(idx); + } TAILQ_INSERT_TAIL(&railist, rai, rai_next); /* set timer */ @@ -767,6 +843,9 @@ getconfig(int idx) rtadvd_set_timer(&rai->rai_timer->rat_tm, rai->rai_timer); return (0); +getconfig_free_rai: + free(rai); + return (-1); } void diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h index 38c19b8b422b..01886a67b719 100644 --- a/usr.sbin/rtadvd/config.h +++ b/usr.sbin/rtadvd/config.h @@ -32,6 +32,7 @@ extern int getconfig(int); extern int rmconfig(int); +extern int loadconfig(char *[], const int); extern void delete_prefix(struct prefix *); extern void invalidate_prefix(struct prefix *); extern void update_prefix(struct prefix *); diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8 index 51596242646f..b41f7c06a07d 100644 --- a/usr.sbin/rtadvd/rtadvd.8 +++ b/usr.sbin/rtadvd/rtadvd.8 @@ -169,6 +169,18 @@ or the file specified with option .Fl F . .Pp Use +.Dv SIGHUP +to reload the configuration file +.Pa /etc/rtadvd.conf . +If an invalid parameter is found in the configuration file upon the reload, +the entry will be ignored and the old configuration will be used. +When parameters in an existing entry are updated, +.Nm +will send Router Advertisement messages with the old configuration but +zero router lifetime to the interface first, and then start to send a new +message. +.Pp +Use .Dv SIGTERM to kill .Nm diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c index 6423e7bf97e4..e9b212c0bd5f 100644 --- a/usr.sbin/rtadvd/rtadvd.c +++ b/usr.sbin/rtadvd/rtadvd.c @@ -83,6 +83,7 @@ static u_char *sndcmsgbuf = NULL; static size_t sndcmsgbuflen; volatile sig_atomic_t do_dump; volatile sig_atomic_t do_die; +volatile sig_atomic_t do_reload; struct msghdr sndmhdr; struct iovec rcviov[2]; struct iovec sndiov[2]; @@ -161,6 +162,7 @@ struct sockaddr_in6 sin6_sitelocal_allrouters = { .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT, }; +static void set_reload(int); static void set_die(int); static void die(void); static void sock_open(void); @@ -175,7 +177,6 @@ static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, static int nd6_options(struct nd_opt_hdr *, int, union nd_opt *, u_int32_t); static void free_ndopts(union nd_opt *); -static void ra_output(struct rainfo *); static void rtmsg_input(void); static void rtadvd_set_dump_file(int); static void set_short_delay(struct rainfo *); @@ -197,7 +198,6 @@ main(int argc, char *argv[]) int i, ch; int fflag = 0, logopt; pid_t pid, otherpid; - int error; /* get command line options and arguments */ while ((ch = getopt(argc, argv, "c:dDfF:M:p:Rs")) != -1) { @@ -273,22 +273,7 @@ main(int argc, char *argv[]) ifl_names = argv; ifl_len = argc; - for (i = 0; i < ifl_len; i++) { - int idx; - - idx = if_nametoindex(ifl_names[i]); - if (idx == 0) { - syslog(LOG_INFO, - "<%s> interface %s not found." - "Ignored at this moment.", __func__, ifl_names[i]); - continue; - } - error = getconfig(idx); - if (error) - syslog(LOG_INFO, - "<%s> invalid configuration for %s." - "Ignored at this moment.", __func__, ifl_names[i]); - } + loadconfig(argv, argc); pfh = pidfile_open(pidfilename, 0600, &otherpid); if (pfh == NULL) { @@ -343,6 +328,7 @@ main(int argc, char *argv[]) #endif signal(SIGTERM, set_die); signal(SIGUSR1, rtadvd_set_dump_file); + signal(SIGHUP, set_reload); while (1) { #ifndef HAVE_POLL_H @@ -358,6 +344,11 @@ main(int argc, char *argv[]) /*NOTREACHED*/ } + if (do_reload) { + loadconfig(argv, argc); + do_reload = 0; + } + /* timer expiration check and reset the timer */ timeout = rtadvd_check_timer(); @@ -420,6 +411,13 @@ rtadvd_set_dump_file(int sig __unused) do_dump = 1; } +static void +set_reload(int sig __unused) +{ + + do_reload = 1; +} + static void set_die(int sig __unused) { @@ -1698,7 +1696,7 @@ if_indextorainfo(int idx) return (NULL); /* search failed */ } -static void +void ra_output(struct rainfo *rai) { int i; diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h index f65a837a17b0..190bb0d0a1c0 100644 --- a/usr.sbin/rtadvd/rtadvd.h +++ b/usr.sbin/rtadvd/rtadvd.h @@ -233,6 +233,7 @@ extern TAILQ_HEAD(railist_head_t, rainfo) railist; struct rtadvd_timer *ra_timeout(void *); void ra_timer_update(void *, struct timeval *); +void ra_output(struct rainfo *); int prefix_match(struct in6_addr *, int, struct in6_addr *, int);