X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=interface-ip.c;h=140ef3441ea50fcf0d9a3dde71a7731e9e25f4e9;hp=919c8cc83f9bf7a830e6540807ecbf1b8df93139;hb=d397e8ca5dd492a1fac2e186e2a8b91ac8e463df;hpb=1dd3df775f0beb089d8ed6ba665ee93dd4997847 diff --git a/interface-ip.c b/interface-ip.c index 919c8cc..140ef34 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -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 = { @@ -366,7 +368,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) const char *mask = strtok_r(NULL, "/", &saveptr); if (!addr || inet_pton(af, addr, &route->source) < 1) { - DPRINTF("Failed to parse route source: %s\n", addr); + DPRINTF("Failed to parse route source: %s\n", addr ? addr : "NULL"); goto error; } @@ -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) @@ -837,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; @@ -857,6 +889,9 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo // End-of-assignment sentinel c = malloc(sizeof(*c) + 1); + if (!c) + return; + c->assigned = 1 << (64 - prefix->length); c->length = 64; c->name[0] = 0; @@ -867,16 +902,24 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo if (prefix->excl_length > 0) { const char name[] = "!excluded"; c = malloc(sizeof(*c) + sizeof(name)); - c->assigned = ntohl(prefix->excl_addr.s6_addr32[1]) & - ((1 << (64 - prefix->length)) - 1); - c->length = prefix->excl_length; - c->addr = in6addr_any; - memcpy(c->name, name, sizeof(name)); - list_add(&c->head, &prefix->assignments); + if (c) { + c->assigned = ntohl(prefix->excl_addr.s6_addr32[1]) & + ((1 << (64 - prefix->length)) - 1); + c->length = prefix->excl_length; + c->addr = in6addr_any; + memcpy(c->name, name, sizeof(name)); + list_add(&c->head, &prefix->assignments); + } } 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) @@ -900,8 +943,12 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo size_t namelen = strlen(iface->name) + 1; c = malloc(sizeof(*c) + namelen); + if (!c) + continue; + 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); @@ -914,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); @@ -943,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) @@ -1001,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); @@ -1033,6 +1083,9 @@ interface_ip_add_device_prefix(struct interface *iface, struct in6_addr *addr, pclass = (iface) ? iface->name : "local"; struct device_prefix *prefix = calloc(1, sizeof(*prefix) + strlen(pclass) + 1); + if (!prefix) + return NULL; + prefix->length = length; prefix->addr = *addr; prefix->preferred_until = preferred_until; @@ -1186,21 +1239,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) @@ -1208,15 +1273,51 @@ 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; + 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) + 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); @@ -1253,6 +1354,9 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled) vlist_for_each_element(&ip->addr, addr, node) { bool v6 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6) ? true : false; + if (addr->flags & DEVADDR_EXTERNAL) + continue; + if (addr->enabled == enabled) continue; @@ -1278,6 +1382,9 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool 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;