X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=config.c;h=a7f9c0b64a44567ecb11c32aa3b059883cc507a7;hb=HEAD;hp=6f8d3b7c50876b30245cc97db2c01b8431380f65;hpb=33f7932e34d10b5429ea05332ef1e8421ef90916;p=project%2Fnetifd.git diff --git a/config.c b/config.c index 6f8d3b7..d187152 100644 --- a/config.c +++ b/config.c @@ -1,139 +1,154 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define _GNU_SOURCE #include #include #include #include +#include + #include "netifd.h" #include "interface.h" +#include "interface-ip.h" +#include "iprule.h" #include "proto.h" +#include "wireless.h" #include "config.h" +#include "ubus.h" bool config_init = false; static struct uci_context *uci_ctx; static struct uci_package *uci_network; +static struct uci_package *uci_wireless; +static struct blob_attr *board_netdevs; static struct blob_buf b; -static void uci_attr_to_blob(struct blob_buf *b, const char *str, - const char *name, enum blobmsg_type type) +static int +config_section_idx(struct uci_section *s) { - char *err; - int intval; - - switch (type) { - case BLOBMSG_TYPE_STRING: - blobmsg_add_string(b, name, str); - break; - case BLOBMSG_TYPE_BOOL: - if (!strcmp(str, "true") || !strcmp(str, "1")) - intval = 1; - else if (!strcmp(str, "false") || !strcmp(str, "0")) - intval = 0; - else - return; + struct uci_element *e; + int idx = 0; - blobmsg_add_u8(b, name, intval); - break; - case BLOBMSG_TYPE_INT32: - intval = strtol(str, &err, 0); - if (*err) - return; + uci_foreach_element(&uci_wireless->sections, e) { + struct uci_section *cur = uci_to_section(e); + + if (s == cur) + return idx; - blobmsg_add_u32(b, name, intval); - break; - default: - break; + if (!strcmp(cur->type, s->type)) + idx++; } + + return -1; } -static void uci_array_to_blob(struct blob_buf *b, struct uci_option *o, - enum blobmsg_type type) +static bool +config_bridge_has_vlans(const char *br_name) { struct uci_element *e; - char *str, *next, *word; - if (o->type == UCI_TYPE_LIST) { - uci_foreach_element(&o->v.list, e) { - uci_attr_to_blob(b, e->name, NULL, type); - } - return; - } + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + const char *name; - str = strdup(o->v.string); - next = str; + if (strcmp(s->type, "bridge-vlan") != 0) + continue; - while ((word = strsep(&next, " \t")) != NULL) { - if (!*word) + name = uci_lookup_option_string(uci_ctx, s, "device"); + if (!name) continue; - uci_attr_to_blob(b, word, NULL, type); + if (!strcmp(name, br_name)) + return true; } - free(str); + return false; } -static void __uci_to_blob(struct blob_buf *b, struct uci_section *s, - const struct config_param_list *p) +static void +config_fixup_bridge_var(struct uci_section *s, const char *name, const char *val) { - const struct blobmsg_policy *attr = NULL; - struct uci_element *e; - struct uci_option *o; - void *array; - int i; - - uci_foreach_element(&s->options, e) { - for (i = 0; i < p->n_params; i++) { - attr = &p->params[i]; - if (!strcmp(attr->name, e->name)) - break; - } - - if (i == p->n_params) - continue; + struct uci_ptr ptr = { + .p = s->package, + .s = s, + .option = name, + .value = val, + }; + + uci_lookup_ptr(uci_ctx, &ptr, NULL, false); + if (ptr.o) + return; - o = uci_to_option(e); + uci_set(uci_ctx, &ptr); +} - if (attr->type == BLOBMSG_TYPE_ARRAY) { - if (!p->info) - continue; +/** + * config_fixup_bridge_ports - translate deprecated configs + * + * Old configs used "ifname" option for specifying bridge ports. For backward + * compatibility translate it into the new "ports" option. + */ +static void config_fixup_bridge_ports(struct uci_section *s) +{ + struct uci_ptr ptr = { + .p = s->package, + .s = s, + .option = "ifname", + }; - array = blobmsg_open_array(b, attr->name); - uci_array_to_blob(b, o, p->info[i].type); - blobmsg_close_array(b, array); - continue; - } + if (uci_lookup_option(uci_ctx, s, "ports")) + return; - if (o->type == UCI_TYPE_LIST) - continue; + uci_lookup_ptr(uci_ctx, &ptr, NULL, false); + if (!ptr.o) + return; - uci_attr_to_blob(b, o->v.string, attr->name, attr->type); - } + ptr.value = "ports"; + uci_rename(uci_ctx, &ptr); } -static void uci_to_blob(struct blob_buf *b, struct uci_section *s, - const struct config_param_list *p) +static void +config_fixup_bridge_vlan_filtering(struct uci_section *s, const char *name) { - int i; + bool has_vlans = config_bridge_has_vlans(name); - __uci_to_blob(b, s, p); - for (i = 0; i < p->n_next; i++) - uci_to_blob(b, s, p->next[i]); + config_fixup_bridge_var(s, "__has_vlans", has_vlans ? "1" : "0"); + + if (!has_vlans) + return; + + config_fixup_bridge_var(s, "vlan_filtering", "1"); } static int -config_parse_bridge_interface(struct uci_section *s) +config_parse_bridge_interface(struct uci_section *s, struct device_type *devtype) { char *name; - name = alloca(strlen(s->e.name) + 4); - sprintf(name, "br-%s", s->e.name); + name = alloca(strlen(s->e.name) + strlen(devtype->name_prefix) + 2); + sprintf(name, "%s-%s", devtype->name_prefix, s->e.name); blobmsg_add_string(&b, "name", name); - uci_to_blob(&b, s, bridge_device_type.config_params); - if (!device_create(name, &bridge_device_type, b.head)) { - D(INTERFACE, "Failed to create bridge for interface '%s'\n", s->e.name); - return -EINVAL; + config_fixup_bridge_ports(s); + config_fixup_bridge_vlan_filtering(s, name); + uci_to_blob(&b, s, devtype->config_params); + if (!device_create(name, devtype, b.head)) { + D(INTERFACE, "Failed to create '%s' device for interface '%s'", + devtype->name, s->e.name); } blob_buf_init(&b, 0); @@ -142,47 +157,109 @@ config_parse_bridge_interface(struct uci_section *s) } static void -config_parse_interface(struct uci_section *s) +config_parse_interface(struct uci_section *s, bool alias) { struct interface *iface; - const char *type; + const char *type = NULL, *disabled; struct blob_attr *config; + bool bridge = false; + struct device_type *devtype = NULL; + + disabled = uci_lookup_option_string(uci_ctx, s, "disabled"); + if (disabled && !strcmp(disabled, "1")) + return; blob_buf_init(&b, 0); - type = uci_lookup_option_string(uci_ctx, s, "type"); - if (type && !strcmp(type, "bridge")) - if (config_parse_bridge_interface(s)) + if (!alias) + type = uci_lookup_option_string(uci_ctx, s, "type"); + + if (type) + devtype = device_type_get(type); + + if (devtype && devtype->bridge_capability) { + if (config_parse_bridge_interface(s, devtype)) return; + bridge = true; + } + uci_to_blob(&b, s, &interface_attr_list); - iface = calloc(1, sizeof(*iface)); + + iface = interface_alloc(s->e.name, b.head, false); if (!iface) return; - interface_init(iface, s->e.name, b.head); - if (iface->proto_handler && iface->proto_handler->config_params) uci_to_blob(&b, s, iface->proto_handler->config_params); - config = malloc(blob_pad_len(b.head)); - if (!config) { - free(iface); - return; + if (!bridge && uci_to_blob(&b, s, simple_device_type.config_params)) + iface->device_config = true; + + config = blob_memdup(b.head); + if (!config) + goto error; + + if (alias) { + if (!interface_add_alias(iface, config)) + goto error_free_config; + } else { + if (!interface_add(iface, config)) + goto error_free_config; } + return; + +error_free_config: + free(config); +error: + free(iface); +} + +static void +config_parse_route(struct uci_section *s, bool v6) +{ + void *route; + + blob_buf_init(&b, 0); + route = blobmsg_open_array(&b, "route"); + uci_to_blob(&b, s, &route_attr_list); + blobmsg_close_array(&b, route); + interface_ip_add_route(NULL, blob_data(b.head), v6); +} + +static void +config_parse_neighbor(struct uci_section *s, bool v6) +{ + void *neighbor; + blob_buf_init(&b,0); + neighbor = blobmsg_open_array(&b, "neighbor"); + uci_to_blob(&b,s, &neighbor_attr_list); + blobmsg_close_array(&b, neighbor); + interface_ip_add_neighbor(NULL, blob_data(b.head), v6); +} + +static void +config_parse_rule(struct uci_section *s, bool v6) +{ + void *rule; - memcpy(config, b.head, blob_pad_len(b.head)); - interface_add(iface, config); + blob_buf_init(&b, 0); + rule = blobmsg_open_array(&b, "rule"); + uci_to_blob(&b, s, &rule_attr_list); + blobmsg_close_array(&b, rule); + iprule_add(blob_data(b.head), v6); } static void -config_init_devices(void) +config_init_devices(bool bridge) { struct uci_element *e; uci_foreach_element(&uci_network->sections, e) { + const struct uci_blob_param_list *params = NULL; struct uci_section *s = uci_to_section(e); - const struct device_type *devtype; + struct device_type *devtype = NULL; + struct device *dev; const char *type, *name; if (strcmp(s->type, "device") != 0) @@ -193,97 +270,166 @@ config_init_devices(void) continue; type = uci_lookup_option_string(uci_ctx, s, "type"); - if (type && !strcmp(type, "bridge")) - devtype = &bridge_device_type; - else - devtype = &simple_device_type; + if (type) + devtype = device_type_get(type); + + if (bridge != (devtype && devtype->bridge_capability)) + continue; + + if (devtype) + params = devtype->config_params; + if (!params) + params = simple_device_type.config_params; + + if (devtype && devtype->bridge_capability) { + config_fixup_bridge_ports(s); + config_fixup_bridge_vlan_filtering(s, name); + } blob_buf_init(&b, 0); - uci_to_blob(&b, s, devtype->config_params); - device_create(name, devtype, b.head); + uci_to_blob(&b, s, params); + if (devtype) { + dev = device_create(name, devtype, b.head); + if (!dev) + continue; + } else { + dev = device_get(name, 1); + if (!dev) + continue; + + dev->current_config = true; + device_apply_config(dev, dev->type, b.head); + } + dev->default_config = false; } } -bool -config_diff(struct blob_attr **tb1, struct blob_attr **tb2, - const struct config_param_list *config, unsigned long *diff) +static void +config_parse_vlan(struct device *dev, struct uci_section *s) { - bool ret = false; - int i; - - for (i = 0; i < config->n_params; i++) { - if (!tb1[i] && !tb2[i]) - continue; - - if (!!tb1[i] != !!tb2[i]) - goto mark; + enum { + BRVLAN_ATTR_VID, + BRVLAN_ATTR_LOCAL, + BRVLAN_ATTR_PORTS, + BRVLAN_ATTR_ALIAS, + __BRVLAN_ATTR_MAX, + }; + static const struct blobmsg_policy vlan_attrs[__BRVLAN_ATTR_MAX] = { + [BRVLAN_ATTR_VID] = { "vlan", BLOBMSG_TYPE_INT32 }, + [BRVLAN_ATTR_LOCAL] = { "local", BLOBMSG_TYPE_BOOL }, + [BRVLAN_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY }, + [BRVLAN_ATTR_ALIAS] = { "alias", BLOBMSG_TYPE_ARRAY }, + }; + static const struct uci_blob_param_info vlan_attr_info[__BRVLAN_ATTR_MAX] = { + [BRVLAN_ATTR_PORTS] = { .type = BLOBMSG_TYPE_STRING }, + [BRVLAN_ATTR_ALIAS] = { .type = BLOBMSG_TYPE_STRING }, + }; + static const struct uci_blob_param_list vlan_attr_list = { + .n_params = __BRVLAN_ATTR_MAX, + .params = vlan_attrs, + .info = vlan_attr_info, + }; + struct blob_attr *tb[__BRVLAN_ATTR_MAX]; + struct blob_attr *cur; + struct bridge_vlan_port *port; + struct bridge_vlan *vlan; + unsigned int vid; + const char *val; + char *name_buf; + int name_len = 0; + int n_ports = 0; + size_t rem; + + val = uci_lookup_option_string(uci_ctx, s, "vlan"); + if (!val) + return; - if (blob_len(tb1[i]) != blob_len(tb2[i])) - goto mark; + blob_buf_init(&b, 0); + uci_to_blob(&b, s, &vlan_attr_list); + blobmsg_parse(vlan_attrs, __BRVLAN_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head)); - if (memcmp(tb1[i], tb2[i], blob_raw_len(tb1[i])) != 0) - goto mark; + if (!tb[BRVLAN_ATTR_VID]) + return; - continue; + vid = blobmsg_get_u32(tb[BRVLAN_ATTR_VID]); + if (!vid || vid > 4095) + return; -mark: - ret = true; - if (diff) - set_bit(diff, i); - else - return ret; + blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { + name_len += strlen(blobmsg_get_string(cur)) + 1; + n_ports++; } - return ret; -} - + vlan = calloc(1, sizeof(*vlan) + n_ports * sizeof(*port) + name_len); + if (!vlan) + return; -static bool -__config_check_equal(struct blob_attr *c1, struct blob_attr *c2, - const struct config_param_list *config) -{ - struct blob_attr **tb1, **tb2; + vlan->vid = vid; + vlan->local = true; + if (tb[BRVLAN_ATTR_LOCAL]) + vlan->local = blobmsg_get_bool(tb[BRVLAN_ATTR_LOCAL]); + + vlan->n_ports = n_ports; + vlan->ports = port = (struct bridge_vlan_port *)&vlan[1]; + INIT_LIST_HEAD(&vlan->hotplug_ports); + name_buf = (char *)&port[n_ports]; + + blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { + char *sep; + + port->ifname = name_buf; + port->flags = BRVLAN_F_UNTAGGED; + strcpy(name_buf, blobmsg_get_string(cur)); + + sep = strchr(name_buf, ':'); + if (sep) { + for (*sep = 0, sep++; *sep; sep++) + switch (*sep) { + case '*': + port->flags |= BRVLAN_F_PVID; + break; + case 't': + port->flags &= ~BRVLAN_F_UNTAGGED; + break; + } + } - tb1 = alloca(config->n_params * sizeof(struct blob_attr *)); - blobmsg_parse(config->params, config->n_params, tb1, - blob_data(c1), blob_len(c1)); + name_buf += strlen(name_buf) + 1; + port++; + } - tb2 = alloca(config->n_params * sizeof(struct blob_attr *)); - blobmsg_parse(config->params, config->n_params, tb2, - blob_data(c2), blob_len(c2)); + blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_ALIAS], rem) + kvlist_set(&dev->vlan_aliases, blobmsg_get_string(cur), &vid); - return !config_diff(tb1, tb2, config, NULL); + vlist_add(&dev->vlans, &vlan->node, &vlan->vid); } -bool -config_check_equal(struct blob_attr *c1, struct blob_attr *c2, - const struct config_param_list *config) -{ - int i; - if (!__config_check_equal(c1, c2, config)) - return false; +static void +config_init_vlans(void) +{ + struct uci_element *e; + struct device *dev; - for (i = 0; i < config->n_next; i++) { - if (!__config_check_equal(c1, c2, config->next[i])) - return false; - } + device_vlan_update(false); + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + const char *name; - return true; -} + if (strcmp(s->type, "bridge-vlan") != 0) + continue; -struct blob_attr * -config_memdup(struct blob_attr *attr) -{ - struct blob_attr *ret; - int size = blob_pad_len(attr); + name = uci_lookup_option_string(uci_ctx, s, "device"); + if (!name) + continue; - ret = malloc(size); - if (!ret) - return NULL; + dev = device_get(name, 0); + if (!dev || !dev->vlans.update) + continue; - memcpy(ret, attr, size); - return ret; + config_parse_vlan(dev, s); + } + device_vlan_update(true); } static struct uci_package * @@ -296,8 +442,11 @@ config_init_package(const char *config) ctx = uci_alloc_context(); uci_ctx = ctx; + ctx->flags &= ~UCI_FLAG_STRICT; + if (config_path) + uci_set_confdir(ctx, config_path); + #ifdef DUMMY_MODE - uci_set_confdir(ctx, "./config"); uci_set_savedir(ctx, "./tmp"); #endif } else { @@ -306,45 +455,438 @@ config_init_package(const char *config) uci_unload(ctx, p); } - if (uci_load(ctx, "network", &p)) + if (uci_load(ctx, config, &p)) return NULL; return p; } -void -config_init_interfaces(const char *name) +static void +config_init_interfaces(void) { - struct uci_context *ctx; - struct uci_package *p = NULL; struct uci_element *e; - p = config_init_package("network"); - ctx = uci_ctx; - if (!p) { - fprintf(stderr, "Failed to load network config\n"); + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "interface")) + config_parse_interface(s, false); + } + + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "alias")) + config_parse_interface(s, true); + } +} + +static void +config_init_ip(void) +{ + struct interface *iface; + struct uci_element *e; + + vlist_for_each_element(&interfaces, iface, node) + interface_ip_update_start(&iface->config_ip); + + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "route")) + config_parse_route(s, false); + else if (!strcmp(s->type, "route6")) + config_parse_route(s, true); + if (!strcmp(s->type, "neighbor")) + config_parse_neighbor(s, false); + else if (!strcmp(s->type, "neighbor6")) + config_parse_neighbor(s, true); + } + + vlist_for_each_element(&interfaces, iface, node) + interface_ip_update_complete(&iface->config_ip); +} + +static void +config_init_rules(void) +{ + struct uci_element *e; + + iprule_update_start(); + + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (!strcmp(s->type, "rule")) + config_parse_rule(s, false); + else if (!strcmp(s->type, "rule6")) + config_parse_rule(s, true); + } + + iprule_update_complete(); +} + +static void +config_init_globals(void) +{ + struct uci_section *globals = uci_lookup_section( + uci_ctx, uci_network, "globals"); + if (!globals) return; + + const char *ula_prefix = uci_lookup_option_string( + uci_ctx, globals, "ula_prefix"); + interface_ip_set_ula_prefix(ula_prefix); +} + +static void +config_parse_wireless_device(struct uci_section *s) +{ + struct wireless_driver *drv; + const char *driver_name; + + driver_name = uci_lookup_option_string(uci_ctx, s, "type"); + if (!driver_name) + return; + + drv = avl_find_element(&wireless_drivers, driver_name, drv, node); + if (!drv) + return; + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, drv->device.config); + wireless_device_create(drv, s->e.name, b.head); +} + +static void +config_parse_wireless_vlan(struct wireless_interface *vif, struct uci_section *s) +{ + char *name; + + name = alloca(strlen(s->type) + 16); + sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, vif->wdev->drv->vlan.config); + wireless_vlan_create(vif, b.head, s->anonymous ? name : s->e.name); +} + +static void +config_parse_wireless_station(struct wireless_interface *vif, struct uci_section *s) +{ + char *name; + + name = alloca(strlen(s->type) + 16); + sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, vif->wdev->drv->station.config); + wireless_station_create(vif, b.head, s->anonymous ? name : s->e.name); +} + +static void +config_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s) +{ + struct wireless_interface *vif; + struct uci_element *f; + char *name; + + name = alloca(strlen(s->type) + 16); + sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); + + blob_buf_init(&b, 0); + uci_to_blob(&b, s, wdev->drv->interface.config); + vif = wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name); + if (!vif) + return; + + if (s->anonymous) + goto out; + + uci_foreach_element(&uci_wireless->sections, f) { + struct uci_section *cur = uci_to_section(f); + const char *vif_name; + + if (strcmp(cur->type, "wifi-vlan") != 0) + continue; + + vif_name = uci_lookup_option_string(uci_ctx, cur, "iface"); + if (vif_name && strcmp(s->e.name, vif_name)) + continue; + config_parse_wireless_vlan(vif, cur); } - uci_network = p; - config_init = true; + uci_foreach_element(&uci_wireless->sections, f) { + struct uci_section *cur = uci_to_section(f); + const char *vif_name; - device_reset_config(); - config_init_devices(); + if (strcmp(cur->type, "wifi-station") != 0) + continue; + + vif_name = uci_lookup_option_string(uci_ctx, cur, "iface"); + if (vif_name && strcmp(s->e.name, vif_name)) + continue; + config_parse_wireless_station(vif, cur); + } + +out: + vlist_flush(&vif->vlans); + vlist_flush(&vif->stations); +} + +static void +config_init_procd_wireless_interface(const char *wdev_name, const char *vif_name, + struct blob_attr *config, + struct blob_attr *vlans, + struct blob_attr *stations) +{ + struct wireless_interface *vif; + struct wireless_device *wdev; + struct blob_attr *cur; + char name[16]; + int idx = 0; + int rem; + + wdev = vlist_find(&wireless_devices, wdev_name, wdev, node); + if (!wdev) { + D(WIRELESS, "device %s not found!", wdev_name); + return; + } + + vif = wireless_interface_create(wdev, config, vif_name); + if (!vif) + return; + + blobmsg_for_each_attr(cur, vlans, rem) { + snprintf(name, sizeof(name), "%d", ++idx); + wireless_vlan_create(vif, cur, name); + } + + blobmsg_for_each_attr(cur, stations, rem) { + snprintf(name, sizeof(name), "%d", ++idx); + wireless_station_create(vif, cur, name); + } + + vlist_flush(&vif->vlans); + vlist_flush(&vif->stations); +} + +static void +config_procd_wireless_interface_cb(struct blob_attr *data) +{ + enum { + UDATA_ATTR_DEVICE, + UDATA_ATTR_CONFIG, + UDATA_ATTR_STATIONS, + UDATA_ATTR_VLANS, + __UDATA_ATTR_MAX, + }; + static const struct blobmsg_policy policy[__UDATA_ATTR_MAX] = { + [UDATA_ATTR_DEVICE] = { "device", BLOBMSG_TYPE_STRING }, + [UDATA_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE }, + [UDATA_ATTR_STATIONS] = { "stations", BLOBMSG_TYPE_ARRAY }, + [UDATA_ATTR_VLANS] = { "vlans", BLOBMSG_TYPE_ARRAY }, + }; + struct blob_attr *tb[__UDATA_ATTR_MAX]; + const char *dev; + + blobmsg_parse_attr(policy, __UDATA_ATTR_MAX, tb, data); + if (!tb[UDATA_ATTR_DEVICE] || !tb[UDATA_ATTR_CONFIG]) + return; + + dev = blobmsg_get_string(tb[UDATA_ATTR_DEVICE]); + config_init_procd_wireless_interface(dev, blobmsg_name(data), + tb[UDATA_ATTR_CONFIG], + tb[UDATA_ATTR_VLANS], + tb[UDATA_ATTR_STATIONS]); +} + +static void +config_init_wireless(void) +{ + struct wireless_device *wdev; + struct uci_element *e; + const char *dev_name; + + if (!uci_wireless) { + D(WIRELESS, "No wireless configuration found"); + return; + } + + vlist_update(&wireless_devices); + + uci_foreach_element(&uci_wireless->sections, e) { + struct uci_section *s = uci_to_section(e); + if (strcmp(s->type, "wifi-device") != 0) + continue; + + config_parse_wireless_device(s); + } + + vlist_flush(&wireless_devices); + + vlist_for_each_element(&wireless_devices, wdev, node) { + wdev->vif_idx = 0; + vlist_update(&wdev->interfaces); + } - uci_foreach_element(&p->sections, e) { + uci_foreach_element(&uci_wireless->sections, e) { struct uci_section *s = uci_to_section(e); - if (name && strcmp(s->e.name, name) != 0) + if (strcmp(s->type, "wifi-iface") != 0) continue; - if (!strcmp(s->type, "interface")) - config_parse_interface(s); + dev_name = uci_lookup_option_string(uci_ctx, s, "device"); + if (!dev_name) + continue; + + wdev = vlist_find(&wireless_devices, dev_name, wdev, node); + if (!wdev) { + D(WIRELESS, "device %s not found!", dev_name); + continue; + } + + config_parse_wireless_interface(wdev, s); } + + netifd_ubus_get_procd_data("wifi-iface", config_procd_wireless_interface_cb); + + vlist_for_each_element(&wireless_devices, wdev, node) + vlist_flush(&wdev->interfaces); +} + + +static struct blob_attr * +config_find_blobmsg_attr(struct blob_attr *attr, const char *name, int type) +{ + struct blobmsg_policy policy = { .name = name, .type = type }; + struct blob_attr *cur; + + blobmsg_parse(&policy, 1, &cur, blobmsg_data(attr), blobmsg_len(attr)); + + return cur; +} + +struct ether_addr *config_get_default_macaddr(const char *ifname) +{ + struct blob_attr *cur; + + if (!board_netdevs) + return NULL; + + cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); + if (!cur) + return NULL; + + cur = config_find_blobmsg_attr(cur, "macaddr", BLOBMSG_TYPE_STRING); + if (!cur) + return NULL; + + return ether_aton(blobmsg_get_string(cur)); +} + +int config_get_default_gro(const char *ifname) +{ + struct blob_attr *cur; + + if (!board_netdevs) + return -1; + + cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); + if (!cur) + return -1; + + cur = config_find_blobmsg_attr(cur, "gro", BLOBMSG_TYPE_BOOL); + if (!cur) + return -1; + + return blobmsg_get_bool(cur); +} + +const char *config_get_default_conduit(const char *ifname) +{ + struct blob_attr *cur; + + if (!board_netdevs) + return NULL; + + cur = config_find_blobmsg_attr(board_netdevs, ifname, BLOBMSG_TYPE_TABLE); + if (!cur) + return NULL; + + cur = config_find_blobmsg_attr(cur, "conduit", BLOBMSG_TYPE_STRING); + if (!cur) + return NULL; + + return blobmsg_get_string(cur); +} + +static void +config_init_board(void) +{ + struct blob_attr *cur; + + blob_buf_init(&b, 0); + + if (!blobmsg_add_json_from_file(&b, DEFAULT_BOARD_JSON)) + return; + + free(board_netdevs); + board_netdevs = NULL; + + cur = config_find_blobmsg_attr(b.head, "network_device", + BLOBMSG_TYPE_TABLE); + if (!cur) + return; + + board_netdevs = blob_memdup(cur); +} + +int +config_init_all(void) +{ + int ret = 0; + char *err; + + uci_network = config_init_package("network"); + if (!uci_network) { + uci_get_errorstr(uci_ctx, &err, NULL); + netifd_log_message(L_CRIT, "Failed to load network config (%s)\n", err); + free(err); + return -1; + } + + uci_wireless = config_init_package("wireless"); + if (!uci_wireless && uci_ctx->err != UCI_ERR_NOTFOUND) { + uci_get_errorstr(uci_ctx, &err, NULL); + netifd_log_message(L_CRIT, "Failed to load wireless config (%s)\n", err); + free(err); + ret = -1; + } + + config_init_board(); + + vlist_update(&interfaces); + config_init = true; + + device_reset_config(); + config_init_devices(true); + config_init_vlans(); + config_init_devices(false); + config_init_interfaces(); + config_init_ip(); + config_init_rules(); + config_init_globals(); + config_init_wireless(); + config_init = false; device_reset_old(); device_init_pending(); - device_free_unused(NULL); + vlist_flush(&interfaces); + interface_refresh_assignments(false); interface_start_pending(); + wireless_start_pending(0); + + return ret; }