[OpenWrt-Devel,v2,3/5] hostapd: add ubus reload
diff mbox series

Message ID 20191109173350.GA103315@makrotopia.org
State Accepted
Delegated to: Daniel Golle
Headers show
Series
  • [OpenWrt-Devel,v2,netifd,1/5] wireless: add ubus method for reloading configuration
Related show

Commit Message

Daniel Golle Nov. 9, 2019, 5:33 p.m. UTC
From: John Crispin <john@phrozen.org>

Add ubus interface to hostapd and wpa_supplicant to allow dynamically
reloading wiface configuration without having to restart the hostapd
process.
As a consequence, both hostapd and wpa_supplicant are now started
persistently on boot for each wifi device in the system and then
receive ubus calls adding, modifying or removing interface
configuration.
At a later stage it would be desirable to reduce the services to one
single instance managing all radios.

Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2: unchanged

 package/network/services/hostapd/Makefile     |   3 +-
 .../services/hostapd/files/hostapd.hotplug    |  14 ++
 .../network/services/hostapd/files/hostapd.sh |  25 +--
 .../hostapd/patches/600-ubus_support.patch    | 127 ++++++++++++-
 .../hostapd/patches/700-wifi-reload.patch     | 118 ++++++++++++
 .../services/hostapd/src/src/ap/ubus.c        | 131 ++++++++++++-
 .../services/hostapd/src/src/ap/ubus.h        |  12 ++
 .../hostapd/src/wpa_supplicant/ubus.c         | 176 +++++++++++++++++-
 .../hostapd/src/wpa_supplicant/ubus.h         |  13 ++
 9 files changed, 600 insertions(+), 19 deletions(-)
 create mode 100644 package/network/services/hostapd/files/hostapd.hotplug
 create mode 100644 package/network/services/hostapd/patches/700-wifi-reload.patch

Comments

Ansuel Smith Nov. 9, 2019, 5:41 p.m. UTC | #1
also by using a single instance we would finally use band steering to
make client autoswitch from 2.4 to 5 ghz

