/* * wlantest control interface * Copyright (c) 2010-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include #include "utils/common.h" #include "utils/eloop.h" #include "common/defs.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "wlantest.h" #include "wlantest_ctrl.h" static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr, size_t *len) { u8 *pos = buf; while (pos + 8 <= buf + buflen) { enum wlantest_ctrl_attr a; size_t alen; a = WPA_GET_BE32(pos); pos += 4; alen = WPA_GET_BE32(pos); pos += 4; if (pos + alen > buf + buflen) { wpa_printf(MSG_DEBUG, "Invalid control message " "attribute"); return NULL; } if (a == attr) { *len = alen; return pos; } pos += alen; } return NULL; } static u8 * attr_get_macaddr(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr) { u8 *addr; size_t addr_len; addr = attr_get(buf, buflen, attr, &addr_len); if (addr && addr_len != ETH_ALEN) addr = NULL; return addr; } static int attr_get_int(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr) { u8 *pos; size_t len; pos = attr_get(buf, buflen, attr, &len); if (pos == NULL || len != 4) return -1; return WPA_GET_BE32(pos); } static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr, const char *str) { size_t len = os_strlen(str); if (pos == NULL || end - pos < 8 + len) return NULL; WPA_PUT_BE32(pos, attr); pos += 4; WPA_PUT_BE32(pos, len); pos += 4; os_memcpy(pos, str, len); pos += len; return pos; } static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr, u32 val) { if (pos == NULL || end - pos < 12) return NULL; WPA_PUT_BE32(pos, attr); pos += 4; WPA_PUT_BE32(pos, 4); pos += 4; WPA_PUT_BE32(pos, val); pos += 4; return pos; } static void ctrl_disconnect(struct wlantest *wt, int sock) { int i; wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d", sock); for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) { if (wt->ctrl_socks[i] == sock) { close(wt->ctrl_socks[i]); eloop_unregister_read_sock(wt->ctrl_socks[i]); wt->ctrl_socks[i] = -1; break; } } } static void ctrl_send(struct wlantest *wt, int sock, const u8 *buf, size_t len) { if (send(sock, buf, len, 0) < 0) { wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno)); ctrl_disconnect(wt, sock); } } static void ctrl_send_simple(struct wlantest *wt, int sock, enum wlantest_ctrl_cmd cmd) { u8 buf[4]; WPA_PUT_BE32(buf, cmd); ctrl_send(wt, sock, buf, sizeof(buf)); } static struct wlantest_bss * ctrl_get_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { struct wlantest_bss *bss; u8 *pos; size_t len; pos = attr_get(cmd, clen, WLANTEST_ATTR_BSSID, &len); if (pos == NULL || len != ETH_ALEN) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return NULL; } bss = bss_find(wt, pos); if (bss == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return NULL; } return bss; } static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen, struct wlantest_bss *bss) { struct wlantest_sta *sta; u8 *pos; size_t len; if (bss == NULL) return NULL; pos = attr_get(cmd, clen, WLANTEST_ATTR_STA_ADDR, &len); if (pos == NULL || len != ETH_ALEN) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return NULL; } sta = sta_find(bss, pos); if (sta == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return NULL; } return sta; } static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock, u8 *cmd, size_t clen, struct wlantest_bss *bss) { struct wlantest_sta *sta; u8 *pos; size_t len; if (bss == NULL) return NULL; pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len); if (pos == NULL || len != ETH_ALEN) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return NULL; } sta = sta_find(bss, pos); if (sta == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return NULL; } return sta; } static void ctrl_list_bss(struct wlantest *wt, int sock) { u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len; struct wlantest_bss *bss; pos = buf; WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID); pos += 4; len = pos; /* to be filled */ pos += 4; dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) { if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN) break; os_memcpy(pos, bss->bssid, ETH_ALEN); pos += ETH_ALEN; } WPA_PUT_BE32(len, pos - len - 4); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_list_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len; struct wlantest_bss *bss; struct wlantest_sta *sta; bss = ctrl_get_bss(wt, sock, cmd, clen); if (bss == NULL) return; pos = buf; WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR); pos += 4; len = pos; /* to be filled */ pos += 4; dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) { if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN) break; os_memcpy(pos, sta->addr, ETH_ALEN); pos += ETH_ALEN; } WPA_PUT_BE32(len, pos - len - 4); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_flush(struct wlantest *wt, int sock) { wpa_printf(MSG_DEBUG, "Drop all collected BSS data"); bss_flush(wt); ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); } static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { struct wlantest_bss *bss; struct wlantest_sta *sta; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); if (sta == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } os_memset(sta->counters, 0, sizeof(sta->counters)); os_memset(sta->tx_tid, 0, sizeof(sta->tx_tid)); os_memset(sta->rx_tid, 0, sizeof(sta->rx_tid)); ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); } static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { struct wlantest_bss *bss; bss = ctrl_get_bss(wt, sock, cmd, clen); if (bss == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } os_memset(bss->counters, 0, sizeof(bss->counters)); ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); } static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { struct wlantest_bss *bss; struct wlantest_sta *sta; struct wlantest_sta *sta2; struct wlantest_tdls *tdls; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss); if (sta == NULL || sta2 == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) { if ((tdls->init == sta && tdls->resp == sta2) || (tdls->init == sta2 && tdls->resp == sta)) os_memset(tdls->counters, 0, sizeof(tdls->counters)); } ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); } static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; struct wlantest_sta *sta; u32 counter; u8 buf[4 + 12], *end, *pos; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); if (sta == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_COUNTER, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } counter = WPA_GET_BE32(addr); if (counter >= NUM_WLANTEST_STA_COUNTER) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER, sta->counters[counter]); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; u32 counter; u8 buf[4 + 12], *end, *pos; bss = ctrl_get_bss(wt, sock, cmd, clen); if (bss == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_COUNTER, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } counter = WPA_GET_BE32(addr); if (counter >= NUM_WLANTEST_BSS_COUNTER) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER, bss->counters[counter]); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; struct wlantest_sta *sta; struct wlantest_sta *sta2; struct wlantest_tdls *tdls; u32 counter; u8 buf[4 + 12], *end, *pos; int found = 0; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss); if (sta == NULL || sta2 == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } counter = WPA_GET_BE32(addr); if (counter >= NUM_WLANTEST_TDLS_COUNTER) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) { if (tdls->init == sta && tdls->resp == sta2) { found = 1; break; } } if (!found) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER, tdls->counters[counter]); ctrl_send(wt, sock, buf, pos - buf); } static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, int stype) { os_memset(mgmt, 0, 24); mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); if (sender_ap) { if (sta) os_memcpy(mgmt->da, sta->addr, ETH_ALEN); else os_memset(mgmt->da, 0xff, ETH_ALEN); os_memcpy(mgmt->sa, bss->bssid, ETH_ALEN); } else { os_memcpy(mgmt->da, bss->bssid, ETH_ALEN); os_memcpy(mgmt->sa, sta->addr, ETH_ALEN); } os_memcpy(mgmt->bssid, bss->bssid, ETH_ALEN); } static int ctrl_inject_auth(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { struct ieee80211_mgmt mgmt; if (prot != WLANTEST_INJECT_NORMAL && prot != WLANTEST_INJECT_UNPROTECTED) return -1; /* Authentication frame is never protected */ if (sta == NULL) return -1; /* No broadcast Authentication frames */ if (sender_ap) wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr)); else wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_AUTH); mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); mgmt.u.auth.auth_transaction = host_to_le16(1); mgmt.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 6, WLANTEST_INJECT_UNPROTECTED); } static int ctrl_inject_assocreq(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { u8 *buf; struct ieee80211_mgmt *mgmt; int ret; if (prot != WLANTEST_INJECT_NORMAL && prot != WLANTEST_INJECT_UNPROTECTED) return -1; /* Association Request frame is never protected */ if (sta == NULL) return -1; /* No broadcast Association Request frames */ if (sender_ap) return -1; /* No Association Request frame sent by AP */ if (sta->assocreq_ies == NULL) { wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association " "Request available for " MACSTR, MAC2STR(sta->addr)); return -1; } wpa_printf(MSG_INFO, "INJECT: AssocReq " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len); if (buf == NULL) return -1; mgmt = (struct ieee80211_mgmt *) buf; build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ASSOC_REQ); mgmt->u.assoc_req.capab_info = host_to_le16(sta->assocreq_capab_info); mgmt->u.assoc_req.listen_interval = host_to_le16(sta->assocreq_listen_int); os_memcpy(mgmt->u.assoc_req.variable, sta->assocreq_ies, sta->assocreq_ies_len); ret = wlantest_inject(wt, bss, sta, buf, 24 + 4 + sta->assocreq_ies_len, WLANTEST_INJECT_UNPROTECTED); os_free(buf); return ret; } static int ctrl_inject_reassocreq(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { u8 *buf; struct ieee80211_mgmt *mgmt; int ret; if (prot != WLANTEST_INJECT_NORMAL && prot != WLANTEST_INJECT_UNPROTECTED) return -1; /* Reassociation Request frame is never protected */ if (sta == NULL) return -1; /* No broadcast Reassociation Request frames */ if (sender_ap) return -1; /* No Reassociation Request frame sent by AP */ if (sta->assocreq_ies == NULL) { wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association " "Request available for " MACSTR, MAC2STR(sta->addr)); return -1; } wpa_printf(MSG_INFO, "INJECT: ReassocReq " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len); if (buf == NULL) return -1; mgmt = (struct ieee80211_mgmt *) buf; build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_REASSOC_REQ); mgmt->u.reassoc_req.capab_info = host_to_le16(sta->assocreq_capab_info); mgmt->u.reassoc_req.listen_interval = host_to_le16(sta->assocreq_listen_int); os_memcpy(mgmt->u.reassoc_req.current_ap, bss->bssid, ETH_ALEN); os_memcpy(mgmt->u.reassoc_req.variable, sta->assocreq_ies, sta->assocreq_ies_len); ret = wlantest_inject(wt, bss, sta, buf, 24 + 10 + sta->assocreq_ies_len, WLANTEST_INJECT_UNPROTECTED); os_free(buf); return ret; } static int ctrl_inject_deauth(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { struct ieee80211_mgmt mgmt; if (sender_ap) { if (sta) wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr)); else wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> broadcast", MAC2STR(bss->bssid)); } else wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DEAUTH); mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED); return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot); } static int ctrl_inject_disassoc(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { struct ieee80211_mgmt mgmt; if (sender_ap) { if (sta) wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr)); else wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> broadcast", MAC2STR(bss->bssid)); } else wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DISASSOC); mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED); return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot); } static int ctrl_inject_saqueryreq(struct wlantest *wt, struct wlantest_bss *bss, struct wlantest_sta *sta, int sender_ap, enum wlantest_inject_protection prot) { struct ieee80211_mgmt mgmt; if (sta == NULL) return -1; /* No broadcast SA Query frames */ if (sender_ap) wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> " MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr)); else wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> " MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid)); build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ACTION); mgmt.u.action.category = WLAN_ACTION_SA_QUERY; mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; mgmt.u.action.u.sa_query_req.trans_id[0] = 0x12; mgmt.u.action.u.sa_query_req.trans_id[1] = 0x34; os_memcpy(sender_ap ? sta->ap_sa_query_tr : sta->sta_sa_query_tr, mgmt.u.action.u.sa_query_req.trans_id, WLAN_SA_QUERY_TR_ID_LEN); return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 4, prot); } static void ctrl_inject(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *bssid, *sta_addr; struct wlantest_bss *bss; struct wlantest_sta *sta; int frame, sender_ap, prot; int ret = 0; bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID); sta_addr = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_STA_ADDR); frame = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_FRAME); sender_ap = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_SENDER_AP); if (sender_ap < 0) sender_ap = 0; prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION); if (bssid == NULL || sta_addr == NULL || frame < 0 || prot < 0) { wpa_printf(MSG_INFO, "Invalid inject command parameters"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } bss = bss_find(wt, bssid); if (bss == NULL) { wpa_printf(MSG_INFO, "BSS not found for inject command"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } if (is_broadcast_ether_addr(sta_addr)) { if (!sender_ap) { wpa_printf(MSG_INFO, "Invalid broadcast inject " "command without sender_ap set"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } sta = NULL; } else { sta = sta_find(bss, sta_addr); if (sta == NULL) { wpa_printf(MSG_INFO, "Station not found for inject " "command"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } } switch (frame) { case WLANTEST_FRAME_AUTH: ret = ctrl_inject_auth(wt, bss, sta, sender_ap, prot); break; case WLANTEST_FRAME_ASSOCREQ: ret = ctrl_inject_assocreq(wt, bss, sta, sender_ap, prot); break; case WLANTEST_FRAME_REASSOCREQ: ret = ctrl_inject_reassocreq(wt, bss, sta, sender_ap, prot); break; case WLANTEST_FRAME_DEAUTH: ret = ctrl_inject_deauth(wt, bss, sta, sender_ap, prot); break; case WLANTEST_FRAME_DISASSOC: ret = ctrl_inject_disassoc(wt, bss, sta, sender_ap, prot); break; case WLANTEST_FRAME_SAQUERYREQ: ret = ctrl_inject_saqueryreq(wt, bss, sta, sender_ap, prot); break; default: wpa_printf(MSG_INFO, "Unsupported inject command frame %d", frame); ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } if (ret) wpa_printf(MSG_INFO, "Failed to inject frame"); else wpa_printf(MSG_INFO, "Frame injected successfully"); ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS : WLANTEST_CTRL_FAILURE); } static void ctrl_version(struct wlantest *wt, int sock) { u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos; pos = buf; WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_str(pos, buf + sizeof(buf), WLANTEST_ATTR_VERSION, VERSION_STR); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_add_passphrase(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *passphrase; size_t len; struct wlantest_passphrase *p, *pa; u8 *bssid; passphrase = attr_get(cmd, clen, WLANTEST_ATTR_PASSPHRASE, &len); if (passphrase == NULL) { u8 *wepkey; char *key; enum wlantest_ctrl_cmd res; wepkey = attr_get(cmd, clen, WLANTEST_ATTR_WEPKEY, &len); if (wepkey == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } key = os_zalloc(len + 1); if (key == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } os_memcpy(key, wepkey, len); if (add_wep(wt, key) < 0) res = WLANTEST_CTRL_FAILURE; else res = WLANTEST_CTRL_SUCCESS; os_free(key); ctrl_send_simple(wt, sock, res); return; } if (len < 8 || len > 63) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } p = os_zalloc(sizeof(*p)); if (p == NULL) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } os_memcpy(p->passphrase, passphrase, len); wpa_printf(MSG_INFO, "Add passphrase '%s'", p->passphrase); bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID); if (bssid) { os_memcpy(p->bssid, bssid, ETH_ALEN); wpa_printf(MSG_INFO, "Limit passphrase for BSSID " MACSTR, MAC2STR(p->bssid)); } dl_list_for_each(pa, &wt->passphrase, struct wlantest_passphrase, list) { if (os_strcmp(p->passphrase, pa->passphrase) == 0 && ether_addr_equal(p->bssid, pa->bssid)) { wpa_printf(MSG_INFO, "Passphrase was already known"); os_free(p); p = NULL; break; } } if (p) { struct wlantest_bss *bss; dl_list_add(&wt->passphrase, &p->list); dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) { if (bssid && !ether_addr_equal(p->bssid, bss->bssid)) continue; bss_add_pmk_from_passphrase(bss, p->passphrase); } } ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); } static void info_print_proto(char *buf, size_t len, int proto) { char *pos, *end; if (proto == 0) { os_snprintf(buf, len, "OPEN"); return; } pos = buf; end = buf + len; if (proto & WPA_PROTO_WPA) pos += os_snprintf(pos, end - pos, "%sWPA", pos == buf ? "" : " "); if (proto & WPA_PROTO_RSN) pos += os_snprintf(pos, end - pos, "%sWPA2", pos == buf ? "" : " "); } static void info_print_cipher(char *buf, size_t len, int cipher) { char *pos, *end; if (cipher == 0) { os_snprintf(buf, len, "N/A"); return; } pos = buf; end = buf + len; if (cipher & WPA_CIPHER_NONE) pos += os_snprintf(pos, end - pos, "%sNONE", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_WEP40) pos += os_snprintf(pos, end - pos, "%sWEP40", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_WEP104) pos += os_snprintf(pos, end - pos, "%sWEP104", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_TKIP) pos += os_snprintf(pos, end - pos, "%sTKIP", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_CCMP) pos += os_snprintf(pos, end - pos, "%sCCMP", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_AES_128_CMAC) pos += os_snprintf(pos, end - pos, "%sBIP", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_BIP_GMAC_128) pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-128", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_BIP_GMAC_256) pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-256", pos == buf ? "" : " "); if (cipher & WPA_CIPHER_BIP_CMAC_256) pos += os_snprintf(pos, end - pos, "%sBIP-CMAC-256", pos == buf ? "" : " "); } static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt) { char *pos, *end; if (key_mgmt == 0) { os_snprintf(buf, len, "N/A"); return; } pos = buf; end = buf + len; if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) pos += os_snprintf(pos, end - pos, "%sEAP", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_PSK) pos += os_snprintf(pos, end - pos, "%sPSK", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_WPA_NONE) pos += os_snprintf(pos, end - pos, "%sWPA-NONE", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) pos += os_snprintf(pos, end - pos, "%sFT-EAP", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_FT_PSK) pos += os_snprintf(pos, end - pos, "%sFT-PSK", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) pos += os_snprintf(pos, end - pos, "%sEAP-SHA256", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) pos += os_snprintf(pos, end - pos, "%sPSK-SHA256", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192", pos == buf ? "" : " "); if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) pos += os_snprintf(pos, end - pos, "%sEAP-SHA384", pos == buf ? "" : " "); } static void info_print_rsn_capab(char *buf, size_t len, int capab) { char *pos, *end; pos = buf; end = buf + len; if (capab & WPA_CAPABILITY_PREAUTH) pos += os_snprintf(pos, end - pos, "%sPREAUTH", pos == buf ? "" : " "); if (capab & WPA_CAPABILITY_NO_PAIRWISE) pos += os_snprintf(pos, end - pos, "%sNO_PAIRWISE", pos == buf ? "" : " "); if (capab & WPA_CAPABILITY_MFPR) pos += os_snprintf(pos, end - pos, "%sMFPR", pos == buf ? "" : " "); if (capab & WPA_CAPABILITY_MFPC) pos += os_snprintf(pos, end - pos, "%sMFPC", pos == buf ? "" : " "); if (capab & WPA_CAPABILITY_PEERKEY_ENABLED) pos += os_snprintf(pos, end - pos, "%sPEERKEY", pos == buf ? "" : " "); if (capab & WPA_CAPABILITY_OCVC) pos += os_snprintf(pos, end - pos, "%sOCVC", pos == buf ? "" : " "); } static void info_print_state(char *buf, size_t len, int state) { switch (state) { case STATE1: os_strlcpy(buf, "NOT-AUTH", len); break; case STATE2: os_strlcpy(buf, "AUTH", len); break; case STATE3: os_strlcpy(buf, "AUTH+ASSOC", len); break; } } static void info_print_gtk(char *buf, size_t len, struct wlantest_sta *sta) { size_t pos; pos = os_snprintf(buf, len, "IDX=%d,GTK=", sta->gtk_idx); wpa_snprintf_hex(buf + pos, len - pos, sta->gtk, sta->gtk_len); } static void ctrl_info_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; struct wlantest_sta *sta; enum wlantest_sta_info info; u8 buf[4 + 108], *end, *pos; char resp[100]; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); if (sta == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_INFO, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } info = WPA_GET_BE32(addr); resp[0] = '\0'; switch (info) { case WLANTEST_STA_INFO_PROTO: info_print_proto(resp, sizeof(resp), sta->proto); break; case WLANTEST_STA_INFO_PAIRWISE: info_print_cipher(resp, sizeof(resp), sta->pairwise_cipher); break; case WLANTEST_STA_INFO_KEY_MGMT: info_print_key_mgmt(resp, sizeof(resp), sta->key_mgmt); break; case WLANTEST_STA_INFO_RSN_CAPAB: info_print_rsn_capab(resp, sizeof(resp), sta->rsn_capab); break; case WLANTEST_STA_INFO_STATE: info_print_state(resp, sizeof(resp), sta->state); break; case WLANTEST_STA_INFO_GTK: info_print_gtk(resp, sizeof(resp), sta); break; default: ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_info_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; enum wlantest_bss_info info; u8 buf[4 + 108], *end, *pos; char resp[100]; bss = ctrl_get_bss(wt, sock, cmd, clen); if (bss == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_INFO, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } info = WPA_GET_BE32(addr); resp[0] = '\0'; switch (info) { case WLANTEST_BSS_INFO_PROTO: info_print_proto(resp, sizeof(resp), bss->proto); break; case WLANTEST_BSS_INFO_PAIRWISE: info_print_cipher(resp, sizeof(resp), bss->pairwise_cipher); break; case WLANTEST_BSS_INFO_GROUP: info_print_cipher(resp, sizeof(resp), bss->group_cipher); break; case WLANTEST_BSS_INFO_GROUP_MGMT: info_print_cipher(resp, sizeof(resp), bss->mgmt_group_cipher); break; case WLANTEST_BSS_INFO_KEY_MGMT: info_print_key_mgmt(resp, sizeof(resp), bss->key_mgmt); break; case WLANTEST_BSS_INFO_RSN_CAPAB: info_print_rsn_capab(resp, sizeof(resp), bss->rsn_capab); break; default: ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { struct wlantest_bss *bss; struct wlantest_sta *sta; u8 *bssid, *sta_addr; int prot; u8 *frame; size_t frame_len; int ret = 0; struct ieee80211_hdr *hdr; u16 fc; frame = attr_get(cmd, clen, WLANTEST_ATTR_FRAME, &frame_len); prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION); if (frame == NULL || frame_len < 24 || prot < 0) { wpa_printf(MSG_INFO, "Invalid send command parameters"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } hdr = (struct ieee80211_hdr *) frame; fc = le_to_host16(hdr->frame_control); switch (WLAN_FC_GET_TYPE(fc)) { case WLAN_FC_TYPE_MGMT: bssid = hdr->addr3; if (ether_addr_equal(hdr->addr2, hdr->addr3)) sta_addr = hdr->addr1; else sta_addr = hdr->addr2; break; case WLAN_FC_TYPE_DATA: switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) { case 0: bssid = hdr->addr3; sta_addr = hdr->addr2; break; case WLAN_FC_TODS: bssid = hdr->addr1; sta_addr = hdr->addr2; break; case WLAN_FC_FROMDS: bssid = hdr->addr2; sta_addr = hdr->addr1; break; default: wpa_printf(MSG_INFO, "Unsupported inject frame"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } break; default: wpa_printf(MSG_INFO, "Unsupported inject frame"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } bss = bss_find(wt, bssid); if (bss == NULL && prot != WLANTEST_INJECT_UNPROTECTED) { wpa_printf(MSG_INFO, "Unknown BSSID"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } if (bss) sta = sta_find(bss, sta_addr); else sta = NULL; if (sta == NULL && prot != WLANTEST_INJECT_UNPROTECTED) { wpa_printf(MSG_INFO, "Unknown STA address"); ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE); return; } ret = wlantest_inject(wt, bss, sta, frame, frame_len, prot); if (ret) wpa_printf(MSG_INFO, "Failed to inject frame"); else wpa_printf(MSG_INFO, "Frame injected successfully"); ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS : WLANTEST_CTRL_FAILURE); } static void ctrl_relog(struct wlantest *wt, int sock) { int res = wlantest_relog(wt); ctrl_send_simple(wt, sock, res ? WLANTEST_CTRL_FAILURE : WLANTEST_CTRL_SUCCESS); } static void ctrl_get_tx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; struct wlantest_sta *sta; u32 counter; u8 buf[4 + 12], *end, *pos; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); if (sta == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } counter = WPA_GET_BE32(addr); if (counter >= 16 + 1) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER, sta->tx_tid[counter]); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_get_rx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen) { u8 *addr; size_t addr_len; struct wlantest_bss *bss; struct wlantest_sta *sta; u32 counter; u8 buf[4 + 12], *end, *pos; bss = ctrl_get_bss(wt, sock, cmd, clen); sta = ctrl_get_sta(wt, sock, cmd, clen, bss); if (sta == NULL) return; addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len); if (addr == NULL || addr_len != 4) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } counter = WPA_GET_BE32(addr); if (counter >= 16 + 1) { ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD); return; } pos = buf; end = buf + sizeof(buf); WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS); pos += 4; pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER, sta->rx_tid[counter]); ctrl_send(wt, sock, buf, pos - buf); } static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx) { struct wlantest *wt = eloop_ctx; u8 buf[WLANTEST_CTRL_MAX_CMD_LEN]; int len; enum wlantest_ctrl_cmd cmd; wpa_printf(MSG_EXCESSIVE, "New control interface message from %d", sock); len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno)); ctrl_disconnect(wt, sock); return; } if (len == 0) { ctrl_disconnect(wt, sock); return; } if (len < 4) { wpa_printf(MSG_INFO, "Too short control interface command " "from %d", sock); ctrl_disconnect(wt, sock); return; } cmd = WPA_GET_BE32(buf); wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d", cmd, sock); switch (cmd) { case WLANTEST_CTRL_PING: ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); break; case WLANTEST_CTRL_TERMINATE: ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS); eloop_terminate(); break; case WLANTEST_CTRL_LIST_BSS: ctrl_list_bss(wt, sock); break; case WLANTEST_CTRL_LIST_STA: ctrl_list_sta(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_FLUSH: ctrl_flush(wt, sock); break; case WLANTEST_CTRL_CLEAR_STA_COUNTERS: ctrl_clear_sta_counters(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_CLEAR_BSS_COUNTERS: ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS: ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_GET_STA_COUNTER: ctrl_get_sta_counter(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_GET_BSS_COUNTER: ctrl_get_bss_counter(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_GET_TDLS_COUNTER: ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_INJECT: ctrl_inject(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_VERSION: ctrl_version(wt, sock); break; case WLANTEST_CTRL_ADD_PASSPHRASE: ctrl_add_passphrase(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_INFO_STA: ctrl_info_sta(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_INFO_BSS: ctrl_info_bss(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_SEND: ctrl_send_(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_RELOG: ctrl_relog(wt, sock); break; case WLANTEST_CTRL_GET_TX_TID: ctrl_get_tx_tid(wt, sock, buf + 4, len - 4); break; case WLANTEST_CTRL_GET_RX_TID: ctrl_get_rx_tid(wt, sock, buf + 4, len - 4); break; default: ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD); break; } } static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx) { struct wlantest *wt = eloop_ctx; int conn, i; conn = accept(sock, NULL, NULL); if (conn < 0) { wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno)); return; } wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn); for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) { if (wt->ctrl_socks[i] < 0) break; } if (i == MAX_CTRL_CONNECTIONS) { wpa_printf(MSG_INFO, "No room for new control connection"); close(conn); return; } wt->ctrl_socks[i] = conn; eloop_register_read_sock(conn, ctrl_read, wt, NULL); } int ctrl_init(struct wlantest *wt) { struct sockaddr_un addr; wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); if (wt->ctrl_sock < 0) { wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } os_memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME, sizeof(addr.sun_path) - 1); if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); close(wt->ctrl_sock); wt->ctrl_sock = -1; return -1; } if (listen(wt->ctrl_sock, 5) < 0) { wpa_printf(MSG_ERROR, "listen: %s", strerror(errno)); close(wt->ctrl_sock); wt->ctrl_sock = -1; return -1; } if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) { close(wt->ctrl_sock); wt->ctrl_sock = -1; return -1; } return 0; } void ctrl_deinit(struct wlantest *wt) { int i; if (wt->ctrl_sock < 0) return; for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) { if (wt->ctrl_socks[i] >= 0) { close(wt->ctrl_socks[i]); eloop_unregister_read_sock(wt->ctrl_socks[i]); wt->ctrl_socks[i] = -1; } } eloop_unregister_read_sock(wt->ctrl_sock); close(wt->ctrl_sock); wt->ctrl_sock = -1; }