hostapd: Add mechanism to track unconnected stations
[mech_eap.git] / hostapd / ctrl_iface.c
index d2055c3..cb6fb17 100644 (file)
@@ -49,6 +49,8 @@
 #include "ctrl_iface.h"
 
 
+#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
 struct wpa_ctrl_dst {
        struct wpa_ctrl_dst *next;
        struct sockaddr_un addr;
@@ -1058,6 +1060,97 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
 #endif /* CONFIG_WNM */
 
 
+static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
+                                          char *buf, size_t buflen)
+{
+       int ret = 0;
+       char *pos, *end;
+
+       pos = buf;
+       end = buf + buflen;
+
+       WPA_ASSERT(hapd->conf->wpa_key_mgmt);
+
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+               ret = os_snprintf(pos, end - pos, "WPA-PSK ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "WPA-EAP ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#ifdef CONFIG_IEEE80211R
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+               ret = os_snprintf(pos, end - pos, "FT-PSK ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "FT-EAP ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#ifdef CONFIG_SAE
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+               ret = os_snprintf(pos, end - pos, "FT-SAE ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+               ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+               ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+               ret = os_snprintf(pos, end - pos, "SAE ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_SAE */
+       if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+       if (hapd->conf->wpa_key_mgmt &
+           WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               ret = os_snprintf(pos, end - pos,
+                                 "WPA-EAP-SUITE-B-192 ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (pos > buf && *(pos - 1) == ' ') {
+               *(pos - 1) = '\0';
+               pos--;
+       }
+
+       return pos - buf;
+}
+
+
 static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                                         char *buf, size_t buflen)
 {
@@ -1107,82 +1200,20 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_WPS */
 
+       if (hapd->conf->wpa) {
+               ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
        if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
                ret = os_snprintf(pos, end - pos, "key_mgmt=");
                if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
-                       ret = os_snprintf(pos, end - pos, "WPA-PSK ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
-                       ret = os_snprintf(pos, end - pos, "WPA-EAP ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-#ifdef CONFIG_IEEE80211R
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
-                       ret = os_snprintf(pos, end - pos, "FT-PSK ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
-                       ret = os_snprintf(pos, end - pos, "FT-EAP ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-#ifdef CONFIG_SAE
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
-                       ret = os_snprintf(pos, end - pos, "FT-SAE ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-#endif /* CONFIG_SAE */
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
-                       ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
-                       ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_SAE
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
-                       ret = os_snprintf(pos, end - pos, "SAE ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-#endif /* CONFIG_SAE */
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
-                       ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
-               if (hapd->conf->wpa_key_mgmt &
-                   WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
-                       ret = os_snprintf(pos, end - pos,
-                                         "WPA-EAP-SUITE-B-192 ");
-                       if (os_snprintf_error(end - pos, ret))
-                               return pos - buf;
-                       pos += ret;
-               }
+               pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
 
                ret = os_snprintf(pos, end - pos, "\n");
                if (os_snprintf_error(end - pos, ret))
@@ -1930,41 +1961,93 @@ static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
 }
 
 
-static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
-                                      void *sock_ctx)
+static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
+                                       char *buf, size_t buflen)
 {
-       struct hostapd_data *hapd = eloop_ctx;
-       char buf[4096];
-       int res;
-       struct sockaddr_un from;
-       socklen_t fromlen = sizeof(from);
-       char *reply;
-       const int reply_size = 4096;
-       int reply_len;
-       int level = MSG_DEBUG;
+       char *pos, *end, *stamp;
+       int ret;
 
-       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
-                      (struct sockaddr *) &from, &fromlen);
-       if (res < 0) {
-               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
-                          strerror(errno));
-               return;
+       /* cmd: "LOG_LEVEL [<level>]" */
+       if (*cmd == '\0') {
+               pos = buf;
+               end = buf + buflen;
+               ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+                                 "Timestamp: %d\n",
+                                 debug_level_str(wpa_debug_level),
+                                 wpa_debug_timestamp);
+               if (os_snprintf_error(end - pos, ret))
+                       ret = 0;
+
+               return ret;
        }
-       buf[res] = '\0';
-       if (os_strcmp(buf, "PING") == 0)
-               level = MSG_EXCESSIVE;
-       wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
 
-       reply = os_malloc(reply_size);
-       if (reply == NULL) {
-               if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-                          fromlen) < 0) {
-                       wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
-                                  strerror(errno));
+       while (*cmd == ' ')
+               cmd++;
+
+       stamp = os_strchr(cmd, ' ');
+       if (stamp) {
+               *stamp++ = '\0';
+               while (*stamp == ' ') {
+                       stamp++;
                }
-               return;
        }
 
+       if (os_strlen(cmd)) {
+               int level = str_to_debug_level(cmd);
+               if (level < 0)
+                       return -1;
+               wpa_debug_level = level;
+       }
+
+       if (stamp && os_strlen(stamp))
+               wpa_debug_timestamp = atoi(stamp);
+
+       os_memcpy(buf, "OK\n", 3);
+       return 3;
+}
+
+
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
+                                            char *buf, size_t buflen)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       char *pos, *end;
+       struct hostapd_sta_info *info;
+       struct os_reltime now;
+
+       sta_track_expire(iface, 0);
+
+       pos = buf;
+       end = buf + buflen;
+
+       os_get_reltime(&now);
+       dl_list_for_each_reverse(info, &iface->sta_seen,
+                                struct hostapd_sta_info, list) {
+               struct os_reltime age;
+               int ret;
+
+               os_reltime_sub(&now, &info->last_seen, &age);
+               ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+                                 MAC2STR(info->addr), (unsigned int) age.sec);
+               if (os_snprintf_error(end - pos, ret))
+                       break;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+                                             char *buf, char *reply,
+                                             int reply_size,
+                                             struct sockaddr_un *from,
+                                             socklen_t fromlen)
+{
+       int reply_len, res;
+
        os_memcpy(reply, "OK\n", 3);
        reply_len = 3;
 
@@ -2021,13 +2104,13 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
                                                        reply_size);
        } else if (os_strcmp(buf, "ATTACH") == 0) {
-               if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
+               if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
                        reply_len = -1;
        } else if (os_strcmp(buf, "DETACH") == 0) {
-               if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
+               if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
                        reply_len = -1;
        } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
-               if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
+               if (hostapd_ctrl_iface_level(hapd, from, fromlen,
                                                    buf + 6))
                        reply_len = -1;
        } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
@@ -2185,6 +2268,14 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
                if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+               reply_len = hostapd_ctrl_iface_log_level(
+                       hapd, buf + 9, reply, reply_size);
+#ifdef NEED_AP_MLME
+       } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
+               reply_len = hostapd_ctrl_iface_track_sta_list(
+                       hapd, reply, reply_size);
+#endif /* NEED_AP_MLME */
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -2194,6 +2285,50 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                os_memcpy(reply, "FAIL\n", 5);
                reply_len = 5;
        }