Il giorno sab 9 nov 2019 alle ore 18:34 Daniel Golle
<daniel@makrotopia.org> ha scritto:
>
> From: John Crispin <john@phrozen.org>
>
> Add ubus interface to hostapd and wpa_supplicant to allow dynamically
> reloading wiface configuration without having to restart the hostapd
> process.
> As a consequence, both hostapd and wpa_supplicant are now started
> persistently on boot for each wifi device in the system and then
> receive ubus calls adding, modifying or removing interface
> configuration.
> At a later stage it would be desirable to reduce the services to one
> single instance managing all radios.
>
> Signed-off-by: John Crispin <john@phrozen.org>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> v2: unchanged
>
>  package/network/services/hostapd/Makefile     |   3 +-
>  .../services/hostapd/files/hostapd.hotplug    |  14 ++
>  .../network/services/hostapd/files/hostapd.sh |  25 +--
>  .../hostapd/patches/600-ubus_support.patch    | 127 ++++++++++++-
>  .../hostapd/patches/700-wifi-reload.patch     | 118 ++++++++++++
>  .../services/hostapd/src/src/ap/ubus.c        | 131 ++++++++++++-
>  .../services/hostapd/src/src/ap/ubus.h        |  12 ++
>  .../hostapd/src/wpa_supplicant/ubus.c         | 176 +++++++++++++++++-
>  .../hostapd/src/wpa_supplicant/ubus.h         |  13 ++
>  9 files changed, 600 insertions(+), 19 deletions(-)
>  create mode 100644 package/network/services/hostapd/files/hostapd.hotplug
>  create mode 100644 package/network/services/hostapd/patches/700-wifi-reload.patch
>
> diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile
> index 4f6420f503..52adb1115e 100644
> --- a/package/network/services/hostapd/Makefile
> +++ b/package/network/services/hostapd/Makefile
> @@ -519,8 +519,9 @@ define Install/supplicant
>  endef
>
>  define Package/hostapd-common/install
> -       $(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button
> +       $(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button $(1)/etc/hotplug.d/ieee80211
>         $(INSTALL_DATA) ./files/hostapd.sh $(1)/lib/netifd/hostapd.sh
> +       $(INSTALL_BIN) ./files/hostapd.hotplug $(1)/etc/hotplug.d/ieee80211/20-hostapd
>         $(INSTALL_BIN) ./files/wps-hotplug.sh $(1)/etc/rc.button/wps
>  endef
>
> diff --git a/package/network/services/hostapd/files/hostapd.hotplug b/package/network/services/hostapd/files/hostapd.hotplug
> new file mode 100644
> index 0000000000..d568b3c85a
> --- /dev/null
> +++ b/package/network/services/hostapd/files/hostapd.hotplug
> @@ -0,0 +1,14 @@
> +#!/bin/sh
> +
> +[ ${ACTION} = "remove" -a -n "${DEVICENAME}" ] && {
> +       kill $(cat /var/run/hostapd-${DEVICENAME}.pid)
> +       rm -rf /var/run/hostapd-${DEVICENAME}.pid /var/run/hostapd-${DEVICENAME}/
> +       kill $(cat /var/run/wpa_supplicant-${DEVICENAME}.pid)
> +       rm -rf /var/run/wpa_supplicant-${DEVICENAME}.pid /var/run/wpa_supplicant-${DEVICENAME}/
> +}
> +
> +[ ${ACTION} = "add" -a -n "${DEVICENAME}" ] && {
> +       /usr/sbin/hostapd -s -n ${DEVICENAME} -P /var/run/hostapd-${DEVICENAME}.pid -g /var/run/hostapd-${DEVICENAME}/global -B &
> +       mkdir -p /var/run/wpa_supplicant-${DEVICENAME}
> +       /usr/sbin/wpa_supplicant -s -n ${DEVICENAME} -P /var/run/wpa_supplicant-${DEVICENAME}.pid -g /var/run/wpa_supplicant-${DEVICENAME}/global -B &
> +}
> diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh
> index 3c1504ca60..f03b98ff85 100644
> --- a/package/network/services/hostapd/files/hostapd.sh
> +++ b/package/network/services/hostapd/files/hostapd.sh
> @@ -254,7 +254,7 @@ hostapd_set_bss_options() {
>
>         wireless_vif_parse_encryption
>
> -       local bss_conf
> +       local bss_conf bss_md5sum
>         local wep_rekey wpa_group_rekey wpa_pair_rekey wpa_master_rekey wpa_key_mgmt
>
>         json_get_vars \
> @@ -627,6 +627,9 @@ hostapd_set_bss_options() {
>                 }
>         }
>
> +       bss_md5sum=$(echo $bss_conf | md5sum | cut -d" " -f1)
> +       append bss_conf "config_id=$bss_md5sum" "$N"
> +
>         append "$var" "$bss_conf" "$N"
>         return 0
>  }
> @@ -950,21 +953,19 @@ EOF
>  }
>
>  wpa_supplicant_run() {
> -       local ifname="$1"; shift
> +       local ifname="$1"
> +       local hostapd_ctrl="$2"
>
>         _wpa_supplicant_common "$ifname"
>
> -       /usr/sbin/wpa_supplicant -B -s \
> -               ${network_bridge:+-b $network_bridge} \
> -               -P "/var/run/wpa_supplicant-${ifname}.pid" \
> -               -D ${_w_driver:-wext} \
> -               -i "$ifname" \
> -               -c "$_config" \
> -               -C "$_rpath" \
> -               "$@"
> +       ubus call wpa_supplicant.$phy config_add "{ \
> +               \"driver\": \"${_w_driver:-wext}\", \"ctrl\": \"$_rpath\", \
> +               \"iface\": \"$ifname\", \"config\": \"$_config\" \
> +               ${network_bridge:+, \"bridge\": \"$network_bridge\"} \
> +               ${hostapd_ctrl:+, \"hostapd_ctrl\": \"$hostapd_ctrl\"} \
> +               }"
>
>         ret="$?"
> -       wireless_add_process "$(cat "/var/run/wpa_supplicant-${ifname}.pid")" /usr/sbin/wpa_supplicant 1
>
>         [ "$ret" != 0 ] && wireless_setup_vif_failed WPA_SUPPLICANT_FAILED
>
> @@ -972,5 +973,5 @@ wpa_supplicant_run() {
>  }
>
>  hostapd_common_cleanup() {
> -       killall hostapd wpa_supplicant meshd-nl80211
> +       killall meshd-nl80211
>  }
> diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
> index 0eb0a4a3ba..6842c0e63e 100644
> --- a/package/network/services/hostapd/patches/600-ubus_support.patch
> +++ b/package/network/services/hostapd/patches/600-ubus_support.patch
> @@ -22,7 +22,16 @@
>
>   #define OCE_STA_CFON_ENABLED(hapd) \
>         ((hapd->conf->oce & OCE_STA_CFON) && \
> -@@ -145,6 +146,7 @@ struct hostapd_data {
> +@@ -72,6 +73,8 @@ struct hapd_interfaces {
> + #ifdef CONFIG_DPP
> +       struct dpp_global *dpp;
> + #endif /* CONFIG_DPP */
> ++      struct ubus_object ubus;
> ++      char *name;
> + };
> +
> + enum hostapd_chan_status {
> +@@ -145,6 +148,7 @@ struct hostapd_data {
>         struct hostapd_iface *iface;
>         struct hostapd_config *iconf;
>         struct hostapd_bss_config *conf;
> @@ -30,6 +39,14 @@
>         int interface_added; /* virtual interface added for this BSS */
>         unsigned int started:1;
>         unsigned int disabled:1;
> +@@ -580,6 +584,7 @@ hostapd_alloc_bss_data(struct hostapd_if
> +                      struct hostapd_bss_config *bss);
> + int hostapd_setup_interface(struct hostapd_iface *iface);
> + int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
> ++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
> + void hostapd_interface_deinit(struct hostapd_iface *iface);
> + void hostapd_interface_free(struct hostapd_iface *iface);
> + struct hostapd_iface * hostapd_alloc_iface(void);
>  --- a/src/ap/hostapd.c
>  +++ b/src/ap/hostapd.c
>  @@ -380,6 +380,7 @@ static void hostapd_free_hapd_data(struc
> @@ -298,6 +315,36 @@
>         /* Remove interface from the global list of interfaces */
>         prev = global->ifaces;
>         if (prev == wpa_s) {
> +@@ -6520,6 +6524,8 @@ struct wpa_global * wpa_supplicant_init(
> +       if (params->override_ctrl_interface)
> +               global->params.override_ctrl_interface =
> +                       os_strdup(params->override_ctrl_interface);
> ++      if (params->name)
> ++              global->params.name = os_strdup(params->name);
> + #ifdef CONFIG_MATCH_IFACE
> +       global->params.match_iface_count = params->match_iface_count;
> +       if (params->match_iface_count) {
> +@@ -6626,8 +6632,12 @@ int wpa_supplicant_run(struct wpa_global
> +       eloop_register_signal_terminate(wpa_supplicant_terminate, global);
> +       eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
> +
> ++      wpas_ubus_add(global);
> ++
> +       eloop_run();
> +
> ++      wpas_ubus_free(global);
> ++
> +       return 0;
> + }
> +
> +@@ -6687,6 +6697,7 @@ void wpa_supplicant_deinit(struct wpa_gl
> + #ifdef CONFIG_MATCH_IFACE
> +       os_free(global->params.match_ifaces);
> + #endif /* CONFIG_MATCH_IFACE */
> ++      os_free(global->params.name);
> + #ifdef CONFIG_P2P
> +       os_free(global->params.conf_p2p_dev);
> + #endif /* CONFIG_P2P */
>  --- a/wpa_supplicant/wpa_supplicant_i.h
>  +++ b/wpa_supplicant/wpa_supplicant_i.h
>  @@ -17,6 +17,7 @@
> @@ -308,7 +355,25 @@
>
>   extern const char *const wpa_supplicant_version;
>   extern const char *const wpa_supplicant_license;
> -@@ -506,6 +507,7 @@ struct wpa_supplicant {
> +@@ -246,6 +247,8 @@ struct wpa_params {
> +        */
> +       int match_iface_count;
> + #endif /* CONFIG_MATCH_IFACE */
> ++
> ++      char *name;
> + };
> +
> + struct p2p_srv_bonjour {
> +@@ -306,6 +309,8 @@ struct wpa_global {
> + #endif /* CONFIG_WIFI_DISPLAY */
> +
> +       struct psk_list_entry *add_psk; /* From group formation */
> ++
> ++      struct ubus_object ubus_global;
> + };
> +
> +
> +@@ -506,6 +511,7 @@ struct wpa_supplicant {
>         unsigned char own_addr[ETH_ALEN];
>         unsigned char perm_addr[ETH_ALEN];
>         char ifname[100];
> @@ -335,3 +400,61 @@
>         if (wpa_s->conf->wps_cred_processing == 1)
>                 return 0;
>
> +--- a/hostapd/main.c
> ++++ b/hostapd/main.c
> +@@ -688,7 +688,7 @@ int main(int argc, char *argv[])
> +       wpa_supplicant_event = hostapd_wpa_event;
> +       wpa_supplicant_event_global = hostapd_wpa_event_global;
> +       for (;;) {
> +-              c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:v::");
> ++              c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:n:v::");
> +               if (c < 0)
> +                       break;
> +               switch (c) {
> +@@ -763,6 +763,8 @@ int main(int argc, char *argv[])
> +                       if (hostapd_get_interface_names(&if_names,
> +                                                       &if_names_size, optarg))
> +                               goto out;
> ++              case 'n':
> ++                      interfaces.name = optarg;
> +                       break;
> +               default:
> +                       usage();
> +@@ -894,6 +896,7 @@ int main(int argc, char *argv[])
> +       }
> +
> +       hostapd_global_ctrl_iface_init(&interfaces);
> ++      hostapd_ubus_add(&interfaces);
> +
> +       if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
> +               wpa_printf(MSG_ERROR, "Failed to start eloop");
> +@@ -903,6 +906,7 @@ int main(int argc, char *argv[])
> +       ret = 0;
> +
> +  out:
> ++      hostapd_ubus_free(&interfaces);
> +       hostapd_global_ctrl_iface_deinit(&interfaces);
> +       /* Deinitialize all interfaces */
> +       for (i = 0; i < interfaces.count; i++) {
> +--- a/wpa_supplicant/main.c
> ++++ b/wpa_supplicant/main.c
> +@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
> +
> +       for (;;) {
> +               c = getopt(argc, argv,
> +-                         "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuv::W");
> ++                         "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:n:No:O:p:P:qsTtuv::W");
> +               if (c < 0)
> +                       break;
> +               switch (c) {
> +@@ -271,6 +271,10 @@ int main(int argc, char *argv[])
> +                       params.conf_p2p_dev = optarg;
> +                       break;
> + #endif /* CONFIG_P2P */
> ++              case 'n':
> ++                      params.name = optarg;
> ++                      iface_count = 0;
> ++                      break;
> +               case 'o':
> +                       params.override_driver = optarg;
> +                       break;
> diff --git a/package/network/services/hostapd/patches/700-wifi-reload.patch b/package/network/services/hostapd/patches/700-wifi-reload.patch
> new file mode 100644
> index 0000000000..c5ba631a0f
> --- /dev/null
> +++ b/package/network/services/hostapd/patches/700-wifi-reload.patch
> @@ -0,0 +1,118 @@
> +--- a/hostapd/config_file.c
> ++++ b/hostapd/config_file.c
> +@@ -2470,6 +2470,8 @@ static int hostapd_config_fill(struct ho
> +               bss->isolate = atoi(pos);
> +       } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
> +               bss->ap_max_inactivity = atoi(pos);
> ++      } else if (os_strcmp(buf, "config_id") == 0) {
> ++              bss->config_id = os_strdup(pos);
> +       } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
> +               bss->skip_inactivity_poll = atoi(pos);
> +       } else if (os_strcmp(buf, "country_code") == 0) {
> +--- a/src/ap/ap_config.c
> ++++ b/src/ap/ap_config.c
> +@@ -698,6 +698,7 @@ void hostapd_config_free_bss(struct host
> +       os_free(conf->radius_req_attr_sqlite);
> +       os_free(conf->rsn_preauth_interfaces);
> +       os_free(conf->ctrl_interface);
> ++      os_free(conf->config_id);
> +       os_free(conf->ca_cert);
> +       os_free(conf->server_cert);
> +       os_free(conf->server_cert2);
> +--- a/src/ap/ap_config.h
> ++++ b/src/ap/ap_config.h
> +@@ -829,6 +829,7 @@ struct hostapd_bss_config {
> +        */
> +       u8 mka_psk_set;
> + #endif /* CONFIG_MACSEC */
> ++      char *config_id;
> + };
> +
> + /**
> +--- a/src/ap/hostapd.c
> ++++ b/src/ap/hostapd.c
> +@@ -242,13 +242,13 @@ int hostapd_reload_config(struct hostapd
> +       if (newconf == NULL)
> +               return -1;
> +
> +-      hostapd_clear_old(iface);
> +-
> +       oldconf = hapd->iconf;
> +       if (hostapd_iface_conf_changed(newconf, oldconf)) {
> +               char *fname;
> +               int res;
> +
> ++              hostapd_clear_old(iface);
> ++
> +               wpa_printf(MSG_DEBUG,
> +                          "Configuration changes include interface/BSS modification - force full disable+enable sequence");
> +               fname = os_strdup(iface->config_fname);
> +@@ -273,6 +273,22 @@ int hostapd_reload_config(struct hostapd
> +                       wpa_printf(MSG_ERROR,
> +                                  "Failed to enable interface on config reload");
> +               return res;
> ++      } else {
> ++              for (j = 0; j < iface->num_bss; j++) {
> ++                      hapd = iface->bss[j];
> ++                      if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
> ++                              hostapd_flush_old_stations(iface->bss[j],
> ++                                                         WLAN_REASON_PREV_AUTH_NOT_VALID);
> ++                              hostapd_broadcast_wep_clear(iface->bss[j]);
> ++
> ++#ifndef CONFIG_NO_RADIUS
> ++                              /* TODO: update dynamic data based on changed configuration
> ++                               * items (e.g., open/close sockets, etc.) */
> ++                              radius_client_flush(iface->bss[j]->radius, 0);
> ++#endif /* CONFIG_NO_RADIUS */
> ++                              wpa_printf(MSG_INFO, "bss %d changed", j);
> ++                      }
> ++              }
> +       }
> +       iface->conf = newconf;
> +
> +@@ -289,6 +305,12 @@ int hostapd_reload_config(struct hostapd
> +
> +       for (j = 0; j < iface->num_bss; j++) {
> +               hapd = iface->bss[j];
> ++              if (hapd->config_id) {
> ++                      os_free(hapd->config_id);
> ++                      hapd->config_id = NULL;
> ++              }
> ++              if (newconf->bss[j]->config_id)
> ++                      hapd->config_id = strdup(newconf->bss[j]->config_id);
> +               hapd->iconf = newconf;
> +               hapd->conf = newconf->bss[j];
> +               hostapd_reload_bss(hapd);
> +@@ -2257,6 +2279,10 @@ hostapd_alloc_bss_data(struct hostapd_if
> +       hapd->iconf = conf;
> +       hapd->conf = bss;
> +       hapd->iface = hapd_iface;
> ++      if (bss->config_id)
> ++              hapd->config_id = strdup(bss->config_id);
> ++      else
> ++              hapd->config_id = NULL;
> +       if (conf)
> +               hapd->driver = conf->driver;
> +       hapd->ctrl_sock = -1;
> +--- a/src/ap/hostapd.h
> ++++ b/src/ap/hostapd.h
> +@@ -149,6 +149,7 @@ struct hostapd_data {
> +       struct hostapd_config *iconf;
> +       struct hostapd_bss_config *conf;
> +       struct hostapd_ubus_bss ubus;
> ++      char *config_id;
> +       int interface_added; /* virtual interface added for this BSS */
> +       unsigned int started:1;
> +       unsigned int disabled:1;
> +--- a/src/drivers/driver_nl80211.c
> ++++ b/src/drivers/driver_nl80211.c
> +@@ -4295,6 +4295,9 @@ static int wpa_driver_nl80211_set_ap(voi
> +       if (ret) {
> +               wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
> +                          ret, strerror(-ret));
> ++              if (!bss->beacon_set)
> ++                      ret = 0;
> ++              bss->beacon_set = 0;
> +       } else {
> +               bss->beacon_set = 1;
> +               nl80211_set_bss(bss, params->cts_protect, params->preamble,
> diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
> index 3b768d0e8c..e25c3294ee 100644
> --- a/package/network/services/hostapd/src/src/ap/ubus.c
> +++ b/package/network/services/hostapd/src/src/ap/ubus.c
> @@ -26,12 +26,16 @@ static struct ubus_context *ctx;
>  static struct blob_buf b;
>  static int ctx_ref;
>
> +static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
> +{
> +       return container_of(obj, struct hapd_interfaces, ubus);
> +}
> +
>  static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
>  {
>         return container_of(obj, struct hostapd_data, ubus.obj);
>  }
>
> -
>  struct ubus_banned_client {
>         struct avl_node avl;
>         u8 addr[ETH_ALEN];
> @@ -142,6 +146,16 @@ hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
>         eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
>  }
>
> +static int
> +hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
> +                  struct ubus_request_data *req, const char *method,
> +                  struct blob_attr *msg)
> +{
> +       struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
> +       hostapd_reload_config(hapd->iface);
> +       hostapd_reload_iface(hapd->iface);
> +}
> +
>  static int
>  hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
>                         struct ubus_request_data *req, const char *method,
> @@ -379,6 +393,70 @@ hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
>         return 0;
>  }
>
> +enum {
> +       CONFIG_IFACE,
> +       CONFIG_FILE,
> +       __CONFIG_MAX
> +};
> +
> +static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
> +       [CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> +       [CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
> +};
> +
> +static int
> +hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
> +                  struct ubus_request_data *req, const char *method,
> +                  struct blob_attr *msg)
> +{
> +       struct blob_attr *tb[__CONFIG_MAX];
> +       struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
> +       char buf[128];
> +
> +       blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +       if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       snprintf(buf, sizeof(buf), "bss_config=%s:%s",
> +               blobmsg_get_string(tb[CONFIG_IFACE]),
> +               blobmsg_get_string(tb[CONFIG_FILE]));
> +
> +       if (hostapd_add_iface(interfaces, buf))
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       return UBUS_STATUS_OK;
> +}
> +
> +enum {
> +       CONFIG_REM_IFACE,
> +       __CONFIG_REM_MAX
> +};
> +
> +static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
> +       [CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> +};
> +
> +static int
> +hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
> +                     struct ubus_request_data *req, const char *method,
> +                     struct blob_attr *msg)
> +{
> +       struct blob_attr *tb[__CONFIG_REM_MAX];
> +       struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
> +       char buf[128];
> +
> +       blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +       if (!tb[CONFIG_REM_IFACE])
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       return UBUS_STATUS_OK;
> +}
> +
>  enum {
>         CSA_FREQ,
>         CSA_BCN_COUNT,
> @@ -949,6 +1027,7 @@ hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
>  #endif
>
>  static const struct ubus_method bss_methods[] = {
> +       UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
>         UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
>         UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
>         UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
> @@ -1021,6 +1100,56 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd)
>         free(name);
>  }
>
> +static const struct ubus_method daemon_methods[] = {
> +       UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
> +       UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
> +};
> +
> +static struct ubus_object_type daemon_object_type =
> +       UBUS_OBJECT_TYPE("hostapd", daemon_methods);
> +
> +void hostapd_ubus_add(struct hapd_interfaces *interfaces)
> +{
> +       struct ubus_object *obj = &interfaces->ubus;
> +       int name_len;
> +       int ret;
> +
> +       if (!hostapd_ubus_init())
> +               return;
> +
> +       name_len = strlen("hostapd") + 1;
> +       if (interfaces->name)
> +               name_len += strlen(interfaces->name) + 1;
> +       obj->name = malloc(name_len);
> +       strcpy(obj->name, "hostapd");
> +       if (interfaces->name) {
> +               strcat(obj->name, ".");
> +               strcat(obj->name, interfaces->name);
> +       }
> +
> +       obj->type = &daemon_object_type;
> +       obj->methods = daemon_object_type.methods;
> +       obj->n_methods = daemon_object_type.n_methods;
> +       ret = ubus_add_object(ctx, obj);
> +       hostapd_ubus_ref_inc();
> +}
> +
> +void hostapd_ubus_free(struct hapd_interfaces *interfaces)
> +{
> +       struct ubus_object *obj = &interfaces->ubus;
> +       char *name = (char *) obj->name;
> +
> +       if (!ctx)
> +               return;
> +
> +       if (obj->id) {
> +               ubus_remove_object(ctx, obj);
> +               hostapd_ubus_ref_dec();
> +       }
> +
> +       free(name);
> +}
> +
>  struct ubus_event_req {
>         struct ubus_notify_request nreq;
>         int resp;
> diff --git a/package/network/services/hostapd/src/src/ap/ubus.h b/package/network/services/hostapd/src/src/ap/ubus.h
> index 58ebe0e65b..27acd32659 100644
> --- a/package/network/services/hostapd/src/src/ap/ubus.h
> +++ b/package/network/services/hostapd/src/src/ap/ubus.h
> @@ -25,6 +25,7 @@ struct hostapd_ubus_request {
>
>  struct hostapd_iface;
>  struct hostapd_data;
> +struct hapd_interfaces;
>
>  #ifdef UBUS_SUPPORT
>
> @@ -45,6 +46,9 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd);
>  int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
>  void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
>
> +void hostapd_ubus_add(struct hapd_interfaces *interfaces);
> +void hostapd_ubus_free(struct hapd_interfaces *interfaces);
> +
>  #else
>
>  struct hostapd_ubus_bss {};
> @@ -73,6 +77,14 @@ static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct ho
>  static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
>  {
>  }
> +
> +static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
> +{
> +}
> +
> +static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
> +{
> +}
>  #endif
>
>  #endif
> diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.c b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> index 5fdb57be7a..9a98979e9a 100644
> --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> @@ -20,6 +20,11 @@ static struct ubus_context *ctx;
>  static struct blob_buf b;
>  static int ctx_ref;
>
> +static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
> +{
> +       return container_of(obj, struct wpa_global, ubus_global);
> +}
> +
>  static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
>  {
>         return container_of(obj, struct wpa_supplicant, ubus.obj);
> @@ -95,6 +100,19 @@ wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
>         return 0;
>  }
>
> +static int
> +wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
> +               struct ubus_request_data *req, const char *method,
> +               struct blob_attr *msg)
> +{
> +       struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
> +
> +       if (wpa_supplicant_reload_configuration(wpa_s))
> +               return UBUS_STATUS_UNKNOWN_ERROR;
> +       else
> +               return 0;
> +}
> +
>  #ifdef CONFIG_WPS
>  enum {
>         WPS_START_MULTI_AP,
> @@ -146,11 +164,12 @@ wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
>  #endif
>
>  static const struct ubus_method bss_methods[] = {
> +       UBUS_METHOD_NOARG("reload", wpas_bss_reload),
> +       UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
>  #ifdef CONFIG_WPS
>         UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
>         UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
>  #endif
> -       UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
>  };
>
>  static struct ubus_object_type bss_object_type =
> @@ -162,8 +181,6 @@ void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
>         char *name;
>         int ret;
>
> -       if (!wpas_ubus_init())
> -               return;
>
>         if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
>                 return;
> @@ -192,6 +209,159 @@ void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
>         free(name);
>  }
>
> +enum {
> +       WPAS_CONFIG_DRIVER,
> +       WPAS_CONFIG_IFACE,
> +       WPAS_CONFIG_BRIDGE,
> +       WPAS_CONFIG_HOSTAPD_CTRL,
> +       WPAS_CONFIG_CTRL,
> +       WPAS_CONFIG_FILE,
> +       __WPAS_CONFIG_MAX
> +};
> +
> +static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = {
> +       [WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING },
> +       [WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> +       [WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING },
> +       [WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING },
> +       [WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING },
> +       [WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
> +};
> +
> +static int
> +wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj,
> +               struct ubus_request_data *req, const char *method,
> +               struct blob_attr *msg)
> +{
> +       struct blob_attr *tb[__WPAS_CONFIG_MAX];
> +       struct wpa_global *global = get_wpa_global_from_object(obj);
> +       struct wpa_interface *iface;
> +
> +       blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +       if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER])
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       iface = os_zalloc(sizeof(struct wpa_interface));
> +       if (iface == NULL)
> +               return UBUS_STATUS_UNKNOWN_ERROR;
> +
> +       iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]);
> +       iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]);
> +       iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]);
> +
> +       if (tb[WPAS_CONFIG_BRIDGE])
> +               iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]);
> +
> +       if (tb[WPAS_CONFIG_CTRL])
> +               iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]);
> +
> +       if (tb[WPAS_CONFIG_HOSTAPD_CTRL])
> +               iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]);
> +
> +       if (!wpa_supplicant_add_iface(global, iface, NULL))
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       return UBUS_STATUS_OK;
> +}
> +
> +enum {
> +       WPAS_CONFIG_REM_IFACE,
> +       __WPAS_CONFIG_REM_MAX
> +};
> +
> +static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = {
> +       [WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> +};
> +
> +static int
> +wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
> +                  struct ubus_request_data *req, const char *method,
> +                  struct blob_attr *msg)
> +{
> +       struct blob_attr *tb[__WPAS_CONFIG_REM_MAX];
> +       struct wpa_global *global = get_wpa_global_from_object(obj);
> +       struct wpa_supplicant *wpa_s = NULL;
> +       unsigned int found = 0;
> +
> +       blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
> +
> +       if (!tb[WPAS_CONFIG_REM_IFACE])
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       /* find wpa_s object for to-be-removed interface */
> +       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
> +               if (!strncmp(wpa_s->ifname,
> +                            blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]),
> +                            sizeof(wpa_s->ifname)))
> +               {
> +                       found = 1;
> +                       break;
> +               }
> +       }
> +
> +       if (!found)
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       if (wpa_supplicant_remove_iface(global, wpa_s, 0))
> +               return UBUS_STATUS_INVALID_ARGUMENT;
> +
> +       return UBUS_STATUS_OK;
> +}
> +
> +static const struct ubus_method wpas_daemon_methods[] = {
> +       UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy),
> +       UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy),
> +};
> +
> +static struct ubus_object_type wpas_daemon_object_type =
> +       UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods);
> +
> +void wpas_ubus_add(struct wpa_global *global)
> +{
> +       struct ubus_object *obj = &global->ubus_global;
> +       int name_len;
> +       int ret;
> +
> +       if (!wpas_ubus_init())
> +               return;
> +
> +       name_len = strlen("wpa_supplicant") + 1;
> +       if (global->params.name)
> +               name_len += strlen(global->params.name) + 1;
> +       obj->name = malloc(name_len);
> +       strcpy(obj->name, "wpa_supplicant");
> +
> +       if (global->params.name)
> +       {
> +               strcat(obj->name, ".");
> +               strcat(obj->name, global->params.name);
> +       }
> +
> +       obj->type = &wpas_daemon_object_type;
> +       obj->methods = wpas_daemon_object_type.methods;
> +       obj->n_methods = wpas_daemon_object_type.n_methods;
> +       ret = ubus_add_object(ctx, obj);
> +       wpas_ubus_ref_inc();
> +}
> +
> +void wpas_ubus_free(struct wpa_global *global)
> +{
> +       struct ubus_object *obj = &global->ubus_global;
> +       char *name = (char *) obj->name;
> +
> +       if (!ctx)
> +               return;
> +
> +       if (obj->id) {
> +               ubus_remove_object(ctx, obj);
> +               wpas_ubus_ref_dec();
> +       }
> +
> +       free(name);
> +}
> +
> +
>  #ifdef CONFIG_WPS
>  void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
>  {
> diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.h b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> index c37e743e73..bf92b98c01 100644
> --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> @@ -10,6 +10,8 @@
>  #define __WPAS_UBUS_H
>
>  struct wpa_supplicant;
> +struct wpa_global;
> +
>  #include "wps_supplicant.h"
>
>  #ifdef UBUS_SUPPORT
> @@ -22,6 +24,9 @@ struct wpas_ubus_bss {
>  void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
>  void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
>
> +void wpas_ubus_add(struct wpa_global *global);
> +void wpas_ubus_free(struct wpa_global *global);
> +
>  #ifdef CONFIG_WPS
>  void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
>  #endif
> @@ -48,6 +53,14 @@ static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
>  static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
>  {
>  }
> +
> +static inline void wpas_ubus_add(struct wpa_global *global)
> +{
> +}
> +
> +static inline void wpas_ubus_free(struct wpa_global *global)
> +{
> +}
>  #endif
>
>  #endif
> --
> 2.23.0
>
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Daniel Golle Nov. 9, 2019, 5:49 p.m. UTC | #2
On Sat, Nov 09, 2019 at 06:41:52PM +0100, Ansuel Smith wrote:
> also by using a single instance we would finally use band steering to
> make client autoswitch from 2.4 to 5 ghz

Obviously this won't just magically happen, but by having a single
instance (of hostapd) managing all wiphy's in the system this becomes
something hostapd could manage internally.
Current solutions rely on adding band-steering functionality externally
by using hostapd's ubus hooks instead, hence this is not necessarily
related.

> 
> Il giorno sab 9 nov 2019 alle ore 18:34 Daniel Golle
> <daniel@makrotopia.org> ha scritto:
> >
> > From: John Crispin <john@phrozen.org>
> >
> > Add ubus interface to hostapd and wpa_supplicant to allow dynamically
> > reloading wiface configuration without having to restart the hostapd
> > process.
> > As a consequence, both hostapd and wpa_supplicant are now started
> > persistently on boot for each wifi device in the system and then
> > receive ubus calls adding, modifying or removing interface
> > configuration.
> > At a later stage it would be desirable to reduce the services to one
> > single instance managing all radios.
> >
> > Signed-off-by: John Crispin <john@phrozen.org>
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > ---
> > v2: unchanged
> >
> >  package/network/services/hostapd/Makefile     |   3 +-
> >  .../services/hostapd/files/hostapd.hotplug    |  14 ++
> >  .../network/services/hostapd/files/hostapd.sh |  25 +--
> >  .../hostapd/patches/600-ubus_support.patch    | 127 ++++++++++++-
> >  .../hostapd/patches/700-wifi-reload.patch     | 118 ++++++++++++
> >  .../services/hostapd/src/src/ap/ubus.c        | 131 ++++++++++++-
> >  .../services/hostapd/src/src/ap/ubus.h        |  12 ++
> >  .../hostapd/src/wpa_supplicant/ubus.c         | 176 +++++++++++++++++-
> >  .../hostapd/src/wpa_supplicant/ubus.h         |  13 ++
> >  9 files changed, 600 insertions(+), 19 deletions(-)
> >  create mode 100644 package/network/services/hostapd/files/hostapd.hotplug
> >  create mode 100644 package/network/services/hostapd/patches/700-wifi-reload.patch
> >
> > diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile
> > index 4f6420f503..52adb1115e 100644
> > --- a/package/network/services/hostapd/Makefile
> > +++ b/package/network/services/hostapd/Makefile
> > @@ -519,8 +519,9 @@ define Install/supplicant
> >  endef
> >
> >  define Package/hostapd-common/install
> > -       $(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button
> > +       $(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button $(1)/etc/hotplug.d/ieee80211
> >         $(INSTALL_DATA) ./files/hostapd.sh $(1)/lib/netifd/hostapd.sh
> > +       $(INSTALL_BIN) ./files/hostapd.hotplug $(1)/etc/hotplug.d/ieee80211/20-hostapd
> >         $(INSTALL_BIN) ./files/wps-hotplug.sh $(1)/etc/rc.button/wps
> >  endef
> >
> > diff --git a/package/network/services/hostapd/files/hostapd.hotplug b/package/network/services/hostapd/files/hostapd.hotplug
> > new file mode 100644
> > index 0000000000..d568b3c85a
> > --- /dev/null
> > +++ b/package/network/services/hostapd/files/hostapd.hotplug
> > @@ -0,0 +1,14 @@
> > +#!/bin/sh
> > +
> > +[ ${ACTION} = "remove" -a -n "${DEVICENAME}" ] && {
> > +       kill $(cat /var/run/hostapd-${DEVICENAME}.pid)
> > +       rm -rf /var/run/hostapd-${DEVICENAME}.pid /var/run/hostapd-${DEVICENAME}/
> > +       kill $(cat /var/run/wpa_supplicant-${DEVICENAME}.pid)
> > +       rm -rf /var/run/wpa_supplicant-${DEVICENAME}.pid /var/run/wpa_supplicant-${DEVICENAME}/
> > +}
> > +
> > +[ ${ACTION} = "add" -a -n "${DEVICENAME}" ] && {
> > +       /usr/sbin/hostapd -s -n ${DEVICENAME} -P /var/run/hostapd-${DEVICENAME}.pid -g /var/run/hostapd-${DEVICENAME}/global -B &
> > +       mkdir -p /var/run/wpa_supplicant-${DEVICENAME}
> > +       /usr/sbin/wpa_supplicant -s -n ${DEVICENAME} -P /var/run/wpa_supplicant-${DEVICENAME}.pid -g /var/run/wpa_supplicant-${DEVICENAME}/global -B &
> > +}
> > diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh
> > index 3c1504ca60..f03b98ff85 100644
> > --- a/package/network/services/hostapd/files/hostapd.sh
> > +++ b/package/network/services/hostapd/files/hostapd.sh
> > @@ -254,7 +254,7 @@ hostapd_set_bss_options() {
> >
> >         wireless_vif_parse_encryption
> >
> > -       local bss_conf
> > +       local bss_conf bss_md5sum
> >         local wep_rekey wpa_group_rekey wpa_pair_rekey wpa_master_rekey wpa_key_mgmt
> >
> >         json_get_vars \
> > @@ -627,6 +627,9 @@ hostapd_set_bss_options() {
> >                 }
> >         }
> >
> > +       bss_md5sum=$(echo $bss_conf | md5sum | cut -d" " -f1)
> > +       append bss_conf "config_id=$bss_md5sum" "$N"
> > +
> >         append "$var" "$bss_conf" "$N"
> >         return 0
> >  }
> > @@ -950,21 +953,19 @@ EOF
> >  }
> >
> >  wpa_supplicant_run() {
> > -       local ifname="$1"; shift
> > +       local ifname="$1"
> > +       local hostapd_ctrl="$2"
> >
> >         _wpa_supplicant_common "$ifname"
> >
> > -       /usr/sbin/wpa_supplicant -B -s \
> > -               ${network_bridge:+-b $network_bridge} \
> > -               -P "/var/run/wpa_supplicant-${ifname}.pid" \
> > -               -D ${_w_driver:-wext} \
> > -               -i "$ifname" \
> > -               -c "$_config" \
> > -               -C "$_rpath" \
> > -               "$@"
> > +       ubus call wpa_supplicant.$phy config_add "{ \
> > +               \"driver\": \"${_w_driver:-wext}\", \"ctrl\": \"$_rpath\", \
> > +               \"iface\": \"$ifname\", \"config\": \"$_config\" \
> > +               ${network_bridge:+, \"bridge\": \"$network_bridge\"} \
> > +               ${hostapd_ctrl:+, \"hostapd_ctrl\": \"$hostapd_ctrl\"} \
> > +               }"
> >
> >         ret="$?"
> > -       wireless_add_process "$(cat "/var/run/wpa_supplicant-${ifname}.pid")" /usr/sbin/wpa_supplicant 1
> >
> >         [ "$ret" != 0 ] && wireless_setup_vif_failed WPA_SUPPLICANT_FAILED
> >
> > @@ -972,5 +973,5 @@ wpa_supplicant_run() {
> >  }
> >
> >  hostapd_common_cleanup() {
> > -       killall hostapd wpa_supplicant meshd-nl80211
> > +       killall meshd-nl80211
> >  }
> > diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
> > index 0eb0a4a3ba..6842c0e63e 100644
> > --- a/package/network/services/hostapd/patches/600-ubus_support.patch
> > +++ b/package/network/services/hostapd/patches/600-ubus_support.patch
> > @@ -22,7 +22,16 @@
> >
> >   #define OCE_STA_CFON_ENABLED(hapd) \
> >         ((hapd->conf->oce & OCE_STA_CFON) && \
> > -@@ -145,6 +146,7 @@ struct hostapd_data {
> > +@@ -72,6 +73,8 @@ struct hapd_interfaces {
> > + #ifdef CONFIG_DPP
> > +       struct dpp_global *dpp;
> > + #endif /* CONFIG_DPP */
> > ++      struct ubus_object ubus;
> > ++      char *name;
> > + };
> > +
> > + enum hostapd_chan_status {
> > +@@ -145,6 +148,7 @@ struct hostapd_data {
> >         struct hostapd_iface *iface;
> >         struct hostapd_config *iconf;
> >         struct hostapd_bss_config *conf;
> > @@ -30,6 +39,14 @@
> >         int interface_added; /* virtual interface added for this BSS */
> >         unsigned int started:1;
> >         unsigned int disabled:1;
> > +@@ -580,6 +584,7 @@ hostapd_alloc_bss_data(struct hostapd_if
> > +                      struct hostapd_bss_config *bss);
> > + int hostapd_setup_interface(struct hostapd_iface *iface);
> > + int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
> > ++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
> > + void hostapd_interface_deinit(struct hostapd_iface *iface);
> > + void hostapd_interface_free(struct hostapd_iface *iface);
> > + struct hostapd_iface * hostapd_alloc_iface(void);
> >  --- a/src/ap/hostapd.c
> >  +++ b/src/ap/hostapd.c
> >  @@ -380,6 +380,7 @@ static void hostapd_free_hapd_data(struc
> > @@ -298,6 +315,36 @@
> >         /* Remove interface from the global list of interfaces */
> >         prev = global->ifaces;
> >         if (prev == wpa_s) {
> > +@@ -6520,6 +6524,8 @@ struct wpa_global * wpa_supplicant_init(
> > +       if (params->override_ctrl_interface)
> > +               global->params.override_ctrl_interface =
> > +                       os_strdup(params->override_ctrl_interface);
> > ++      if (params->name)
> > ++              global->params.name = os_strdup(params->name);
> > + #ifdef CONFIG_MATCH_IFACE
> > +       global->params.match_iface_count = params->match_iface_count;
> > +       if (params->match_iface_count) {
> > +@@ -6626,8 +6632,12 @@ int wpa_supplicant_run(struct wpa_global
> > +       eloop_register_signal_terminate(wpa_supplicant_terminate, global);
> > +       eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
> > +
> > ++      wpas_ubus_add(global);
> > ++
> > +       eloop_run();
> > +
> > ++      wpas_ubus_free(global);
> > ++
> > +       return 0;
> > + }
> > +
> > +@@ -6687,6 +6697,7 @@ void wpa_supplicant_deinit(struct wpa_gl
> > + #ifdef CONFIG_MATCH_IFACE
> > +       os_free(global->params.match_ifaces);
> > + #endif /* CONFIG_MATCH_IFACE */
> > ++      os_free(global->params.name);
> > + #ifdef CONFIG_P2P
> > +       os_free(global->params.conf_p2p_dev);
> > + #endif /* CONFIG_P2P */
> >  --- a/wpa_supplicant/wpa_supplicant_i.h
> >  +++ b/wpa_supplicant/wpa_supplicant_i.h
> >  @@ -17,6 +17,7 @@
> > @@ -308,7 +355,25 @@
> >
> >   extern const char *const wpa_supplicant_version;
> >   extern const char *const wpa_supplicant_license;
> > -@@ -506,6 +507,7 @@ struct wpa_supplicant {
> > +@@ -246,6 +247,8 @@ struct wpa_params {
> > +        */
> > +       int match_iface_count;
> > + #endif /* CONFIG_MATCH_IFACE */
> > ++
> > ++      char *name;
> > + };
> > +
> > + struct p2p_srv_bonjour {
> > +@@ -306,6 +309,8 @@ struct wpa_global {
> > + #endif /* CONFIG_WIFI_DISPLAY */
> > +
> > +       struct psk_list_entry *add_psk; /* From group formation */
> > ++
> > ++      struct ubus_object ubus_global;
> > + };
> > +
> > +
> > +@@ -506,6 +511,7 @@ struct wpa_supplicant {
> >         unsigned char own_addr[ETH_ALEN];
> >         unsigned char perm_addr[ETH_ALEN];
> >         char ifname[100];
> > @@ -335,3 +400,61 @@
> >         if (wpa_s->conf->wps_cred_processing == 1)
> >                 return 0;
> >
> > +--- a/hostapd/main.c
> > ++++ b/hostapd/main.c
> > +@@ -688,7 +688,7 @@ int main(int argc, char *argv[])
> > +       wpa_supplicant_event = hostapd_wpa_event;
> > +       wpa_supplicant_event_global = hostapd_wpa_event_global;
> > +       for (;;) {
> > +-              c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:v::");
> > ++              c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:n:v::");
> > +               if (c < 0)
> > +                       break;
> > +               switch (c) {
> > +@@ -763,6 +763,8 @@ int main(int argc, char *argv[])
> > +                       if (hostapd_get_interface_names(&if_names,
> > +                                                       &if_names_size, optarg))
> > +                               goto out;
> > ++              case 'n':
> > ++                      interfaces.name = optarg;
> > +                       break;
> > +               default:
> > +                       usage();
> > +@@ -894,6 +896,7 @@ int main(int argc, char *argv[])
> > +       }
> > +
> > +       hostapd_global_ctrl_iface_init(&interfaces);
> > ++      hostapd_ubus_add(&interfaces);
> > +
> > +       if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
> > +               wpa_printf(MSG_ERROR, "Failed to start eloop");
> > +@@ -903,6 +906,7 @@ int main(int argc, char *argv[])
> > +       ret = 0;
> > +
> > +  out:
> > ++      hostapd_ubus_free(&interfaces);
> > +       hostapd_global_ctrl_iface_deinit(&interfaces);
> > +       /* Deinitialize all interfaces */
> > +       for (i = 0; i < interfaces.count; i++) {
> > +--- a/wpa_supplicant/main.c
> > ++++ b/wpa_supplicant/main.c
> > +@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
> > +
> > +       for (;;) {
> > +               c = getopt(argc, argv,
> > +-                         "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuv::W");
> > ++                         "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:n:No:O:p:P:qsTtuv::W");
> > +               if (c < 0)
> > +                       break;
> > +               switch (c) {
> > +@@ -271,6 +271,10 @@ int main(int argc, char *argv[])
> > +                       params.conf_p2p_dev = optarg;
> > +                       break;
> > + #endif /* CONFIG_P2P */
> > ++              case 'n':
> > ++                      params.name = optarg;
> > ++                      iface_count = 0;
> > ++                      break;
> > +               case 'o':
> > +                       params.override_driver = optarg;
> > +                       break;
> > diff --git a/package/network/services/hostapd/patches/700-wifi-reload.patch b/package/network/services/hostapd/patches/700-wifi-reload.patch
> > new file mode 100644
> > index 0000000000..c5ba631a0f
> > --- /dev/null
> > +++ b/package/network/services/hostapd/patches/700-wifi-reload.patch
> > @@ -0,0 +1,118 @@
> > +--- a/hostapd/config_file.c
> > ++++ b/hostapd/config_file.c
> > +@@ -2470,6 +2470,8 @@ static int hostapd_config_fill(struct ho
> > +               bss->isolate = atoi(pos);
> > +       } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
> > +               bss->ap_max_inactivity = atoi(pos);
> > ++      } else if (os_strcmp(buf, "config_id") == 0) {
> > ++              bss->config_id = os_strdup(pos);
> > +       } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
> > +               bss->skip_inactivity_poll = atoi(pos);
> > +       } else if (os_strcmp(buf, "country_code") == 0) {
> > +--- a/src/ap/ap_config.c
> > ++++ b/src/ap/ap_config.c
> > +@@ -698,6 +698,7 @@ void hostapd_config_free_bss(struct host
> > +       os_free(conf->radius_req_attr_sqlite);
> > +       os_free(conf->rsn_preauth_interfaces);
> > +       os_free(conf->ctrl_interface);
> > ++      os_free(conf->config_id);
> > +       os_free(conf->ca_cert);
> > +       os_free(conf->server_cert);
> > +       os_free(conf->server_cert2);
> > +--- a/src/ap/ap_config.h
> > ++++ b/src/ap/ap_config.h
> > +@@ -829,6 +829,7 @@ struct hostapd_bss_config {
> > +        */
> > +       u8 mka_psk_set;
> > + #endif /* CONFIG_MACSEC */
> > ++      char *config_id;
> > + };
> > +
> > + /**
> > +--- a/src/ap/hostapd.c
> > ++++ b/src/ap/hostapd.c
> > +@@ -242,13 +242,13 @@ int hostapd_reload_config(struct hostapd
> > +       if (newconf == NULL)
> > +               return -1;
> > +
> > +-      hostapd_clear_old(iface);
> > +-
> > +       oldconf = hapd->iconf;
> > +       if (hostapd_iface_conf_changed(newconf, oldconf)) {
> > +               char *fname;
> > +               int res;
> > +
> > ++              hostapd_clear_old(iface);
> > ++
> > +               wpa_printf(MSG_DEBUG,
> > +                          "Configuration changes include interface/BSS modification - force full disable+enable sequence");
> > +               fname = os_strdup(iface->config_fname);
> > +@@ -273,6 +273,22 @@ int hostapd_reload_config(struct hostapd
> > +                       wpa_printf(MSG_ERROR,
> > +                                  "Failed to enable interface on config reload");
> > +               return res;
> > ++      } else {
> > ++              for (j = 0; j < iface->num_bss; j++) {
> > ++                      hapd = iface->bss[j];
> > ++                      if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
> > ++                              hostapd_flush_old_stations(iface->bss[j],
> > ++                                                         WLAN_REASON_PREV_AUTH_NOT_VALID);
> > ++                              hostapd_broadcast_wep_clear(iface->bss[j]);
> > ++
> > ++#ifndef CONFIG_NO_RADIUS
> > ++                              /* TODO: update dynamic data based on changed configuration
> > ++                               * items (e.g., open/close sockets, etc.) */
> > ++                              radius_client_flush(iface->bss[j]->radius, 0);
> > ++#endif /* CONFIG_NO_RADIUS */
> > ++                              wpa_printf(MSG_INFO, "bss %d changed", j);
> > ++                      }
> > ++              }
> > +       }
> > +       iface->conf = newconf;
> > +
> > +@@ -289,6 +305,12 @@ int hostapd_reload_config(struct hostapd
> > +
> > +       for (j = 0; j < iface->num_bss; j++) {
> > +               hapd = iface->bss[j];
> > ++              if (hapd->config_id) {
> > ++                      os_free(hapd->config_id);
> > ++                      hapd->config_id = NULL;
> > ++              }
> > ++              if (newconf->bss[j]->config_id)
> > ++                      hapd->config_id = strdup(newconf->bss[j]->config_id);
> > +               hapd->iconf = newconf;
> > +               hapd->conf = newconf->bss[j];
> > +               hostapd_reload_bss(hapd);
> > +@@ -2257,6 +2279,10 @@ hostapd_alloc_bss_data(struct hostapd_if
> > +       hapd->iconf = conf;
> > +       hapd->conf = bss;
> > +       hapd->iface = hapd_iface;
> > ++      if (bss->config_id)
> > ++              hapd->config_id = strdup(bss->config_id);
> > ++      else
> > ++              hapd->config_id = NULL;
> > +       if (conf)
> > +               hapd->driver = conf->driver;
> > +       hapd->ctrl_sock = -1;
> > +--- a/src/ap/hostapd.h
> > ++++ b/src/ap/hostapd.h
> > +@@ -149,6 +149,7 @@ struct hostapd_data {
> > +       struct hostapd_config *iconf;
> > +       struct hostapd_bss_config *conf;
> > +       struct hostapd_ubus_bss ubus;
> > ++      char *config_id;
> > +       int interface_added; /* virtual interface added for this BSS */
> > +       unsigned int started:1;
> > +       unsigned int disabled:1;
> > +--- a/src/drivers/driver_nl80211.c
> > ++++ b/src/drivers/driver_nl80211.c
> > +@@ -4295,6 +4295,9 @@ static int wpa_driver_nl80211_set_ap(voi
> > +       if (ret) {
> > +               wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
> > +                          ret, strerror(-ret));
> > ++              if (!bss->beacon_set)
> > ++                      ret = 0;
> > ++              bss->beacon_set = 0;
> > +       } else {
> > +               bss->beacon_set = 1;
> > +               nl80211_set_bss(bss, params->cts_protect, params->preamble,
> > diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
> > index 3b768d0e8c..e25c3294ee 100644
> > --- a/package/network/services/hostapd/src/src/ap/ubus.c
> > +++ b/package/network/services/hostapd/src/src/ap/ubus.c
> > @@ -26,12 +26,16 @@ static struct ubus_context *ctx;
> >  static struct blob_buf b;
> >  static int ctx_ref;
> >
> > +static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
> > +{
> > +       return container_of(obj, struct hapd_interfaces, ubus);
> > +}
> > +
> >  static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
> >  {
> >         return container_of(obj, struct hostapd_data, ubus.obj);
> >  }
> >
> > -
> >  struct ubus_banned_client {
> >         struct avl_node avl;
> >         u8 addr[ETH_ALEN];
> > @@ -142,6 +146,16 @@ hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
> >         eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
> >  }
> >
> > +static int
> > +hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
> > +                  struct ubus_request_data *req, const char *method,
> > +                  struct blob_attr *msg)
> > +{
> > +       struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
> > +       hostapd_reload_config(hapd->iface);
> > +       hostapd_reload_iface(hapd->iface);
> > +}
> > +
> >  static int
> >  hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
> >                         struct ubus_request_data *req, const char *method,
> > @@ -379,6 +393,70 @@ hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
> >         return 0;
> >  }
> >
> > +enum {
> > +       CONFIG_IFACE,
> > +       CONFIG_FILE,
> > +       __CONFIG_MAX
> > +};
> > +
> > +static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
> > +       [CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> > +       [CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
> > +};
> > +
> > +static int
> > +hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
> > +                  struct ubus_request_data *req, const char *method,
> > +                  struct blob_attr *msg)
> > +{
> > +       struct blob_attr *tb[__CONFIG_MAX];
> > +       struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
> > +       char buf[128];
> > +
> > +       blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
> > +
> > +       if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       snprintf(buf, sizeof(buf), "bss_config=%s:%s",
> > +               blobmsg_get_string(tb[CONFIG_IFACE]),
> > +               blobmsg_get_string(tb[CONFIG_FILE]));
> > +
> > +       if (hostapd_add_iface(interfaces, buf))
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       return UBUS_STATUS_OK;
> > +}
> > +
> > +enum {
> > +       CONFIG_REM_IFACE,
> > +       __CONFIG_REM_MAX
> > +};
> > +
> > +static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
> > +       [CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> > +};
> > +
> > +static int
> > +hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
> > +                     struct ubus_request_data *req, const char *method,
> > +                     struct blob_attr *msg)
> > +{
> > +       struct blob_attr *tb[__CONFIG_REM_MAX];
> > +       struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
> > +       char buf[128];
> > +
> > +       blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
> > +
> > +       if (!tb[CONFIG_REM_IFACE])
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       return UBUS_STATUS_OK;
> > +}
> > +
> >  enum {
> >         CSA_FREQ,
> >         CSA_BCN_COUNT,
> > @@ -949,6 +1027,7 @@ hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
> >  #endif
> >
> >  static const struct ubus_method bss_methods[] = {
> > +       UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
> >         UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
> >         UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
> >         UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
> > @@ -1021,6 +1100,56 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd)
> >         free(name);
> >  }
> >
> > +static const struct ubus_method daemon_methods[] = {
> > +       UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
> > +       UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
> > +};
> > +
> > +static struct ubus_object_type daemon_object_type =
> > +       UBUS_OBJECT_TYPE("hostapd", daemon_methods);
> > +
> > +void hostapd_ubus_add(struct hapd_interfaces *interfaces)
> > +{
> > +       struct ubus_object *obj = &interfaces->ubus;
> > +       int name_len;
> > +       int ret;
> > +
> > +       if (!hostapd_ubus_init())
> > +               return;
> > +
> > +       name_len = strlen("hostapd") + 1;
> > +       if (interfaces->name)
> > +               name_len += strlen(interfaces->name) + 1;
> > +       obj->name = malloc(name_len);
> > +       strcpy(obj->name, "hostapd");
> > +       if (interfaces->name) {
> > +               strcat(obj->name, ".");
> > +               strcat(obj->name, interfaces->name);
> > +       }
> > +
> > +       obj->type = &daemon_object_type;
> > +       obj->methods = daemon_object_type.methods;
> > +       obj->n_methods = daemon_object_type.n_methods;
> > +       ret = ubus_add_object(ctx, obj);
> > +       hostapd_ubus_ref_inc();
> > +}
> > +
> > +void hostapd_ubus_free(struct hapd_interfaces *interfaces)
> > +{
> > +       struct ubus_object *obj = &interfaces->ubus;
> > +       char *name = (char *) obj->name;
> > +
> > +       if (!ctx)
> > +               return;
> > +
> > +       if (obj->id) {
> > +               ubus_remove_object(ctx, obj);
> > +               hostapd_ubus_ref_dec();
> > +       }
> > +
> > +       free(name);
> > +}
> > +
> >  struct ubus_event_req {
> >         struct ubus_notify_request nreq;
> >         int resp;
> > diff --git a/package/network/services/hostapd/src/src/ap/ubus.h b/package/network/services/hostapd/src/src/ap/ubus.h
> > index 58ebe0e65b..27acd32659 100644
> > --- a/package/network/services/hostapd/src/src/ap/ubus.h
> > +++ b/package/network/services/hostapd/src/src/ap/ubus.h
> > @@ -25,6 +25,7 @@ struct hostapd_ubus_request {
> >
> >  struct hostapd_iface;
> >  struct hostapd_data;
> > +struct hapd_interfaces;
> >
> >  #ifdef UBUS_SUPPORT
> >
> > @@ -45,6 +46,9 @@ void hostapd_ubus_free_bss(struct hostapd_data *hapd);
> >  int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
> >  void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
> >
> > +void hostapd_ubus_add(struct hapd_interfaces *interfaces);
> > +void hostapd_ubus_free(struct hapd_interfaces *interfaces);
> > +
> >  #else
> >
> >  struct hostapd_ubus_bss {};
> > @@ -73,6 +77,14 @@ static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct ho
> >  static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
> >  {
> >  }
> > +
> > +static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
> > +{
> > +}
> > +
> > +static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
> > +{
> > +}
> >  #endif
> >
> >  #endif
> > diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.c b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> > index 5fdb57be7a..9a98979e9a 100644
> > --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> > +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
> > @@ -20,6 +20,11 @@ static struct ubus_context *ctx;
> >  static struct blob_buf b;
> >  static int ctx_ref;
> >
> > +static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
> > +{
> > +       return container_of(obj, struct wpa_global, ubus_global);
> > +}
> > +
> >  static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
> >  {
> >         return container_of(obj, struct wpa_supplicant, ubus.obj);
> > @@ -95,6 +100,19 @@ wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
> >         return 0;
> >  }
> >
> > +static int
> > +wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
> > +               struct ubus_request_data *req, const char *method,
> > +               struct blob_attr *msg)
> > +{
> > +       struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
> > +
> > +       if (wpa_supplicant_reload_configuration(wpa_s))
> > +               return UBUS_STATUS_UNKNOWN_ERROR;
> > +       else
> > +               return 0;
> > +}
> > +
> >  #ifdef CONFIG_WPS
> >  enum {
> >         WPS_START_MULTI_AP,
> > @@ -146,11 +164,12 @@ wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
> >  #endif
> >
> >  static const struct ubus_method bss_methods[] = {
> > +       UBUS_METHOD_NOARG("reload", wpas_bss_reload),
> > +       UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
> >  #ifdef CONFIG_WPS
> >         UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
> >         UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
> >  #endif
> > -       UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
> >  };
> >
> >  static struct ubus_object_type bss_object_type =
> > @@ -162,8 +181,6 @@ void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
> >         char *name;
> >         int ret;
> >
> > -       if (!wpas_ubus_init())
> > -               return;
> >
> >         if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
> >                 return;
> > @@ -192,6 +209,159 @@ void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
> >         free(name);
> >  }
> >
> > +enum {
> > +       WPAS_CONFIG_DRIVER,
> > +       WPAS_CONFIG_IFACE,
> > +       WPAS_CONFIG_BRIDGE,
> > +       WPAS_CONFIG_HOSTAPD_CTRL,
> > +       WPAS_CONFIG_CTRL,
> > +       WPAS_CONFIG_FILE,
> > +       __WPAS_CONFIG_MAX
> > +};
> > +
> > +static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = {
> > +       [WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING },
> > +       [WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> > +       [WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING },
> > +       [WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING },
> > +       [WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING },
> > +       [WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
> > +};
> > +
> > +static int
> > +wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj,
> > +               struct ubus_request_data *req, const char *method,
> > +               struct blob_attr *msg)
> > +{
> > +       struct blob_attr *tb[__WPAS_CONFIG_MAX];
> > +       struct wpa_global *global = get_wpa_global_from_object(obj);
> > +       struct wpa_interface *iface;
> > +
> > +       blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
> > +
> > +       if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER])
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       iface = os_zalloc(sizeof(struct wpa_interface));
> > +       if (iface == NULL)
> > +               return UBUS_STATUS_UNKNOWN_ERROR;
> > +
> > +       iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]);
> > +       iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]);
> > +       iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]);
> > +
> > +       if (tb[WPAS_CONFIG_BRIDGE])
> > +               iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]);
> > +
> > +       if (tb[WPAS_CONFIG_CTRL])
> > +               iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]);
> > +
> > +       if (tb[WPAS_CONFIG_HOSTAPD_CTRL])
> > +               iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]);
> > +
> > +       if (!wpa_supplicant_add_iface(global, iface, NULL))
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       return UBUS_STATUS_OK;
> > +}
> > +
> > +enum {
> > +       WPAS_CONFIG_REM_IFACE,
> > +       __WPAS_CONFIG_REM_MAX
> > +};
> > +
> > +static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = {
> > +       [WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
> > +};
> > +
> > +static int
> > +wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
> > +                  struct ubus_request_data *req, const char *method,
> > +                  struct blob_attr *msg)
> > +{
> > +       struct blob_attr *tb[__WPAS_CONFIG_REM_MAX];
> > +       struct wpa_global *global = get_wpa_global_from_object(obj);
> > +       struct wpa_supplicant *wpa_s = NULL;
> > +       unsigned int found = 0;
> > +
> > +       blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
> > +
> > +       if (!tb[WPAS_CONFIG_REM_IFACE])
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       /* find wpa_s object for to-be-removed interface */
> > +       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
> > +               if (!strncmp(wpa_s->ifname,
> > +                            blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]),
> > +                            sizeof(wpa_s->ifname)))
> > +               {
> > +                       found = 1;
> > +                       break;
> > +               }
> > +       }
> > +
> > +       if (!found)
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       if (wpa_supplicant_remove_iface(global, wpa_s, 0))
> > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > +
> > +       return UBUS_STATUS_OK;
> > +}
> > +
> > +static const struct ubus_method wpas_daemon_methods[] = {
> > +       UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy),
> > +       UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy),
> > +};
> > +
> > +static struct ubus_object_type wpas_daemon_object_type =
> > +       UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods);
> > +
> > +void wpas_ubus_add(struct wpa_global *global)
> > +{
> > +       struct ubus_object *obj = &global->ubus_global;
> > +       int name_len;
> > +       int ret;
> > +
> > +       if (!wpas_ubus_init())
> > +               return;
> > +
> > +       name_len = strlen("wpa_supplicant") + 1;
> > +       if (global->params.name)
> > +               name_len += strlen(global->params.name) + 1;
> > +       obj->name = malloc(name_len);
> > +       strcpy(obj->name, "wpa_supplicant");
> > +
> > +       if (global->params.name)
> > +       {
> > +               strcat(obj->name, ".");
> > +               strcat(obj->name, global->params.name);
> > +       }
> > +
> > +       obj->type = &wpas_daemon_object_type;
> > +       obj->methods = wpas_daemon_object_type.methods;
> > +       obj->n_methods = wpas_daemon_object_type.n_methods;
> > +       ret = ubus_add_object(ctx, obj);
> > +       wpas_ubus_ref_inc();
> > +}
> > +
> > +void wpas_ubus_free(struct wpa_global *global)
> > +{
> > +       struct ubus_object *obj = &global->ubus_global;
> > +       char *name = (char *) obj->name;
> > +
> > +       if (!ctx)
> > +               return;
> > +
> > +       if (obj->id) {
> > +               ubus_remove_object(ctx, obj);
> > +               wpas_ubus_ref_dec();
> > +       }
> > +
> > +       free(name);
> > +}
> > +
> > +
> >  #ifdef CONFIG_WPS
> >  void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
> >  {
> > diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.h b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> > index c37e743e73..bf92b98c01 100644
> > --- a/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> > +++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
> > @@ -10,6 +10,8 @@
> >  #define __WPAS_UBUS_H
> >
> >  struct wpa_supplicant;
> > +struct wpa_global;
> > +
> >  #include "wps_supplicant.h"
> >
> >  #ifdef UBUS_SUPPORT
> > @@ -22,6 +24,9 @@ struct wpas_ubus_bss {
> >  void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
> >  void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
> >
> > +void wpas_ubus_add(struct wpa_global *global);
> > +void wpas_ubus_free(struct wpa_global *global);
> > +
> >  #ifdef CONFIG_WPS
> >  void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
> >  #endif
> > @@ -48,6 +53,14 @@ static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
> >  static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
> >  {
> >  }
> > +
> > +static inline void wpas_ubus_add(struct wpa_global *global)
> > +{
> > +}
> > +
> > +static inline void wpas_ubus_free(struct wpa_global *global)
> > +{
> > +}
> >  #endif
> >
> >  #endif
> > --
> > 2.23.0
> >
> >
> > _______________________________________________
> > openwrt-devel mailing list
> > openwrt-devel@lists.openwrt.org
> > https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Patch
diff mbox series

diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile
index 4f6420f503..52adb1115e 100644
--- a/package/network/services/hostapd/Makefile
+++ b/package/network/services/hostapd/Makefile
@@ -519,8 +519,9 @@  define Install/supplicant
 endef
 
 define Package/hostapd-common/install
-	$(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button
+	$(INSTALL_DIR) $(1)/lib/netifd $(1)/etc/rc.button $(1)/etc/hotplug.d/ieee80211
 	$(INSTALL_DATA) ./files/hostapd.sh $(1)/lib/netifd/hostapd.sh
+	$(INSTALL_BIN) ./files/hostapd.hotplug $(1)/etc/hotplug.d/ieee80211/20-hostapd
 	$(INSTALL_BIN) ./files/wps-hotplug.sh $(1)/etc/rc.button/wps
 endef
 
diff --git a/package/network/services/hostapd/files/hostapd.hotplug b/package/network/services/hostapd/files/hostapd.hotplug
new file mode 100644
index 0000000000..d568b3c85a
--- /dev/null
+++ b/package/network/services/hostapd/files/hostapd.hotplug
@@ -0,0 +1,14 @@ 
+#!/bin/sh
+
+[ ${ACTION} = "remove" -a -n "${DEVICENAME}" ] && {
+	kill $(cat /var/run/hostapd-${DEVICENAME}.pid)
+	rm -rf /var/run/hostapd-${DEVICENAME}.pid /var/run/hostapd-${DEVICENAME}/
+	kill $(cat /var/run/wpa_supplicant-${DEVICENAME}.pid)
+	rm -rf /var/run/wpa_supplicant-${DEVICENAME}.pid /var/run/wpa_supplicant-${DEVICENAME}/
+}
+
+[ ${ACTION} = "add" -a -n "${DEVICENAME}" ] && {
+	/usr/sbin/hostapd -s -n ${DEVICENAME} -P /var/run/hostapd-${DEVICENAME}.pid -g /var/run/hostapd-${DEVICENAME}/global -B &
+	mkdir -p /var/run/wpa_supplicant-${DEVICENAME}
+	/usr/sbin/wpa_supplicant -s -n ${DEVICENAME} -P /var/run/wpa_supplicant-${DEVICENAME}.pid -g /var/run/wpa_supplicant-${DEVICENAME}/global -B &
+}
diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh
index 3c1504ca60..f03b98ff85 100644
--- a/package/network/services/hostapd/files/hostapd.sh
+++ b/package/network/services/hostapd/files/hostapd.sh
@@ -254,7 +254,7 @@  hostapd_set_bss_options() {
 
 	wireless_vif_parse_encryption
 
-	local bss_conf
+	local bss_conf bss_md5sum
 	local wep_rekey wpa_group_rekey wpa_pair_rekey wpa_master_rekey wpa_key_mgmt
 
 	json_get_vars \
@@ -627,6 +627,9 @@  hostapd_set_bss_options() {
 		}
 	}
 
+	bss_md5sum=$(echo $bss_conf | md5sum | cut -d" " -f1)
+	append bss_conf "config_id=$bss_md5sum" "$N"
+
 	append "$var" "$bss_conf" "$N"
 	return 0
 }
@@ -950,21 +953,19 @@  EOF
 }
 
 wpa_supplicant_run() {
-	local ifname="$1"; shift
+	local ifname="$1"
+	local hostapd_ctrl="$2"
 
 	_wpa_supplicant_common "$ifname"
 
-	/usr/sbin/wpa_supplicant -B -s \
-		${network_bridge:+-b $network_bridge} \
-		-P "/var/run/wpa_supplicant-${ifname}.pid" \
-		-D ${_w_driver:-wext} \
-		-i "$ifname" \
-		-c "$_config" \
-		-C "$_rpath" \
-		"$@"
+	ubus call wpa_supplicant.$phy config_add "{ \
+		\"driver\": \"${_w_driver:-wext}\", \"ctrl\": \"$_rpath\", \
+		\"iface\": \"$ifname\", \"config\": \"$_config\" \
+		${network_bridge:+, \"bridge\": \"$network_bridge\"} \
+		${hostapd_ctrl:+, \"hostapd_ctrl\": \"$hostapd_ctrl\"} \
+		}"
 
 	ret="$?"
-	wireless_add_process "$(cat "/var/run/wpa_supplicant-${ifname}.pid")" /usr/sbin/wpa_supplicant 1
 
 	[ "$ret" != 0 ] && wireless_setup_vif_failed WPA_SUPPLICANT_FAILED
 
@@ -972,5 +973,5 @@  wpa_supplicant_run() {
 }
 
 hostapd_common_cleanup() {
-	killall hostapd wpa_supplicant meshd-nl80211
+	killall meshd-nl80211
 }
diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch
index 0eb0a4a3ba..6842c0e63e 100644
--- a/package/network/services/hostapd/patches/600-ubus_support.patch
+++ b/package/network/services/hostapd/patches/600-ubus_support.patch
@@ -22,7 +22,16 @@ 
  
  #define OCE_STA_CFON_ENABLED(hapd) \
  	((hapd->conf->oce & OCE_STA_CFON) && \
-@@ -145,6 +146,7 @@ struct hostapd_data {
+@@ -72,6 +73,8 @@ struct hapd_interfaces {
+ #ifdef CONFIG_DPP
+ 	struct dpp_global *dpp;
+ #endif /* CONFIG_DPP */
++	struct ubus_object ubus;
++	char *name;
+ };
+ 
+ enum hostapd_chan_status {
+@@ -145,6 +148,7 @@ struct hostapd_data {
  	struct hostapd_iface *iface;
  	struct hostapd_config *iconf;
  	struct hostapd_bss_config *conf;
@@ -30,6 +39,14 @@ 
  	int interface_added; /* virtual interface added for this BSS */
  	unsigned int started:1;
  	unsigned int disabled:1;
+@@ -580,6 +584,7 @@ hostapd_alloc_bss_data(struct hostapd_if
+ 		       struct hostapd_bss_config *bss);
+ int hostapd_setup_interface(struct hostapd_iface *iface);
+ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
++void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+ void hostapd_interface_deinit(struct hostapd_iface *iface);
+ void hostapd_interface_free(struct hostapd_iface *iface);
+ struct hostapd_iface * hostapd_alloc_iface(void);
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
 @@ -380,6 +380,7 @@ static void hostapd_free_hapd_data(struc
@@ -298,6 +315,36 @@ 
  	/* Remove interface from the global list of interfaces */
  	prev = global->ifaces;
  	if (prev == wpa_s) {
+@@ -6520,6 +6524,8 @@ struct wpa_global * wpa_supplicant_init(
+ 	if (params->override_ctrl_interface)
+ 		global->params.override_ctrl_interface =
+ 			os_strdup(params->override_ctrl_interface);
++	if (params->name)
++		global->params.name = os_strdup(params->name);
+ #ifdef CONFIG_MATCH_IFACE
+ 	global->params.match_iface_count = params->match_iface_count;
+ 	if (params->match_iface_count) {
+@@ -6626,8 +6632,12 @@ int wpa_supplicant_run(struct wpa_global
+ 	eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+ 	eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+ 
++	wpas_ubus_add(global);
++
+ 	eloop_run();
+ 
++	wpas_ubus_free(global);
++
+ 	return 0;
+ }
+ 
+@@ -6687,6 +6697,7 @@ void wpa_supplicant_deinit(struct wpa_gl
+ #ifdef CONFIG_MATCH_IFACE
+ 	os_free(global->params.match_ifaces);
+ #endif /* CONFIG_MATCH_IFACE */
++	os_free(global->params.name);
+ #ifdef CONFIG_P2P
+ 	os_free(global->params.conf_p2p_dev);
+ #endif /* CONFIG_P2P */
 --- a/wpa_supplicant/wpa_supplicant_i.h
 +++ b/wpa_supplicant/wpa_supplicant_i.h
 @@ -17,6 +17,7 @@
@@ -308,7 +355,25 @@ 
  
  extern const char *const wpa_supplicant_version;
  extern const char *const wpa_supplicant_license;
-@@ -506,6 +507,7 @@ struct wpa_supplicant {
+@@ -246,6 +247,8 @@ struct wpa_params {
+ 	 */
+ 	int match_iface_count;
+ #endif /* CONFIG_MATCH_IFACE */
++
++	char *name;
+ };
+ 
+ struct p2p_srv_bonjour {
+@@ -306,6 +309,8 @@ struct wpa_global {
+ #endif /* CONFIG_WIFI_DISPLAY */
+ 
+ 	struct psk_list_entry *add_psk; /* From group formation */
++
++	struct ubus_object ubus_global;
+ };
+ 
+ 
+@@ -506,6 +511,7 @@ struct wpa_supplicant {
  	unsigned char own_addr[ETH_ALEN];
  	unsigned char perm_addr[ETH_ALEN];
  	char ifname[100];
@@ -335,3 +400,61 @@ 
  	if (wpa_s->conf->wps_cred_processing == 1)
  		return 0;
  
+--- a/hostapd/main.c
++++ b/hostapd/main.c
+@@ -688,7 +688,7 @@ int main(int argc, char *argv[])
+ 	wpa_supplicant_event = hostapd_wpa_event;
+ 	wpa_supplicant_event_global = hostapd_wpa_event_global;
+ 	for (;;) {
+-		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:v::");
++		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:g:G:n:v::");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -763,6 +763,8 @@ int main(int argc, char *argv[])
+ 			if (hostapd_get_interface_names(&if_names,
+ 							&if_names_size, optarg))
+ 				goto out;
++		case 'n':
++			interfaces.name = optarg;
+ 			break;
+ 		default:
+ 			usage();
+@@ -894,6 +896,7 @@ int main(int argc, char *argv[])
+ 	}
+ 
+ 	hostapd_global_ctrl_iface_init(&interfaces);
++	hostapd_ubus_add(&interfaces);
+ 
+ 	if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
+ 		wpa_printf(MSG_ERROR, "Failed to start eloop");
+@@ -903,6 +906,7 @@ int main(int argc, char *argv[])
+ 	ret = 0;
+ 
+  out:
++	hostapd_ubus_free(&interfaces);
+ 	hostapd_global_ctrl_iface_deinit(&interfaces);
+ 	/* Deinitialize all interfaces */
+ 	for (i = 0; i < interfaces.count; i++) {
+--- a/wpa_supplicant/main.c
++++ b/wpa_supplicant/main.c
+@@ -203,7 +203,7 @@ int main(int argc, char *argv[])
+ 
+ 	for (;;) {
+ 		c = getopt(argc, argv,
+-			   "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:No:O:p:P:qsTtuv::W");
++			   "b:Bc:C:D:de:f:g:G:hH:i:I:KLMm:n:No:O:p:P:qsTtuv::W");
+ 		if (c < 0)
+ 			break;
+ 		switch (c) {
+@@ -271,6 +271,10 @@ int main(int argc, char *argv[])
+ 			params.conf_p2p_dev = optarg;
+ 			break;
+ #endif /* CONFIG_P2P */
++		case 'n':
++			params.name = optarg;
++			iface_count = 0;
++			break;
+ 		case 'o':
+ 			params.override_driver = optarg;
+ 			break;
diff --git a/package/network/services/hostapd/patches/700-wifi-reload.patch b/package/network/services/hostapd/patches/700-wifi-reload.patch
new file mode 100644
index 0000000000..c5ba631a0f
--- /dev/null
+++ b/package/network/services/hostapd/patches/700-wifi-reload.patch
@@ -0,0 +1,118 @@ 
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -2470,6 +2470,8 @@ static int hostapd_config_fill(struct ho
+ 		bss->isolate = atoi(pos);
+ 	} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
+ 		bss->ap_max_inactivity = atoi(pos);
++	} else if (os_strcmp(buf, "config_id") == 0) {
++		bss->config_id = os_strdup(pos);
+ 	} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+ 		bss->skip_inactivity_poll = atoi(pos);
+ 	} else if (os_strcmp(buf, "country_code") == 0) {
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -698,6 +698,7 @@ void hostapd_config_free_bss(struct host
+ 	os_free(conf->radius_req_attr_sqlite);
+ 	os_free(conf->rsn_preauth_interfaces);
+ 	os_free(conf->ctrl_interface);
++	os_free(conf->config_id);
+ 	os_free(conf->ca_cert);
+ 	os_free(conf->server_cert);
+ 	os_free(conf->server_cert2);
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -829,6 +829,7 @@ struct hostapd_bss_config {
+ 	 */
+ 	u8 mka_psk_set;
+ #endif /* CONFIG_MACSEC */
++	char *config_id;
+ };
+ 
+ /**
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -242,13 +242,13 @@ int hostapd_reload_config(struct hostapd
+ 	if (newconf == NULL)
+ 		return -1;
+ 
+-	hostapd_clear_old(iface);
+-
+ 	oldconf = hapd->iconf;
+ 	if (hostapd_iface_conf_changed(newconf, oldconf)) {
+ 		char *fname;
+ 		int res;
+ 
++		hostapd_clear_old(iface);
++
+ 		wpa_printf(MSG_DEBUG,
+ 			   "Configuration changes include interface/BSS modification - force full disable+enable sequence");
+ 		fname = os_strdup(iface->config_fname);
+@@ -273,6 +273,22 @@ int hostapd_reload_config(struct hostapd
+ 			wpa_printf(MSG_ERROR,
+ 				   "Failed to enable interface on config reload");
+ 		return res;
++	} else {
++		for (j = 0; j < iface->num_bss; j++) {
++			hapd = iface->bss[j];
++			if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
++				hostapd_flush_old_stations(iface->bss[j],
++							   WLAN_REASON_PREV_AUTH_NOT_VALID);
++				hostapd_broadcast_wep_clear(iface->bss[j]);
++
++#ifndef CONFIG_NO_RADIUS
++				/* TODO: update dynamic data based on changed configuration
++				 * items (e.g., open/close sockets, etc.) */
++				radius_client_flush(iface->bss[j]->radius, 0);
++#endif /* CONFIG_NO_RADIUS */
++				wpa_printf(MSG_INFO, "bss %d changed", j);
++			}
++		}
+ 	}
+ 	iface->conf = newconf;
+ 
+@@ -289,6 +305,12 @@ int hostapd_reload_config(struct hostapd
+ 
+ 	for (j = 0; j < iface->num_bss; j++) {
+ 		hapd = iface->bss[j];
++		if (hapd->config_id) {
++			os_free(hapd->config_id);
++			hapd->config_id = NULL;
++		}
++		if (newconf->bss[j]->config_id)
++			hapd->config_id = strdup(newconf->bss[j]->config_id);
+ 		hapd->iconf = newconf;
+ 		hapd->conf = newconf->bss[j];
+ 		hostapd_reload_bss(hapd);
+@@ -2257,6 +2279,10 @@ hostapd_alloc_bss_data(struct hostapd_if
+ 	hapd->iconf = conf;
+ 	hapd->conf = bss;
+ 	hapd->iface = hapd_iface;
++	if (bss->config_id)
++		hapd->config_id = strdup(bss->config_id);
++	else
++		hapd->config_id = NULL;
+ 	if (conf)
+ 		hapd->driver = conf->driver;
+ 	hapd->ctrl_sock = -1;
+--- a/src/ap/hostapd.h
++++ b/src/ap/hostapd.h
+@@ -149,6 +149,7 @@ struct hostapd_data {
+ 	struct hostapd_config *iconf;
+ 	struct hostapd_bss_config *conf;
+ 	struct hostapd_ubus_bss ubus;
++	char *config_id;
+ 	int interface_added; /* virtual interface added for this BSS */
+ 	unsigned int started:1;
+ 	unsigned int disabled:1;
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -4295,6 +4295,9 @@ static int wpa_driver_nl80211_set_ap(voi
+ 	if (ret) {
+ 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+ 			   ret, strerror(-ret));
++		if (!bss->beacon_set)
++			ret = 0;
++		bss->beacon_set = 0;
+ 	} else {
+ 		bss->beacon_set = 1;
+ 		nl80211_set_bss(bss, params->cts_protect, params->preamble,
diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c
index 3b768d0e8c..e25c3294ee 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.c
+++ b/package/network/services/hostapd/src/src/ap/ubus.c
@@ -26,12 +26,16 @@  static struct ubus_context *ctx;
 static struct blob_buf b;
 static int ctx_ref;
 
+static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct hapd_interfaces, ubus);
+}
+
 static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
 {
 	return container_of(obj, struct hostapd_data, ubus.obj);
 }
 
-
 struct ubus_banned_client {
 	struct avl_node avl;
 	u8 addr[ETH_ALEN];
@@ -142,6 +146,16 @@  hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
 	eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
 }
 
+static int
+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	hostapd_reload_config(hapd->iface);
+	hostapd_reload_iface(hapd->iface);
+}
+
 static int
 hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
 			struct ubus_request_data *req, const char *method,
@@ -379,6 +393,70 @@  hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
 	return 0;
 }
 
+enum {
+	CONFIG_IFACE,
+	CONFIG_FILE,
+	__CONFIG_MAX
+};
+
+static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
+	[CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+	[CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CONFIG_MAX];
+	struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+	char buf[128];
+
+	blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	snprintf(buf, sizeof(buf), "bss_config=%s:%s",
+		blobmsg_get_string(tb[CONFIG_IFACE]),
+		blobmsg_get_string(tb[CONFIG_FILE]));
+
+	if (hostapd_add_iface(interfaces, buf))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	CONFIG_REM_IFACE,
+	__CONFIG_REM_MAX
+};
+
+static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
+	[CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CONFIG_REM_MAX];
+	struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+	char buf[128];
+
+	blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CONFIG_REM_IFACE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	return UBUS_STATUS_OK;
+}
+
 enum {
 	CSA_FREQ,
 	CSA_BCN_COUNT,
@@ -949,6 +1027,7 @@  hostapd_wnm_disassoc_imminent(struct ubus_context *ctx, struct ubus_object *obj,
 #endif
 
 static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
 	UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
 	UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
 	UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
@@ -1021,6 +1100,56 @@  void hostapd_ubus_free_bss(struct hostapd_data *hapd)
 	free(name);
 }
 
+static const struct ubus_method daemon_methods[] = {
+	UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
+	UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
+};
+
+static struct ubus_object_type daemon_object_type =
+	UBUS_OBJECT_TYPE("hostapd", daemon_methods);
+
+void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+	struct ubus_object *obj = &interfaces->ubus;
+	int name_len;
+	int ret;
+
+	if (!hostapd_ubus_init())
+		return;
+
+	name_len = strlen("hostapd") + 1;
+	if (interfaces->name)
+		name_len += strlen(interfaces->name) + 1;
+	obj->name = malloc(name_len);
+	strcpy(obj->name, "hostapd");
+	if (interfaces->name) {
+		strcat(obj->name, ".");
+		strcat(obj->name, interfaces->name);
+	}
+
+	obj->type = &daemon_object_type;
+	obj->methods = daemon_object_type.methods;
+	obj->n_methods = daemon_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	hostapd_ubus_ref_inc();
+}
+
+void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+	struct ubus_object *obj = &interfaces->ubus;
+	char *name = (char *) obj->name;
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		hostapd_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
 struct ubus_event_req {
 	struct ubus_notify_request nreq;
 	int resp;
diff --git a/package/network/services/hostapd/src/src/ap/ubus.h b/package/network/services/hostapd/src/src/ap/ubus.h
index 58ebe0e65b..27acd32659 100644
--- a/package/network/services/hostapd/src/src/ap/ubus.h
+++ b/package/network/services/hostapd/src/src/ap/ubus.h
@@ -25,6 +25,7 @@  struct hostapd_ubus_request {
 
 struct hostapd_iface;
 struct hostapd_data;
+struct hapd_interfaces;
 
 #ifdef UBUS_SUPPORT
 
@@ -45,6 +46,9 @@  void hostapd_ubus_free_bss(struct hostapd_data *hapd);
 int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
 void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
 
+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
+
 #else
 
 struct hostapd_ubus_bss {};
@@ -73,6 +77,14 @@  static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct ho
 static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
 {
 }
+
+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+}
 #endif
 
 #endif
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.c b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
index 5fdb57be7a..9a98979e9a 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ubus.c
+++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.c
@@ -20,6 +20,11 @@  static struct ubus_context *ctx;
 static struct blob_buf b;
 static int ctx_ref;
 
+static inline struct wpa_global *get_wpa_global_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct wpa_global, ubus_global);
+}
+
 static inline struct wpa_supplicant *get_wpas_from_object(struct ubus_object *obj)
 {
 	return container_of(obj, struct wpa_supplicant, ubus.obj);
@@ -95,6 +100,19 @@  wpas_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
 	return 0;
 }
 
+static int
+wpas_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct wpa_supplicant *wpa_s = get_wpas_from_object(obj);
+
+	if (wpa_supplicant_reload_configuration(wpa_s))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	else
+		return 0;
+}
+
 #ifdef CONFIG_WPS
 enum {
 	WPS_START_MULTI_AP,
@@ -146,11 +164,12 @@  wpas_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
 #endif
 
 static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", wpas_bss_reload),
+	UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
 #ifdef CONFIG_WPS
 	UBUS_METHOD_NOARG("wps_start", wpas_bss_wps_start),
 	UBUS_METHOD_NOARG("wps_cancel", wpas_bss_wps_cancel),
 #endif
-	UBUS_METHOD_NOARG("get_features", wpas_bss_get_features),
 };
 
 static struct ubus_object_type bss_object_type =
@@ -162,8 +181,6 @@  void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s)
 	char *name;
 	int ret;
 
-	if (!wpas_ubus_init())
-		return;
 
 	if (asprintf(&name, "wpa_supplicant.%s", wpa_s->ifname) < 0)
 		return;
@@ -192,6 +209,159 @@  void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
 	free(name);
 }
 
+enum {
+	WPAS_CONFIG_DRIVER,
+	WPAS_CONFIG_IFACE,
+	WPAS_CONFIG_BRIDGE,
+	WPAS_CONFIG_HOSTAPD_CTRL,
+	WPAS_CONFIG_CTRL,
+	WPAS_CONFIG_FILE,
+	__WPAS_CONFIG_MAX
+};
+
+static const struct blobmsg_policy wpas_config_add_policy[__WPAS_CONFIG_MAX] = {
+	[WPAS_CONFIG_DRIVER] = { "driver", BLOBMSG_TYPE_STRING },
+	[WPAS_CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+	[WPAS_CONFIG_BRIDGE] = { "bridge", BLOBMSG_TYPE_STRING },
+	[WPAS_CONFIG_HOSTAPD_CTRL] = { "hostapd_ctrl", BLOBMSG_TYPE_STRING },
+	[WPAS_CONFIG_CTRL] = { "ctrl", BLOBMSG_TYPE_STRING },
+	[WPAS_CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
+};
+
+static int
+wpas_config_add(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct blob_attr *tb[__WPAS_CONFIG_MAX];
+	struct wpa_global *global = get_wpa_global_from_object(obj);
+	struct wpa_interface *iface;
+
+	blobmsg_parse(wpas_config_add_policy, __WPAS_CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[WPAS_CONFIG_FILE] || !tb[WPAS_CONFIG_IFACE] || !tb[WPAS_CONFIG_DRIVER])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	iface = os_zalloc(sizeof(struct wpa_interface));
+	if (iface == NULL)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	iface->driver = blobmsg_get_string(tb[WPAS_CONFIG_DRIVER]);
+	iface->ifname = blobmsg_get_string(tb[WPAS_CONFIG_IFACE]);
+	iface->confname = blobmsg_get_string(tb[WPAS_CONFIG_FILE]);
+
+	if (tb[WPAS_CONFIG_BRIDGE])
+		iface->bridge_ifname = blobmsg_get_string(tb[WPAS_CONFIG_BRIDGE]);
+
+	if (tb[WPAS_CONFIG_CTRL])
+		iface->ctrl_interface = blobmsg_get_string(tb[WPAS_CONFIG_CTRL]);
+
+	if (tb[WPAS_CONFIG_HOSTAPD_CTRL])
+		iface->hostapd_ctrl = blobmsg_get_string(tb[WPAS_CONFIG_HOSTAPD_CTRL]);
+
+	if (!wpa_supplicant_add_iface(global, iface, NULL))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	WPAS_CONFIG_REM_IFACE,
+	__WPAS_CONFIG_REM_MAX
+};
+
+static const struct blobmsg_policy wpas_config_remove_policy[__WPAS_CONFIG_REM_MAX] = {
+	[WPAS_CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+};
+
+static int
+wpas_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct blob_attr *tb[__WPAS_CONFIG_REM_MAX];
+	struct wpa_global *global = get_wpa_global_from_object(obj);
+	struct wpa_supplicant *wpa_s = NULL;
+	unsigned int found = 0;
+
+	blobmsg_parse(wpas_config_remove_policy, __WPAS_CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[WPAS_CONFIG_REM_IFACE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	/* find wpa_s object for to-be-removed interface */
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (!strncmp(wpa_s->ifname,
+			     blobmsg_get_string(tb[WPAS_CONFIG_REM_IFACE]),
+			     sizeof(wpa_s->ifname)))
+		{
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (wpa_supplicant_remove_iface(global, wpa_s, 0))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	return UBUS_STATUS_OK;
+}
+
+static const struct ubus_method wpas_daemon_methods[] = {
+	UBUS_METHOD("config_add", wpas_config_add, wpas_config_add_policy),
+	UBUS_METHOD("config_remove", wpas_config_remove, wpas_config_remove_policy),
+};
+
+static struct ubus_object_type wpas_daemon_object_type =
+	UBUS_OBJECT_TYPE("wpa_supplicant", wpas_daemon_methods);
+
+void wpas_ubus_add(struct wpa_global *global)
+{
+	struct ubus_object *obj = &global->ubus_global;
+	int name_len;
+	int ret;
+
+	if (!wpas_ubus_init())
+		return;
+
+	name_len = strlen("wpa_supplicant") + 1;
+	if (global->params.name)
+		name_len += strlen(global->params.name) + 1;
+	obj->name = malloc(name_len);
+	strcpy(obj->name, "wpa_supplicant");
+
+	if (global->params.name)
+	{
+		strcat(obj->name, ".");
+		strcat(obj->name, global->params.name);
+	}
+
+	obj->type = &wpas_daemon_object_type;
+	obj->methods = wpas_daemon_object_type.methods;
+	obj->n_methods = wpas_daemon_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	wpas_ubus_ref_inc();
+}
+
+void wpas_ubus_free(struct wpa_global *global)
+{
+	struct ubus_object *obj = &global->ubus_global;
+	char *name = (char *) obj->name;
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		wpas_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+
 #ifdef CONFIG_WPS
 void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
 {
diff --git a/package/network/services/hostapd/src/wpa_supplicant/ubus.h b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
index c37e743e73..bf92b98c01 100644
--- a/package/network/services/hostapd/src/wpa_supplicant/ubus.h
+++ b/package/network/services/hostapd/src/wpa_supplicant/ubus.h
@@ -10,6 +10,8 @@ 
 #define __WPAS_UBUS_H
 
 struct wpa_supplicant;
+struct wpa_global;
+
 #include "wps_supplicant.h"
 
 #ifdef UBUS_SUPPORT
@@ -22,6 +24,9 @@  struct wpas_ubus_bss {
 void wpas_ubus_add_bss(struct wpa_supplicant *wpa_s);
 void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s);
 
+void wpas_ubus_add(struct wpa_global *global);
+void wpas_ubus_free(struct wpa_global *global);
+
 #ifdef CONFIG_WPS
 void wpas_ubus_notify(struct wpa_supplicant *wpa_s, const struct wps_credential *cred);
 #endif
@@ -48,6 +53,14 @@  static inline void wpas_ubus_free_bss(struct wpa_supplicant *wpa_s)
 static inline void wpas_ubus_notify(struct wpa_supplicant *wpa_s, struct wps_credential *cred)
 {
 }
+
+static inline void wpas_ubus_add(struct wpa_global *global)
+{
+}
+
+static inline void wpas_ubus_free(struct wpa_global *global)
+{
+}
 #endif
 
 #endif