Index: cfparse.y diff -u cfparse.y.orig cfparse.y --- cfparse.y.orig Tue Oct 3 21:19:43 2006 +++ cfparse.y Tue Feb 6 16:36:51 2007 @@ -92,6 +92,7 @@ struct cf_list *cf_nisp_list, *cf_nisp_name_list; struct cf_list *cf_bcmcs_list, *cf_bcmcs_name_list; long long cf_refreshtime = -1; +int install_route; extern int yylex __P((void)); extern int cfswitch_buffer __P((char *)); @@ -118,6 +119,7 @@ %token AUTHNAME RDM KEY %token KEYINFO REALM KEYID SECRET KEYNAME EXPIRE %token ADDRPOOL POOLNAME RANGE TO ADDRESS_POOL +%token INSTALL_ROUTE %token INCLUDE %token NUMBER SLASH EOS BCL ECL STRING QSTRING PREFIX INFINITY @@ -342,6 +344,10 @@ yywarn("multiple refresh times (ignored)"); } } + | OPTION INSTALL_ROUTE EOS + { + install_route = 1; + } ; ia_statement: Index: cftoken.l diff -u cftoken.l.orig cftoken.l --- cftoken.l.orig Tue Oct 3 21:19:43 2006 +++ cftoken.l Tue Feb 6 16:36:51 2007 @@ -174,6 +174,8 @@ delayedkey { DECHO; return (DELAYEDKEY); } +install-route { DECHO; return (INSTALL_ROUTE); } + /* request */ request { DECHO; return (REQUEST); } Index: common.c diff -u -p common.c.orig common.c --- common.c.orig Wed Sep 20 17:22:24 2006 +++ common.c Tue Feb 6 16:36:51 2007 @@ -71,6 +71,9 @@ #include #include #include +#include +#include +#include #ifdef HAVE_GETIFADDRS # ifdef HAVE_IFADDRS_H @@ -95,10 +98,25 @@ struct in6_ifreq { #endif #define MAXDNAME 255 +#define PATH_ROUTE "/sbin/route" int foreground; int debug_thresh; +#ifndef TAILQ_HEAD_INITIALIZER +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } +#endif + +struct route_entry { + TAILQ_ENTRY(route_entry) rt_next; + struct in6_addr rt_dst; + int rt_plen; +}; + +static TAILQ_HEAD(, route_entry) installed_routes = + TAILQ_HEAD_INITIALIZER(installed_routes); + static int dhcp6_count_list __P((struct dhcp6_list *)); static int in6_matchflags __P((struct sockaddr *, char *, int)); static ssize_t dnsencode __P((const char *, char *, size_t)); @@ -3076,6 +3094,214 @@ dprintf(int level, const char *fname, co logbuf); } else syslog(level, "%s%s%s", fname, printfname ? ": " : "", logbuf); +} + +int +execute(path, argv) + char *path; + char **argv; +{ + int fd, status; + pid_t pid; + + switch (pid = fork()) { + case -1: + dprintf(LOG_ERR, FNAME, "cannot fork: %s", strerror(errno)); + break; + case 0: + if (debug_thresh < LOG_DEBUG) { + if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close(fd); + } + } + (void)execv(path, argv); + exit(EX_UNAVAILABLE); + default: + (void)waitpid(pid, &status, 0); + } + return (status); +} + +static int +rt_add(dst, plen, gw, gw_len) + struct in6_addr *dst; + int plen; + struct sockaddr *gw; + int gw_len; +{ + char strdst[INET6_ADDRSTRLEN]; + char strgw[INET6_ADDRSTRLEN]; + char prefixlen[4]; +#ifdef __linux__ + char strprefix[INET6_ADDRSTRLEN + 1 + 4]; + char *dev; + char *argv[10]; +#else + char *argv[8]; +#endif + int argc = 0; + + inet_ntop(AF_INET6, dst, strdst, sizeof(strdst)); + getnameinfo(gw, gw_len, strgw, sizeof(strgw), NULL, 0, NI_NUMERICHOST); +#ifdef __linux__ + if ((dev = strrchr(strgw, '%')) != NULL) + *dev++ = '\0'; + snprintf(strprefix, sizeof(strprefix), "%s/%d", strdst, plen); + if (dev != NULL) + dprintf(LOG_DEBUG, FNAME, "route -A inet6 add %s gw %s dev %s", + strprefix, strgw, dev); + else + dprintf(LOG_DEBUG, FNAME, "route -A inet6 add %s gw %s", + strprefix, strgw); + argv[argc++] = PATH_ROUTE; + argv[argc++] = "-A"; + argv[argc++] = "inet6"; + argv[argc++] = "add"; + argv[argc++] = strprefix; + argv[argc++] = "gw"; + argv[argc++] = strgw; + if (dev != NULL) { + argv[argc++] = "dev"; + argv[argc++] = dev; + } +#else + snprintf(prefixlen, sizeof(prefixlen), "%d", plen); + dprintf(LOG_DEBUG, FNAME, "route add -inet6 %s -prefixlen %s %s", + strdst, prefixlen, strgw); + argv[argc++] = PATH_ROUTE; + argv[argc++] = "add"; + argv[argc++] = "-inet6"; + argv[argc++] = strdst; + argv[argc++] = "-prefixlen"; + argv[argc++] = prefixlen; + argv[argc++] = strgw; +#endif + argv[argc++] = NULL; + return (execute(PATH_ROUTE, argv)); +} + +static int +rt_add_reject(dst, plen) + struct in6_addr *dst; + int plen; +{ + char strdst[INET6_ADDRSTRLEN]; + char prefixlen[4]; + char *argv[9]; + int argc = 0; + + inet_ntop(AF_INET6, dst, strdst, sizeof(strdst)); + snprintf(prefixlen, sizeof(prefixlen), "%d", plen); + dprintf(LOG_DEBUG, FNAME, + "route add -inet6 %s -prefixlen %s ::1 -reject", + strdst, prefixlen); + argv[argc++] = PATH_ROUTE; + argv[argc++] = "add"; + argv[argc++] = "-inet6"; + argv[argc++] = strdst; + argv[argc++] = "-prefixlen"; + argv[argc++] = prefixlen; + argv[argc++] = "::1"; + argv[argc++] = "-reject"; + argv[argc++] = NULL; + return (execute(PATH_ROUTE, argv)); +} + +static int +rt_del(dst, plen) + struct in6_addr *dst; + int plen; +{ + char strdst[INET6_ADDRSTRLEN]; + char prefixlen[4]; +#ifdef __linux__ + char strprefix[INET6_ADDRSTRLEN + 1 + 4]; + char *argv[6]; +#else + char *argv[8]; +#endif + int argc = 0; + int fd, status; + pid_t pid; + + inet_ntop(AF_INET6, dst, strdst, sizeof(strdst)); + snprintf(prefixlen, sizeof(prefixlen), "%d", plen); +#ifdef __linux__ + snprintf(strprefix, sizeof(strprefix), "%s/%d", strdst, plen); + dprintf(LOG_DEBUG, FNAME, "route -A inet6 del %s", strprefix); + argv[argc++] = PATH_ROUTE; + argv[argc++] = "-A"; + argv[argc++] = "inet6"; + argv[argc++] = "del"; + argv[argc++] = strprefix; +#else + dprintf(LOG_DEBUG, FNAME, "route delete -inet6 %s -prefixlen %s", + strdst, prefixlen); + argv[argc++] = PATH_ROUTE; + argv[argc++] = "delete"; + argv[argc++] = "-inet6"; + argv[argc++] = strdst; + argv[argc++] = "-prefixlen"; + argv[argc++] = prefixlen; +#endif + argv[argc++] = NULL; + return (execute(PATH_ROUTE, argv)); +} + +int +route_add(dst, plen, gw, gw_len) + struct in6_addr *dst; + int plen; + struct sockaddr *gw; + int gw_len; +{ + struct route_entry *rt; + + if ((rt = malloc(sizeof(*rt))) == NULL) { + dprintf(LOG_ERR, FNAME, "malloc: %s", strerror(errno)); + return (-1); + } + memset(rt, 0, sizeof(*rt)); + memcpy(&rt->rt_dst, dst, sizeof(rt->rt_dst)); + rt->rt_plen = plen; + TAILQ_INSERT_TAIL(&installed_routes, rt, rt_next); + + if (gw == NULL) + return (rt_add_reject(dst, plen)); + return (rt_add(dst, plen, gw, gw_len)); +} + +int +route_delete(dst, plen) + struct in6_addr *dst; + int plen; +{ + struct route_entry *rt; + + TAILQ_FOREACH(rt, &installed_routes, rt_next) + if (rt->rt_plen == plen && + memcmp(&rt->rt_dst, dst, sizeof(rt->rt_dst)) == 0) { + TAILQ_REMOVE(&installed_routes, rt, rt_next); + free(rt); + break; + } + + return (rt_del(dst, plen)); +} + +void +route_flush() +{ + struct route_entry *rt; + + while ((rt = TAILQ_FIRST(&installed_routes)) != NULL) { + rt_del(&rt->rt_dst, rt->rt_plen); + TAILQ_REMOVE(&installed_routes, rt, rt_next); + } } int Index: common.h diff -u common.h.orig common.h --- common.h.orig Sat Sep 2 17:00:24 2006 +++ common.h Tue Feb 6 16:36:51 2007 @@ -178,6 +178,10 @@ extern void duidfree __P((struct duid *)); extern int ifaddrconf __P((ifaddrconf_cmd_t, char *, struct sockaddr_in6 *, int, int, int)); +extern int execute __P((char *, char **)); +extern int route_add __P((struct in6_addr *, int, struct sockaddr *, int)); +extern int route_delete __P((struct in6_addr *, int)); +extern void route_flush __P((void)); /* missing */ #ifndef HAVE_STRLCAT Index: config.h diff -u config.h.orig config.h --- config.h.orig Wed Oct 4 22:10:23 2006 +++ config.h Tue Feb 6 16:36:51 2007 @@ -298,6 +298,7 @@ extern struct dhcp6_list bcmcslist; extern struct dhcp6_list bcmcsnamelist; extern long long optrefreshtime; +extern int install_route; extern struct dhcp6_if *ifinit __P((char *)); extern int ifreset __P((struct dhcp6_if *)); Index: dhcp6c.c diff -u -p dhcp6c.c.orig dhcp6c.c --- dhcp6c.c.orig Sun Jul 30 19:24:19 2006 +++ dhcp6c.c Tue Feb 6 16:36:51 2007 @@ -462,6 +462,8 @@ free_resources(freeifp) dhcp6_remove_event(ev); } } + + route_flush(); } static void Index: dhcp6c.conf.5 diff -u dhcp6c.conf.5.orig dhcp6c.conf.5 --- dhcp6c.conf.5.orig Sun Oct 1 17:31:41 2006 +++ dhcp6c.conf.5 Tue Feb 6 16:36:51 2007 @@ -141,6 +141,25 @@ must be provided. .El .\" +.Sh Option statement +An option statement specifies configuration parameters. +The format of the statement is as follows. +.Bl -tag -width Ds -compact +.It Xo +.Ic option Ar option-name Op Ar option-value +; +.Xc +The following options can be specified in an option statement. +.Bl -tag -width Ds -compact +.It Xo +.Ic install-route ; +.Xc +This statement specifies +.Nm dhcp6c +to install the reject route for the delegated prefix. +.El +.El +.\" .Sh Interface statement An interface statement specifies configuration parameters on the interface. Index: dhcp6s.c diff -u -p dhcp6s.c.orig dhcp6s.c --- dhcp6s.c.orig Mon Aug 7 13:35:32 2006 +++ dhcp6s.c Tue Feb 6 16:36:51 2007 @@ -212,6 +212,7 @@ static char *bindingstr __P((struct dhcp static int process_auth __P((struct dhcp6 *, ssize_t, struct host_conf *, struct dhcp6_optinfo *, struct dhcp6_optinfo *)); static inline char *clientstr __P((struct host_conf *, struct duid *)); +static int server6_route_add __P((struct dhcp6_list *, struct sockaddr *, int)); int main(argc, argv) @@ -578,6 +579,7 @@ static void process_signals() { if ((sig_flags & SIGF_TERM)) { + route_flush(); unlink(pid_file); exit(0); } @@ -831,6 +833,7 @@ server6_stop() /* Right now, we simply stop running */ dprintf(LOG_NOTICE, FNAME, "exiting"); + route_flush(); exit (0); } @@ -1381,6 +1384,9 @@ react_solicit(ifp, dh6, len, optinfo, fr } else resptype = DH6_ADVERTISE; + if (install_route && do_binding && !TAILQ_EMPTY(&optinfo->iapd_list)) + server6_route_add(&roptinfo.iapd_list, from, fromlen); + error = server6_send(resptype, ifp, dh6, optinfo, from, fromlen, &roptinfo, relayinfohead, client_conf); dhcp6_clear_options(&roptinfo); @@ -1610,6 +1616,9 @@ react_request(ifp, pi, dh6, len, optinfo goto fail; } + if (install_route && !TAILQ_EMPTY(&optinfo->iapd_list)) + server6_route_add(&roptinfo.iapd_list, from, fromlen); + /* send a reply message. */ (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, &roptinfo, relayinfohead, client_conf); @@ -2262,6 +2271,9 @@ release_binding_ia(iap, retlist, optinfo in6addr2str(&lvia->val_prefix6.addr, 0), lvia->val_prefix6.plen); + if (install_route) + route_delete(&lvia->val_prefix6.addr, + lvia->val_prefix6.plen); break; case DHCP6_LISTVAL_IANA: release_address(&lvia->val_prefix6.addr); @@ -2957,6 +2969,22 @@ free_binding(binding) } release_address(&lv->val_statefuladdr6.addr); } + } else if (binding->iatype == DHCP6_LISTVAL_IAPD && + install_route) { + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *lv; + + for (lv = TAILQ_FIRST(ia_list); lv; + lv = TAILQ_NEXT(lv, link)) { + if (lv->type != DHCP6_LISTVAL_PREFIX6) { + dprintf(LOG_ERR, FNAME, + "unexpected binding value type(%d)", + lv->type); + continue; + } + route_delete(&lv->val_prefix6.addr, + lv->val_prefix6.plen); + } } dhcp6_clear_list(&binding->val_list); break; @@ -3009,6 +3037,10 @@ binding_timo(arg) bindingstr(binding)); if (binding->iatype == DHCP6_LISTVAL_IANA) release_address(&iav->val_prefix6.addr); + else if (binding->iatype == DHCP6_LISTVAL_IAPD) + if (install_route) + route_delete(&iav->val_prefix6.addr, + iav->val_prefix6.plen); TAILQ_REMOVE(ia_list, iav, link); dhcp6_clear_listval(iav); } @@ -3291,4 +3323,27 @@ clientstr(conf, duid) return (conf->name); return (duidstr(duid)); +} + +static int +server6_route_add(iapd_list, gw, gw_len) + struct dhcp6_list *iapd_list; + struct sockaddr *gw; + int gw_len; +{ + struct dhcp6_listval *iapd, *dp; + + for (iapd = TAILQ_FIRST(iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + for (dp = TAILQ_FIRST(&iapd->sublist); dp; + dp = TAILQ_NEXT(dp, link)) { + if (dp->type != DHCP6_LISTVAL_PREFIX6) + continue; + route_delete(&dp->val_prefix6.addr, + dp->val_prefix6.plen); + route_add(&dp->val_prefix6.addr, dp->val_prefix6.plen, + gw, gw_len); + } + } + return (1); } Index: dhcp6s.conf.5 diff -u dhcp6s.conf.5.orig dhcp6s.conf.5 --- dhcp6s.conf.5.orig Sun Oct 1 17:31:41 2006 +++ dhcp6s.conf.5 Tue Feb 6 16:36:51 2007 @@ -178,6 +178,12 @@ particular lease duration in seconds. This option is only applicable to stateless configuration by information-request and reply exchanges. +.It Xo +.Ic install-route ; +.Xc +This statement specifies +.Nm dhcp6s +to install the route for the delegated prefix. .El .El .\" Index: prefixconf.c diff -u -p prefixconf.c.orig prefixconf.c --- prefixconf.c.orig Mon Aug 7 13:35:32 2006 +++ prefixconf.c Tue Feb 6 16:36:51 2007 @@ -217,6 +217,20 @@ update_prefix(ia, pinfo, pifc, dhcpifp, add_ifprefix(sp, pinfo, pif); } + if (install_route) { + struct dhcp6_ifprefix *ip; + int plen = sizeof(struct in6_addr) * 8; + + for (ip = TAILQ_FIRST(&sp->ifprefix_list); ip; + ip = TAILQ_NEXT(ip, plink)) { + if (ip->plen < plen) + plen = ip->plen; + } + if (pinfo->plen < plen) { + route_delete(&pinfo->addr, pinfo->plen); + route_add(&pinfo->addr, pinfo->plen, NULL, 0); + } + } } /* @@ -281,6 +295,18 @@ remove_siteprefix(sp) if (sp->timer) dhcp6_remove_timer(&sp->timer); + + if (install_route) { + int plen = sizeof(struct in6_addr) * 8; + + for (ip = TAILQ_FIRST(&sp->ifprefix_list); ip; + ip = TAILQ_NEXT(ip, plink)) { + if (ip->plen < plen) + plen = ip->plen; + } + if (sp->prefix.plen < plen) + route_delete(&sp->prefix.addr, sp->prefix.plen); + } /* remove all interface prefixes */ while ((ip = TAILQ_FIRST(&sp->ifprefix_list)) != NULL) {