enum {
VIF_ATTR_DISABLED,
VIF_ATTR_NETWORK,
+ VIF_ATTR_ISOLATE,
+ VIF_ATTR_MODE,
__VIF_ATTR_MAX,
};
static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = {
[VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
- [VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_STRING },
+ [VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY },
+ [VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
+ [VIF_ATTR_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list vif_param = {
static void
put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name)
{
- void *c = blobmsg_open_table(&b, name);
- blob_put_raw(&b, blob_data(attr), blob_len(attr));
- blobmsg_close_table(&b, c);
+ void *c = blobmsg_open_table(buf, name);
+ blob_put_raw(buf, blob_data(attr), blob_len(attr));
+ blobmsg_close_table(buf, c);
}
static void
-vif_config_add_bridge(struct blob_buf *buf, const char *network, bool prepare)
+vif_config_add_bridge(struct blob_buf *buf, struct blob_attr *networks, bool prepare)
{
struct interface *iface;
- struct device *dev;
+ struct device *dev = NULL;
+ struct blob_attr *cur;
+ const char *network;
+ int rem;
- if (!network)
+ if (!networks)
return;
- iface = vlist_find(&interfaces, network, iface, node);
- if (!iface)
- return;
+ blobmsg_for_each_attr(cur, networks, rem) {
+ network = blobmsg_data(cur);
+
+ iface = vlist_find(&interfaces, network, iface, node);
+ if (!iface)
+ continue;
+
+ dev = iface->main_dev.dev;
+ if (!dev)
+ return;
+
+ if (dev->type != &bridge_device_type)
+ return;
+ }
- dev = iface->main_dev.dev;
if (!dev)
return;
- if (dev->type != &bridge_device_type)
- return;
+ if (dev->hotplug_ops && dev->hotplug_ops->prepare)
+ dev->hotplug_ops->prepare(dev);
blobmsg_add_string(buf, "bridge", dev->ifname);
+
+ if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST)
+ blobmsg_add_u8(buf, "multicast_to_unicast",
+ dev->settings.multicast_to_unicast);
}
static void
{
struct wireless_interface *vif;
+ uloop_timeout_cancel(&wdev->script_check);
+ uloop_timeout_cancel(&wdev->timeout);
wireless_complete_kill_request(wdev);
free(wdev->data);
wdev->data = NULL;
static void wireless_interface_handle_link(struct wireless_interface *vif, bool up)
{
struct interface *iface;
+ struct blob_attr *cur;
+ const char *network;
+ int rem;
if (!vif->network || !vif->ifname)
return;
- iface = vlist_find(&interfaces, vif->network, iface, node);
- if (!iface)
- return;
+ if (up) {
+ struct device *dev = device_get(vif->ifname, 2);
+ if (dev) {
+ dev->wireless_isolate = vif->isolate;
+ dev->wireless = true;
+ dev->wireless_ap = vif->ap_mode;
+ }
+ }
+
+ blobmsg_for_each_attr(cur, vif->network, rem) {
+ network = blobmsg_data(cur);
+
+ iface = vlist_find(&interfaces, network, iface, node);
+ if (!iface)
+ continue;
- interface_handle_link(iface, vif->ifname, up);
+ interface_handle_link(iface, vif->ifname, up, true);
+ }
}
static void
if (wdev->cancel)
return;
+ D(WIRELESS, "Cancel wireless device '%s' setup\n", wdev->name);
wdev->cancel = true;
uloop_timeout_set(&wdev->timeout, 10 * 1000);
}
int fds[2] = { -1, -1 };
D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
- prepare_config(wdev, &b, up);
- config = blobmsg_format_json(b.head, true);
+ if (!up && wdev->prev_config) {
+ config = blobmsg_format_json(wdev->prev_config, true);
+ free(wdev->prev_config);
+ wdev->prev_config = NULL;
+ } else {
+ prepare_config(wdev, &b, up);
+ config = blobmsg_format_json(b.head, true);
+ }
argv[i++] = wdev->drv->script;
argv[i++] = wdev->drv->name;
static void
__wireless_device_set_up(struct wireless_device *wdev)
{
+ if (wdev->disabled)
+ return;
+
+ if (wdev->retry_setup_failed)
+ return;
+
+ if (!wdev->autostart)
+ return;
+
if (wdev->state != IFS_DOWN || config_init)
return;
+ free(wdev->prev_config);
+ wdev->prev_config = NULL;
wdev->state = IFS_SETUP;
wireless_device_run_handler(wdev, true);
}
static void
+wireless_device_free(struct wireless_device *wdev)
+{
+ vlist_flush_all(&wdev->interfaces);
+ avl_delete(&wireless_devices.avl, &wdev->node.avl);
+ free(wdev->config);
+ free(wdev->prev_config);
+ free(wdev);
+}
+
+static void
+wdev_handle_config_change(struct wireless_device *wdev)
+{
+ enum interface_config_state state = wdev->config_state;
+
+ switch(state) {
+ case IFC_NORMAL:
+ case IFC_RELOAD:
+ __wireless_device_set_up(wdev);
+
+ wdev->config_state = IFC_NORMAL;
+ break;
+ case IFC_REMOVE:
+ wireless_device_free(wdev);
+ break;
+ }
+}
+
+static void
wireless_device_mark_down(struct wireless_device *wdev)
{
struct wireless_interface *vif;
wireless_process_kill_all(wdev, SIGTERM, true);
+ wdev->cancel = false;
wdev->state = IFS_DOWN;
wireless_device_free_state(wdev);
-
- if (wdev->autostart)
- __wireless_device_set_up(wdev);
-}
-
-static void
-wireless_device_mark_up(struct wireless_device *wdev)
-{
- struct wireless_interface *vif;
-
- D(WIRELESS, "Wireless device '%s' is now up\n", wdev->name);
- wdev->state = IFS_UP;
- vlist_for_each_element(&wdev->interfaces, vif, node)
- wireless_interface_handle_link(vif, true);
+ wdev_handle_config_change(wdev);
}
static void
}
static void
+wireless_device_mark_up(struct wireless_device *wdev)
+{
+ struct wireless_interface *vif;
+
+ if (wdev->cancel) {
+ wdev->cancel = false;
+ __wireless_device_set_down(wdev);
+ return;
+ }
+
+ D(WIRELESS, "Wireless device '%s' is now up\n", wdev->name);
+ wdev->state = IFS_UP;
+ vlist_for_each_element(&wdev->interfaces, vif, node)
+ wireless_interface_handle_link(vif, true);
+}
+
+static void
wireless_device_retry_setup(struct wireless_device *wdev)
{
+ if (wdev->state == IFS_TEARDOWN || wdev->state == IFS_DOWN || wdev->cancel)
+ return;
+
if (--wdev->retry < 0)
- wdev->autostart = false;
+ wdev->retry_setup_failed = true;
__wireless_device_set_down(wdev);
}
void
wireless_device_set_down(struct wireless_device *wdev)
{
+ wdev->retry_setup_failed = false;
wdev->autostart = false;
__wireless_device_set_down(wdev);
}
static void
-wireless_device_free(struct wireless_device *wdev)
-{
- vlist_flush_all(&wdev->interfaces);
- free(wdev->config);
- free(wdev);
-}
-
-static void
-wdev_handle_config_change(struct wireless_device *wdev)
-{
- enum interface_config_state state = wdev->config_state;
-
- wdev->config_state = IFC_NORMAL;
- switch(state) {
- case IFC_NORMAL:
- break;
- case IFC_RELOAD:
- if (wdev->autostart)
- __wireless_device_set_up(wdev);
- break;
- case IFC_REMOVE:
- wireless_device_free(wdev);
- break;
- }
-}
-
-static void
wdev_set_config_state(struct wireless_device *wdev, enum interface_config_state s)
{
- enum interface_config_state old_state = wdev->config_state;
-
- wdev->config_state = s;
- if (old_state != IFC_NORMAL)
+ if (wdev->config_state != IFC_NORMAL)
return;
+ wdev->config_state = s;
if (wdev->state == IFS_DOWN)
wdev_handle_config_change(wdev);
else
}
static void
+wdev_prepare_prev_config(struct wireless_device *wdev)
+{
+ if (wdev->prev_config)
+ return;
+
+ prepare_config(wdev, &b, false);
+ wdev->prev_config = blob_memdup(b.head);
+}
+
+static void
wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new)
{
struct blob_attr *new_config = wd_new->config;
+ bool disabled = wd_new->disabled;
free(wd_new);
- if (blob_attr_equal(wdev->config, new_config))
+ wdev_prepare_prev_config(wdev);
+ if (blob_attr_equal(wdev->config, new_config) && wdev->disabled == disabled)
return;
D(WIRELESS, "Update configuration of wireless device '%s'\n", wdev->name);
free(wdev->config);
wdev->config = blob_memdup(new_config);
+ wdev->disabled = disabled;
+ wdev->retry_setup_failed = false;
wdev_set_config_state(wdev, IFC_RELOAD);
}
return;
drv = calloc_a(sizeof(*drv),
- &name_str, strlen(name) + 1,
- &script_str, strlen(script) + 1,
&dev_config, sizeof(*dev_config) + sizeof(void *),
- &iface_config, sizeof(*iface_config) + sizeof(void *));
+ &iface_config, sizeof(*iface_config) + sizeof(void *),
+ &name_str, strlen(name) + 1,
+ &script_str, strlen(script) + 1);
drv->name = strcpy(name_str, name);
drv->script = strcpy(script_str, script);
D(WIRELESS, "Add handler for script %s: %s\n", script, name);
}
-static void __init
-wireless_init_list(void)
+void wireless_init(void)
{
vlist_init(&wireless_devices, avl_strcmp, wdev_update);
wireless_devices.keep_old = true;
blobmsg_parse(vif_policy, __VIF_ATTR_MAX, tb, blob_data(vif->config), blob_len(vif->config));
if ((cur = tb[VIF_ATTR_NETWORK]))
- vif->network = blobmsg_data(cur);
+ vif->network = cur;
+
+ cur = tb[VIF_ATTR_ISOLATE];
+ if (cur)
+ vif->isolate = blobmsg_get_bool(cur);
+
+ cur = tb[VIF_ATTR_MODE];
+ if (cur)
+ vif->ap_mode = !strcmp(blobmsg_get_string(cur), "ap");
}
static void
wdev = vif_new->wdev;
if (vif_old && vif_new) {
- vif_old->section = vif_new->section;
+ free((void *) vif_old->section);
+ vif_old->section = strdup(vif_new->section);
if (blob_attr_equal(vif_old->config, vif_new->config)) {
free(vif_new);
return;
}
D(WIRELESS, "Update wireless interface %s on device %s\n", vif_new->name, wdev->name);
+ wireless_interface_handle_link(vif_old, false);
free(vif_old->config);
vif_old->config = blob_memdup(vif_new->config);
+ vif_old->isolate = vif_new->isolate;
+ vif_old->ap_mode = vif_new->ap_mode;
wireless_interface_init_config(vif_old);
free(vif_new);
} else if (vif_new) {
D(WIRELESS, "Create new wireless interface %s on device %s\n", vif_new->name, wdev->name);
+ vif_new->section = strdup(vif_new->section);
vif_new->config = blob_memdup(vif_new->config);
wireless_interface_init_config(vif_new);
} else if (vif_old) {
D(WIRELESS, "Delete wireless interface %s on device %s\n", vif_old->name, wdev->name);
+ free((void *) vif_old->section);
free(vif_old->config);
free(vif_old);
}
struct blob_attr *disabled;
blobmsg_parse(&wdev_policy, 1, &disabled, blob_data(data), blob_len(data));
- if (disabled && blobmsg_get_bool(disabled))
- return;
wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1);
+ if (disabled && blobmsg_get_bool(disabled))
+ wdev->disabled = true;
wdev->drv = drv;
wdev->state = IFS_DOWN;
wdev->config_state = IFC_NORMAL;
wdev->name = strcpy(name_buf, name);
wdev->config = data;
+ wdev->retry_setup_failed = false;
wdev->config_autostart = true;
wdev->autostart = wdev->config_autostart;
INIT_LIST_HEAD(&wdev->script_proc);
vlist_init(&wdev->interfaces, avl_strcmp, vif_update);
wdev->interfaces.keep_old = true;
- wdev->interfaces.no_delete = true;
- vlist_add(&wireless_devices, &wdev->node, wdev->name);
wdev->timeout.cb = wireless_device_setup_timeout;
wdev->script_task.cb = wireless_device_script_task_cb;
wdev->script_proc_fd.cb = wireless_proc_poll_fd;
wdev->script_check.cb = wireless_device_check_script_tasks;
+
+ vlist_add(&wireless_devices, &wdev->node, wdev->name);
}
void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section)
sprintf(name, "%d", wdev->vif_idx++);
- vif = calloc_a(sizeof(*vif), &name_buf, strlen(name) + 1);
+ vif = calloc_a(sizeof(*vif),
+ &name_buf, strlen(name) + 1);
vif->name = strcpy(name_buf, name);
vif->wdev = wdev;
vif->config = data;
vif->section = section;
+ vif->isolate = false;
+
vlist_add(&wdev->interfaces, &vif->node, vif->name);
}
{
void *i;
- i = blobmsg_open_table(b, iface->name);
+ i = blobmsg_open_table(b, NULL);
if (iface->section)
blobmsg_add_string(b, "section", iface->section);
if (iface->ifname)
blobmsg_add_string(b, "ifname", iface->ifname);
- if (iface->data)
- blob_put_raw(b, blob_data(iface->data), blob_len(iface->data));
+ put_container(b, iface->config, "config");
blobmsg_close_table(b, i);
}
blobmsg_add_u8(b, "up", wdev->state == IFS_UP);
blobmsg_add_u8(b, "pending", wdev->state == IFS_SETUP || wdev->state == IFS_TEARDOWN);
blobmsg_add_u8(b, "autostart", wdev->autostart);
- i = blobmsg_open_table(b, "interfaces");
+ blobmsg_add_u8(b, "disabled", wdev->disabled);
+ blobmsg_add_u8(b, "retry_setup_failed", wdev->retry_setup_failed);
+ put_container(b, wdev->config, "config");
+
+ i = blobmsg_open_array(b, "interfaces");
vlist_for_each_element(&wdev->interfaces, iface, node)
wireless_interface_status(iface, b);
- blobmsg_close_table(b, i);
+ blobmsg_close_array(b, i);
+ blobmsg_close_table(b, c);
+}
+
+void
+wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b)
+{
+ struct uci_blob_param_list *p;
+ void *c, *d;
+ int i;
+
+ c = blobmsg_open_table(b, wdev->name);
+
+ d = blobmsg_open_table(b, "device");
+ p = wdev->drv->device.config;
+ for (i = 0; i < p->n_params; i++)
+ blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i));
+ blobmsg_close_table(b, d);
+
+ d = blobmsg_open_table(b, "interface");
+ p = wdev->drv->interface.config;
+ for (i = 0; i < p->n_params; i++)
+ blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i));
+ blobmsg_close_table(b, d);
+
blobmsg_close_table(b, c);
}
if (wdev->state != IFS_SETUP)
return UBUS_STATUS_PERMISSION_DENIED;
- if (wdev->cancel)
- return 0;
-
wireless_device_mark_up(wdev);
break;
case NOTIFY_CMD_SET_DATA:
struct wireless_device *wdev;
vlist_for_each_element(&wireless_devices, wdev, node)
- if (wdev->autostart)
- __wireless_device_set_up(wdev);
+ __wireless_device_set_up(wdev);
}