wireless: clean up prev_config handling
[project/netifd.git] / interface-ip.c
index 1444fbfcdecf1e53927ae19a184649d7895dd182..a06a514b6ddea9771d5a82f088b7ad7ee94529fc 100644 (file)
@@ -43,6 +43,7 @@ enum {
        ROUTE_ONLINK,
        ROUTE_TYPE,
        ROUTE_PROTO,
+       ROUTE_DISABLED,
        __ROUTE_MAX
 };
 
@@ -59,6 +60,7 @@ static const struct blobmsg_policy route_attr[__ROUTE_MAX] = {
        [ROUTE_ONLINK] = { .name = "onlink", .type = BLOBMSG_TYPE_BOOL },
        [ROUTE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
        [ROUTE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING },
+       [ROUTE_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
 };
 
 const struct uci_blob_param_list route_attr_list = {
@@ -97,7 +99,7 @@ static struct uloop_timeout valid_until_timeout;
 static void
 clear_if_addr(union if_addr *a, int mask)
 {
-       int m_bytes = (mask + 7) / 8;
+       size_t m_bytes = (mask + 7) / 8;
        uint8_t m_clear = (1 << (m_bytes * 8 - mask)) - 1;
        uint8_t *p = (uint8_t *) a;
 
@@ -232,7 +234,9 @@ __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 ||
+                   ((route->mask == (*res)->mask) && (route->flags & DEVROUTE_METRIC)
+                    && (route->metric < (*res)->metric)))
                        *res = route;
        }
 }
@@ -253,12 +257,19 @@ interface_ip_find_route_target(struct interface *iface, union if_addr *a,
 }
 
 struct interface *
-interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *iface)
+interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *iface,
+                             bool exclude)
 {
        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);
+       struct interface *exclude_iface = NULL;
+
+       if (exclude) {
+               exclude_iface = iface;
+               iface = NULL;
+       }
 
        memset(&addr_zero, 0, sizeof(addr_zero));
        if (memcmp(&addr_zero, addr, addrsize) == 0)
