X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=interface-ip.c;h=6ccc03ee762029c36db15af0e7a0ad090f080561;hp=f8dab840490981a2793226b8a4df862d906d4b3b;hb=1f5a29c3de6e3fec5883796ee772e25d56db6a69;hpb=650758b16e5185505a3fbc1307949340af70b611 diff --git a/interface-ip.c b/interface-ip.c index f8dab84..6ccc03e 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -262,6 +262,7 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if } } +done: if (!r_next) { free(route); return NULL; @@ -272,8 +273,6 @@ interface_ip_add_target_route(union if_addr *addr, bool v6, struct interface *if route->mtu = r_next->mtu; route->metric = r_next->metric; route->table = r_next->table; - -done: route->iface = iface; if (defaultroute_target) free(route); @@ -566,8 +565,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 +711,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 +729,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 +751,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 +775,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 +784,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 +805,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 +883,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 +946,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 +981,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 +994,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); @@ -964,9 +1021,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 +1080,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 +1320,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)