]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Automate the assignment of SLA ids if not given for Prefix Delegation.
authorRoy Marples <roy@marples.name>
Sun, 2 Jun 2013 09:51:30 +0000 (09:51 +0000)
committerRoy Marples <roy@marples.name>
Sun, 2 Jun 2013 09:51:30 +0000 (09:51 +0000)
This means you can just request an ia_pd and let dhcpcd do the rest :)

dhcp6.c
dhcp6.h
dhcpcd.conf.5.in
ipv6.c
ipv6.h

diff --git a/dhcp6.c b/dhcp6.c
index 4fcde6694429a131fe192f1074fa5683bd909642..af98fd64223377e4373a887d5e9d3b8dfb325aae 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1495,22 +1495,10 @@ dhcp6_startinit(struct interface *ifp)
        dhcp6_startdiscover(ifp);
 }
 
-static struct ipv6_addr *
-dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
-    const struct if_sla *sla, struct interface *ifs)
+static struct dhcp6_state *
+dhcp6_getstate(struct interface *ifp)
 {
-       struct in6_addr addr;
        struct dhcp6_state *state;
-       struct ipv6_addr *a, *ap;
-       char iabuf[INET6_ADDRSTRLEN];
-       const char *ia;
-
-       if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
-               sla->sla, &addr, sla->prefix_len) == -1)
-       {
-               syslog(LOG_ERR, "%s: invalid prefix", ifp->name);
-               return NULL;
-       }
 
        state = D6_STATE(ifp);
        if (state == NULL) {
@@ -1525,6 +1513,33 @@ dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
                state->state = DH6S_DELEGATED;
                state->reason = "DELEGATED6";
        }
+       return state;
+}
+
+static struct ipv6_addr *
+dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
+    const struct if_sla *sla, struct interface *ifs)
+{
+       struct in6_addr addr;
+       struct dhcp6_state *state;
+       struct ipv6_addr *a, *ap;
+       char iabuf[INET6_ADDRSTRLEN];
+       const char *ia;
+
+       if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
+               sla->sla, &addr, sla->prefix_len) == -1)
+       {
+               ia = inet_ntop(AF_INET6, &prefix->prefix.s6_addr,
+                   iabuf, sizeof(iabuf));
+               syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
+                       ifp->name, ia, prefix->prefix_len,
+                       sla->sla, sla->prefix_len);
+               return NULL;
+       }
+
+       state = dhcp6_getstate(ifp);
+       if (state == NULL)
+               return NULL;
 
        a = calloc(1, sizeof(*a));
        if (a == NULL) {
@@ -1565,6 +1580,29 @@ dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
        return a;
 }
 
