hostapd: Add mechanism to track unconnected stations
[mech_eap.git] / hostapd / ctrl_iface.c
index 5ef3597..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)
 {
@@ -1120,76 +1213,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                        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))
@@ -1937,6 +1961,85 @@ static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
 }
 
 
+static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
+                                       char *buf, size_t buflen)
+{
+       char *pos, *end, *stamp;
+       int ret;
+
+       /* 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;
+       }
+
+       while (*cmd == ' ')
+               cmd++;
+
+       stamp = os_strchr(cmd, ' ');
+       if (stamp) {
+               *stamp++ = '\0';
+               while (*stamp == ' ') {
+                       stamp++;
+               }
+       }
+
+       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,
@@ -2165,6 +2268,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
        } 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;
@@ -2554,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);
@@ -2562,7 +2677,7 @@ hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
                }
        }
 
-       return EINVAL;
+       return -EINVAL;
 }
 
 
@@ -2584,32 +2699,152 @@ hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
                }
        }
 
-       return EINVAL;
+       return -EINVAL;
 }
 
 #endif /* CONFIG_FST */
 
-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)
+
+static struct hostapd_data *
+hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
+                           const char *ifname)
 {
        size_t i, j;
-       struct hostapd_data *hapd = NULL;
 
-       for (i = 0; hapd == NULL && i < interfaces->count; i++) {
+       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)
-                               break;
-                       hapd = NULL;
+                               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;
 
@@ -2713,6 +2948,12 @@ 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");