IPv6 prefixes: conform to RFC 6204 requirement L13
[project/netifd.git] / interface-ip.c
index 183935e..56f8bd9 100644 (file)
@@ -456,8 +456,13 @@ interface_set_prefix_address(struct interface *iface, bool add,
        addr.valid_until = assignment->prefix->valid_until;
 
        if (!add) {
-               if (assignment->enabled)
-                       system_del_address(l3_downlink, &addr);
+               if (assignment->enabled) {
+                       time_t now = system_get_rtime();
+                       addr.preferred_until = now;
+                       if (addr.valid_until - now > 7200)
+                               addr.valid_until = now + 7200;
+                       system_add_address(l3_downlink, &addr);
+               }
        } else {
                system_add_address(l3_downlink, &addr);
 
@@ -492,7 +497,6 @@ interface_update_prefix_assignments(struct vlist_tree *tree,
        } else if (node_old) {
                if (iface)
                        interface_set_prefix_address(iface, false, old);
-               free(old->name);
                free(old);
        } else if (node_new) {
                struct device_prefix *prefix = new->prefix;
@@ -523,14 +527,16 @@ void
 interface_ip_set_prefix_assignment(struct device_prefix *prefix,
                struct interface *iface, uint8_t length)
 {
+       struct device_prefix_assignment *assignment;
+
        if (!length || length > 64) {
-               struct device_prefix_assignment *assignment = vlist_find(
-                               prefix->assignments, &iface, assignment, node);
+               assignment = vlist_find(prefix->assignments, iface->name, assignment, node);
                if (assignment)
                        interface_set_prefix_address(iface, false, assignment);
        } else {
-               uint8_t length = iface->proto_ip.assignment_length;
                uint64_t want = 1ULL << (64 - length);
+               char *name;
+
                if (prefix->avail < want && prefix->avail > 0) {
                        do {
                                want = 1ULL << (64 - ++length);
@@ -540,11 +546,11 @@ interface_ip_set_prefix_assignment(struct device_prefix *prefix,
                if (prefix->avail < want)
                        return;
 
-               // Assignment
-               struct device_prefix_assignment *assignment = calloc(1, sizeof(*assignment));
+               assignment = calloc_a(sizeof(*assignment),
+                       &name, strlen(iface->name) + 1);
                assignment->prefix = prefix;
                assignment->length = length;
-               assignment->name = strdup(iface->name);
+               assignment->name = strcpy(name, iface->name);
 
                vlist_add(prefix->assignments, &assignment->node, assignment->name);
        }
@@ -574,9 +580,11 @@ interface_update_prefix(struct vlist_tree *tree,
                // Update all assignments
                struct device_prefix_assignment *assignment;
                struct vlist_tree *assignments = prefix_new->assignments;
-               vlist_for_each_element(assignments, assignment, node)
+               vlist_for_each_element(assignments, assignment, node) {
+                       assignment->prefix = prefix_new;
                        assignments->update(assignments,
                                        &assignment->node, &assignment->node);
+               }
        } else if (node_new) {
                prefix_new->avail = 1ULL << (64 - prefix_new->length);
                prefix_new->assignments = calloc(1, sizeof(*prefix_new->assignments));
@@ -589,8 +597,6 @@ interface_update_prefix(struct vlist_tree *tree,
                        interface_ip_set_prefix_assignment(prefix_new, iface,
                                        iface->proto_ip.assignment_length);
 
-               list_add(&prefix_new->head, &prefixes);
-
                // Set null-route to avoid routing loops
                system_add_route(NULL, &route);
        }
@@ -607,6 +613,9 @@ interface_update_prefix(struct vlist_tree *tree,
                }
                free(prefix_old);
        }
+
+       if (node_new)
+               list_add(&prefix_new->head, &prefixes);
 }
 
 void
@@ -630,12 +639,18 @@ void
 interface_ip_set_ula_prefix(const char *prefix)
 {
        char buf[INET6_ADDRSTRLEN + 4] = {0}, *saveptr;
-       strncpy(buf, prefix, sizeof(buf) - 1);
+       if (prefix)
+               strncpy(buf, prefix, sizeof(buf) - 1);
        char *prefixaddr = strtok_r(buf, "/", &saveptr);
 
        struct in6_addr addr;
-       if (!prefixaddr || inet_pton(AF_INET6, prefixaddr, &addr) < 1)
+       if (!prefixaddr || inet_pton(AF_INET6, prefixaddr, &addr) < 1) {
+               if (ula_prefix) {
+                       interface_update_prefix(NULL, NULL, &ula_prefix->node);
+                       ula_prefix = NULL;
+               }
                return;
+       }
 
        int length;
        char *prefixlen = strtok_r(NULL, ",", &saveptr);