hostapd: add internal API for renaming AP interfaces
authorFelix Fietkau <nbd@nbd.name>
Wed, 6 Sep 2023 08:33:30 +0000 (10:33 +0200)
committerFelix Fietkau <nbd@nbd.name>
Wed, 13 Sep 2023 10:37:44 +0000 (12:37 +0200)
Will be used for improving reload support

Signed-off-by: Felix Fietkau <nbd@nbd.name>
package/network/services/hostapd/patches/601-ucode_support.patch
package/network/services/hostapd/src/src/ap/ucode.c

index e0bbf1337da8adeadbb0ac2814468a9b3b0a64a0..dc3f60ca30b294cc3cbadb80558e7c2d26135558 100644 (file)
  
 --- a/src/drivers/driver.h
 +++ b/src/drivers/driver.h
-@@ -6426,6 +6426,7 @@ union wpa_event_data {
+@@ -3787,6 +3787,25 @@ struct wpa_driver_ops {
+                        const char *ifname);
+       /**
++       * if_rename - Rename a virtual interface
++       * @priv: Private driver interface data
++       * @type: Interface type
++       * @ifname: Interface name of the virtual interface to be renamed
++       *          (NULL when renaming the AP BSS interface)
++       * @new_name: New interface name of the virtual interface
++       * Returns: 0 on success, -1 on failure
++       */
++      int (*if_rename)(void *priv, enum wpa_driver_if_type type,
++                       const char *ifname, const char *new_name);
++
++      /**
++       * set_first_bss - Make a virtual interface the first (primary) bss
++       * @priv: Private driver interface data
++       * Returns: 0 on success, -1 on failure
++       */
++      int (*set_first_bss)(void *priv);
++
++      /**
+        * set_sta_vlan - Bind a station into a specific interface (AP only)
+        * @priv: Private driver interface data
+        * @ifname: Interface (main or virtual BSS or VLAN)
+@@ -6426,6 +6445,7 @@ union wpa_event_data {
  
        /**
         * struct ch_switch
         * @freq: Frequency of new channel in MHz
         * @ht_enabled: Whether this is an HT channel
         * @ch_offset: Secondary channel offset
-@@ -6436,6 +6437,7 @@ union wpa_event_data {
+@@ -6436,6 +6456,7 @@ union wpa_event_data {
         * @punct_bitmap: Puncturing bitmap
         */
        struct ch_switch {
        switch (event) {
        case EVENT_AUTH:
  #ifdef CONFIG_FST
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -393,6 +393,23 @@ static inline int hostapd_drv_stop_ap(st
+       return hapd->driver->stop_ap(hapd->drv_priv);
+ }
++static inline int hostapd_drv_if_rename(struct hostapd_data *hapd,
++                                      enum wpa_driver_if_type type,
++                                      const char *ifname,
++                                      const char *new_name)
++{
++      if (!hapd->driver || !hapd->driver->if_rename || !hapd->drv_priv)
++              return -1;
++      return hapd->driver->if_rename(hapd->drv_priv, type, ifname, new_name);
++}
++
++static inline int hostapd_drv_set_first_bss(struct hostapd_data *hapd)
++{
++      if (!hapd->driver || !hapd->driver->set_first_bss || !hapd->drv_priv)
++              return 0;
++      return hapd->driver->set_first_bss(hapd->drv_priv);
++}
++
+ static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+                                          struct wpa_channel_info *ci)
+ {
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1333,7 +1333,7 @@ static void wpa_driver_nl80211_event_rtm
+               }
+               wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+                          namebuf, ifname);
+-              if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++              if (drv->first_bss->ifindex != ifi->ifi_index) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Not the main interface (%s) - do not indicate interface down",
+                                  drv->first_bss->ifname);
+@@ -1369,7 +1369,7 @@ static void wpa_driver_nl80211_event_rtm
+               }
+               wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+                          namebuf, ifname);
+-              if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
++              if (drv->first_bss->ifindex != ifi->ifi_index) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Not the main interface (%s) - do not indicate interface up",
+                                  drv->first_bss->ifname);
+@@ -8432,6 +8432,7 @@ static void *i802_init(struct hostapd_da
+       char master_ifname[IFNAMSIZ];
+       int ifindex, br_ifindex = 0;
+       int br_added = 0;
++      int err;
+       bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+                                         params->global_priv, 1,
+@@ -8491,21 +8492,17 @@ static void *i802_init(struct hostapd_da
+           (params->num_bridge == 0 || !params->bridge[0]))
+               add_ifidx(drv, br_ifindex, drv->ifindex);
+-      if (bss->added_if_into_bridge || bss->already_in_bridge) {
+-              int err;
+-
+-              drv->rtnl_sk = nl_socket_alloc();
+-              if (drv->rtnl_sk == NULL) {
+-                      wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+-                      goto failed;
+-              }
++      drv->rtnl_sk = nl_socket_alloc();
++      if (drv->rtnl_sk == NULL) {
++              wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
++              goto failed;
++      }
+-              err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+-              if (err) {
+-                      wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+-                                 nl_geterror(err));
+-                      goto failed;
+-              }
++      err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
++      if (err) {
++              wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
++                         nl_geterror(err));
++              goto failed;
+       }
+       if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+@@ -8874,6 +8871,50 @@ static int wpa_driver_nl80211_if_remove(
+       return 0;
+ }
++static int wpa_driver_nl80211_if_rename(struct i802_bss *bss,
++                                      enum wpa_driver_if_type type,
++                                      const char *ifname, const char *new_name)
++{
++      struct wpa_driver_nl80211_data *drv = bss->drv;
++      struct ifinfomsg ifi = {
++              .ifi_family = AF_UNSPEC,
++              .ifi_index = bss->ifindex,
++      };
++      struct nl_msg *msg;
++      int res = -ENOMEM;
++
++      if (ifname)
++              ifi.ifi_index = if_nametoindex(ifname);
++
++      msg = nlmsg_alloc_simple(RTM_SETLINK, 0);
++      if (!msg)
++              return res;
++
++      if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
++              goto out;
++
++      if (nla_put_string(msg, IFLA_IFNAME, new_name))
++              goto out;
++
++      res = nl_send_auto_complete(drv->rtnl_sk, msg);
++      if (res < 0)
++              goto out;
++
++      res = nl_wait_for_ack(drv->rtnl_sk);
++      if (res) {
++              wpa_printf(MSG_INFO,
++                         "nl80211: Renaming device %s to %s failed: %s",
++                         ifname ? ifname : bss->ifname, new_name, nl_geterror(res));
++              goto out;
++      }
++
++      if (type == WPA_IF_AP_BSS && !ifname)
++              os_strlcpy(bss->ifname, new_name, sizeof(bss->ifname));
++
++out:
++      nlmsg_free(msg);
++      return res;
++}
+ static int cookie_handler(struct nl_msg *msg, void *arg)
+ {
+@@ -10472,6 +10513,37 @@ static int driver_nl80211_if_remove(void
+ }
++static int driver_nl80211_if_rename(void *priv, enum wpa_driver_if_type type,
++                                  const char *ifname, const char *new_name)
++{
++      struct i802_bss *bss = priv;
++      return wpa_driver_nl80211_if_rename(bss, type, ifname, new_name);
++}
++
++
++static int driver_nl80211_set_first_bss(void *priv)
++{
++      struct i802_bss *bss = priv, *tbss;
++      struct wpa_driver_nl80211_data *drv = bss->drv;
++
++      if (drv->first_bss == bss)
++              return 0;
++
++      for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
++              if (tbss->next != bss)
++                      continue;
++
++              tbss->next = bss->next;
++              bss->next = drv->first_bss;
++              drv->first_bss = bss;
++              drv->ctx = bss->ctx;
++              return 0;
++      }
++
++      return -1;
++}
++
++
+ static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+                                   size_t data_len, int noack,
+                                   unsigned int freq,
+@@ -13656,6 +13728,8 @@ const struct wpa_driver_ops wpa_driver_n
+       .set_acl = wpa_driver_nl80211_set_acl,
+       .if_add = wpa_driver_nl80211_if_add,
+       .if_remove = driver_nl80211_if_remove,
++      .if_rename = driver_nl80211_if_rename,
++      .set_first_bss = driver_nl80211_set_first_bss,
+       .send_mlme = driver_nl80211_send_mlme,
+       .get_hw_feature_data = nl80211_get_hw_feature_data,
+       .sta_add = wpa_driver_nl80211_sta_add,
index fc8e7c5d77a61f716f578507d2d3b1c82aceedc6..849f8028e6f7e86db56f3d66964fa287dd679ebb 100644 (file)
@@ -120,6 +120,7 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
        struct hostapd_config *conf;
        uc_value_t *file = uc_fn_arg(0);
        uc_value_t *index = uc_fn_arg(1);