@@ -274,6 +285,9 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if
                interface_ip_find_route_target(iface, addr, v6, &r_next);
        } else {
                vlist_for_each_element(&interfaces, iface, node) {
+                       if (iface == exclude_iface)
+                               continue;
+
                        /* look for locally addressable target first */
                        if (interface_ip_find_addr_target(iface, addr, v6))
                                return iface;
@@ -394,6 +408,9 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
 
        blobmsg_parse(route_attr, __ROUTE_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
 
+       if ((cur = tb[ROUTE_DISABLED]) != NULL && blobmsg_get_bool(cur))
+               return;
+
        if (!iface) {
                if ((cur = tb[ROUTE_INTERFACE]) == NULL)
                        return;
@@ -799,18 +816,15 @@ interface_update_proto_neighbor(struct vlist_tree *tree,
 }
 
 static void
-interface_update_proto_route(struct vlist_tree *tree,
-                            struct vlist_node *node_new,
-                            struct vlist_node *node_old)
+__interface_update_route(struct interface_ip_settings *ip,
+                        struct vlist_node *node_new,
+                        struct vlist_node *node_old)
 {
-       struct interface_ip_settings *ip;
-       struct interface *iface;
+       struct interface *iface = ip->iface;
        struct device *dev;
        struct device_route *route_old, *route_new;
        bool keep = false;
 
-       ip = container_of(tree, struct interface_ip_settings, route);
-       iface = ip->iface;
        dev = iface->l3_dev.dev;
 
        if (!node_new || !node_old)
@@ -843,30 +857,26 @@ interface_update_proto_route(struct vlist_tree *tree,
        }
 }
 
+static void
+interface_update_proto_route(struct vlist_tree *tree,
+                            struct vlist_node *node_new,
+                            struct vlist_node *node_old)
+{
+       struct interface_ip_settings *ip;
+
+       ip = container_of(tree, struct interface_ip_settings, route);
+       __interface_update_route(ip, node_new, node_old);
+}
+
 static void
 interface_update_host_route(struct vlist_tree *tree,
                             struct vlist_node *node_new,
                             struct vlist_node *node_old)
 {
        struct interface *iface;
-       struct device *dev;
-       struct device_route *route_old, *route_new;
 
        iface = container_of(tree, struct interface, host_routes);
-       dev = iface->l3_dev.dev;
-
-       route_old = container_of(node_old, struct device_route, node);
-       route_new = container_of(node_new, struct device_route, node);
-
-       if (node_old) {
-               system_del_route(dev, route_old);
-               free(route_old);
-       }
-
-       if (node_new) {
-               if (system_add_route(dev, route_new))
-                       route_new->failed = true;
-       }
+       __interface_update_route(&iface->proto_ip, node_new, node_old);
 }
 
 static void
@@ -962,9 +972,16 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
        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 (addr.valid_until && addr.valid_until - 1 <= now) {
+                       addr.valid_until = 0;
+                       addr.preferred_until = 0;
+               } else {
+                       /* Address is still valid; pass its ownership to kernel (see L-14 RFC 7084). */
+                       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,
@@ -983,7 +1000,10 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
                interface_set_route_info(iface, &route);
 
                system_del_route(l3_downlink, &route);
-               system_add_address(l3_downlink, &addr);
+               if (addr.valid_until)
+                       system_add_address(l3_downlink, &addr);
+               else
+                       system_del_address(l3_downlink, &addr);
 
                assignment->addr = in6addr_any;
                assignment->enabled = false;
@@ -1222,8 +1242,13 @@ void interface_refresh_assignments(bool hint)
        static bool refresh = false;
        if (!hint && refresh) {
                struct device_prefix *p;
-               list_for_each_entry(p, &prefixes, head)
-                       interface_update_prefix_assignments(p, true);
+               time_t now = system_get_rtime();
+
+               list_for_each_entry(p, &prefixes, head) {
+                       bool valid = !(p->valid_until && p->valid_until - 1 <= now);
+
+                       interface_update_prefix_assignments(p, valid);
+               }
        }
        refresh = hint;
 }
@@ -1231,9 +1256,12 @@ void interface_refresh_assignments(bool hint)
 void interface_update_prefix_delegation(struct interface_ip_settings *ip)
 {
        struct device_prefix *prefix;
+       time_t now = system_get_rtime();
 
        vlist_for_each_element(&ip->prefix, prefix, node) {
-               interface_update_prefix_assignments(prefix, !ip->no_delegation);
+               bool valid = !(prefix->valid_until && prefix->valid_until - 1 <= now);
+
+               interface_update_prefix_assignments(prefix, !ip->no_delegation && valid);
 
                if (ip->no_delegation) {
                        if (prefix->head.next)
@@ -1263,9 +1291,9 @@ interface_update_prefix(struct vlist_tree *tree,
        route.mask = (node_new) ? prefix_new->length : prefix_old->length;
        route.addr.in6 = (node_new) ? prefix_new->addr : prefix_old->addr;
 
-
        struct device_prefix_assignment *c;
        struct interface *iface;
+       bool new_valid = node_new && !(prefix_new->valid_until && prefix_new->valid_until - 1 <= system_get_rtime());
 
        if (node_old && node_new) {
                /* Move assignments and refresh addresses to update valid times */
@@ -1273,7 +1301,7 @@ 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);
+                               interface_set_prefix_address(c, prefix_new, iface, new_valid);
 
                if (prefix_new->preferred_until != prefix_old->preferred_until ||
                                prefix_new->valid_until != prefix_old->valid_until)
@@ -1283,7 +1311,7 @@ interface_update_prefix(struct vlist_tree *tree,
                system_add_route(NULL, &route);
 
                if (!prefix_new->iface || !prefix_new->iface->proto_ip.no_delegation)
-                       interface_update_prefix_assignments(prefix_new, true);
+                       interface_update_prefix_assignments(prefix_new, new_valid);
        } else if (node_old) {
                /* Remove null-route */
                interface_update_prefix_assignments(prefix_old, false);
@@ -1401,7 +1429,7 @@ void
 interface_add_dns_server_list(struct interface_ip_settings *ip, struct blob_attr *list)
 {
        struct blob_attr *cur;
-       int rem;
+       size_t rem;
 
        blobmsg_for_each_attr(cur, list, rem) {
                if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
@@ -1433,7 +1461,7 @@ void
 interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob_attr *list)
 {
        struct blob_attr *cur;
-       int rem;
+       size_t rem;
 
        blobmsg_for_each_attr(cur, list, rem) {
                if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
@@ -1538,7 +1566,9 @@ __interface_write_dns_entries(FILE *f, const char *jail)
 void
 interface_write_resolv_conf(const char *jail)
 {
-       size_t plen = (jail ? strlen(jail) + 1 : 0 ) + strlen(resolv_conf) + 1;
+       size_t plen = (jail ? strlen(jail) + 1 : 0 ) +
+           (strlen(resolv_conf) >= strlen(DEFAULT_RESOLV_CONF) ?
+           strlen(resolv_conf) : strlen(DEFAULT_RESOLV_CONF) ) + 1;
        char *path = alloca(plen);
        char *dpath = alloca(plen);
        char *tmppath = alloca(plen + 4);
@@ -1584,6 +1614,32 @@ interface_write_resolv_conf(const char *jail)
        }
 }
 
+static void
+interface_ip_set_route_enabled(struct interface_ip_settings *ip,
+                              struct device_route *route, bool enabled)
+{
+       struct device *dev = ip->iface->l3_dev.dev;
+
+       if (route->flags & DEVADDR_EXTERNAL)
+               return;
+
+       if (!enable_route(ip, route))
+               enabled = false;
+
+       if (route->enabled == enabled)
+               return;
+
+       if (enabled) {
+               interface_set_route_info(ip->iface, route);
+
+               if (system_add_route(dev, route))
+                       route->failed = true;
+       } else
+               system_del_route(dev, route);
+
+       route->enabled = enabled;
+}
+
 void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 {
        struct device_addr *addr;
@@ -1659,27 +1715,11 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
                addr->enabled = enabled;
        }
 
-       vlist_for_each_element(&ip->route, route, node) {
-               bool _enabled = enabled;
-
-               if (route->flags & DEVADDR_EXTERNAL)
-                       continue;
-
-               if (!enable_route(ip, route))
-                       _enabled = false;
-               if (route->enabled == _enabled)
-                       continue;
-
-               if (_enabled) {
-                       interface_set_route_info(ip->iface, route);
-
-                       if (system_add_route(dev, route))
-                               route->failed = true;
-               } else
-                       system_del_route(dev, route);
-
-               route->enabled = _enabled;
-       }
+       vlist_for_each_element(&ip->route, route, node)
+               interface_ip_set_route_enabled(ip, route, enabled);
+       if (ip == &iface->proto_ip)
+               vlist_for_each_element(&iface->host_routes, route, node)
+                       interface_ip_set_route_enabled(ip, route, enabled);
 
        vlist_for_each_element(&ip->neighbor, neighbor, node) {
                if (neighbor->enabled == enabled)
@@ -1703,11 +1743,13 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 
        if (ip->iface->policy_rules_set != enabled &&
            ip->iface->l3_dev.dev) {
-               set_ip_lo_policy(enabled, true, ip->iface);
+               if (ip->iface->l3_dev.dev->settings.ipv6) {
+                       set_ip_lo_policy(enabled, true, ip->iface);
+                       set_ip_source_policy(enabled, true, IPRULE_PRIORITY_REJECT + ip->iface->l3_dev.dev->ifindex,
+                                            NULL, 0, 0, ip->iface, "failed_policy", true);
+               }
                set_ip_lo_policy(enabled, false, ip->iface);
 
-               set_ip_source_policy(enabled, true, IPRULE_PRIORITY_REJECT + ip->iface->l3_dev.dev->ifindex,
-                       NULL, 0, 0, ip->iface, "failed_policy", true);
                ip->iface->policy_rules_set = enabled;
        }
 }