X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=interface-ip.c;h=c159e09133165aec461d34c8a42311d7f5e69bd3;hb=64f4eb79fe2977320660f8940bc908fa4def807b;hp=919c8cc83f9bf7a830e6540807ecbf1b8df93139;hpb=1dd3df775f0beb089d8ed6ba665ee93dd4997847;p=oweals%2Fnetifd.git diff --git a/interface-ip.c b/interface-ip.c index 919c8cc..c159e09 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -20,6 +20,10 @@ #include #include +#ifdef linux +#include +#endif + #include "netifd.h" #include "device.h" #include "interface.h" @@ -40,6 +44,7 @@ enum { ROUTE_SOURCE, ROUTE_ONLINK, ROUTE_TYPE, + ROUTE_PROTO, __ROUTE_MAX }; @@ -54,7 +59,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 = { @@ -62,6 +68,28 @@ const struct uci_blob_param_list route_attr_list = { .params = route_attr, }; +enum { + NEIGHBOR_INTERFACE, + NEIGHBOR_ADDRESS, + NEIGHBOR_MAC, + NEIGHBOR_PROXY, + NEIGHBOR_ROUTER, + __NEIGHBOR_MAX +}; + +static const struct blobmsg_policy neighbor_attr[__NEIGHBOR_MAX]={ + [NEIGHBOR_INTERFACE]= { .name = "interface", .type = BLOBMSG_TYPE_STRING}, + [NEIGHBOR_ADDRESS]= { .name = "ipaddr", .type = BLOBMSG_TYPE_STRING}, + [NEIGHBOR_MAC]= { .name = "mac", .type = BLOBMSG_TYPE_STRING}, + [NEIGHBOR_PROXY]= { .name = "proxy", .type = BLOBMSG_TYPE_BOOL}, + [NEIGHBOR_ROUTER]= {.name = "router", .type = BLOBMSG_TYPE_BOOL}, +}; + +const struct uci_blob_param_list neighbor_attr_list = { + .n_params = __NEIGHBOR_MAX, + .params = neighbor_attr, +}; + struct list_head prefixes = LIST_HEAD_INIT(prefixes); static struct device_prefix *ula_prefix = NULL; @@ -168,7 +196,11 @@ __find_ip_addr_target(struct interface_ip_settings *ip, union if_addr *a, bool v if (v6 != ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6)) continue; - // Handle offlink addresses correctly + if (((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4) && + addr->point_to_point && a->in.s_addr == addr->point_to_point) + return true; + + /* Handle offlink addresses correctly */ unsigned int mask = addr->mask; if ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6 && (addr->flags & DEVADDR_OFFLINK)) @@ -202,7 +234,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; } } @@ -227,23 +259,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 */ @@ -252,7 +278,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 */ @@ -260,23 +286,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; } @@ -298,6 +328,64 @@ interface_set_route_info(struct interface *iface, struct device_route *route) } } +void +interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6) +{ + struct interface_ip_settings *ip; + struct blob_attr *tb[__NEIGHBOR_MAX], *cur; + struct device_neighbor *neighbor; + int af = v6 ? AF_INET6: AF_INET; + struct ether_addr *ea; + + blobmsg_parse(neighbor_attr, __NEIGHBOR_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr)); + + if (!iface) { + if ((cur = tb[NEIGHBOR_INTERFACE]) == NULL) + return; + + iface = vlist_find(&interfaces, blobmsg_data(cur), iface, node); + + if (!iface) + return; + + ip = &iface->config_ip; + } else + ip = &iface->proto_ip; + + neighbor = calloc(1,sizeof(*neighbor)); + if (!neighbor) + return; + + neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4; + + if ((cur = tb[NEIGHBOR_ADDRESS]) != NULL){ + if (!inet_pton(af, blobmsg_data(cur), &neighbor->addr)) + goto error; + } else + goto error; + + if ((cur = tb[NEIGHBOR_MAC]) != NULL) { + neighbor->flags |= DEVNEIGH_MAC; + ea = ether_aton(blobmsg_data(cur)); + if (!ea) + goto error; + + memcpy(neighbor->macaddr, ea, 6); + } + + if ((cur = tb[NEIGHBOR_PROXY]) != NULL) + neighbor->proxy = blobmsg_get_bool(cur); + + if ((cur = tb[NEIGHBOR_ROUTER]) != NULL) + neighbor->router = blobmsg_get_bool(cur); + + vlist_add(&ip->neighbor, &neighbor->node, neighbor); + return; + +error: + free(neighbor); +} + void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) { @@ -357,7 +445,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) route->flags |= DEVROUTE_MTU; } - // Use source-based routing + /* Use source-based routing */ if ((cur = tb[ROUTE_SOURCE]) != NULL) { char *saveptr, *source = alloca(blobmsg_data_len(cur)); memcpy(source, blobmsg_data(cur), blobmsg_data_len(cur)); @@ -366,7 +454,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; } @@ -393,7 +481,7 @@ interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6) if ((cur = tb[ROUTE_VALID]) != NULL) { int64_t valid = blobmsg_get_u32(cur); int64_t valid_until = valid + (int64_t)system_get_rtime(); - if (valid_until <= LONG_MAX && valid != 0xffffffffLL) // Catch overflow + if (valid_until <= LONG_MAX && valid != 0xffffffffLL) /* Catch overflow */ route->valid_until = valid_until; } @@ -405,6 +493,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; @@ -420,6 +516,14 @@ addr_cmp(const void *k1, const void *k2, void *ptr) offsetof(struct device_addr, flags)); } +static int +neighbor_cmp(const void *k1, const void *k2, void *ptr) +{ + const struct device_neighbor *n1 = k1, *n2 = k2; + + return memcmp(&n1->addr, &n2->addr, sizeof(n2->addr)); +} + static int route_cmp(const void *k1, const void *k2, void *ptr) { @@ -478,10 +582,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); @@ -553,17 +660,20 @@ 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; } if (node_old) { if (a_old->enabled && !keep) { - //This is needed for source routing to work correctly. If a device - //has two connections to a network using the same subnet, adding - //only the network-rule will cause packets to be routed through the - //first matching network (source IP matches both masks). + /* + * This is needed for source routing to work correctly. If a device + * has two connections to a network using the same subnet, adding + * only the network-rule will cause packets to be routed through the + * first matching network (source IP matches both masks) + */ if (a_old->policy_table) interface_add_addr_rules(a_old, false); @@ -610,6 +720,44 @@ enable_route(struct interface_ip_settings *ip, struct device_route *route) return ip->enabled; } +static void +interface_update_proto_neighbor(struct vlist_tree *tree, + struct vlist_node * node_new, + struct vlist_node *node_old) +{ + struct device *dev; + struct device_neighbor *neighbor_old, *neighbor_new; + struct interface_ip_settings *ip; + bool keep = false; + + ip = container_of(tree, struct interface_ip_settings, neighbor); + dev = ip->iface->l3_dev.dev; + + neighbor_old = container_of(node_old, struct device_neighbor, node); + neighbor_new = container_of(node_new, struct device_neighbor, node); + + if (node_old && node_new) { + keep = (!memcmp(neighbor_old->macaddr, neighbor_new->macaddr, sizeof(neighbor_old->macaddr)) && + (neighbor_old->proxy == neighbor_new->proxy) && + (neighbor_old->router == neighbor_new->router)); + } + + if (node_old) { + if (!keep && neighbor_old->enabled) + system_del_neighbor(dev, neighbor_old); + + free(neighbor_old); + } + + if (node_new) { + if (!keep && ip->enabled) + if (system_add_neighbor(dev, neighbor_new)) + neighbor_new->failed = true; + + neighbor_new->enabled = ip->enabled; + } +} + static void interface_update_proto_route(struct vlist_tree *tree, struct vlist_node *node_new, @@ -634,7 +782,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) @@ -698,22 +846,32 @@ 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; - memcpy(ifaceid,macaddr,3); - memcpy(ifaceid + 5,macaddr + 3, 3); + memcpy(ifaceid, st.macaddr, 3); + memcpy(ifaceid + 5, st.macaddr + 3, 3); 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: @@ -727,9 +885,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 @@ -747,15 +909,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; @@ -764,15 +918,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, @@ -782,31 +939,57 @@ 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)) { int mtu = system_update_ipv6_mtu(uplink->l3_dev.dev, 0); int mtu_old = system_update_ipv6_mtu(l3_downlink, 0); - if (mtu > 0 && mtu_old > mtu) - system_update_ipv6_mtu(l3_downlink, mtu); + if (mtu > 0 && mtu_old != mtu) { + if (system_update_ipv6_mtu(l3_downlink, mtu) < 0 && mtu < mtu_old) + netifd_log_message(L_WARNING, "Failed to set IPv6 mtu to %d " + "on interface '%s'\n", mtu, iface->name); + } } assignment->enabled = true; @@ -837,12 +1020,31 @@ 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; struct interface *iface; - // Delete all assignments + /* Delete all assignments */ while (!list_empty(&prefix->assignments)) { c = list_first_entry(&prefix->assignments, struct device_prefix_assignment, head); @@ -855,34 +1057,45 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo if (!setup) return; - // End-of-assignment sentinel + /* 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; c->addr = in6addr_any; list_add(&c->head, &prefix->assignments); - // Excluded prefix + /* Excluded prefix */ 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) continue; - // Test whether there is a matching class + /* Test whether there is a matching class */ if (!list_empty(&iface->assignment_classes)) { bool found = false; @@ -900,13 +1113,17 @@ 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); - // First process all custom assignments, put all others in later-list + /* First process all custom assignments, put all others in later-list */ if (c->assigned == -1 || !interface_prefix_assign(&prefix->assignments, c)) { if (c->assigned != -1) { c->assigned = -1; @@ -914,27 +1131,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); @@ -943,9 +1160,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) @@ -969,6 +1187,20 @@ void interface_refresh_assignments(bool hint) refresh = hint; } +void interface_update_prefix_delegation(struct interface_ip_settings *ip) +{ + struct device_prefix *prefix; + + vlist_for_each_element(&ip->prefix, prefix, node) { + interface_update_prefix_assignments(prefix, !ip->no_delegation); + + if (ip->no_delegation) { + if (prefix->head.next) + list_del(&prefix->head); + } else + list_add(&prefix->head, &prefixes); + } +} static void interface_update_prefix(struct vlist_tree *tree, @@ -995,20 +1227,24 @@ interface_update_prefix(struct vlist_tree *tree, struct interface *iface; if (node_old && node_new) { - // Move assignments and refresh addresses to update valid times + /* Move assignments and refresh addresses to update valid times */ list_splice(&prefix_old->assignments, &prefix_new->assignments); 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 + /* Set null-route to avoid routing loops */ system_add_route(NULL, &route); if (!prefix_new->iface || !prefix_new->iface->proto_ip.no_delegation) interface_update_prefix_assignments(prefix_new, true); } else if (node_old) { - // Remove null-route + /* Remove null-route */ interface_update_prefix_assignments(prefix_old, false); system_del_route(NULL, &route); } @@ -1033,6 +1269,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; @@ -1087,7 +1326,7 @@ interface_ip_set_ula_prefix(const char *prefix) } } -void +static void interface_add_dns_server(struct interface_ip_settings *ip, const char *str) { struct dns_server *s; @@ -1123,7 +1362,7 @@ interface_add_dns_server_list(struct interface_ip_settings *ip, struct blob_attr if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) continue; - if (!blobmsg_check_attr(cur, NULL)) + if (!blobmsg_check_attr(cur, false)) continue; interface_add_dns_server(ip, blobmsg_data(cur)); @@ -1155,7 +1394,7 @@ interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob_attr if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) continue; - if (!blobmsg_check_attr(cur, NULL)) + if (!blobmsg_check_attr(cur, false)) continue; interface_add_dns_search_domain(ip, blobmsg_data(cur)); @@ -1186,21 +1425,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 +1459,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); @@ -1241,6 +1528,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled) { struct device_addr *addr; struct device_route *route; + struct device_neighbor *neighbor; struct device *dev; struct interface *iface; @@ -1253,6 +1541,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,9 +1569,11 @@ 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; - if (route->enabled == _enabled) continue; @@ -1294,6 +1587,19 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled) route->enabled = _enabled; } + vlist_for_each_element(&ip->neighbor, neighbor, node) { + if (neighbor->enabled == enabled) + continue; + + if (enabled) { + if(system_add_neighbor(dev, neighbor)) + neighbor->failed = true; + } else + system_del_neighbor(dev, neighbor); + + neighbor->enabled = enabled; + } + struct device_prefix *c; struct device_prefix_assignment *a; list_for_each_entry(c, &prefixes, head) @@ -1301,7 +1607,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); @@ -1322,6 +1628,7 @@ interface_ip_update_start(struct interface_ip_settings *ip) vlist_update(&ip->route); vlist_update(&ip->addr); vlist_update(&ip->prefix); + vlist_update(&ip->neighbor); } void @@ -1332,6 +1639,7 @@ interface_ip_update_complete(struct interface_ip_settings *ip) vlist_flush(&ip->route); vlist_flush(&ip->addr); vlist_flush(&ip->prefix); + vlist_flush(&ip->neighbor); interface_write_resolv_conf(); } @@ -1344,6 +1652,7 @@ interface_ip_flush(struct interface_ip_settings *ip) vlist_simple_flush_all(&ip->dns_search); vlist_flush_all(&ip->route); vlist_flush_all(&ip->addr); + vlist_flush_all(&ip->neighbor); vlist_flush_all(&ip->prefix); } @@ -1355,6 +1664,7 @@ __interface_ip_init(struct interface_ip_settings *ip, struct interface *iface) vlist_simple_init(&ip->dns_search, struct dns_search_domain, node); vlist_simple_init(&ip->dns_servers, struct dns_server, node); vlist_init(&ip->route, route_cmp, interface_update_proto_route); + vlist_init(&ip->neighbor, neighbor_cmp, interface_update_proto_neighbor); vlist_init(&ip->addr, addr_cmp, interface_update_proto_addr); vlist_init(&ip->prefix, prefix_cmp, interface_update_prefix); }