+       uc_value_t *files_only = uc_fn_arg(2);
        unsigned int i, idx = 0;
        int ret = -1;
 
@@ -131,9 +132,27 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
 
        iface = hapd->iface;
        conf = interfaces->config_read_cb(ucv_string_get(file));
-       if (!conf || idx > conf->num_bss || !conf->bss[idx])
+       if (!conf)
                goto out;
 
+       if (idx > conf->num_bss || !conf->bss[idx])
+               goto free;
+
+       if (ucv_boolean_get(files_only)) {
+               struct hostapd_bss_config *bss = conf->bss[idx];
+               struct hostapd_bss_config *old_bss = hapd->conf;
+
+#define swap_field(name)                               \
+       do {                                                            \
+               void *ptr = old_bss->name;              \
+               old_bss->name = bss->name;              \
+               bss->name = ptr;                                \
+       } while (0)
+
+               swap_field(ssid.wpa_psk_file);
+               goto done;
+       }
+
        hostapd_bss_deinit_no_free(hapd);
        hostapd_drv_stop_ap(hapd);
        hostapd_free_hapd_data(hapd);
@@ -144,13 +163,14 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
                        iface->conf->bss[i] = conf->bss[idx];
        hapd->conf = conf->bss[idx];
        conf->bss[idx] = old_bss;
