nterface-ip: remove superfluous iface check in interface_ip_set_enabled()
[project/netifd.git] / interface-ip.c
index f8dab84..1e49fe6 100644 (file)
@@ -204,7 +204,7 @@ __find_ip_route_target(struct interface_ip_settings *ip, union if_addr *a,
                if (route->flags & DEVROUTE_TABLE)
                        continue;
 
-               if (!*res || route->mask < (*res)->mask)
+               if (!*res || route->mask > (*res)->mask)
                        *res = route;
        }
 }
@@ -229,23 +229,17 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if
 {
        struct device_route *route, *r_next = NULL;
        bool defaultroute_target = false;
+       union if_addr addr_zero;
        int addrsize = v6 ? sizeof(addr->in6) : sizeof(addr->in);
 
-       route = calloc(1, sizeof(*route));
-       if (!route)
-               return NULL;
-
-       route->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
-       route->mask = v6 ? 128 : 32;
-       if (memcmp(&route->addr, addr, addrsize) == 0)
+       memset(&addr_zero, 0, sizeof(addr_zero));
+       if (memcmp(&addr_zero, addr, addrsize) == 0)
                defaultroute_target = true;
-       else
-               memcpy(&route->addr, addr, addrsize);
 
        if (iface) {
                /* look for locally addressable target first */
                if (interface_ip_find_addr_target(iface, addr, v6))
-                       goto done;
+                       return iface;
 
                /* do not stop at the first route, let the lookup compare
                 * masks to find the best match */
@@ -254,7 +248,7 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if
                vlist_for_each_element(&interfaces, iface, node) {
                        /* look for locally addressable target first */
                        if (interface_ip_find_addr_target(iface, addr, v6))
-                               goto done;
+                               return iface;
 
                        /* do not stop at the first route, let the lookup compare
                         * masks to find the best match */
@@ -262,23 +256,27 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if
                }
        }
 
-       if (!r_next) {
-               free(route);
+       if (!r_next)
                return NULL;
-       }
 
        iface = r_next->iface;
+       if (defaultroute_target)
+               return iface;
+
+       route = calloc(1, sizeof(*route));
+       if (!route)
+               return NULL;
+
+       route->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+       route->mask = v6 ? 128 : 32;
+       memcpy(&route->addr, addr, addrsize);
        memcpy(&route->nexthop, &r_next->nexthop, sizeof(route->nexthop));
        route->mtu = r_next->mtu;
        route->metric = r_next->metric;
        route->table = r_next->table;
-
-done:
        route->iface = iface;
-       if (defaultroute_target)
-               free(route);
-       else
-               vlist_add(&iface->host_routes, &route->node, route);
+       vlist_add(&iface->host_routes, &route->node, route);
+
        return iface;
 }
 
@@ -566,8 +564,9 @@ interface_update_proto_addr(struct vlist_tree *tree,
                                a_old->preferred_until != a_new->preferred_until)
                        replace = true;
 
-               if ((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET4 &&
-                   a_new->broadcast != a_old->broadcast)
+               if (((a_new->flags & DEVADDR_FAMILY) == DEVADDR_INET4) &&
+                    (a_new->broadcast != a_old->broadcast ||
+                     a_new->point_to_point != a_old->point_to_point))
                        keep = false;
        }
 
@@ -711,9 +710,16 @@ random_ifaceid(struct in6_addr *addr)
        addr->s6_addr32[3] = (uint32_t)mrand48();
 }
 
-static void
+static bool
 eui64_ifaceid(struct interface *iface, struct in6_addr *addr)
 {
+       struct device_settings st;
+
+       device_merge_settings(iface->l3_dev.dev, &st);
+
+       if (!(st.flags & DEV_OPT_MACADDR))
+               return false;
+
        /* get mac address */
        uint8_t *macaddr = iface->l3_dev.dev->settings.macaddr;
        uint8_t *ifaceid = addr->s6_addr + 8;
@@ -722,11 +728,15 @@ eui64_ifaceid(struct interface *iface, struct in6_addr *addr)
        ifaceid[3] = 0xff;
        ifaceid[4] = 0xfe;
        ifaceid[0] ^= 0x02;
+
+       return true;
 }
 
-static void
+static bool
 generate_ifaceid(struct interface *iface, struct in6_addr *addr)
 {
+       bool ret = true;
+
        /* generate new iface id */
        switch (iface->assignment_iface_id_selection) {
        case IFID_FIXED:
@@ -740,9 +750,13 @@ generate_ifaceid(struct interface *iface, struct in6_addr *addr)
                break;
        case IFID_EUI64:
                /* eui64 */
-               eui64_ifaceid(iface, addr);
+               ret = eui64_ifaceid(iface, addr);
+               break;
+       default:
+               ret = false;
                break;
        }
+       return ret;
 }
 
 static void
@@ -760,15 +774,7 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
        memset(&addr, 0, sizeof(addr));
        memset(&route, 0, sizeof(route));
 
-       if (IN6_IS_ADDR_UNSPECIFIED(&assignment->addr)) {
-               addr.addr.in6 = prefix->addr;
-               addr.addr.in6.s6_addr32[1] |= htonl(assignment->assigned);
-               generate_ifaceid(iface, &addr.addr.in6);
-               assignment->addr = addr.addr.in6;
-       }
-       else
-               addr.addr.in6 = assignment->addr;
-
+       addr.addr.in6 = assignment->addr;
        addr.mask = assignment->length;
        addr.flags = DEVADDR_INET6 | DEVADDR_OFFLINK;
        addr.preferred_until = prefix->preferred_until;
