+ if (hapd->l2_test)
+ return 0;
+
+ pos = os_strstr(cmd, " ifname=");
+ if (pos)
+ ifname = pos + 8;
+ else
+ ifname = hapd->conf->iface;
+
+ hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
+ ETHERTYPE_IP, hostapd_data_test_rx,
+ hapd, 1);
+ if (hapd->l2_test == NULL)
+ return -1;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
+
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
+{
+ u8 dst[ETH_ALEN], src[ETH_ALEN];
+ char *pos;
+ int used;
+ long int val;
+ u8 tos;
+ u8 buf[2 + HWSIM_PACKETLEN];
+ struct ether_header *eth;
+ struct iphdr *ip;
+ u8 *dpos;
+ unsigned int i;
+
+ if (hapd->l2_test == NULL)
+ return -1;
+
+ /* format: <dst> <src> <tos> */
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, dst);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+ used = hwaddr_aton2(pos, src);
+ if (used < 0)
+ return -1;
+ pos += used;
+
+ val = strtol(pos, NULL, 0);
+ if (val < 0 || val > 0xff)
+ return -1;
+ tos = val;
+
+ eth = (struct ether_header *) &buf[2];
+ os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+ os_memcpy(eth->ether_shost, src, ETH_ALEN);
+ eth->ether_type = htons(ETHERTYPE_IP);
+ ip = (struct iphdr *) (eth + 1);
+ os_memset(ip, 0, sizeof(*ip));
+ ip->ihl = 5;
+ ip->version = 4;
+ ip->ttl = 64;
+ ip->tos = tos;
+ ip->tot_len = htons(HWSIM_IP_LEN);
+ ip->protocol = 1;
+ ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+ ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
+ ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+ dpos = (u8 *) (ip + 1);
+ for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+ *dpos++ = i;
+
+ if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
+ HWSIM_PACKETLEN) < 0)
+ return -1;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
+ " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
+ char *cmd)
+{
+ u8 *buf;
+ struct ether_header *eth;
+ struct l2_packet_data *l2 = NULL;
+ size_t len;
+ u16 ethertype;
+ int res = -1;
+ const char *ifname = hapd->conf->iface;
+
+ if (os_strncmp(cmd, "ifname=", 7) == 0) {
+ cmd += 7;
+ ifname = cmd;
+ cmd = os_strchr(cmd, ' ');
+ if (cmd == NULL)
+ return -1;
+ *cmd++ = '\0';
+ }
+
+ len = os_strlen(cmd);
+ if (len & 1 || len < ETH_HLEN * 2)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(cmd, buf, len) < 0)
+ goto done;
+
+ eth = (struct ether_header *) buf;
+ ethertype = ntohs(eth->ether_type);
+
+ l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
+ hostapd_data_test_rx, hapd, 1);
+ if (l2 == NULL)
+ goto done;
+
+ res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+ if (l2)
+ l2_packet_deinit(l2);
+ os_free(buf);
+
+ return res < 0 ? -1 : 0;
+}
+
+
+static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ char *pos;
+
+ wpa_trace_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_fail_func, pos,
+ sizeof(wpa_trace_fail_func));
+ } else {
+ wpa_trace_fail_after = 0;
+ }
+
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+ wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ char *pos;
+
+ wpa_trace_test_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_test_fail_func, pos,
+ sizeof(wpa_trace_test_fail_func));
+ } else {
+ wpa_trace_test_fail_after = 0;
+ }
+
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+ wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+ char *pos)
+{
+#ifdef NEED_AP_MLME
+ struct csa_settings settings;
+ int ret;
+ unsigned int i;
+
+ ret = hostapd_parse_csa_settings(pos, &settings);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ ret = hostapd_switch_channel(iface->bss[i], &settings);
+ if (ret) {
+ /* FIX: What do we do if CSA fails in the middle of
+ * submitting multi-BSS CSA requests? */
+ return ret;
+ }
+ }
+
+ return 0;
+#else /* NEED_AP_MLME */
+ return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
+ int reply_size, const char *param)
+{
+#ifdef RADIUS_SERVER
+ if (os_strcmp(param, "radius_server") == 0) {
+ return radius_server_get_mib(hapd->radius_srv, reply,
+ reply_size);
+ }
+#endif /* RADIUS_SERVER */
+ return -1;
+}
+
+
+static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos;
+ u8 *data = NULL;
+ unsigned int vendor_id, subcmd;
+ struct wpabuf *reply;
+ size_t data_len = 0;
+
+ /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ vendor_id = strtoul(cmd, &pos, 16);
+ if (!isblank((unsigned char) *pos))
+ return -EINVAL;
+
+ subcmd = strtoul(pos, &pos, 10);
+
+ if (*pos != '\0') {
+ if (!isblank((unsigned char) *pos++))
+ return -EINVAL;
+ data_len = os_strlen(pos);
+ }
+
+ if (data_len) {
+ data_len /= 2;
+ data = os_malloc(data_len);
+ if (!data)
+ return -ENOBUFS;
+
+ if (hexstr2bin(pos, data, data_len)) {
+ wpa_printf(MSG_DEBUG,
+ "Vendor command: wrong parameter format");
+ os_free(data);
+ return -EINVAL;
+ }
+ }
+
+ reply = wpabuf_alloc((buflen - 1) / 2);
+ if (!reply) {
+ os_free(data);
+ return -ENOBUFS;
+ }
+
+ ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
+ reply);
+
+ if (ret == 0)
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+ wpabuf_len(reply));
+
+ wpabuf_free(reply);
+ os_free(data);
+
+ return ret;
+}
+
+
+static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->eapol_sm)
+ return -1;
+
+ eapol_auth_reauthenticate(sta->eapol_sm);
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ char *pos = cmd, *param;
+
+ if (hwaddr_aton(pos, addr) || pos[17] != ' ')
+ return -1;
+ pos += 18;
+ param = pos;
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ *pos++ = '\0';
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->eapol_sm)
+ return -1;
+
+ return eapol_auth_set_conf(sta->eapol_sm, param, pos);
+}
+
+
+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;
+
+ if (!iface->num_sta_seen)
+ return 0;
+
+ 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_req_lci(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
+ return -1;
+ }
+
+ return hostapd_send_lci_req(hapd, addr);
+}
+
+
+static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ char *token, *context = NULL;
+ int random_interval, min_ap;
+ u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
+ unsigned int n_responders;
+
+ token = str_token(cmd, " ", &context);
+ if (!token || hwaddr_aton(token, addr)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: REQ_RANGE - Bad destination address");
+ return -1;
+ }
+
+ token = str_token(cmd, " ", &context);
+ if (!token)
+ return -1;
+
+ random_interval = atoi(token);
+ if (random_interval < 0 || random_interval > 0xffff)
+ return -1;
+
+ token = str_token(cmd, " ", &context);
+ if (!token)
+ return -1;
+
+ min_ap = atoi(token);
+ if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
+ return -1;
+
+ n_responders = 0;
+ while ((token = str_token(cmd, " ", &context))) {
+ if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
+ wpa_printf(MSG_INFO,
+ "CTRL: REQ_RANGE: Too many responders");
+ return -1;
+ }
+
+ if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
+ wpa_printf(MSG_INFO,
+ "CTRL: REQ_RANGE: Bad responder address");
+ return -1;
+ }
+
+ n_responders++;
+ }
+
+ if (!n_responders) {
+ wpa_printf(MSG_INFO,
+ "CTRL: REQ_RANGE - No FTM responder address");
+ return -1;
+ }
+
+ return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
+ responders, n_responders);
+}
+
+
+static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
+{
+ struct wpa_ssid_value ssid;
+ u8 bssid[ETH_ALEN];
+ struct wpabuf *nr, *lci = NULL, *civic = NULL;
+ char *tmp;
+ int ret;
+
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
+ return -1;
+ }
+
+ if (hwaddr_aton(buf, bssid)) {
+ wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
+ return -1;
+ }
+
+ tmp = os_strstr(buf, "ssid=");
+ if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Bad or missing SSID");
+ return -1;
+ }
+ buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
+ if (!buf)
+ return -1;
+
+ tmp = os_strstr(buf, "nr=");
+ if (!tmp) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
+ return -1;
+ }
+
+ buf = os_strchr(tmp, ' ');
+ if (buf)
+ *buf++ = '\0';
+
+ nr = wpabuf_parse_bin(tmp + 3);
+ if (!nr) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
+ return -1;
+ }
+
+ if (!buf)
+ goto set;
+
+ tmp = os_strstr(buf, "lci=");
+ if (tmp) {
+ buf = os_strchr(tmp, ' ');
+ if (buf)
+ *buf++ = '\0';
+ lci = wpabuf_parse_bin(tmp + 4);
+ if (!lci) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Bad LCI subelement");
+ wpabuf_free(nr);
+ return -1;
+ }
+ }
+
+ if (!buf)
+ goto set;
+
+ tmp = os_strstr(buf, "civic=");
+ if (tmp) {
+ buf = os_strchr(tmp, ' ');
+ if (buf)
+ *buf++ = '\0';
+ civic = wpabuf_parse_bin(tmp + 6);
+ if (!civic) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Bad civic subelement");
+ wpabuf_free(nr);
+ wpabuf_free(lci);
+ return -1;
+ }
+ }
+
+set:
+ ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic);
+
+ wpabuf_free(nr);
+ wpabuf_free(lci);
+ wpabuf_free(civic);
+
+ return ret;
+}
+
+
+static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
+ char *buf)
+{
+ struct wpa_ssid_value ssid;
+ u8 bssid[ETH_ALEN];
+ char *tmp;
+
+ if (hwaddr_aton(buf, bssid)) {
+ wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
+ return -1;
+ }
+
+ tmp = os_strstr(buf, "ssid=");
+ if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
+ return -1;
+ }
+
+ return hostapd_neighbor_remove(hapd, bssid, &ssid);
+}
+
+
+static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
+ size_t buflen)
+{
+ int ret, i;
+ char *pos, *end;
+
+ ret = os_snprintf(buf, buflen, "%016llX:\n",
+ (long long unsigned) iface->drv_flags);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+
+ pos = buf + ret;
+ end = buf + buflen;
+
+ for (i = 0; i < 64; i++) {
+ if (iface->drv_flags & (1LLU << i)) {
+ ret = os_snprintf(pos, end - pos, "%s\n",
+ driver_flag_to_string(1LLU << i));
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
+static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+ struct sockaddr_storage *from,
+ socklen_t fromlen)
+{
+ int reply_len, res;
+