From: Roy Marples Date: Sat, 22 Mar 2014 19:55:29 +0000 (+0000) Subject: If not specified, Delegated Prefixes will get an automatic SLA of the X-Git-Tag: v6.4.0~132 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=94a79cea059b28b40656ada0766863812c310a26;p=thirdparty%2Fdhcpcd.git If not specified, Delegated Prefixes will get an automatic SLA of the interface index. If the biggest SLA and the assigned prefix fits into a /64 then dhcpcd creates a /64 prefix so that SLAAC works. If bigger than /64 is needed then dhcpcd creates one rounded upto the nearest multiple of 8. Unless a configured SLA of 0 is assigned, a reject route for the Delegated Prefix is installed to stop unassigned addresses trying to be resolved upstream. Addresses added from Delegated Prefixes now have a default address suffix of 1 instead of using a SLAAC style address. --- diff --git a/dhcp6.c b/dhcp6.c index 2f40133a..716a3c22 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -1716,31 +1716,8 @@ dhcp6_startinit(struct interface *ifp) dhcp6_startdiscover(ifp); } -static uint32_t -dhcp6_findsla(const struct dhcpcd_ctx *ctx) -{ - uint32_t sla; - const struct interface *ifp; - const struct dhcp6_state *state; - - /* Slow, but finding the lowest free SLA is needed if we get a - * /62 or /63 prefix from upstream */ - for (sla = 0; sla < UINT32_MAX; sla++) { - TAILQ_FOREACH(ifp, ctx->ifaces, next) { - state = D6_CSTATE(ifp); - if (state && state->sla_set && state->sla == sla) - break; - } - if (ifp == NULL) - return sla; - } - - errno = E2BIG; - return 0; -} - static struct ipv6_addr * -dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix, +dhcp6_delegate_addr(struct interface *ifp, struct ipv6_addr *prefix, const struct if_sla *sla, struct interface *ifs) { struct dhcp6_state *state; @@ -1764,21 +1741,25 @@ dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix, state->reason = "DELEGATED6"; } - if (sla == NULL || sla->sla_set == 0) { - if (!state->sla_set) { - errno = 0; - state->sla = dhcp6_findsla(ifp->ctx); - if (errno) { - syslog(LOG_ERR, "%s: dhcp6_find_sla: %m", - ifp->name); - return NULL; - } - syslog(LOG_DEBUG, "%s: set SLA %d", - ifp->name, state->sla); - state->sla_set = 1; + if (sla == NULL) { + struct interface *ifi; + unsigned int idx; + + asla.sla = ifp->index; + /* Work out our largest index delegating to. */ + idx = 0; + TAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) { + if (ifi != ifp && ifi->index > idx) + idx = ifi->index; } - asla.sla = state->sla; - asla.prefix_len = 64; + asla.prefix_len = prefix->prefix_len + ffs((int)idx); + + /* Make a 64 prefix by default, as this maks SLAAC + * possible. Otherwise round up to the nearest octet. */ + if (asla.prefix_len < 64) + asla.prefix_len = 64; + else + asla.prefix_len = ROUNDUP8(asla.prefix_len); sla = &asla; } @@ -1808,14 +1789,10 @@ dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix, memcpy(&a->prefix.s6_addr, &addr.s6_addr, sizeof(a->prefix.s6_addr)); a->prefix_len = sla->prefix_len; - if (ipv6_makeaddr(&a->addr, ifp, &a->prefix, a->prefix_len) == -1) - { - ia = inet_ntop(AF_INET6, &a->addr.s6_addr, - iabuf, sizeof(iabuf)); - syslog(LOG_ERR, "%s: %m (%s/%d)", __func__, ia, a->prefix_len); - free(a); - return NULL; - } + /* Wang a 1 at the end as the prefix could be >64 + * making SLAAC impossible. */ + memcpy(&a->addr.s6_addr, &a->prefix.s6_addr, sizeof(a->addr.s6_addr)); + a->addr.s6_addr[sizeof(a->addr.s6_addr) - 1] += 1; /* Remove any exiting address */ TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) { @@ -1844,13 +1821,13 @@ dhcp6_delegate_prefix(struct interface *ifp) struct if_ia *ia; struct if_sla *sla; struct interface *ifd; - uint8_t carrier_warned; + uint8_t carrier_warned, abrt; ifo = ifp->options; state = D6_STATE(ifp); TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) { k = 0; - carrier_warned = 0; + carrier_warned = abrt = 0; TAILQ_FOREACH(ap, &state->addrs, next) { if (!(ap->flags & IPV6_AF_DELEGATEDPFX)) continue; @@ -1877,12 +1854,23 @@ dhcp6_delegate_prefix(struct interface *ifp) carrier_warned = 1; break; } + if (ap->prefix_len >= 64 && k) { + syslog(LOG_WARNING, + "%s: cannot automatically" + " assign more prefixes", + ifp->name); + abrt = 1; + break; + } if (dhcp6_delegate_addr(ifd, ap, NULL, ifp)) k++; } for (j = 0; j < ia->sla_len; j++) { sla = &ia->sla[j]; + if (sla->sla == 0) + ap->flags |= + IPV6_AF_DELEGATEDZERO; if (strcmp(ifd->name, sla->ifname)) continue; if (ifd->carrier == LINK_DOWN) { @@ -1897,10 +1885,10 @@ dhcp6_delegate_prefix(struct interface *ifp) sla, ifp)) k++; } - if (carrier_warned) + if (carrier_warned ||abrt) break; } - if (carrier_warned) + if (carrier_warned || abrt) break; } if (k && !carrier_warned) { diff --git a/dhcp6.h b/dhcp6.h index 949154fe..e5194975 100644 --- a/dhcp6.h +++ b/dhcp6.h @@ -196,8 +196,6 @@ struct dhcp6_state { struct in6_addr unicast; struct ipv6_addrhead addrs; uint32_t lowpl; - uint32_t sla; - uint8_t sla_set; char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE]; const char *reason; diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index 4ba033aa..60fb0c1b 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 March 18, 2014 +.Dd March 22, 2014 .Dt DHCPCD.CONF 5 .Os .Sh NAME @@ -221,25 +221,37 @@ Request a DHCPv6 Delegated Prefix for This option must be used in an .Ic interface block. +Unless a +.Ar sla_id +of 0 is assigned, a reject route is installed for the Delegated Prefix to +stop unallocated addresses being resolved upstream. If no .Ar interface -is given then we will assign a prefix to every other interface with a unique +is given then we will assign a prefix to every other interface with a .Ar sla_id -for each, starting from 0. +equivalent to the interface index assigned by the OS. Otherwise addresses are only assigned for each .Ar interface and .Ar sla_id . +Each assigned address will have a suffix of 1. You cannot assign a prefix to the requesting interface. .Nm dhcpcd has to be running for all the interfaces it is delegating to. A default .Ar prefix_len -of 64 is assumed. +of 64 is assumed, unless the maximum +.Ar sla_id +does not fit. +In this case +.Ar prefix_len +is increased to the highest multiple of 8 that can accomodate the +.Ar sla_id . .Ar sla_id is an integer and is added to the prefix which must fit inside .Ar prefix_len less the length of the delegated prefix. +.Ar sla_id can be 0 only if the Delegated Prefix is assigned to one interface. You can specify multiple .Ar interface / .Ar sla_id / @@ -254,7 +266,7 @@ IPv6RS should be disabled globally when requesting a Prefix Delegation like so: .D1 denyinterfaces eth3 .Pp .D1 interface eth0 -.D1 ia_pd 1 eth1/0 eth2/1 +.D1 ia_pd 1 eth1/1 eth2/2 .Pp .D1 # Disable automatic address configuration for eth1 .D1 # eth1 still gets a delegated prefix diff --git a/if-options.c b/if-options.c index ca311702..51743eae 100644 --- a/if-options.c +++ b/if-options.c @@ -1260,14 +1260,14 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, syslog(LOG_ERR, "%s: cannot assign IA_PD to itself", ifname); - return -1; + goto err_sla; } if (strlcpy(sla->ifname, p, sizeof(sla->ifname)) >= sizeof(sla->ifname)) { syslog(LOG_ERR, "%s: interface name too long", arg); - return -1; + goto err_sla; } p = np; if (p) { @@ -1279,15 +1279,22 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, else { errno = 0; sla->sla = atoint(p); + if (sla->sla == 0 && ia->sla_len > 1) { + syslog(LOG_ERR, "%s: cannot" + " assign multiple prefixes" + " with a SLA of 0", + ifname); + goto err_sla; + } sla->sla_set = 1; if (errno) - return -1; + goto err_sla; } if (np) { sla->prefix_len = atoint(np); if (sla->prefix_len < 0 || sla->prefix_len > 128) - return -1; + goto err_sla; } else sla->prefix_len = 64; } else { @@ -1311,8 +1318,11 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, } } } -#endif break; +err_sla: + ia->sla_len--; + return -1; +#endif case O_HOSTNAME_SHORT: ifo->options |= DHCPCD_HOSTNAME | DHCPCD_HOSTNAME_SHORT; break; diff --git a/ipv6.c b/ipv6.c index 0edb200d..b20bcd2a 100644 --- a/ipv6.c +++ b/ipv6.c @@ -328,8 +328,8 @@ ipv6_userprefix( { uint64_t vh, vl, user_low, user_high; - if (prefix_len < 0 || prefix_len > 64 || - result_len < 0 || result_len > 64) + if (prefix_len < 0 || prefix_len > 120 || + result_len < 0 || result_len > 120) { errno = EINVAL; return -1; @@ -869,6 +869,10 @@ make_prefix(const struct interface * ifp, const struct ra *rap, !(addr->flags & (IPV6_AF_ONLINK | IPV6_AF_DELEGATEDPFX))) return NULL; + /* Don't install a blackhole route when not creating bigger prefixes */ + if (addr->flags & IPV6_AF_DELEGATEDZERO) + return NULL; + r = make_route(ifp, rap); if (r == NULL) return NULL; diff --git a/ipv6.h b/ipv6.h index b602598f..c5d29e0b 100644 --- a/ipv6.h +++ b/ipv6.h @@ -42,7 +42,8 @@ #define ALLROUTERS "ff02::2" -#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) +#define ROUNDUP8(a) (1 + (((a) - 1) | 7)) +#define ROUNDUP16(a) (1 + (((a) - 1) | 16)) #ifndef ND6_INFINITE_LIFETIME # define ND6_INFINITE_LIFETIME ((uint32_t)~0) @@ -98,6 +99,7 @@ TAILQ_HEAD(ipv6_addrhead, ipv6_addr); #define IPV6_AF_DADCOMPLETED 0x0040 #define IPV6_AF_DELEGATED 0x0080 #define IPV6_AF_DELEGATEDPFX 0x0100 +#define IPV6_AF_DELEGATEDZERO 0X0200 struct rt6 { TAILQ_ENTRY(rt6) next;