]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
If not specified, Delegated Prefixes will get an automatic SLA of the
authorRoy Marples <roy@marples.name>
Sat, 22 Mar 2014 19:55:29 +0000 (19:55 +0000)
committerRoy Marples <roy@marples.name>
Sat, 22 Mar 2014 19:55:29 +0000 (19:55 +0000)
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.

dhcp6.c
dhcp6.h
dhcpcd.conf.5.in
if-options.c
ipv6.c
ipv6.h

diff --git a/dhcp6.c b/dhcp6.c
index 2f40133aa54e41ed292a20be957305d1a9070d45..716a3c22f7e6cbce81fa4f5092b596089b2d233b 100644 (file)
--- 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 949154fea49251f0118322bf3f351cfb5d1761e4..e51949757c0b1edabe3d804b25a366f1e99df8df 100644 (file)
--- 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;
 
index 4ba033aad54eb1b0de25b5c2631643fd79741acc..60fb0c1ba2a7acd4f8e1b39b24652e94eb61d16f 100644 (file)
@@ -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
index ca311702bccfda5d50130f3aa7b07c16037bff5a..51743eae187fe43147a41896fb8b56ecdb0ac138 100644 (file)
@@ -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 0edb200dfa72a290585de80ae3ceacd38c559edf..b20bcd2a4e95590cd5e7e6529064755764517538 100644 (file)
--- 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 b602598f44695c2d47d88161da361f50d7fd9aa8..c5d29e0bffd293bbae8806449895721046140f96 100644 (file)
--- 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;