X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=interface.c;h=23c70326131dc6a5e0d2e45700178d04d95fb6e9;hp=245622766b4e14a33e22601b049df759adfb8af4;hb=37769eb666aa614b76df9b537db35c2c70e3ac7a;hpb=3b5a5c3e99a4ceb1428d58aecc38d5adf9438541 diff --git a/interface.c b/interface.c index 2456227..23c7032 100644 --- a/interface.c +++ b/interface.c @@ -26,6 +26,7 @@ struct vlist_tree interfaces; static LIST_HEAD(iface_all_users); +static unsigned int interface_serial = 0; enum { IFACE_ATTR_IFNAME, @@ -37,6 +38,12 @@ enum { IFACE_ATTR_DNS_SEARCH, IFACE_ATTR_METRIC, IFACE_ATTR_INTERFACE, + IFACE_ATTR_IP6ASSIGN, + IFACE_ATTR_IP6HINT, + IFACE_ATTR_IP4TABLE, + IFACE_ATTR_IP6TABLE, + IFACE_ATTR_IP6CLASS, + IFACE_ATTR_DELEGATE, IFACE_ATTR_MAX }; @@ -50,13 +57,20 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY }, [IFACE_ATTR_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY }, [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_IP6ASSIGN] = { .name = "ip6assign", .type = BLOBMSG_TYPE_INT32 }, + [IFACE_ATTR_IP6HINT] = { .name = "ip6hint", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_IP4TABLE] = { .name = "ip4table", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY }, + [IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL }, }; -static const union config_param_info iface_attr_info[IFACE_ATTR_MAX] = { +static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = { [IFACE_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_IP6CLASS] = { .type = BLOBMSG_TYPE_STRING }, }; -const struct config_param_list interface_attr_list = { +const struct uci_blob_param_list interface_attr_list = { .n_params = IFACE_ATTR_MAX, .params = iface_attrs, .info = iface_attr_info, @@ -79,7 +93,7 @@ void interface_add_error(struct interface *iface, const char *subsystem, struct interface_error *error; int i, len = 0; int *datalen = NULL; - char *dest; + char *dest, *d_subsys, *d_code; if (n_data) { len = n_data * sizeof(char *); @@ -90,13 +104,13 @@ void interface_add_error(struct interface *iface, const char *subsystem, } } - error = calloc(1, sizeof(*error) + sizeof(char *) + len); + error = calloc_a(sizeof(*error) + sizeof(char *) + len, + &d_subsys, subsystem ? strlen(subsystem) + 1 : 0, + &d_code, code ? strlen(code) + 1 : 0); if (!error) return; list_add_tail(&error->list, &iface->errors); - error->subsystem = subsystem; - error->code = code; dest = (char *) &error->data[n_data + 1]; for (i = 0; i < n_data; i++) { @@ -104,7 +118,13 @@ void interface_add_error(struct interface *iface, const char *subsystem, memcpy(dest, data[i], datalen[i]); dest += datalen[i]; } - error->data[n_data] = NULL; + error->data[n_data++] = NULL; + + if (subsystem) + error->subsystem = strcpy(d_subsys, subsystem); + + if (code) + error->code = strcpy(d_code, code); } static void @@ -133,7 +153,7 @@ interface_add_data(struct interface *iface, const struct blob_attr *data) n = calloc(1, sizeof(*n) + blob_pad_len(data)); memcpy(n->data, data, blob_pad_len(data)); - n->node.key = blobmsg_name(data); + n->node.key = blobmsg_name(n->data); o = avl_find_element(&iface->data, n->node.key, o, node); if (o) @@ -157,7 +177,7 @@ interface_event(struct interface *iface, enum interface_event ev) switch (ev) { case IFEV_UP: - adev = iface->main_dev.dev; + adev = iface->l3_dev.dev; /* fall through */ case IFEV_DOWN: alias_notify_device(iface->name, adev); @@ -192,8 +212,6 @@ mark_interface_down(struct interface *iface) void __interface_set_down(struct interface *iface, bool force) { - interface_clear_errors(iface); - if (iface->state == IFS_DOWN || iface->state == IFS_TEARDOWN) return; @@ -204,6 +222,9 @@ __interface_set_down(struct interface *iface, bool force) interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, force); if (force) interface_flush_state(iface); + + if (iface->dynamic) + vlist_delete(&interfaces, &iface->node); } static void @@ -267,6 +288,77 @@ interface_remove_user(struct interface_user *dep) } static void +interface_add_assignment_classes(struct interface *iface, struct blob_attr *list) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, list, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (!blobmsg_check_attr(cur, NULL)) + continue; + + struct interface_assignment_class *c = malloc(sizeof(*c) + blobmsg_data_len(cur)); + memcpy(c->name, blobmsg_data(cur), blobmsg_data_len(cur)); + list_add(&c->head, &iface->assignment_classes); + } +} + +static void +interface_clear_assignment_classes(struct interface *iface) +{ + while (!list_empty(&iface->assignment_classes)) { + struct interface_assignment_class *c = list_first_entry(&iface->assignment_classes, + struct interface_assignment_class, head); + list_del(&c->head); + free(c); + } +} + +static void +interface_merge_assignment_data(struct interface *old, struct interface *new) +{ + bool changed = (old->assignment_hint != new->assignment_hint || + old->assignment_length != new->assignment_length || + list_empty(&old->assignment_classes) != list_empty(&new->assignment_classes)); + + struct interface_assignment_class *c; + list_for_each_entry(c, &new->assignment_classes, head) { + // Compare list entries one-by-one to see if there was a change + if (list_empty(&old->assignment_classes)) // The new list is longer + changed = true; + + if (changed) + break; + + struct interface_assignment_class *c_old = list_first_entry(&old->assignment_classes, + struct interface_assignment_class, head); + + if (strcmp(c_old->name, c->name)) // An entry didn't match + break; + + list_del(&c_old->head); + free(c_old); + } + + // The old list was longer than the new one or the last entry didn't match + if (!list_empty(&old->assignment_classes)) { + interface_clear_assignment_classes(old); + changed = true; + } + + list_splice_init(&new->assignment_classes, &old->assignment_classes); + + if (changed) { + old->assignment_hint = new->assignment_hint; + old->assignment_length = new->assignment_length; + interface_refresh_assignments(true); + } +} + +static void interface_alias_cb(struct interface_user *dep, struct interface *iface, enum interface_event ev) { struct interface *alias = container_of(dep, struct interface, parent_iface); @@ -288,6 +380,7 @@ interface_alias_cb(struct interface_user *dep, struct interface *iface, enum int interface_remove_user(dep); break; case IFEV_RELOAD: + case IFEV_UPDATE: break; } } @@ -317,9 +410,21 @@ interface_claim_device(struct interface *iface) interface_set_available(iface, true); } +static void +interface_cleanup_state(struct interface *iface) +{ + interface_set_available(iface, false); + + interface_flush_state(iface); + interface_clear_errors(iface); + interface_set_proto_state(iface, NULL); + + if (iface->main_dev.dev) + interface_set_main_dev(iface, NULL); +} static void -interface_cleanup(struct interface *iface, bool reload) +interface_cleanup(struct interface *iface) { struct interface_user *dep, *tmp; @@ -329,20 +434,16 @@ interface_cleanup(struct interface *iface, bool reload) list_for_each_entry_safe(dep, tmp, &iface->users, list) interface_remove_user(dep); + interface_clear_assignment_classes(iface); interface_ip_flush(&iface->config_ip); - interface_flush_state(iface); - interface_clear_errors(iface); - - if (iface->main_dev.dev && !reload) - interface_set_main_dev(iface, NULL); - interface_set_proto_state(iface, NULL); + interface_cleanup_state(iface); } static void interface_do_free(struct interface *iface) { interface_event(iface, IFEV_FREE); - interface_cleanup(iface, false); + interface_cleanup(iface); free(iface->config); netifd_ubus_remove_interface(iface); avl_delete(&interfaces.avl, &iface->node.avl); @@ -353,7 +454,7 @@ static void interface_do_reload(struct interface *iface) { interface_event(iface, IFEV_RELOAD); - interface_cleanup(iface, true); + interface_cleanup_state(iface); proto_init_interface(iface, iface->config); interface_claim_device(iface); } @@ -385,15 +486,16 @@ interface_proto_cb(struct interface_proto_state *state, enum interface_proto_eve switch (ev) { case IFPEV_UP: - if (iface->state != IFS_SETUP) + if (iface->state != IFS_SETUP) { + interface_event(iface, IFEV_UPDATE); return; + } interface_ip_set_enabled(&iface->config_ip, true); system_flush_routes(); iface->state = IFS_UP; iface->start_time = system_get_rtime(); interface_event(iface, IFEV_UP); - interface_write_resolv_conf(); netifd_log_message(L_NOTICE, "Interface '%s' is now up\n", iface->name); break; case IFPEV_DOWN: @@ -415,6 +517,8 @@ interface_proto_cb(struct interface_proto_state *state, enum interface_proto_eve iface->state = IFS_SETUP; break; } + + interface_write_resolv_conf(); } void interface_set_proto_state(struct interface *iface, struct interface_proto_state *state) @@ -432,18 +536,21 @@ void interface_set_proto_state(struct interface *iface, struct interface_proto_s state->iface = iface; } -void -interface_init(struct interface *iface, const char *name, - struct blob_attr *config) +struct interface * +interface_alloc(const char *name, struct blob_attr *config) { + struct interface *iface; struct blob_attr *tb[IFACE_ATTR_MAX]; struct blob_attr *cur; const char *proto_name = NULL; + char *iface_name; - strncpy(iface->name, name, sizeof(iface->name) - 1); + iface = calloc_a(sizeof(*iface), &iface_name, strlen(name) + 1); + iface->name = strcpy(iface_name, name); INIT_LIST_HEAD(&iface->errors); INIT_LIST_HEAD(&iface->users); INIT_LIST_HEAD(&iface->hotplug_list); + INIT_LIST_HEAD(&iface->assignment_classes); interface_ip_init(iface); avl_init(&iface->data, avl_strcmp, false, NULL); iface->config_ip.enabled = false; @@ -473,7 +580,45 @@ interface_init(struct interface *iface, const char *name, if ((cur = tb[IFACE_ATTR_METRIC])) iface->metric = blobmsg_get_u32(cur); + if ((cur = tb[IFACE_ATTR_IP6ASSIGN])) + iface->assignment_length = blobmsg_get_u32(cur); + + iface->assignment_hint = -1; + if ((cur = tb[IFACE_ATTR_IP6HINT])) + iface->assignment_hint = strtol(blobmsg_get_string(cur), NULL, 16) & + ~((1 << (64 - iface->assignment_length)) - 1); + + if ((cur = tb[IFACE_ATTR_IP6CLASS])) + interface_add_assignment_classes(iface, cur); + + + if ((cur = tb[IFACE_ATTR_IP4TABLE])) { + if (!system_resolve_rt_table(blobmsg_data(cur), &iface->ip4table)) + DPRINTF("Failed to resolve routing table: %s\n", (char *) blobmsg_data(cur)); + } + + // Set a default exteranl routing table for IPv6 to do source-based-filtering + struct interface *iface_old = vlist_find(&interfaces, name, iface_old, node); + if (iface_old && iface_old->ip6table > 1000 && iface_old->ip6table < 2000) + iface->ip6table = iface_old->ip6table; + else + iface->ip6table = 1000 + ++interface_serial; + + if ((cur = tb[IFACE_ATTR_IP6TABLE])) { + if (!system_resolve_rt_table(blobmsg_data(cur), &iface->ip6table)) + DPRINTF("Failed to resolve routing table: %s\n", (char *) blobmsg_data(cur)); + } + + iface->proto_ip.no_delegation = !blobmsg_get_bool_default(tb[IFACE_ATTR_DELEGATE], true); + iface->config_autostart = iface->autostart; + return iface; +} + +void interface_set_dynamic(struct interface *iface) +{ + iface->dynamic = true; + iface->node.version = -1; // Don't delete on reload } static bool __interface_add(struct interface *iface, struct blob_attr *config, bool alias) @@ -549,6 +694,9 @@ interface_set_main_dev(struct interface *iface, struct device *dev) interface_set_l3_dev(iface, dev); device_add_user(&iface->main_dev, dev); + if (!dev) + return; + if (claimed) device_claim(&iface->l3_dev); @@ -598,6 +746,37 @@ interface_add_link(struct interface *iface, struct device *dev) } int +interface_handle_link(struct interface *iface, const char *name, bool add) +{ + struct device *dev; + int ret; + + device_lock(); + + dev = device_get(name, add ? 2 : 0); + if (!dev) { + ret = UBUS_STATUS_NOT_FOUND; + goto out; + } + + if (add) { + device_set_present(dev, true); + if (iface->device_config) + device_set_config(dev, &simple_device_type, iface->config); + + system_if_apply_settings(dev, &dev->settings); + ret = interface_add_link(iface, dev); + } else { + ret = interface_remove_link(iface, dev); + } + +out: + device_unlock(); + + return ret; +} + +int interface_set_up(struct interface *iface) { int ret; @@ -687,11 +866,36 @@ static void interface_change_config(struct interface *if_old, struct interface *if_new) { struct blob_attr *old_config = if_old->config; - const char *old_ifname = if_old->ifname; - const char *old_parent_ifname = if_old->parent_ifname; - const struct proto_handler *proto = if_old->proto_handler; + bool reload = false, reload_ip = false; + +#define FIELD_CHANGED_STR(field) \ + ((!!if_old->field != !!if_new->field) || \ + (if_old->field && \ + strcmp(if_old->field, if_new->field) != 0)) + + if (FIELD_CHANGED_STR(parent_ifname)) { + if (if_old->parent_iface.iface) + interface_remove_user(&if_old->parent_iface); + reload = true; + } + + if (FIELD_CHANGED_STR(ifname) || + if_old->proto_handler != if_new->proto_handler) + reload = true; + + if (!if_old->proto_handler->config_params) + D(INTERFACE, "No config parameters for interface '%s'\n", + if_old->name); + else if (!uci_blob_check_equal(if_old->config, if_new->config, + if_old->proto_handler->config_params)) + reload = true; + +#define UPDATE(field, __var) ({ \ + bool __changed = (if_old->field != if_new->field); \ + if_old->field = if_new->field; \ + __var |= __changed; \ + }) - interface_clear_errors(if_old); if_old->config = if_new->config; if (!if_old->config_autostart && if_new->config_autostart) if_old->autostart = true; @@ -702,59 +906,37 @@ interface_change_config(struct interface *if_old, struct interface *if_new) if_old->parent_ifname = if_new->parent_ifname; if_old->proto_handler = if_new->proto_handler; -#define FIELD_CHANGED_STR(field) \ - ((!!if_old->field != !!old_ ## field) || \ - (old_ ## field && \ - strcmp(old_ ## field, if_old->field) != 0)) + if_old->proto_ip.no_dns = if_new->proto_ip.no_dns; + interface_replace_dns(&if_old->config_ip, &if_new->config_ip); - if (FIELD_CHANGED_STR(parent_ifname)) { - if (if_old->parent_iface.iface) - interface_remove_user(&if_old->parent_iface); - goto reload; - } + UPDATE(metric, reload_ip); + UPDATE(proto_ip.no_defaultroute, reload_ip); + UPDATE(ip4table, reload_ip); + UPDATE(ip6table, reload_ip); + interface_merge_assignment_data(if_old, if_new); - if (FIELD_CHANGED_STR(ifname) || proto != if_new->proto_handler) { - D(INTERFACE, "Reload interface '%s' because of ifname/proto change\n", - if_old->name); - goto reload; - } +#undef UPDATE - if (!proto->config_params) - D(INTERFACE, "No config parameters for interface '%s'\n", - if_old->name); - else if (!config_check_equal(old_config, if_new->config, - proto->config_params)) { + if (reload) { D(INTERFACE, "Reload interface '%s because of config changes\n", if_old->name); - goto reload; + interface_clear_errors(if_old); + set_config_state(if_old, IFC_RELOAD); + goto out; } -#define UPDATE(field) ({ \ - bool __changed = (if_old->field != if_new->field); \ - if_old->field = if_new->field; \ - __changed; \ - }) - - if (UPDATE(metric) || UPDATE(proto_ip.no_defaultroute)) { + if (reload_ip) { interface_ip_set_enabled(&if_old->config_ip, false); - interface_ip_set_enabled(&if_old->config_ip, if_new->config_ip.enabled); interface_ip_set_enabled(&if_old->proto_ip, false); interface_ip_set_enabled(&if_old->proto_ip, if_new->proto_ip.enabled); + interface_ip_set_enabled(&if_old->config_ip, if_new->config_ip.enabled); } - UPDATE(proto_ip.no_dns); - interface_replace_dns(&if_old->config_ip, &if_new->config_ip); interface_write_resolv_conf(); -#undef UPDATE - - goto out; - -reload: - set_config_state(if_old, IFC_RELOAD); out: if_new->config = NULL; - interface_cleanup(if_new, false); + interface_cleanup(if_new); free(old_config); free(if_new); }