interface-ip: set prefix indicator flag when IPv6 prefix lifetime changes
[project/netifd.git] / interface-ip.c
index f8dab84..dd4e01c 100644 (file)
@@ -850,6 +850,25 @@ static bool interface_prefix_assign(struct list_head *list,
        return false;
 }
 
+/*
+ * Sorting of assignment entries:
+ * Primary on assignment length: smallest assignment first
+ * Secondary on assignment weight: highest weight first
+ * Finally alphabetical order of interface names
+ */
+static int prefix_assignment_cmp(const void *k1, const void *k2, void *ptr)
+{
+       const struct device_prefix_assignment *a1 = k1, *a2 = k2;
+
+       if (a1->length != a2->length)
+               return a1->length - a2->length;
+
+       if (a1->weight != a2->weight)
+               return a2->weight - a1->weight;
+
+       return strcmp(a1->name, a2->name);
+}
+
 static void interface_update_prefix_assignments(struct device_prefix *prefix, bool setup)
 {
        struct device_prefix_assignment *c;
@@ -894,7 +913,13 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
        }
 
        bool assigned_any = false;
-       struct list_head assign_later = LIST_HEAD_INIT(assign_later);
+       struct {
+               struct avl_node node;
+       } *entry, *n_entry;
+       struct avl_tree assign_later;
+
+       avl_init(&assign_later, prefix_assignment_cmp, false, NULL);
+
        vlist_for_each_element(&interfaces, iface, node) {
                if (iface->assignment_length < 48 ||
                                iface->assignment_length > 64)
@@ -923,6 +948,7 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
 
                c->length = iface->assignment_length;
                c->assigned = iface->assignment_hint;
+               c->weight = iface->assignment_weight;
                c->addr = in6addr_any;
                c->enabled = false;
                memcpy(c->name, iface->name, namelen);
@@ -935,27 +961,25 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
                                                "of size %hhu for %s, trying other\n", c->length, c->name);
                        }
 
-                       struct list_head *next = &assign_later;
-                       struct device_prefix_assignment *n;
-                       list_for_each_entry(n, &assign_later, head) {
-                               if (n->length < c->length) {
-                                       next = &n->head;
-                                       break;
-                               }
-                       }
-                       list_add_tail(&c->head, next);
+                       entry = calloc(1, sizeof(*entry));
+                       if (!entry)
+                               continue;
+
+                       entry->node.key = c;
+                       avl_insert(&assign_later, &entry->node);
                }
 
                if (c->assigned != -1)
                        assigned_any = true;
        }
 
-       // Then try to assign all other + failed custom assignments
-       while (!list_empty(&assign_later)) {
-               c = list_first_entry(&assign_later, struct device_prefix_assignment, head);
-               list_del(&c->head);
-
+       /* Then try to assign all other + failed custom assignments */
+       avl_for_each_element_safe(&assign_later, entry, node, n_entry) {
                bool assigned = false;
+
+               c = (struct device_prefix_assignment *)entry->node.key;
+               avl_delete(&assign_later, &entry->node);
+
                do {
                        assigned = interface_prefix_assign(&prefix->assignments, c);
                } while (!assigned && ++c->length <= 64);
@@ -964,9 +988,10 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
                        netifd_log_message(L_WARNING, "Failed to assign subprefix "
                                        "of size %hhu for %s\n", c->length, c->name);
                        free(c);
-               } else {
+               } else
                        assigned_any = true;
-               }
+
+               free(entry);
        }
 
        list_for_each_entry(c, &prefix->assignments, head)
@@ -1022,6 +1047,10 @@ interface_update_prefix(struct vlist_tree *tree,
                list_for_each_entry(c, &prefix_new->assignments, head)
                        if ((iface = vlist_find(&interfaces, c->name, iface, node)))
                                interface_set_prefix_address(c, prefix_new, iface, true);
+
+               if (prefix_new->preferred_until != prefix_old->preferred_until ||
+                               prefix_new->valid_until != prefix_old->valid_until)
+                       ip->iface->updated |= IUF_PREFIX;
        } else if (node_new) {
                // Set null-route to avoid routing loops
                system_add_route(NULL, &route);