+
+       return reply_len;
+}
+
+
+static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
+                                      void *sock_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       char buf[4096];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+       char *reply;
+       const int reply_size = 4096;
+       int reply_len;
+       int level = MSG_DEBUG;
+
+       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
+               return;
+       }
+       buf[res] = '\0';
+       if (os_strcmp(buf, "PING") == 0)
+               level = MSG_EXCESSIVE;
+       wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
+
+       reply = os_malloc(reply_size);
+       if (reply == NULL) {
+               if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                          fromlen) < 0) {
+                       wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+                                  strerror(errno));
+               }
+               return;
+       }
+
+       reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
+                                                      reply, reply_size,
+                                                      &from, fromlen);
+
        if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
                   fromlen) < 0) {
                wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
@@ -2530,6 +2665,10 @@ hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
        if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
                hapd = hostapd_get_iface(interfaces, ifname);
                if (hapd) {
+                       if (hapd->iface->fst) {
+                               wpa_printf(MSG_INFO, "FST: Already attached");
+                               return -1;
+                       }
                        fst_hostapd_fill_iface_obj(hapd, &iface_obj);
                        hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
                                                      &iface_obj, &cfg);
@@ -2538,7 +2677,7 @@ hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
                }
        }
 
-       return EINVAL;
+       return -EINVAL;
 }
 
 
@@ -2554,17 +2693,172 @@ hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
                if (hapd) {
                        if (!fst_iface_detach(ifname)) {
                                hapd->iface->fst = NULL;
+                               hapd->iface->fst_ies = NULL;
                                return 0;
                        }
                }
        }
 
-       return EINVAL;
+       return -EINVAL;
 }
 
 #endif /* CONFIG_FST */
 
 