-       hostapd_config_free(conf);
 
-       hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
+       hostapd_setup_bss(hapd, hapd == iface->bss[0], true);
        hostapd_ucode_update_interfaces();
 
+done:
        ret = 0;
-
+free:
+       hostapd_config_free(conf);
 out:
        return ucv_int64_new(ret);
 }
@@ -181,10 +201,15 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
        struct hostapd_iface *iface;
        int i, idx;
 
-       if (!hapd || hapd == hapd->iface->bss[0])
+       if (!hapd)
                return NULL;
 
        iface = hapd->iface;
+       if (iface->num_bss == 1) {
+               wpa_printf(MSG_ERROR, "trying to delete last bss of an iface: %s\n", hapd->conf->iface);
+               return NULL;
+       }
+
        for (idx = 0; idx < iface->num_bss; idx++)
                if (iface->bss[idx] == hapd)
                        break;
@@ -194,8 +219,13 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
 
        for (i = idx + 1; i < iface->num_bss; i++)
                iface->bss[i - 1] = iface->bss[i];
+
        iface->num_bss--;
 
+       iface->bss[0]->interface_added = 0;
+       hostapd_drv_set_first_bss(iface->bss[0]);
+       hapd->interface_added = 1;
+
        hostapd_drv_stop_ap(hapd);
        hostapd_bss_deinit(hapd);
        hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
@@ -269,6 +299,58 @@ out:
        return ret;
 }
 