@@ -777,15 +783,18 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
        route.flags = DEVADDR_INET6;
        route.mask = addr.mask < 64 ? 64 : addr.mask;
        route.addr = addr.addr;
-       clear_if_addr(&route.addr, route.mask);
-       interface_set_route_info(iface, &route);
 
        if (!add && assignment->enabled) {
                time_t now = system_get_rtime();
+
                addr.preferred_until = now;
                if (!addr.valid_until || addr.valid_until - now > 7200)
                        addr.valid_until = now + 7200;
 
+               if (iface->ip6table)
+                       set_ip_source_policy(false, true, IPRULE_PRIORITY_ADDR_MASK, &addr.addr,
+                                       addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
+
                if (prefix->iface) {
                        if (prefix->iface->ip6table)
                                set_ip_source_policy(false, true, IPRULE_PRIORITY_NW, &addr.addr,
@@ -795,23 +804,46 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
                                                        addr.mask, 0, iface, "unreachable", true);
                }
 
+               clear_if_addr(&route.addr, route.mask);
+               interface_set_route_info(iface, &route);
+
                system_del_route(l3_downlink, &route);
                system_add_address(l3_downlink, &addr);
 
+               assignment->addr = in6addr_any;
                assignment->enabled = false;
-       } else if (add && (iface->state == IFS_UP || iface->state == IFS_SETUP) &&
-                       !system_add_address(l3_downlink, &addr)) {
+       } else if (add && (iface->state == IFS_UP || iface->state == IFS_SETUP)) {
+               if (IN6_IS_ADDR_UNSPECIFIED(&addr.addr.in6)) {
+                       addr.addr.in6 = prefix->addr;
+                       addr.addr.in6.s6_addr32[1] |= htonl(assignment->assigned);
+                       if (!generate_ifaceid(iface, &addr.addr.in6))
+                               return;
+
+                       assignment->addr = addr.addr.in6;
+                       route.addr = addr.addr;
+               }
 
-               if (prefix->iface && !assignment->enabled) {
-                       set_ip_source_policy(true, true, IPRULE_PRIORITY_REJECT, &addr.addr,
-                                       addr.mask, 0, iface, "unreachable", true);
+               if (system_add_address(l3_downlink, &addr))
+                       return;
 
-                       if (prefix->iface->ip6table)
-                               set_ip_source_policy(true, true, IPRULE_PRIORITY_NW, &addr.addr,
-                                               addr.mask, prefix->iface->ip6table, iface, NULL, true);
+               if (!assignment->enabled) {
+                       if (iface->ip6table)
+                               set_ip_source_policy(true, true, IPRULE_PRIORITY_ADDR_MASK, &addr.addr,
+                                               addr.mask < 64 ? 64 : addr.mask, iface->ip6table, NULL, NULL, false);
+
+                       if (prefix->iface) {
+                               set_ip_source_policy(true, true, IPRULE_PRIORITY_REJECT, &addr.addr,
+                                               addr.mask, 0, iface, "unreachable", true);
+
+                               if (prefix->iface->ip6table)
+                                       set_ip_source_policy(true, true, IPRULE_PRIORITY_NW, &addr.addr,
+                                                       addr.mask, prefix->iface->ip6table, iface, NULL, true);
+                       }
                }
 
-               route.metric = iface->metric;
+               clear_if_addr(&route.addr, route.mask);
+               interface_set_route_info(iface, &route);
+
                system_add_route(l3_downlink, &route);
 
                if (uplink && uplink->l3_dev.dev && !(l3_downlink->settings.flags & DEV_OPT_MTU6)) {
@@ -850,6 +882,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 +945,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 +980,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 +993,27 @@ 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;
-                               }
+                       entry = calloc(1, sizeof(*entry));
+                       if (!entry) {
+                               free(c);
+                               continue;
                        }
-                       list_add_tail(&c->head, next);
+
+                       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 +1022,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 +1081,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);
@@ -1258,13 +1321,14 @@ __interface_write_dns_entries(FILE *f)
 
        avl_for_each_element(&resolv_conf_iface_entries, entry, node) {
                iface = (struct interface *)entry->node.key;
+               struct device *dev = iface->l3_dev.dev;
 
                fprintf(f, "# Interface %s\n", iface->name);
 
-               write_resolv_conf_entries(f, &iface->config_ip, iface->ifname);
+               write_resolv_conf_entries(f, &iface->config_ip, dev->ifname);
 
                if (!iface->proto_ip.no_dns)
-                       write_resolv_conf_entries(f, &iface->proto_ip, iface->ifname);
+                       write_resolv_conf_entries(f, &iface->proto_ip, dev->ifname);
        }
 
        avl_remove_all_elements(&resolv_conf_iface_entries, entry, node, n_entry)
@@ -1378,7 +1442,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
                        if (!strcmp(a->name, ip->iface->name))
                                interface_set_prefix_address(a, c, ip->iface, enabled);
 
-       if (ip->iface && ip->iface->policy_rules_set != enabled &&
+       if (ip->iface->policy_rules_set != enabled &&
            ip->iface->l3_dev.dev) {
                set_ip_lo_policy(enabled, true, ip->iface);
                set_ip_lo_policy(enabled, false, ip->iface);