X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=wireless.c;h=efb799204d76db073587bccc678c746d73da301e;hb=24ce1eab4910869576406bafd0489daf0d3e6e28;hp=6e71fc6f07607d854711fbc9068d30a64e896fc3;hpb=501a088692cdf95feb237e030698f365c4465fdc;p=project%2Fnetifd.git diff --git a/wireless.c b/wireless.c index 6e71fc6..efb7992 100644 --- a/wireless.c +++ b/wireless.c @@ -23,24 +23,40 @@ struct vlist_tree wireless_devices; struct avl_tree wireless_drivers; static struct blob_buf b; static int drv_fd; +static LIST_HEAD(handlers); +static bool handler_pending; -static const struct blobmsg_policy wdev_policy = - { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }; +enum { + WDEV_ATTR_DISABLED, + WDEV_ATTR_RECONF, + WDEV_ATTR_SERIALIZE, + __WDEV_ATTR_MAX, +}; + +static const struct blobmsg_policy wdev_policy[__WDEV_ATTR_MAX] = { + [WDEV_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, + [WDEV_ATTR_RECONF] = { .name = "reconf", .type = BLOBMSG_TYPE_BOOL }, + [WDEV_ATTR_SERIALIZE] = { .name = "serialize", .type = BLOBMSG_TYPE_BOOL }, +}; static const struct uci_blob_param_list wdev_param = { - .n_params = 1, - .params = &wdev_policy, + .n_params = ARRAY_SIZE(wdev_policy), + .params = wdev_policy, }; 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 = { @@ -48,42 +64,102 @@ static const struct uci_blob_param_list vif_param = { .params = vif_policy, }; +enum { + VLAN_ATTR_DISABLED, + VLAN_ATTR_NETWORK, + VLAN_ATTR_ISOLATE, + __VLAN_ATTR_MAX, +}; + +static const struct blobmsg_policy vlan_policy[__VLAN_ATTR_MAX] = { + [VLAN_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, + [VLAN_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY }, + [VLAN_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL }, +}; + +static const struct uci_blob_param_list vlan_param = { + .n_params = ARRAY_SIZE(vlan_policy), + .params = vlan_policy, +}; + +enum { + STA_ATTR_DISABLED, + __STA_ATTR_MAX, +}; + +static const struct blobmsg_policy sta_policy[__STA_ATTR_MAX] = { + [STA_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, +}; + +static const struct uci_blob_param_list station_param = { + .n_params = ARRAY_SIZE(sta_policy), + .params = sta_policy, +}; + +static void +wireless_handler_stop(struct wireless_device *wdev) +{ + if (wdev->handler_pending) { + wdev->handler_pending = false; + list_del(&wdev->handler); + } +} + 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_capability) + 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 prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up) { struct wireless_interface *vif; - void *l, *i; + struct wireless_vlan *vlan; + struct wireless_station *sta; + void *l, *i, *j, *k; blob_buf_init(&b, 0); put_container(&b, wdev->config, "config"); @@ -97,9 +173,35 @@ prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up) put_container(&b, vif->config, "config"); if (vif->data) blobmsg_add_blob(&b, vif->data); + + j = blobmsg_open_table(&b, "vlans"); + vlist_for_each_element(&wdev->vlans, vlan, node) { + if (strcmp(vlan->vif, vif->name)) + continue; + k = blobmsg_open_table(&b, vlan->name); + vif_config_add_bridge(&b, vlan->network, up); + put_container(&b, vlan->config, "config"); + if (vlan->data) + blobmsg_add_blob(&b, vlan->data); + blobmsg_close_table(&b, k); + } + blobmsg_close_table(&b, j); + + j = blobmsg_open_table(&b, "stas"); + vlist_for_each_element(&wdev->stations, sta, node) { + if (strcmp(sta->vif, vif->name)) + continue; + k = blobmsg_open_table(&b, sta->name); + put_container(&b, sta->config, "config"); + if (sta->data) + blobmsg_add_blob(&b, sta->data); + blobmsg_close_table(&b, k); + } + blobmsg_close_table(&b, j); blobmsg_close_table(&b, i); } blobmsg_close_table(&b, l); + } static bool @@ -166,7 +268,11 @@ static void wireless_device_free_state(struct wireless_device *wdev) { struct wireless_interface *vif; + struct wireless_vlan *vlan; + struct wireless_station *sta; + wireless_handler_stop(wdev); + uloop_timeout_cancel(&wdev->script_check); uloop_timeout_cancel(&wdev->timeout); wireless_complete_kill_request(wdev); free(wdev->data); @@ -176,20 +282,75 @@ wireless_device_free_state(struct wireless_device *wdev) vif->data = NULL; vif->ifname = NULL; } + vlist_for_each_element(&wdev->vlans, vlan, node) { + free(vlan->data); + vlan->data = NULL; + vlan->ifname = NULL; + } + vlist_for_each_element(&wdev->stations, sta, node) { + free(sta->data); + sta->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) + 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, true); + } +} + +static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up) +{ + struct interface *iface; + struct blob_attr *cur; + const char *network; + int rem; + + if (!vlan->network || !vlan->ifname) return; - interface_handle_link(iface, vif->ifname, up); + if (up) { + struct device *dev = device_get(vlan->ifname, 2); + if (dev) { + dev->wireless_isolate = vlan->isolate; + dev->wireless = true; + dev->wireless_ap = true; + } + } + + blobmsg_for_each_attr(cur, vlan->network, rem) { + network = blobmsg_data(cur); + + iface = vlist_find(&interfaces, network, iface, node); + if (!iface) + continue; + + interface_handle_link(iface, vlan->ifname, up, true); + } } static void @@ -198,6 +359,7 @@ wireless_device_setup_cancel(struct wireless_device *wdev) if (wdev->cancel) return; + wireless_handler_stop(wdev); D(WIRELESS, "Cancel wireless device '%s' setup\n", wdev->name); wdev->cancel = true; uloop_timeout_set(&wdev->timeout, 10 * 1000); @@ -212,9 +374,26 @@ wireless_device_run_handler(struct wireless_device *wdev, bool up) int i = 0; int fds[2] = { -1, -1 }; + wireless_handler_stop(wdev); + + if (handler_pending && wdev->serialize) { + wdev->handler_action = up; + wdev->handler_pending = true; + list_add_tail(&wdev->handler, &handlers); + return; + } + if (wdev->serialize) + handler_pending = true; + 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; @@ -238,11 +417,37 @@ wireless_device_run_handler(struct wireless_device *wdev, bool up) } static void -__wireless_device_set_up(struct wireless_device *wdev) +wireless_handler_next(void) +{ + struct wireless_device *wdev; + + if (handler_pending) + return; + if (list_empty(&handlers)) + return; + wdev = list_first_entry(&handlers, struct wireless_device, handler); + list_del(&wdev->handler); + wdev->handler_pending = false; + wireless_device_run_handler(wdev, wdev->handler_action); +} + +static void +__wireless_device_set_up(struct wireless_device *wdev, int force) { - if (wdev->state != IFS_DOWN || config_init) + if (wdev->disabled) + return; + + if (wdev->retry_setup_failed) + return; + + if (!wdev->autostart) + return; + + if (!force && (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); } @@ -250,9 +455,14 @@ __wireless_device_set_up(struct wireless_device *wdev) static void wireless_device_free(struct wireless_device *wdev) { - vlist_flush_all(&wdev->interfaces); - free(wdev->config); - free(wdev); + wireless_handler_stop(wdev); + vlist_flush_all(&wdev->interfaces); + vlist_flush_all(&wdev->vlans); + vlist_flush_all(&wdev->stations); + avl_delete(&wireless_devices.avl, &wdev->node.avl); + free(wdev->config); + free(wdev->prev_config); + free(wdev); } static void @@ -260,12 +470,12 @@ 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: case IFC_RELOAD: - if (wdev->autostart) - __wireless_device_set_up(wdev); + __wireless_device_set_up(wdev, 0); + + wdev->config_state = IFC_NORMAL; break; case IFC_REMOVE: wireless_device_free(wdev); @@ -277,9 +487,13 @@ static void wireless_device_mark_down(struct wireless_device *wdev) { struct wireless_interface *vif; + struct wireless_vlan *vlan; D(WIRELESS, "Wireless device '%s' is now down\n", wdev->name); + vlist_for_each_element(&wdev->vlans, vlan, node) + wireless_vlan_handle_link(vlan, false); + vlist_for_each_element(&wdev->interfaces, vif, node) wireless_interface_handle_link(vif, false); @@ -291,22 +505,15 @@ wireless_device_mark_down(struct wireless_device *wdev) wdev_handle_config_change(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); -} - static void wireless_device_setup_timeout(struct uloop_timeout *timeout) { struct wireless_device *wdev = container_of(timeout, struct wireless_device, timeout); + if (wdev->handler_pending) { + wdev->handler_pending = false; + list_del(&wdev->handler); + } netifd_kill_process(&wdev->script_task); wdev->script_task.cb(&wdev->script_task, -1); wireless_device_mark_down(wdev); @@ -317,7 +524,15 @@ wireless_device_set_up(struct wireless_device *wdev) { wdev->retry = WIRELESS_SETUP_RETRY; wdev->autostart = true; - __wireless_device_set_up(wdev); + __wireless_device_set_up(wdev, 0); +} + +void +wireless_device_reconf(struct wireless_device *wdev) +{ + wdev->retry = WIRELESS_SETUP_RETRY; + wdev->autostart = true; + __wireless_device_set_up(wdev, wdev->reconf && (wdev->state == IFS_UP)); } static void @@ -335,6 +550,26 @@ __wireless_device_set_down(struct wireless_device *wdev) wireless_device_run_handler(wdev, false); } +static void +wireless_device_mark_up(struct wireless_device *wdev) +{ + struct wireless_interface *vif; + struct wireless_vlan *vlan; + + 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); + vlist_for_each_element(&wdev->vlans, vlan, node) + wireless_vlan_handle_link(vlan, true); +} + static void wireless_device_retry_setup(struct wireless_device *wdev) { @@ -342,7 +577,7 @@ wireless_device_retry_setup(struct wireless_device *wdev) return; if (--wdev->retry < 0) - wdev->autostart = false; + wdev->retry_setup_failed = true; __wireless_device_set_down(wdev); } @@ -362,11 +597,17 @@ wireless_device_script_task_cb(struct netifd_process *proc, int ret) default: break; } + + if (wdev->serialize) { + handler_pending = false; + wireless_handler_next(); + } } void wireless_device_set_down(struct wireless_device *wdev) { + wdev->retry_setup_failed = false; wdev->autostart = false; __wireless_device_set_down(wdev); } @@ -374,31 +615,43 @@ wireless_device_set_down(struct wireless_device *wdev) 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 + else if (!wdev->reconf || wdev->state != IFS_UP) __wireless_device_set_down(wdev); } +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); } @@ -417,6 +670,7 @@ wdev_update(struct vlist_tree *tree, struct vlist_node *node_new, struct wireless_device *wd_new = container_of(node_new, struct wireless_device, node); if (wd_old && wd_new) { + D(WIRELESS, "Update wireless device '%s'\n", wd_old->name); wdev_change_config(wd_old, wd_new); } else if (wd_old) { D(WIRELESS, "Delete wireless device '%s'\n", wd_old->name); @@ -432,20 +686,24 @@ wireless_add_handler(const char *script, const char *name, json_object *obj) { struct wireless_driver *drv; char *name_str, *script_str; - json_object *dev_config_obj, *iface_config_obj; - struct uci_blob_param_list *dev_config, *iface_config; + json_object *dev_config_obj, *iface_config_obj, *vlan_config_obj, *station_config_obj; + struct uci_blob_param_list *dev_config, *iface_config, *vlan_config, *station_config; dev_config_obj = json_get_field(obj, "device", json_type_array); iface_config_obj = json_get_field(obj, "iface", json_type_array); + vlan_config_obj = json_get_field(obj, "vlan", json_type_array); + station_config_obj = json_get_field(obj, "station", json_type_array); - if (!dev_config_obj || !iface_config_obj) + if (!dev_config_obj || !iface_config_obj || !vlan_config_obj || !station_config_obj) 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 *), + &vlan_config, sizeof(*vlan_config) + sizeof(void *), + &station_config, sizeof(*station_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); @@ -458,16 +716,25 @@ wireless_add_handler(const char *script, const char *name, json_object *obj) iface_config->next[0] = &vif_param; drv->interface.config = iface_config; + vlan_config->n_next = 1; + vlan_config->next[0] = &vlan_param; + drv->vlan.config = vlan_config; + + station_config->n_next = 1; + station_config->next[0] = &station_param; + drv->station.config = station_config; + drv->device.buf = netifd_handler_parse_config(drv->device.config, dev_config_obj); drv->interface.buf = netifd_handler_parse_config(drv->interface.config, iface_config_obj); + drv->vlan.buf = netifd_handler_parse_config(drv->vlan.config, vlan_config_obj); + drv->station.buf = netifd_handler_parse_config(drv->station.config, station_config_obj); drv->node.key = drv->name; avl_insert(&wireless_drivers, &drv->node); 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; @@ -491,7 +758,15 @@ wireless_interface_init_config(struct wireless_interface *vif) 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 @@ -508,23 +783,30 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new, 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); + wireless_interface_handle_link(vif_old, false); + free((void *) vif_old->section); free(vif_old->config); free(vif_old); } @@ -532,6 +814,106 @@ vif_update(struct vlist_tree *tree, struct vlist_node *node_new, wdev_set_config_state(wdev, IFC_RELOAD); } +static void +wireless_vlan_init_config(struct wireless_vlan *vlan) +{ + struct blob_attr *tb[__VLAN_ATTR_MAX]; + struct blob_attr *cur; + + vlan->network = NULL; + blobmsg_parse(vlan_policy, __VLAN_ATTR_MAX, tb, blob_data(vlan->config), blob_len(vlan->config)); + + if ((cur = tb[VLAN_ATTR_NETWORK])) + vlan->network = cur; + + cur = tb[VLAN_ATTR_ISOLATE]; + if (cur) + vlan->isolate = blobmsg_get_bool(cur); +} + +static void +vlan_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct wireless_vlan *vlan_old = container_of(node_old, struct wireless_vlan, node); + struct wireless_vlan *vlan_new = container_of(node_new, struct wireless_vlan, node); + struct wireless_device *wdev; + + if (vlan_old) + wdev = vlan_old->wdev; + else + wdev = vlan_new->wdev; + + if (vlan_old && vlan_new) { + free((void *) vlan_old->section); + vlan_old->section = strdup(vlan_new->section); + if (blob_attr_equal(vlan_old->config, vlan_new->config)) { + free(vlan_new); + return; + } + + D(WIRELESS, "Update wireless vlan %s on device %s\n", vlan_new->name, wdev->name); + wireless_vlan_handle_link(vlan_old, false); + free(vlan_old->config); + vlan_old->config = blob_memdup(vlan_new->config); + vlan_old->isolate = vlan_new->isolate; + wireless_vlan_init_config(vlan_old); + free(vlan_new); + } else if (vlan_new) { + D(WIRELESS, "Create new wireless vlan %s on device %s\n", vlan_new->name, wdev->name); + vlan_new->section = strdup(vlan_new->section); + vlan_new->config = blob_memdup(vlan_new->config); + wireless_vlan_init_config(vlan_new); + } else if (vlan_old) { + D(WIRELESS, "Delete wireless interface %s on device %s\n", vlan_old->name, wdev->name); + wireless_vlan_handle_link(vlan_old, false); + free((void *) vlan_old->section); + free(vlan_old->config); + free(vlan_old); + } + + wdev_set_config_state(wdev, IFC_RELOAD); +} + +static void +station_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct wireless_station *sta_old = container_of(node_old, struct wireless_station, node); + struct wireless_station *sta_new = container_of(node_new, struct wireless_station, node); + struct wireless_device *wdev; + + if (sta_old) + wdev = sta_old->wdev; + else + wdev = sta_new->wdev; + + if (sta_old && sta_new) { + free((void *) sta_old->section); + sta_old->section = strdup(sta_new->section); + if (blob_attr_equal(sta_old->config, sta_new->config)) { + free(sta_new); + return; + } + + D(WIRELESS, "Update wireless station %s on device %s\n", sta_new->name, wdev->name); + free(sta_old->config); + sta_old->config = blob_memdup(sta_new->config); + free(sta_new); + } else if (sta_new) { + D(WIRELESS, "Create new wireless station %s on device %s\n", sta_new->name, wdev->name); + sta_new->section = strdup(sta_new->section); + sta_new->config = blob_memdup(sta_new->config); + } else if (sta_old) { + D(WIRELESS, "Delete wireless station %s on device %s\n", sta_old->name, wdev->name); + free((void *) sta_old->section); + free(sta_old->config); + free(sta_old); + } + + wdev_set_config_state(wdev, IFC_RELOAD); +} + static void wireless_proc_poll_fd(struct uloop_fd *fd, unsigned int events) { @@ -588,25 +970,38 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo { struct wireless_device *wdev; char *name_buf; - struct blob_attr *disabled; + struct blob_attr *tb[__WDEV_ATTR_MAX]; + struct blob_attr *cur; - blobmsg_parse(&wdev_policy, 1, &disabled, blob_data(data), blob_len(data)); - if (disabled && blobmsg_get_bool(disabled)) - return; + blobmsg_parse(wdev_policy, __WDEV_ATTR_MAX, tb, blob_data(data), blob_len(data)); wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1); + + cur = tb[WDEV_ATTR_DISABLED]; + wdev->disabled = cur && blobmsg_get_bool(cur); + wdev->drv = drv; wdev->state = IFS_DOWN; wdev->config_state = IFC_NORMAL; wdev->name = strcpy(name_buf, name); wdev->config = data; - wdev->config_autostart = true; - wdev->autostart = wdev->config_autostart; + wdev->handler_pending = false; + + cur = tb[WDEV_ATTR_SERIALIZE]; + wdev->serialize = cur && blobmsg_get_bool(cur); + + cur = tb[WDEV_ATTR_RECONF]; + wdev->reconf = cur && blobmsg_get_bool(cur); + + wdev->retry_setup_failed = false; + wdev->autostart = true; 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); + vlist_init(&wdev->vlans, avl_strcmp, vlan_update); + wdev->vlans.keep_old = true; + vlist_init(&wdev->stations, avl_strcmp, station_update); + wdev->stations.keep_old = true; wdev->timeout.cb = wireless_device_setup_timeout; wdev->script_task.cb = wireless_device_script_task_cb; @@ -617,9 +1012,96 @@ wireless_device_create(struct wireless_driver *drv, const char *name, struct blo 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_station_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section) +{ + struct wireless_station *sta; + struct blob_attr *tb[__STA_ATTR_MAX]; + struct blob_attr *cur; + char *name_buf, *vif_buf; + char name[8]; + + blobmsg_parse(sta_policy, __STA_ATTR_MAX, tb, blob_data(data), blob_len(data)); + + cur = tb[STA_ATTR_DISABLED]; + if (cur && blobmsg_get_bool(cur)) + return; + + sprintf(name, "%d", wdev->vlan_idx++); + + sta = calloc_a(sizeof(*sta), + &name_buf, strlen(name) + 1, + &vif_buf, strlen(vif) + 1); + sta->name = strcpy(name_buf, name); + sta->vif = strcpy(vif_buf, vif); + sta->wdev = wdev; + sta->config = data; + sta->section = section; + + vlist_add(&wdev->stations, &sta->node, sta->name); +} + +static void +wireless_station_status(struct wireless_station *sta, struct blob_buf *b) +{ + void *i; + + i = blobmsg_open_table(b, NULL); + if (sta->section) + blobmsg_add_string(b, "section", sta->section); + put_container(b, sta->config, "config"); + blobmsg_close_table(b, i); +} + +void +wireless_vlan_create(struct wireless_device *wdev, char *vif, struct blob_attr *data, const char *section) +{ + struct wireless_vlan *vlan; + struct blob_attr *tb[__VLAN_ATTR_MAX]; + struct blob_attr *cur; + char *name_buf, *vif_buf; + char name[8]; + + blobmsg_parse(vlan_policy, __VLAN_ATTR_MAX, tb, blob_data(data), blob_len(data)); + + cur = tb[VLAN_ATTR_DISABLED]; + if (cur && blobmsg_get_bool(cur)) + return; + + sprintf(name, "%d", wdev->vlan_idx++); + + vlan = calloc_a(sizeof(*vlan), + &name_buf, strlen(name) + 1, + &vif_buf, strlen(vif) + 1); + vlan->name = strcpy(name_buf, name); + vlan->vif = strcpy(vif_buf, vif); + vlan->wdev = wdev; + vlan->config = data; + vlan->section = section; + vlan->isolate = false; + + vlist_add(&wdev->vlans, &vlan->node, vlan->name); +} + +static void +wireless_vlan_status(struct wireless_vlan *vlan, struct blob_buf *b) +{ + void *i; + + i = blobmsg_open_table(b, NULL); + if (vlan->section) + blobmsg_add_string(b, "section", vlan->section); + if (vlan->ifname) + blobmsg_add_string(b, "ifname", vlan->ifname); + put_container(b, vlan->config, "config"); + blobmsg_close_table(b, i); } -void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section) +struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section) { struct wireless_interface *vif; struct blob_attr *tb[__VIF_ATTR_MAX]; @@ -631,30 +1113,45 @@ void wireless_interface_create(struct wireless_device *wdev, struct blob_attr *d cur = tb[VIF_ATTR_DISABLED]; if (cur && blobmsg_get_bool(cur)) - return; + return NULL; 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); + return vif; } static void wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b) { - void *i; + struct wireless_vlan *vlan; + struct wireless_station *sta; + void *i, *j; - 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"); + j = blobmsg_open_array(b, "vlans"); + vlist_for_each_element(&iface->wdev->vlans, vlan, node) + if (!strcmp(iface->name, vlan->vif)) + wireless_vlan_status(vlan, b); + blobmsg_close_array(b, j); + j = blobmsg_open_array(b, "stations"); + vlist_for_each_element(&iface->wdev->stations, sta, node) + if (!strcmp(iface->name, sta->vif)) + wireless_station_status(sta, b); + blobmsg_close_array(b, j); blobmsg_close_table(b, i); } @@ -668,10 +1165,38 @@ wireless_device_status(struct wireless_device *wdev, struct blob_buf *b) 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); } @@ -695,6 +1220,26 @@ wireless_interface_set_data(struct wireless_interface *vif) vif->ifname = blobmsg_data(cur); } +static void +wireless_vlan_set_data(struct wireless_vlan *vlan) +{ + enum { + VLAN_DATA_IFNAME, + __VLAN_DATA_MAX, + }; + static const struct blobmsg_policy data_policy[__VLAN_DATA_MAX] = { + [VLAN_DATA_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb[__VLAN_DATA_MAX]; + struct blob_attr *cur; + + blobmsg_parse(data_policy, __VLAN_DATA_MAX, tb, + blobmsg_data(vlan->data), blobmsg_data_len(vlan->data)); + + if ((cur = tb[VLAN_DATA_IFNAME])) + vlan->ifname = blobmsg_data(cur); +} + static int wireless_device_add_process(struct wireless_device *wdev, struct blob_attr *data) { @@ -812,15 +1357,18 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data, enum { NOTIFY_ATTR_COMMAND, NOTIFY_ATTR_VIF, + NOTIFY_ATTR_VLAN, NOTIFY_ATTR_DATA, __NOTIFY_MAX, }; static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = { [NOTIFY_ATTR_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_INT32 }, [NOTIFY_ATTR_VIF] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, + [NOTIFY_ATTR_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_STRING }, [NOTIFY_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, }; struct wireless_interface *vif = NULL; + struct wireless_vlan *vlan = NULL; struct blob_attr *tb[__NOTIFY_MAX]; struct blob_attr *cur, **pdata; @@ -835,26 +1383,31 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data, return UBUS_STATUS_NOT_FOUND; } + if ((cur = tb[NOTIFY_ATTR_VLAN]) != NULL) { + vlan = vlist_find(&wdev->vlans, blobmsg_data(cur), vlan, node); + if (!vlan) + return UBUS_STATUS_NOT_FOUND; + } + cur = tb[NOTIFY_ATTR_DATA]; if (!cur) return UBUS_STATUS_INVALID_ARGUMENT; switch (blobmsg_get_u32(tb[NOTIFY_ATTR_COMMAND])) { case NOTIFY_CMD_UP: - if (vif) + if (vif || vlan) return UBUS_STATUS_INVALID_ARGUMENT; 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: if (vif) pdata = &vif->data; + else if (vlan) + pdata = &vlan->data; else pdata = &wdev->data; @@ -864,6 +1417,8 @@ wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data, *pdata = blob_memdup(cur); if (vif) wireless_interface_set_data(vif); + else if (vlan) + wireless_vlan_set_data(vlan); break; case NOTIFY_CMD_PROCESS_ADD: return wireless_device_add_process(wdev, cur); @@ -884,6 +1439,5 @@ wireless_start_pending(void) 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, 0); }