From: Roy Marples Date: Sun, 14 Feb 2016 08:04:55 +0000 (+0000) Subject: Implement IPv6 static address, fixes [29417b793e]. X-Git-Tag: v6.10.2~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=408fe75557cf974bae70d3cf4c10a63fc2b2da91;p=thirdparty%2Fdhcpcd.git Implement IPv6 static address, fixes [29417b793e]. --- diff --git a/dhcp6.c b/dhcp6.c index 143de845..2b13479f 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -2431,7 +2431,7 @@ dhcp6_script_try_run(struct interface *ifp, int delegated) continue; if (ap->flags & IPV6_AF_ONLINK) { if (!(ap->flags & IPV6_AF_DADCOMPLETED) && - ipv6_iffindaddr(ap->iface, &ap->addr)) + ipv6_iffindaddr(ap->iface, &ap->addr, IN6_IFF_TENTATIVE)) ap->flags |= IPV6_AF_DADCOMPLETED; if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0 && ((delegated && ap->delegating_iface) || diff --git a/dhcpcd.c b/dhcpcd.c index 4e37a681..78e08196 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -926,6 +926,8 @@ dhcpcd_startinterface(void *arg) } if (ifo->options & DHCPCD_IPV6) { + ipv6_startstatic(ifp); + if (ifo->options & DHCPCD_IPV6RS) ipv6nd_startrs(ifp); diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index 9a7ad827..182fb101 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 5, 2016 +.Dd February 14, 2016 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -558,12 +558,25 @@ then .Nm dhcpcd will not attempt to obtain a lease and just use the value for the address with an infinite lease time. +If you set +.Ic ip6_address , +.Nm dhcpcd +will continue auto-configuation as normal. .Pp -Here is an example which configures a static address, routes and dns. +Here is an example which configures two static addresss, an IPv4 router, DNS +and disables IPv6 auto-configuration. +You could also use the +.Ic inform6 +command here if you wished to obtain more information via DHCPv6. +For IPv4, you should use the +.Ic inform Ar ipaddress +option instead of setting a static address. .D1 interface eth0 +.D1 noipv6rs .D1 static ip_address=192.168.0.10/24 +.D1 static ip6_address=fd51:42f8:caae:d92e::ff/64 .D1 static routers=192.168.0.1 -.D1 static domain_name_servers=192.168.0.1 +.D1 static domain_name_servers=192.168.0.1 fd51:42f8:caae:d92e::1 .Pp Here is an example for PPP which gives the destination a default route. It uses the special destination keyword to insert the destination address diff --git a/if-options.c b/if-options.c index 9097fa0d..57362f9d 100644 --- a/if-options.c +++ b/if-options.c @@ -1110,6 +1110,24 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, logger(ctx, LOG_ERR, "invalid MTU %s", p); return -1; } + } else if (strncmp(arg, "ip6_address=", strlen("ip6_address=")) == 0) { + np = strchr(p, '/'); + if (np) + *np++ = '\0'; + if (inet_pton(AF_INET6, p, &ifo->req_addr6) == 1) { + if (np) { + ifo->req_prefix_len = (uint8_t)strtou(np, + NULL, 0, 0, 128, &e); + if (e) { + logger(ctx, LOG_ERR, + "%s: failed to " + "convert prefix len", + ifname); + return -1; + } + } else + ifo->req_prefix_len = 128; + } } else { dl = 0; if (ifo->config != NULL) { diff --git a/if-options.h b/if-options.h index 7d7e7c11..d87096fd 100644 --- a/if-options.h +++ b/if-options.h @@ -176,6 +176,8 @@ struct if_options { struct in_addr req_addr; struct in_addr req_mask; struct rt_head *routes; + struct in6_addr req_addr6; + uint8_t req_prefix_len; unsigned int mtu; char **config; diff --git a/ipv6.c b/ipv6.c index 5c5f93e9..ee4f275f 100644 --- a/ipv6.c +++ b/ipv6.c @@ -65,6 +65,7 @@ #include "eloop.h" #include "ipv6.h" #include "ipv6nd.h" +#include "script.h" #ifdef HAVE_MD5_H # ifndef DEPGEN @@ -89,25 +90,6 @@ # warning polling tentative address flags periodically #endif -#ifdef __linux__ - /* Match Linux defines to BSD */ -# define IN6_IFF_TEMPORARY IFA_F_TEMPORARY -# ifdef IFA_F_OPTIMISTIC -# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC) -# else -# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | 0x04) -# endif -# ifdef IF_F_DADFAILED -# define IN6_IFF_DUPLICATED IFA_F_DADFAILED -# else -# define IN6_IFF_DUPLICATED 0x08 -# endif -# define IN6_IFF_DETACHED 0 -#endif - -#define IN6_IFF_NOTUSEABLE \ - (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED) - /* Hackery at it's finest. */ #ifndef s6_addr32 # ifdef __sun @@ -382,7 +364,7 @@ ipv6_makestableprivate(struct in6_addr *addr, } int -ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp, +ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp, const struct in6_addr *prefix, int prefix_len) { const struct ipv6_addr *ap; @@ -647,7 +629,7 @@ ipv6_addaddr(struct ipv6_addr *ap, const struct timespec *now) } if (!(ap->flags & IPV6_AF_DADCOMPLETED) && - ipv6_iffindaddr(ap->iface, &ap->addr)) + ipv6_iffindaddr(ap->iface, &ap->addr, IN6_IFF_NOTUSEABLE)) ap->flags |= IPV6_AF_DADCOMPLETED; logger(ap->iface->ctx, ap->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG, @@ -1039,7 +1021,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, if (ap->addr_flags & IN6_IFF_TEMPORARY) ap->flags |= IPV6_AF_TEMPORARY; #endif - if (IN6_IS_ADDR_LINKLOCAL(&ap->addr)) { + if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) || ap->dadcallback) { #ifdef IPV6_POLLADDRFLAG if (ap->addr_flags & IN6_IFF_TENTATIVE) { struct timespec tv; @@ -1052,7 +1034,12 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, } #endif - if (!(ap->addr_flags & IN6_IFF_NOTUSEABLE)) { + if (ap->dadcallback) + ap->dadcallback(ap); + + if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) && + !(ap->addr_flags & IN6_IFF_NOTUSEABLE)) + { /* Now run any callbacks. * Typically IPv6RS or DHCPv6 */ while ((cb = @@ -1081,22 +1068,23 @@ ipv6_hasaddr(const struct interface *ifp) return 0; } -const struct ipv6_addr * -ipv6_iffindaddr(const struct interface *ifp, const struct in6_addr *addr) +struct ipv6_addr * +ipv6_iffindaddr(struct interface *ifp, const struct in6_addr *addr, + int revflags) { - const struct ipv6_state *state; - const struct ipv6_addr *ap; + struct ipv6_state *state; + struct ipv6_addr *ap; - state = IPV6_CSTATE(ifp); + state = IPV6_STATE(ifp); if (state) { TAILQ_FOREACH(ap, &state->addrs, next) { if (addr == NULL) { if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) && - !(ap->addr_flags & IN6_IFF_NOTUSEABLE)) + (!revflags || !(ap->addr_flags & revflags))) return ap; } else { if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr) && - !(ap->addr_flags & IN6_IFF_TENTATIVE)) + (!revflags || !(ap->addr_flags & revflags))) return ap; } } @@ -1295,39 +1283,146 @@ nextslaacprivate: return 1; } -/* Ensure the interface has a link-local address */ -int -ipv6_start(struct interface *ifp) +static int +ipv6_tryaddlinklocal(struct interface *ifp) { - const struct ipv6_state *state; - const struct ipv6_addr *ap; /* We can't assign a link-locak address to this, * the ppp process has to. */ if (ifp->flags & IFF_POINTOPOINT) return 0; - state = IPV6_CSTATE(ifp); - if (state) { - TAILQ_FOREACH(ap, &state->addrs, next) { - if (IN6_IS_ADDR_LINKLOCAL(&ap->addr) && - !(ap->addr_flags & IN6_IFF_DUPLICATED)) - break; - } + if (ipv6_iffindaddr(ifp, NULL, IN6_IFF_DUPLICATED) != NULL || + !CAN_ADD_LLADDR(ifp)) + return 0; + + return ipv6_addlinklocal(ifp); +} + +struct ipv6_addr * +ipv6_newaddr(struct interface *ifp, struct in6_addr *addr, uint8_t prefix_len) +{ + struct ipv6_addr *ia; + char buf[INET6_ADDRSTRLEN]; + const char *cbp; + struct ipv6_state *state; + + if ((ia = calloc(1, sizeof(*ia))) == NULL) + return NULL; + ia->iface = ifp; + ia->flags = IPV6_AF_NEW; + ia->addr_flags = IN6_IFF_TENTATIVE; + ia->addr = *addr; + ia->prefix_len = prefix_len; + if (ipv6_makeprefix(&ia->prefix, &ia->addr, ia->prefix_len) == -1) { + free(ia); + return NULL; + } + cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf)); + if (cbp) + snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d", + cbp, ia->prefix_len); + else + ia->saddr[0] = '\0'; + + state = IPV6_STATE(ifp); + TAILQ_INSERT_TAIL(&state->addrs, ia, next); + return ia; +} + +static void +ipv6_staticdadcallback(void *arg) +{ + struct ipv6_addr *ia = arg; + int wascompleted; + + wascompleted = (ia->flags & IPV6_AF_DADCOMPLETED); + ia->flags |= IPV6_AF_DADCOMPLETED; + if (ia->flags & IPV6_AF_DUPLICATED) + logger(ia->iface->ctx, LOG_WARNING, "%s: DAD detected %s", + ia->iface->name, ia->saddr); + else if (!wascompleted) { + logger(ia->iface->ctx, LOG_DEBUG, "%s: IPv6 static DAD completed", + ia->iface->name); + script_runreason(ia->iface, "STATIC6"); + } +} + +ssize_t +ipv6_env(char **env, const char *prefix, const struct interface *ifp) +{ + char **ep; + ssize_t n; + struct ipv6_addr *ia; + + ep = env; + n = 0; + ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6, IN6_IFF_NOTUSEABLE); + if (ia) { + if (env) + addvar(ifp->ctx, &ep, prefix, "ip6_address", ia->saddr); + n++; + } + + return n; +} + +int +ipv6_startstatic(struct interface *ifp) +{ + struct ipv6_addr *ia; + int run_script; + + if (IN6_IS_ADDR_UNSPECIFIED(&ifp->options->req_addr6)) + return 0; + + ia = ipv6_iffindaddr(ifp, &ifp->options->req_addr6, 0); + if (ia != NULL && + (ia->prefix_len != ifp->options->req_prefix_len || + ia->addr_flags & IN6_IFF_NOTUSEABLE)) + { + ipv6_deleteaddr(ia); + ia = NULL; + } + if (ia == NULL) { + ia = ipv6_newaddr(ifp, &ifp->options->req_addr6, + ifp->options->req_prefix_len); + if (ia == NULL) + return -1; + run_script = 0; + } else + run_script = 1; + ia->flags |= IPV6_AF_STATIC | IPV6_AF_ONLINK; + ia->prefix_vltime = ND6_INFINITE_LIFETIME; + ia->prefix_pltime = ND6_INFINITE_LIFETIME; + ia->dadcallback = ipv6_staticdadcallback; + ipv6_addaddr(ia, NULL); + if_initrt6(ifp); + ipv6_buildroutes(ifp->ctx); + if (run_script) + script_runreason(ifp, "STATIC6"); + return 1; +} + +/* Ensure the interface has a link-local address */ +int +ipv6_start(struct interface *ifp) +{ + + if (ipv6_tryaddlinklocal(ifp) == -1) + return -1; + + if (IPV6_CSTATE(ifp)) { /* Regenerate new ids */ if (ifp->options->options & DHCPCD_IPV6RA_OWN && ip6_use_tempaddr(ifp->name)) ipv6_regentempifid(ifp); - } else - ap = NULL; - - if (ap == NULL && - CAN_ADD_LLADDR(ifp) && - ipv6_addlinklocal(ifp) == -1) - return -1; + } /* Load existing routes */ if_initrt6(ifp); + if (!IN6_IS_ADDR_UNSPECIFIED(&ifp->options->req_addr6)) + ipv6_buildroutes(ifp->ctx); return 0; } @@ -1344,10 +1439,12 @@ ipv6_freedrop(struct interface *ifp, int drop) return; ipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, NULL); - - /* Because we need to cache the addresses we don't control, - * we only free the state on when NOT dropping addresses. */ - if (drop == 0) { + if (drop) { + if_initrt6(ifp); + ipv6_buildroutes(ifp->ctx); + } else { + /* Because we need to cache the addresses we don't control, + * we only free the state on when NOT dropping addresses. */ while ((cb = TAILQ_FIRST(&state->ll_callbacks))) { TAILQ_REMOVE(&state->ll_callbacks, cb, next); free(cb); @@ -2063,6 +2160,29 @@ make_router(const struct ra *rap) (IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \ IN6_ARE_ADDR_EQUAL(&((rtp)->net), &in6addr_any)) +static void +ipv6_build_static_routes(struct dhcpcd_ctx *ctx, struct rt6_head *dnr) +{ + const struct interface *ifp; + const struct ipv6_state *state; + const struct ipv6_addr *ia; + struct rt6 *rt; + + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + if ((state = IPV6_CSTATE(ifp)) == NULL) + continue; + TAILQ_FOREACH(ia, &state->addrs, next) { + if ((ia->flags & (IPV6_AF_ADDED | IPV6_AF_STATIC)) == + (IPV6_AF_ADDED | IPV6_AF_STATIC)) + { + rt = make_prefix(ifp, NULL, ia); + if (rt) + TAILQ_INSERT_TAIL(dnr, rt, next); + } + } + } +} + static void ipv6_build_ra_routes(struct ipv6_ctx *ctx, struct rt6_head *dnr, int expired) { @@ -2126,6 +2246,9 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx) TAILQ_INIT(&dnr); + /* Should static take priority? */ + ipv6_build_static_routes(ctx, &dnr); + /* First add reachable routers and their prefixes */ ipv6_build_ra_routes(ctx->ipv6, &dnr, 0); #ifdef HAVE_ROUTE_METRIC diff --git a/ipv6.h b/ipv6.h index 031610fb..a02f102a 100644 --- a/ipv6.h +++ b/ipv6.h @@ -119,6 +119,25 @@ #define IPV6_MANAGETEMPADDR #endif +#ifdef __linux__ + /* Match Linux defines to BSD */ +# define IN6_IFF_TEMPORARY IFA_F_TEMPORARY +# ifdef IFA_F_OPTIMISTIC +# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC) +# else +# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | 0x04) +# endif +# ifdef IF_F_DADFAILED +# define IN6_IFF_DUPLICATED IFA_F_DADFAILED +# else +# define IN6_IFF_DUPLICATED 0x08 +# endif +# define IN6_IFF_DETACHED 0 +#endif + +#define IN6_IFF_NOTUSEABLE \ + (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED) + struct ipv6_addr { TAILQ_ENTRY(ipv6_addr) next; struct interface *iface; @@ -157,8 +176,9 @@ TAILQ_HEAD(ipv6_addrhead, ipv6_addr); #define IPV6_AF_DELEGATEDPFX 0x0100 #define IPV6_AF_DELEGATEDZERO 0x0200 #define IPV6_AF_REQUEST 0x0400 +#define IPV6_AF_STATIC 0x0800 #ifdef IPV6_MANAGETEMPADDR -#define IPV6_AF_TEMPORARY 0X0800 +#define IPV6_AF_TEMPORARY 0X1000 #endif struct rt6 { @@ -244,7 +264,7 @@ struct ipv6_ctx *ipv6_init(struct dhcpcd_ctx *); int ipv6_makestableprivate(struct in6_addr *addr, const struct in6_addr *prefix, int prefix_len, const struct interface *ifp, int *dad_counter); -int ipv6_makeaddr(struct in6_addr *, const struct interface *, +int ipv6_makeaddr(struct in6_addr *, struct interface *, const struct in6_addr *, int); int ipv6_makeprefix(struct in6_addr *, const struct in6_addr *, int); int ipv6_mask(struct in6_addr *, int); @@ -261,8 +281,9 @@ void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *, int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct in6_addr *, int); int ipv6_publicaddr(const struct ipv6_addr *); -const struct ipv6_addr *ipv6_iffindaddr(const struct interface *, - const struct in6_addr *); +struct ipv6_addr *ipv6_newaddr(struct interface *, struct in6_addr *, uint8_t); +struct ipv6_addr *ipv6_iffindaddr(struct interface *, + const struct in6_addr *, int); int ipv6_hasaddr(const struct interface *); int ipv6_findaddrmatch(const struct ipv6_addr *, const struct in6_addr *, short); @@ -270,7 +291,7 @@ struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *, const struct in6_addr *, short); struct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *, const struct in6_addr *); -#define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL) +#define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE) int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *); void ipv6_freeaddr(struct ipv6_addr *); void ipv6_freedrop(struct interface *, int); @@ -290,6 +311,8 @@ void ipv6_addtempaddrs(struct interface *, const struct timespec *); #endif int ipv6_start(struct interface *); +int ipv6_startstatic(struct interface *); +ssize_t ipv6_env(char **, const char *, const struct interface *); void ipv6_ctxfree(struct dhcpcd_ctx *); int ipv6_handlert(struct dhcpcd_ctx *, int cmd, struct rt6 *); void ipv6_freerts(struct rt6_head *); @@ -298,6 +321,7 @@ void ipv6_buildroutes(struct dhcpcd_ctx *); #else #define ipv6_init(a) (NULL) #define ipv6_start(a) (-1) +#define ipv6_startstatic(a) (0) #define ipv6_hasaddr(a) (0) #define ipv6_free_ll_callbacks(a) {} #define ipv6_free(a) {} diff --git a/ipv6nd.c b/ipv6nd.c index 78be3b0b..43df4090 100644 --- a/ipv6nd.c +++ b/ipv6nd.c @@ -551,7 +551,7 @@ ipv6nd_scriptrun(struct ra *rap) { hasaddress = 1; if (!(ap->flags & IPV6_AF_DADCOMPLETED) && - ipv6_iffindaddr(ap->iface, &ap->addr)) + ipv6_iffindaddr(ap->iface, &ap->addr, IN6_IFF_TENTATIVE)) ap->flags |= IPV6_AF_DADCOMPLETED; if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) { logger(ap->iface->ctx, LOG_DEBUG, @@ -798,7 +798,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp, return; } - if (ipv6_iffindaddr(ifp, &ctx->from.sin6_addr)) { + if (ipv6_iffindaddr(ifp, &ctx->from.sin6_addr, IN6_IFF_TENTATIVE)) { logger(ifp->ctx, LOG_DEBUG, "%s: ignoring RA from ourself %s", ifp->name, ctx->sfrom); return; diff --git a/script.c b/script.c index 44bfed9b..6909f187 100644 --- a/script.c +++ b/script.c @@ -240,7 +240,7 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) #endif #ifdef INET6 const struct dhcp6_state *d6_state; - int dhcp6, ra; + int static6, dhcp6, ra; #endif #ifdef INET @@ -249,7 +249,7 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) istate = IPV4LL_CSTATE(ifp); #endif #ifdef INET6 - dhcp6 = ra = 0; + static6 = dhcp6 = ra = 0; d6_state = D6_CSTATE(ifp); #endif if (strcmp(reason, "TEST") == 0) { @@ -268,6 +268,8 @@ make_env(const struct interface *ifp, const char *reason, char ***argv) #endif } #ifdef INET6 + else if (strcmp(reason, "STATIC6") == 0) + static6 = 1; else if (reason[strlen(reason) - 1] == '6') dhcp6 = 1; else if (strcmp(reason, "ROUTERADVERT") == 0) @@ -492,6 +494,20 @@ dumplease: } #endif #ifdef INET6 + if (static6) { + n = ipv6_env(NULL, NULL, ifp); + if (n > 0) { + nenv = realloc(env, sizeof(char *) * + (elen + (size_t)n + 1)); + if (nenv == NULL) + goto eexit; + env = nenv; + n = ipv6_env(env + elen, "new", ifp); + if (n == -1) + goto eexit; + elen += (size_t)n; + } + } if (dhcp6 && D6_STATE_RUNNING(ifp)) { n = dhcp6_env(NULL, NULL, ifp, d6_state->new, d6_state->new_len);