#include <uci.h>
+#include <libubox/blobmsg_json.h>
+
#include "netifd.h"
#include "interface.h"
#include "interface-ip.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
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)
+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);
struct interface *iface;
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"))
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 = interface_alloc(s->e.name, b.head);
+ iface = interface_alloc(s->e.name, b.head, false);
if (!iface)
return;
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;
-
- if (iface->device_config || dev->settings.flags)
- device_apply_config(dev, dev->type, b.head);
return;
+
error_free_config:
free(config);
error:
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)
{
}
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;
continue;
type = uci_lookup_option_string(uci_ctx, s, "type");
- if (type) {
- if (!strcmp(type, "8021ad"))
- devtype = &vlandev_device_type;
- else if (!strcmp(type, "8021q"))
- devtype = &vlandev_device_type;
- else if (!strcmp(type, "bridge"))
- devtype = &bridge_device_type;
- else if (!strcmp(type, "macvlan"))
- devtype = &macvlan_device_type;
- else if (!strcmp(type, "tunnel"))
- devtype = &tunnel_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, params);
if (devtype) {
}
}
+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 *
config_init_package(const char *config)
{
}
static void
-config_init_routes(void)
+config_init_ip(void)
{
struct interface *iface;
struct uci_element *e;
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)
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);
blob_buf_init(&b, 0);
uci_to_blob(&b, s, wdev->drv->interface.config);
- wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name);
+ 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
const char *dev_name;
if (!uci_wireless) {
- DPRINTF("No wireless configuration found\n");
+ D(WIRELESS, "No wireless configuration found");
return;
}
wdev = vlist_find(&wireless_devices, dev_name, wdev, node);
if (!wdev) {
- DPRINTF("device %s not found!\n", dev_name);
+ 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);
}
-void
+
+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();
+ wireless_start_pending(0);
+
+ return ret;
}