+static uint32_t
+dhcp6_findsla(void)
+{
+       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, 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 void
 dhcp6_delegate_prefix(struct interface *ifp)
 {
@@ -1573,10 +1611,11 @@ dhcp6_delegate_prefix(struct interface *ifp)
        struct ipv6_addr *ap;
        size_t i, j, k;
        struct if_iaid *iaid;
-       struct if_sla *sla;
+       struct if_sla *sla, asla;
        struct interface *ifd;
        uint8_t carrier_warned;
 
+       asla.prefix_len = 64;
        ifo = ifp->options;
        state = D6_STATE(ifp);
        TAILQ_FOREACH(ifd, ifaces, next) {
@@ -1593,6 +1632,42 @@ dhcp6_delegate_prefix(struct interface *ifp)
                                if (memcmp(iaid->iaid, ap->iaid,
                                    sizeof(iaid->iaid)))
                                        continue;
+                               if (iaid->sla_len == 0) {
+                                       /* no SLA configured, so lets
+                                        * automate it */
+                                       if (ifp == ifd)
+                                               continue;
+                                       ifd_state = dhcp6_getstate(ifd);
+                                       if (ifd_state == NULL)
+                                               return;
+                                       if (!ifd_state->sla_set) {
+                                               errno = 0;
+                                               ifd_state->sla =
+                                                   dhcp6_findsla();
+                                               if (errno) {
+                                                       syslog(LOG_ERR,
+                                                       "%s: dhcp6_find_sla:"
+                                                       " %m", ifd->name);
+                                                       continue;
+                                               }
+                                               syslog(LOG_DEBUG,
+                                                   "%s: set SLA %d",
+                                                   ifd->name, ifd_state->sla);
+                                               ifd_state->sla_set = 1;
+                                       }
+                                       asla.sla = ifd_state->sla;
+                                       if (ifd->carrier == LINK_DOWN) {
+                                               syslog(LOG_DEBUG,
+                                                   "%s: has no carrier, cannot"
+                                                   " delegate addresses",
+                                                   ifd->name);
+                                               carrier_warned = 1;
+                                               break;
+                                       }
+                                       if (dhcp6_delegate_addr(ifd, ap,
+                                           &asla, ifp))
+                                               k++;
+                               }
                                for (j = 0; j < iaid->sla_len; j++) {
                                        sla = &iaid->sla[j];
                                        if (strcmp(ifd->name, sla->ifname))
diff --git a/dhcp6.h b/dhcp6.h
index 036e0df9623f0e8b1cfe9c30431fa0ac58eb92ef..151c3f5eb4a9309ef388d641ea8d1115dd729e11 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -179,6 +179,8 @@ struct dhcp6_state {
        uint32_t expire;
        struct ipv6_addrhead addrs;
        uint32_t lowpl;
+       uint32_t sla;
+       uint8_t sla_set;
        char leasefile[PATH_MAX];
        const char *reason;
 };
index 9f374e1d83f1ffd0c32bbc6618e7a49c335b8524..6db7c5b5b1ae739b0defdaf22dde5410c56b82f3 100644 (file)
@@ -133,7 +133,7 @@ is an empty string then the current system hostname is sent.
 If
 .Ar hostname
 is a FQDN (ie, contains a .) then it will be encoded as such.
-.It Ic ia_na Ar iaid
+.It Ic ia_na Op Ar iaid
 Request a DHCPv6 Normal Address for
 .Ar iaid .
 If none is specified, a default
@@ -141,26 +141,35 @@ If none is specified, a default
 is used.
 If the interface name is 4 characters or less then that is used,
 otherwise the interface index is used.
-.It Ic ia_ta Ar iaid
+.It Ic ia_ta Op Ar iaid
 Request a DHCPv6 Temporary Address for
 .Ar iaid .
-.It Ic ia_pd Ar iaid Op Ar interface / Ar sla_id Op / Ar prefix_len
+.It Ic ia_pd Op Ar iaid Op Ar interface / Ar sla_id Op / Ar prefix_len
 Request a DHCPv6 Delegated Prefix for
 .Ar iaid .
-If an
+If no
+.Ar interface
+is given then we will assign a prefix to every other interface with a unique
+.Ar sla_id
+for each, starting from 0.
+Otherwise addresses are only assigned for each
 .Ar interface
 and
 .Ar sla_id
-is given,
-then an address is also assigned to that
-.Ar interface .
 A default
 .Ar prefix_len
-of 64 is assumed if not given.
+of 64 is assumed.
 .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.
+You can specify multiple
+.Ar interface /
+.Ar sla_id /
+.Ar prefix_len
+per
+.Ic ia_pd ,
+space separated.
 IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
 .Pp
 .D1 noipv6rs
diff --git a/ipv6.c b/ipv6.c
index 6708d4498534030d476160e0904bb1f299de721a..e0014ad0bbe073172ab7e30f8de9a4b3daa57f1d 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -363,10 +363,10 @@ h64_to_in6(uint64_t vhigh, uint64_t vlow, struct in6_addr *add)
 int
 ipv6_userprefix(
        const struct in6_addr *prefix,  // prefix from router
-       int prefix_len,                 // length of prefix received
+       short prefix_len,               // length of prefix received
        uint64_t user_number,           // "random" number from user
        struct in6_addr *result,        // resultant prefix
-       int result_len)                 // desired prefix length
+       short result_len)               // desired prefix length
 {
        uint64_t vh, vl, user_low, user_high;
 
diff --git a/ipv6.h b/ipv6.h
index 635f16b66c691211342a675c00d95c77f672ca78..dcd1480cee391d1b91330e518771861f77da6fd1 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -144,8 +144,8 @@ int ipv6_makeaddr(struct in6_addr *, const struct interface *,
 int ipv6_makeprefix(struct in6_addr *, const struct in6_addr *, int);
 int ipv6_mask(struct in6_addr *, int);
 int ipv6_prefixlen(const struct in6_addr *);
-int ipv6_userprefix( const struct in6_addr *, int prefix_len,
-    uint64_t user_number, struct in6_addr *result, int result_len);
+int ipv6_userprefix( const struct in6_addr *, short prefix_len,
+    uint64_t user_number, struct in6_addr *result, short result_len);
 int ipv6_addaddr(struct ipv6_addr *);
 ssize_t ipv6_addaddrs(struct ipv6_addrhead *);
 void ipv6_handleifa(int, struct if_head *,