From 5a6c19988cf2c6b5653e138c0e026c352a9d9ad6 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Sun, 19 Aug 2018 22:07:15 -0500 Subject: [PATCH] device/netdev: Properly implement mode switching --- src/device.c | 109 ++++++++++++++--------------------- src/device.h | 7 --- src/netdev.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++---- src/netdev.h | 4 +- 4 files changed, 195 insertions(+), 85 deletions(-) diff --git a/src/device.c b/src/device.c index 9b25f536..435392a1 100644 --- a/src/device.c +++ b/src/device.c @@ -95,8 +95,6 @@ struct device { bool seen_hidden_networks : 1; uint32_t ap_roam_watch; - - enum device_mode mode; }; struct signal_agent { @@ -454,11 +452,6 @@ enum device_state device_get_state(struct device *device) return device->state; } -enum device_mode device_get_mode(struct device *device) -{ - return device->mode; -} - static void periodic_scan_trigger(int err, void *user_data) { struct device *device = user_data; @@ -2054,29 +2047,6 @@ static struct l_dbus_message *device_signal_agent_unregister( return l_dbus_message_new_method_return(message); } -static void device_prepare_adhoc_ap_mode(struct device *device) -{ - periodic_scan_stop(device); - - /* Drop all state we can related to client mode */ - - if (device->scan_pending) - dbus_pending_reply(&device->scan_pending, - dbus_error_aborted(device->scan_pending)); - - l_hashmap_foreach_remove(device->networks, - device_remove_network, device); - - l_queue_destroy(device->autoconnect_list, l_free); - device->autoconnect_list = l_queue_new(); - - l_queue_destroy(device->bss_list, bss_free); - device->bss_list = l_queue_new(); - - l_queue_destroy(device->networks_sorted, NULL); - device->networks_sorted = l_queue_new(); -} - static void device_hidden_network_scan_triggered(int err, void *user_data) { struct device *device = user_data; @@ -2472,35 +2442,19 @@ static bool device_property_get_mode(struct l_dbus *dbus, return true; } -static struct l_dbus_message *device_change_mode(struct device *device, - struct l_dbus_message *message, enum device_mode mode) +static void set_mode_cb(struct netdev *netdev, int result, void *user_data) { - if (device->mode == mode) - return dbus_error_already_exists(message); + struct set_generic_cb_data *cb_data = user_data; + struct l_dbus_message *reply = NULL; - /* ensure correct connection state in AP/AdHoc mode */ - if ((mode == DEVICE_MODE_AP || mode == DEVICE_MODE_ADHOC) && - (device->state != DEVICE_STATE_DISCONNECTED && - device->state != DEVICE_STATE_AUTOCONNECT)) - return dbus_error_busy(message); + if (result < 0) + reply = dbus_error_from_errno(result, cb_data->message); - switch (mode) { - case DEVICE_MODE_AP: - device_prepare_adhoc_ap_mode(device); - netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP); - break; - case DEVICE_MODE_ADHOC: - device_prepare_adhoc_ap_mode(device); - netdev_set_iftype(device->netdev, NETDEV_IFTYPE_ADHOC); - break; - case DEVICE_MODE_STATION: - netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION); - break; - } + cb_data->complete(cb_data->dbus, cb_data->message, reply); + cb_data->message = NULL; - device->mode = mode; - - return NULL; + l_dbus_property_changed(cb_data->dbus, device_get_path(cb_data->device), + IWD_DEVICE_INTERFACE, "Mode"); } static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus, @@ -2510,27 +2464,45 @@ static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus, void *user_data) { struct device *device = user_data; - struct l_dbus_message *reply; - const char* mode; - enum device_mode change; + struct netdev *netdev = device->netdev; + const char *mode; + enum netdev_iftype iftype; + int r; + struct set_generic_cb_data *cb_data; if (!l_dbus_message_iter_get_variant(new_value, "s", &mode)) return dbus_error_invalid_args(message); if (!strcmp(mode, "station")) - change = DEVICE_MODE_STATION; + iftype = NETDEV_IFTYPE_STATION; else if (!strcmp(mode, "ap")) - change = DEVICE_MODE_AP; + iftype = NETDEV_IFTYPE_AP; else if (!strcmp(mode, "ad-hoc")) - change = DEVICE_MODE_ADHOC; + iftype = NETDEV_IFTYPE_ADHOC; else return dbus_error_invalid_args(message); - reply = device_change_mode(device, message, change); - if (reply) - return reply; + if (iftype == netdev_get_iftype(netdev)) { + complete(dbus, message, NULL); + return NULL; + } - complete(dbus, message, NULL); + /* TODO: Special case, remove when Device/Station split is made */ + if (iftype != NETDEV_IFTYPE_STATION && device_is_busy(device)) + return dbus_error_busy(message); + + cb_data = l_new(struct set_generic_cb_data, 1); + cb_data->device = device; + cb_data->dbus = dbus; + cb_data->message = message; + cb_data->complete = complete; + + r = netdev_set_iftype(device->netdev, iftype, set_mode_cb, + cb_data, set_generic_destroy); + if (r < 0) { + l_free(cb_data); + return dbus_error_from_errno(r, message); + } return NULL; } @@ -2591,6 +2563,10 @@ static void device_netdev_notify(struct netdev *netdev, l_dbus_property_changed(dbus, device_get_path(device), IWD_DEVICE_INTERFACE, "Powered"); + /* TODO: Remove when Device/Station split is done */ + if (netdev_get_iftype(device->netdev) != NETDEV_IFTYPE_STATION) + return; + if (device->autoconnect) device_enter_state(device, DEVICE_STATE_AUTOCONNECT); else @@ -2677,7 +2653,8 @@ struct device *device_create(struct wiphy *wiphy, struct netdev *netdev) action_ap_roam_prefix, sizeof(action_ap_roam_prefix), device_ap_roam_frame_event, device); - if (netdev_get_is_up(netdev)) + if (netdev_get_is_up(netdev) && + netdev_get_iftype(netdev) == NETDEV_IFTYPE_STATION) device_enter_state(device, DEVICE_STATE_AUTOCONNECT); return device; diff --git a/src/device.h b/src/device.h index eb5f1f31..b42a85a8 100644 --- a/src/device.h +++ b/src/device.h @@ -44,12 +44,6 @@ enum device_state { DEVICE_STATE_ROAMING }; -enum device_mode { - DEVICE_MODE_STATION, - DEVICE_MODE_AP, - DEVICE_MODE_ADHOC, -}; - typedef void (*device_watch_func_t)(struct device *device, enum device_event event, void *userdata); @@ -64,7 +58,6 @@ struct netdev *device_get_netdev(struct device *device); uint32_t device_get_ifindex(struct device *device); const uint8_t *device_get_address(struct device *device); enum device_state device_get_state(struct device *device); -enum device_mode device_get_mode(struct device *device); uint32_t device_add_state_watch(struct device *device, device_state_watch_func_t func, diff --git a/src/netdev.c b/src/netdev.c index 5f4b677c..a9a99cd5 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -92,6 +92,7 @@ struct netdev { uint32_t disconnect_cmd_id; uint32_t join_adhoc_cmd_id; uint32_t leave_adhoc_cmd_id; + uint32_t set_interface_cmd_id; enum netdev_result result; struct l_timeout *neighbor_report_timeout; struct l_timeout *sa_query_timeout; @@ -3823,10 +3824,126 @@ static int netdev_cqm_rssi_update(struct netdev *netdev) return 0; } -int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type) +static struct l_genl_msg *netdev_build_cmd_set_interface(struct netdev *netdev, + uint32_t iftype) { + struct l_genl_msg *msg = + l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32); + + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype); + + return msg; +} + +struct netdev_set_iftype_request { + netdev_command_cb_t cb; + void *user_data; + netdev_destroy_func_t destroy; + uint32_t pending_type; + uint32_t ref; + struct netdev *netdev; + bool bring_up; +}; + +static void netdev_set_iftype_request_destroy(void *user_data) +{ + struct netdev_set_iftype_request *req = user_data; + struct netdev *netdev = req->netdev; + + req->ref--; + if (req->ref) + return; + + netdev->set_powered_cmd_id = 0; + netdev->set_interface_cmd_id = 0; + + if (req->destroy) + req->destroy(req->user_data); + + l_free(req); +} + +static void netdev_set_iftype_up_cb(int error, uint16_t type, + const void *data, + uint32_t len, void *user_data) +{ + struct netdev_set_iftype_request *req = user_data; + struct netdev *netdev = req->netdev; + + if (req->cb) + req->cb(netdev, error, req->user_data); +} + +static void netdev_set_iftype_cb(struct l_genl_msg *msg, void *user_data) +{ + struct netdev_set_iftype_request *req = user_data; + struct netdev *netdev = req->netdev; + int error = l_genl_msg_get_error(msg); + + if (error != 0) + goto done; + + netdev->type = req->pending_type; + + /* If the netdev was down originally, we're done */ + if (!req->bring_up) + goto done; + + netdev->set_powered_cmd_id = + rtnl_set_powered(netdev->index, true, + netdev_set_iftype_up_cb, req, + netdev_set_iftype_request_destroy); + if (!netdev->set_powered_cmd_id) { + error = -EIO; + goto done; + } + + req->ref++; + netdev->set_interface_cmd_id = 0; + return; + +done: + if (req->cb) + req->cb(netdev, error, req->user_data); +} + +static void netdev_set_iftype_down_cb(int error, uint16_t type, + const void *data, + uint32_t len, void *user_data) +{ + struct netdev_set_iftype_request *req = user_data; + struct netdev *netdev = req->netdev; struct l_genl_msg *msg; + + if (error != 0) + goto error; + + msg = netdev_build_cmd_set_interface(netdev, req->pending_type); + netdev->set_interface_cmd_id = + l_genl_family_send(nl80211, msg, netdev_set_iftype_cb, req, + netdev_set_iftype_request_destroy); + if (!netdev->set_interface_cmd_id) { + l_genl_msg_unref(msg); + error = -EIO; + goto error; + } + + req->ref++; + netdev->set_powered_cmd_id = 0; + return; + +error: + if (req->cb) + req->cb(netdev, error, req->user_data); +} + +int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type, + netdev_command_cb_t cb, void *user_data, + netdev_destroy_func_t destroy) +{ uint32_t iftype; + struct netdev_set_iftype_request *req; switch (type) { case NETDEV_IFTYPE_AP: @@ -3843,21 +3960,42 @@ int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type) return -EINVAL; } - msg = l_genl_msg_new_sized(NL80211_CMD_SET_INTERFACE, 32); - l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); - l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype); + if (netdev->set_powered_cmd_id || + netdev->set_interface_cmd_id) + return -EBUSY; - if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL)) { - l_error("CMD_SET_INTERFACE failed"); + req = l_new(struct netdev_set_iftype_request, 1); + req->cb = cb; + req->user_data = user_data; + req->destroy = destroy; + req->pending_type = iftype; + req->netdev = netdev; + req->ref = 1; + req->bring_up = netdev_get_is_up(netdev); + + if (!req->bring_up) { + struct l_genl_msg *msg = + netdev_build_cmd_set_interface(netdev, iftype); + + netdev->set_interface_cmd_id = + l_genl_family_send(nl80211, msg, + netdev_set_iftype_cb, req, + netdev_set_iftype_request_destroy); + if (netdev->set_interface_cmd_id) + return 0; l_genl_msg_unref(msg); - - return -EIO; + } else { + netdev->set_powered_cmd_id = + rtnl_set_powered(netdev->index, false, + netdev_set_iftype_down_cb, req, + netdev_set_iftype_request_destroy); + if (netdev->set_powered_cmd_id) + return 0; } - netdev->type = iftype; - - return 0; + l_free(req); + return -EIO; } static void netdev_bridge_port_event(const struct ifinfomsg *ifi, int bytes, diff --git a/src/netdev.h b/src/netdev.h index 09e74701..12fb3832 100644 --- a/src/netdev.h +++ b/src/netdev.h @@ -99,7 +99,9 @@ struct wiphy *netdev_get_wiphy(struct netdev *netdev); const uint8_t *netdev_get_address(struct netdev *netdev); uint32_t netdev_get_ifindex(struct netdev *netdev); enum netdev_iftype netdev_get_iftype(struct netdev *netdev); -int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type); +int netdev_set_iftype(struct netdev *netdev, enum netdev_iftype type, + netdev_command_cb_t cb, void *user_data, + netdev_destroy_func_t destroy); int netdev_set_4addr(struct netdev *netdev, bool use_4addr, netdev_command_cb_t cb, void *user_data, netdev_destroy_func_t destroy);