interface-ip: route proto config support (FS#170)
[project/netifd.git] / interface-ip.c
index 26a2865..f8dab84 100644 (file)
@@ -40,6 +40,7 @@ enum {
        ROUTE_SOURCE,
        ROUTE_ONLINK,
        ROUTE_TYPE,
+       ROUTE_PROTO,
        __ROUTE_MAX
 };
 
@@ -54,7 +55,8 @@ static const struct blobmsg_policy route_attr[__ROUTE_MAX] = {
        [ROUTE_VALID] = { .name = "valid", .type = BLOBMSG_TYPE_INT32 },
        [ROUTE_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_STRING },
        [ROUTE_ONLINK] = { .name = "onlink", .type = BLOBMSG_TYPE_BOOL },
-       [ROUTE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }
+       [ROUTE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
+       [ROUTE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING },
 };
 
 const struct uci_blob_param_list route_attr_list = {
@@ -405,6 +407,14 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
                route->flags |= DEVROUTE_TYPE;
        }
 
+       if ((cur = tb[ROUTE_PROTO]) != NULL) {
+               if (!system_resolve_rt_proto(blobmsg_data(cur), &route->proto)) {
+                       DPRINTF("Failed to resolve proto type: %s\n", (char *) blobmsg_data(cur));
+                       goto error;
+               }
+               route->flags |= DEVROUTE_PROTO;
+       }
+
        interface_set_route_info(iface, route);
        vlist_add(&ip->route, &route->node, route);
        return;
@@ -478,10 +488,13 @@ interface_handle_subnet_route(struct interface *iface, struct device_addr *addr,
        memcpy(&r->addr, &addr->addr, sizeof(r->addr));
        clear_if_addr(&r->addr, r->mask);
 
-       r->flags |= DEVADDR_KERNEL;
+       if (!system_resolve_rt_proto("kernel", &r->proto))
+               return;
+
+       r->flags |= DEVROUTE_PROTO;
        system_del_route(dev, r);
 
-       r->flags &= ~DEVADDR_KERNEL;
+       r->flags &= ~DEVROUTE_PROTO;
        interface_set_route_info(iface, r);
 
        system_add_route(dev, r);
@@ -634,7 +647,7 @@ interface_update_proto_route(struct vlist_tree *tree,
        if (node_old && node_new)
                keep = !memcmp(&route_old->nexthop, &route_new->nexthop, sizeof(route_old->nexthop)) &&
                        (route_old->mtu == route_new->mtu) && (route_old->type == route_new->type) &&
-                       !route_old->failed;
+                       (route_old->proto == route_new->proto) && !route_old->failed;
 
        if (node_old) {
                if (!(route_old->flags & DEVADDR_EXTERNAL) && route_old->enabled && !keep)
@@ -1197,21 +1210,33 @@ write_resolv_conf_entries(FILE *f, struct interface_ip_settings *ip, const char
        }
 }
 
-void
-interface_write_resolv_conf(void)
+/* Sorting of interface resolver entries :               */
+/* Primary on interface dns_metric : lowest metric first */
+/* Secondary on interface metric : lowest metric first   */
+/* Finally alphabetical order of interface names         */
+static int resolv_conf_iface_cmp(const void *k1, const void *k2, void *ptr)
+{
+       const struct interface *iface1 = k1, *iface2 = k2;
+
+       if (iface1->dns_metric != iface2->dns_metric)
+               return iface1->dns_metric - iface2->dns_metric;
+
+       if (iface1->metric != iface2->metric)
+               return iface1->metric - iface2->metric;
+
+       return strcmp(iface1->name, iface2->name);
+}
+
+static void
+__interface_write_dns_entries(FILE *f)
 {
        struct interface *iface;
-       char *path = alloca(strlen(resolv_conf) + 5);
-       FILE *f;
-       uint32_t crcold, crcnew;
+       struct {
+               struct avl_node node;
+       } *entry, *n_entry;
+       struct avl_tree resolv_conf_iface_entries;
 
-       sprintf(path, "%s.tmp", resolv_conf);
-       unlink(path);
-       f = fopen(path, "w+");
-       if (!f) {
-               D(INTERFACE, "Failed to open %s for writing\n", path);
-               return;
-       }
+       avl_init(&resolv_conf_iface_entries, resolv_conf_iface_cmp, false, NULL);
 
        vlist_for_each_element(&interfaces, iface, node) {
                if (iface->state != IFS_UP)
@@ -1219,15 +1244,50 @@ interface_write_resolv_conf(void)
 
                if (vlist_simple_empty(&iface->proto_ip.dns_search) &&
                    vlist_simple_empty(&iface->proto_ip.dns_servers) &&
-                       vlist_simple_empty(&iface->config_ip.dns_search) &&
+                   vlist_simple_empty(&iface->config_ip.dns_search) &&
                    vlist_simple_empty(&iface->config_ip.dns_servers))
                        continue;
 
+               entry = calloc(1, sizeof(*entry));
+               if (!entry)
+                       continue;
+
+               entry->node.key = iface;
+               avl_insert(&resolv_conf_iface_entries, &entry->node);
+       }
+
+       avl_for_each_element(&resolv_conf_iface_entries, entry, node) {
+               iface = (struct interface *)entry->node.key;
+
                fprintf(f, "# Interface %s\n", iface->name);
+
                write_resolv_conf_entries(f, &iface->config_ip, iface->ifname);
+
                if (!iface->proto_ip.no_dns)
                        write_resolv_conf_entries(f, &iface->proto_ip, iface->ifname);
        }
+
+       avl_remove_all_elements(&resolv_conf_iface_entries, entry, node, n_entry)
+               free(entry);
+}
+
+void
+interface_write_resolv_conf(void)
+{
+       char *path = alloca(strlen(resolv_conf) + 5);
+       FILE *f;
+       uint32_t crcold, crcnew;
+
+       sprintf(path, "%s.tmp", resolv_conf);
+       unlink(path);
+       f = fopen(path, "w+");
+       if (!f) {
+               D(INTERFACE, "Failed to open %s for writing\n", path);
+               return;
+       }
+
+       __interface_write_dns_entries(f);
+
        fflush(f);
        rewind(f);
        crcnew = crc32_file(f);