+static struct hostapd_data *
+hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
+                           const char *ifname)
+{
+       size_t i, j;
+
+       for (i = 0; i < interfaces->count; i++) {
+               struct hostapd_iface *iface = interfaces->iface[i];
+
+               for (j = 0; j < iface->num_bss; j++) {
+                       struct hostapd_data *hapd;
+
+                       hapd = iface->bss[j];
+                       if (os_strcmp(ifname, hapd->conf->iface) == 0)
+                               return hapd;
+               }
+       }
+
+       return NULL;
+}
+
+
+static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
+                                       struct hostapd_data *dst_hapd,
+                                       const char *param)
+{
+       int res;
+       char *value;
+
+       value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+       if (!value) {
+               wpa_printf(MSG_ERROR,
+                          "DUP: cannot allocate buffer to stringify %s",
+                          param);
+               goto error_return;
+       }
+
+       if (os_strcmp(param, "wpa") == 0) {
+               os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
+                           src_hapd->conf->wpa);
+       } else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
+                  src_hapd->conf->wpa_key_mgmt) {
+               res = hostapd_ctrl_iface_get_key_mgmt(
+                       src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+               if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
+                       goto error_stringify;
+       } else if (os_strcmp(param, "wpa_pairwise") == 0 &&
+                  src_hapd->conf->wpa_pairwise) {
+               res = wpa_write_ciphers(value,
+                                       value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+                                       src_hapd->conf->wpa_pairwise, " ");
+               if (res < 0)
+                       goto error_stringify;
+       } else if (os_strcmp(param, "rsn_pairwise") == 0 &&
+                  src_hapd->conf->rsn_pairwise) {
+               res = wpa_write_ciphers(value,
+                                       value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+                                       src_hapd->conf->rsn_pairwise, " ");
+               if (res < 0)
+                       goto error_stringify;
+       } else if (os_strcmp(param, "wpa_passphrase") == 0 &&
+                  src_hapd->conf->ssid.wpa_passphrase) {
+               os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
+                           src_hapd->conf->ssid.wpa_passphrase);
+       } else if (os_strcmp(param, "wpa_psk") == 0 &&
+                  src_hapd->conf->ssid.wpa_psk_set) {
+               wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+                       src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+       } else {
+               wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
+               goto error_return;
+       }
+
+       res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
+       os_free(value);
+       return res;
+
+error_stringify:
+       wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
+error_return:
+       os_free(value);
+       return -1;
+}
+
+
+static int
+hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
+                                     char *cmd)
+{
+       char *p_start = cmd, *p_end;
+       struct hostapd_data *src_hapd, *dst_hapd;
+
+       /* cmd: "<src ifname> <dst ifname> <variable name> */
+
+       p_end = os_strchr(p_start, ' ');
+       if (!p_end) {
+               wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
+                          cmd);
+               return -1;
+       }
+
+       *p_end = '\0';
+       src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+       if (!src_hapd) {
+               wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
+                          p_start);
+               return -1;
+       }
+
+       p_start = p_end + 1;
+       p_end = os_strchr(p_start, ' ');
+       if (!p_end) {
+               wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
+                          cmd);
+               return -1;
+       }
+
+       *p_end = '\0';
+       dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+       if (!dst_hapd) {
+               wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
+                          p_start);
+               return -1;
+       }
+
+       p_start = p_end + 1;
+       return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
+}
+
+
+static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
+                                           const char *ifname,
+                                           char *buf, char *reply,
+                                           int reply_size,
+                                           struct sockaddr_un *from,
+                                           socklen_t fromlen)
+{
+       struct hostapd_data *hapd;
+
+       hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
+       if (hapd == NULL) {
+               int res;
+
+               res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
+               if (os_snprintf_error(reply_size, res))
+                       return -1;
+               return res;
+       }
+
+       return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
+                                                 from, fromlen);
+}
+
+
 static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                                              void *sock_ctx)
 {
@@ -2600,6 +2894,18 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
        os_memcpy(reply, "OK\n", 3);
        reply_len = 3;
 
+       if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+               char *pos = os_strchr(buf + 7, ' ');
+
+               if (pos) {
+                       *pos++ = '\0';
+                       reply_len = hostapd_global_ctrl_iface_ifname(
+                               interfaces, buf + 7, pos, reply, reply_size,
+                               &from, fromlen);
+                       goto send_reply;
+               }
+       }
+
        if (os_strcmp(buf, "PING") == 0) {
                os_memcpy(reply, "PONG\n", 5);
                reply_len = 5;
@@ -2642,12 +2948,19 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
                reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
 #endif /* CONFIG_FST */
+       } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+               if (!hostapd_global_ctrl_iface_dup_network(interfaces,
+                                                          buf + 12))
+                       reply_len = os_snprintf(reply, reply_size, "OK\n");
+               else
+                       reply_len = -1;
        } else {
                wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
                           "ignored");
                reply_len = -1;
        }
 
+send_reply:
        if (reply_len < 0) {
                os_memcpy(reply, "FAIL\n", 5);
                reply_len = 5;