+static uc_value_t *
+uc_hostapd_iface_set_bss_order(uc_vm_t *vm, size_t nargs)
+{
+       struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
+       uc_value_t *bss_list = uc_fn_arg(0);
+       struct hostapd_data **new_bss;
+       struct hostapd_bss_config **new_conf;
+
+       if (!iface)
+               return NULL;
+
+       if (ucv_type(bss_list) != UC_ARRAY ||
+           ucv_array_length(bss_list) != iface->num_bss)
+               return NULL;
+
+       new_bss = calloc(iface->num_bss, sizeof(*new_bss));
+       new_conf = calloc(iface->num_bss, sizeof(*new_conf));
+       for (size_t i = 0; i < iface->num_bss; i++) {
+               struct hostapd_data *bss;
+
+               bss = ucv_resource_data(ucv_array_get(bss_list, i), "hostapd.bss");
+               if (bss->iface != iface)
+                       goto free;
+
+               for (size_t k = 0; k < i; k++)
+                       if (new_bss[k] == bss)
+                               goto free;
+
+               new_bss[i] = bss;
+               new_conf[i] = bss->conf;
+       }
+
+       new_bss[0]->interface_added = 0;
+       for (size_t i = 1; i < iface->num_bss; i++)
+               new_bss[i]->interface_added = 1;
+
+       free(iface->bss);
+       iface->bss = new_bss;
+
+       free(iface->conf->bss);
+       iface->conf->bss = new_conf;
+       iface->conf->num_bss = iface->num_bss;
+       hostapd_drv_set_first_bss(iface->bss[0]);
+
+       return ucv_boolean_new(true);
+
+free:
+       free(new_bss);
+       free(new_conf);
+       return NULL;
+}
+
 static uc_value_t *
 uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
 {
@@ -456,6 +538,55 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
        return ucv_boolean_new(!ret);
 }
 
+static uc_value_t *
+uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs)
+{
+       struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
+       uc_value_t *ifname_arg = uc_fn_arg(0);
+       char prev_ifname[IFNAMSIZ + 1];
+       struct sta_info *sta;
+       const char *ifname;
+       int ret;
+
+       if (!hapd || ucv_type(ifname_arg) != UC_STRING)
+               return NULL;
+
+       os_strlcpy(prev_ifname, hapd->conf->iface, sizeof(prev_ifname));
+       ifname = ucv_string_get(ifname_arg);
+
+       hostapd_ubus_free_bss(hapd);
+       if (interfaces->ctrl_iface_deinit)
+               interfaces->ctrl_iface_deinit(hapd);
+
+       ret = hostapd_drv_if_rename(hapd, WPA_IF_AP_BSS, NULL, ifname);
+       if (ret)
+               goto out;
+
+       for (sta = hapd->sta_list; sta; sta = sta->next) {
+               char cur_name[IFNAMSIZ + 1], new_name[IFNAMSIZ + 1];
+
+               if (!(sta->flags & WLAN_STA_WDS) || sta->pending_wds_enable)
+                       continue;
+
+               snprintf(cur_name, sizeof(cur_name), "%s.sta%d", prev_ifname, sta->aid);
+               snprintf(new_name, sizeof(new_name), "%s.sta%d", ifname, sta->aid);
+               hostapd_drv_if_rename(hapd, WPA_IF_AP_VLAN, cur_name, new_name);
+       }
+
+       if (!strncmp(hapd->conf->ssid.vlan, hapd->conf->iface, sizeof(hapd->conf->ssid.vlan)))
+               os_strlcpy(hapd->conf->ssid.vlan, ifname, sizeof(hapd->conf->ssid.vlan));
+       os_strlcpy(hapd->conf->iface, ifname, sizeof(hapd->conf->iface));
+       hostapd_ubus_add_bss(hapd);
+
+       hostapd_ucode_update_interfaces();
+out:
+       if (interfaces->ctrl_iface_init)
+               interfaces->ctrl_iface_init(hapd);
+
+       return ret ? NULL : ucv_boolean_new(true);
+}
+
+
 int hostapd_ucode_init(struct hapd_interfaces *ifaces)
 {
        static const uc_function_list_t global_fns[] = {
@@ -469,9 +600,11 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
        static const uc_function_list_t bss_fns[] = {
                { "ctrl", uc_hostapd_bss_ctrl },
                { "set_config", uc_hostapd_bss_set_config },
+               { "rename", uc_hostapd_bss_rename },
                { "delete", uc_hostapd_bss_delete },
        };
        static const uc_function_list_t iface_fns[] = {
+               { "set_bss_order", uc_hostapd_iface_set_bss_order },
                { "add_bss", uc_hostapd_iface_add_bss },
                { "stop", uc_hostapd_iface_stop },
                { "start", uc_hostapd_iface_start },