X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=config.c;h=a7f9c0b64a44567ecb11c32aa3b059883cc507a7;hb=HEAD;hp=682db54d536b42c65db1835368494f79af5c3fcd;hpb=5dd87fd8ad3a42c71d90a8fecc893af973e552bf;p=project%2Fnetifd.git diff --git a/config.c b/config.c index 682db54..d187152 100644 --- a/config.c +++ b/config.c @@ -11,38 +11,144 @@ * 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 int -config_parse_bridge_interface(struct uci_section *s) +config_section_idx(struct uci_section *s) +{ + struct uci_element *e; + int idx = 0; + + uci_foreach_element(&uci_wireless->sections, e) { + struct uci_section *cur = uci_to_section(e); + + if (s == cur) + return idx; + + if (!strcmp(cur->type, s->type)) + idx++; + } + + return -1; +} + +static bool +config_bridge_has_vlans(const char *br_name) +{ + struct uci_element *e; + + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + const char *name; + + if (strcmp(s->type, "bridge-vlan") != 0) + continue; + + name = uci_lookup_option_string(uci_ctx, s, "device"); + if (!name) + continue; + + if (!strcmp(name, br_name)) + return true; + } + + return false; +} + +static void +config_fixup_bridge_var(struct uci_section *s, const char *name, const char *val) +{ + 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; + + uci_set(uci_ctx, &ptr); +} + +/** + * 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", + }; + + if (uci_lookup_option(uci_ctx, s, "ports")) + return; + + uci_lookup_ptr(uci_ctx, &ptr, NULL, false); + if (!ptr.o) + return; + + ptr.value = "ports"; + uci_rename(uci_ctx, &ptr); +} + +static void +config_fixup_bridge_vlan_filtering(struct uci_section *s, const char *name) +{ + bool has_vlans = config_bridge_has_vlans(name); + + 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, 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); @@ -54,29 +160,36 @@ static void config_parse_interface(struct uci_section *s, bool alias) { struct interface *iface; - const char *type = NULL; + const char *type = NULL, *disabled; struct blob_attr *config; - struct device *dev; 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); if (!alias) type = uci_lookup_option_string(uci_ctx, s, "type"); - if (type && !strcmp(type, "bridge")) { - if (config_parse_bridge_interface(s)) + + 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); @@ -91,28 +204,11 @@ config_parse_interface(struct uci_section *s, bool alias) if (!interface_add_alias(iface, config)) goto error_free_config; } else { - interface_add(iface, config); + if (!interface_add(iface, config)) + goto error_free_config; } - - /* - * need to look up the interface name again, in case of config update, - * the pointer will have changed - */ - iface = vlist_find(&interfaces, s->e.name, iface, node); - if (!iface) - return; - - dev = iface->main_dev.dev; - if (!dev || !dev->default_config) - return; - - blob_buf_init(&b, 0); - uci_to_blob(&b, s, dev->type->config_params); - if (blob_len(b.head) == 0) - return; - - device_set_config(dev, dev->type, b.head); return; + error_free_config: free(config); error: @@ -131,6 +227,17 @@ config_parse_route(struct uci_section *s, bool v6) 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) { @@ -144,13 +251,15 @@ config_parse_rule(struct uci_section *s, bool 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 = NULL; + struct device_type *devtype = NULL; + struct device *dev; const char *type, *name; if (strcmp(s->type, "device") != 0) @@ -161,22 +270,166 @@ config_init_devices(void) continue; type = uci_lookup_option_string(uci_ctx, s, "type"); - if (type) { - if (!strcmp(type, "bridge")) - devtype = &bridge_device_type; - else if (!strcmp(type, "tunnel")) - devtype = &tunnel_device_type; - else if (!strcmp(type, "macvlan")) - devtype = &macvlan_device_type; - } + if (type) + devtype = device_type_get(type); + + if (bridge != (devtype && devtype->bridge_capability)) + continue; - if (!devtype) - devtype = &simple_device_type; + 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; + } +} + +static void +config_parse_vlan(struct device *dev, struct uci_section *s) +{ + 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; + + 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 (!tb[BRVLAN_ATTR_VID]) + return; + + vid = blobmsg_get_u32(tb[BRVLAN_ATTR_VID]); + if (!vid || vid > 4095) + return; + + blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_PORTS], rem) { + name_len += strlen(blobmsg_get_string(cur)) + 1; + n_ports++; } + + vlan = calloc(1, sizeof(*vlan) + n_ports * sizeof(*port) + name_len); + if (!vlan) + return; + + 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; + } + } + + name_buf += strlen(name_buf) + 1; + port++; + } + + blobmsg_for_each_attr(cur, tb[BRVLAN_ATTR_ALIAS], rem) + kvlist_set(&dev->vlan_aliases, blobmsg_get_string(cur), &vid); + + vlist_add(&dev->vlans, &vlan->node, &vlan->vid); +} + + +static void +config_init_vlans(void) +{ + struct uci_element *e; + struct device *dev; + + device_vlan_update(false); + uci_foreach_element(&uci_network->sections, e) { + struct uci_section *s = uci_to_section(e); + const char *name; + + if (strcmp(s->type, "bridge-vlan") != 0) + continue; + + name = uci_lookup_option_string(uci_ctx, s, "device"); + if (!name) + continue; + + dev = device_get(name, 0); + if (!dev || !dev->vlans.update) + continue; + + config_parse_vlan(dev, s); + } + device_vlan_update(true); } static struct uci_package * @@ -189,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 { @@ -226,7 +482,7 @@ config_init_interfaces(void) } static void -config_init_routes(void) +config_init_ip(void) { struct interface *iface; struct uci_element *e; @@ -241,6 +497,10 @@ config_init_routes(void) 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) @@ -279,33 +539,354 @@ config_init_globals(void) interface_ip_set_ula_prefix(ula_prefix); } -void +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_foreach_element(&uci_wireless->sections, f) { + struct uci_section *cur = uci_to_section(f); + const char *vif_name; + + 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(&uci_wireless->sections, e) { + struct uci_section *s = uci_to_section(e); + + if (strcmp(s->type, "wifi-iface") != 0) + continue; + + 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) { - fprintf(stderr, "Failed to load network config\n"); - return; + 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_lock(); device_reset_config(); - config_init_devices(); + config_init_devices(true); + config_init_vlans(); + config_init_devices(false); config_init_interfaces(); - config_init_routes(); + config_init_ip(); config_init_rules(); config_init_globals(); + config_init_wireless(); config_init = false; - device_unlock(); device_reset_old(); device_init_pending(); vlist_flush(&interfaces); - device_free_unused(NULL); interface_refresh_assignments(false); interface_start_pending(); + wireless_start_pending(0); + + return ret; }