Home | History | Annotate | Download | only in ap
      1 /*
      2  * Control interface for shared AP commands
      3  * Copyright (c) 2004-2013, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "utils/includes.h"
     10 
     11 #include "utils/common.h"
     12 #include "common/ieee802_11_defs.h"
     13 #include "eapol_auth/eapol_auth_sm.h"
     14 #include "hostapd.h"
     15 #include "ieee802_1x.h"
     16 #include "wpa_auth.h"
     17 #include "ieee802_11.h"
     18 #include "sta_info.h"
     19 #include "wps_hostapd.h"
     20 #include "p2p_hostapd.h"
     21 #include "ctrl_iface_ap.h"
     22 #include "ap_drv_ops.h"
     23 
     24 
     25 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
     26 				 struct sta_info *sta,
     27 				 char *buf, size_t buflen)
     28 {
     29 	struct hostap_sta_driver_data data;
     30 	int ret;
     31 
     32 	if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
     33 		return 0;
     34 
     35 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
     36 			  "rx_bytes=%lu\ntx_bytes=%lu\n",
     37 			  data.rx_packets, data.tx_packets,
     38 			  data.rx_bytes, data.tx_bytes);
     39 	if (ret < 0 || (size_t) ret >= buflen)
     40 		return 0;
     41 	return ret;
     42 }
     43 
     44 
     45 static int hostapd_get_sta_conn_time(struct sta_info *sta,
     46 				     char *buf, size_t buflen)
     47 {
     48 	struct os_reltime age;
     49 	int ret;
     50 
     51 	if (!sta->connected_time.sec)
     52 		return 0;
     53 
     54 	os_reltime_age(&sta->connected_time, &age);
     55 
     56 	ret = os_snprintf(buf, buflen, "connected_time=%u\n",
     57 			  (unsigned int) age.sec);
     58 	if (ret < 0 || (size_t) ret >= buflen)
     59 		return 0;
     60 	return ret;
     61 }
     62 
     63 
     64 static const char * timeout_next_str(int val)
     65 {
     66 	switch (val) {
     67 	case STA_NULLFUNC:
     68 		return "NULLFUNC POLL";
     69 	case STA_DISASSOC:
     70 		return "DISASSOC";
     71 	case STA_DEAUTH:
     72 		return "DEAUTH";
     73 	case STA_REMOVE:
     74 		return "REMOVE";
     75 	case STA_DISASSOC_FROM_CLI:
     76 		return "DISASSOC_FROM_CLI";
     77 	}
     78 
     79 	return "?";
     80 }
     81 
     82 
     83 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
     84 				      struct sta_info *sta,
     85 				      char *buf, size_t buflen)
     86 {
     87 	int len, res, ret, i;
     88 
     89 	if (!sta)
     90 		return 0;
     91 
     92 	len = 0;
     93 	ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
     94 			  MAC2STR(sta->addr));
     95 	if (ret < 0 || (size_t) ret >= buflen - len)
     96 		return len;
     97 	len += ret;
     98 
     99 	ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
    100 	if (ret < 0)
    101 		return len;
    102 	len += ret;
    103 
    104 	ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
    105 			  "listen_interval=%d\nsupported_rates=",
    106 			  sta->aid, sta->capability, sta->listen_interval);
    107 	if (ret < 0 || (size_t) ret >= buflen - len)
    108 		return len;
    109 	len += ret;
    110 
    111 	for (i = 0; i < sta->supported_rates_len; i++) {
    112 		ret = os_snprintf(buf + len, buflen - len, "%02x%s",
    113 				  sta->supported_rates[i],
    114 				  i + 1 < sta->supported_rates_len ? " " : "");
    115 		if (ret < 0 || (size_t) ret >= buflen - len)
    116 			return len;
    117 		len += ret;
    118 	}
    119 
    120 	ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
    121 			  timeout_next_str(sta->timeout_next));
    122 	if (ret < 0 || (size_t) ret >= buflen - len)
    123 		return len;
    124 	len += ret;
    125 
    126 	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
    127 	if (res >= 0)
    128 		len += res;
    129 	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
    130 	if (res >= 0)
    131 		len += res;
    132 	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
    133 	if (res >= 0)
    134 		len += res;
    135 	res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
    136 				      buflen - len);
    137 	if (res >= 0)
    138 		len += res;
    139 	res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
    140 	if (res >= 0)
    141 		len += res;
    142 
    143 	len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
    144 	len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
    145 
    146 	return len;
    147 }
    148 
    149 
    150 int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
    151 				 char *buf, size_t buflen)
    152 {
    153 	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
    154 }
    155 
    156 
    157 int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
    158 			   char *buf, size_t buflen)
    159 {
    160 	u8 addr[ETH_ALEN];
    161 	int ret;
    162 	const char *pos;
    163 	struct sta_info *sta;
    164 
    165 	if (hwaddr_aton(txtaddr, addr)) {
    166 		ret = os_snprintf(buf, buflen, "FAIL\n");
    167 		if (ret < 0 || (size_t) ret >= buflen)
    168 			return 0;
    169 		return ret;
    170 	}
    171 
    172 	sta = ap_get_sta(hapd, addr);
    173 	if (sta == NULL)
    174 		return -1;
    175 
    176 	pos = os_strchr(txtaddr, ' ');
    177 	if (pos) {
    178 		pos++;
    179 
    180 #ifdef HOSTAPD_DUMP_STATE
    181 		if (os_strcmp(pos, "eapol") == 0) {
    182 			if (sta->eapol_sm == NULL)
    183 				return -1;
    184 			return eapol_auth_dump_state(sta->eapol_sm, buf,
    185 						     buflen);
    186 		}
    187 #endif /* HOSTAPD_DUMP_STATE */
    188 
    189 		return -1;
    190 	}
    191 
    192 	return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
    193 }
    194 
    195 
    196 int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
    197 				char *buf, size_t buflen)
    198 {
    199 	u8 addr[ETH_ALEN];
    200 	struct sta_info *sta;
    201 	int ret;
    202 
    203 	if (hwaddr_aton(txtaddr, addr) ||
    204 	    (sta = ap_get_sta(hapd, addr)) == NULL) {
    205 		ret = os_snprintf(buf, buflen, "FAIL\n");
    206 		if (ret < 0 || (size_t) ret >= buflen)
    207 			return 0;
    208 		return ret;
    209 	}
    210 
    211 	if (!sta->next)
    212 		return 0;
    213 
    214 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
    215 }
    216 
    217 
    218 #ifdef CONFIG_P2P_MANAGER
    219 static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
    220 				  u8 minor_reason_code, const u8 *addr)
    221 {
    222 	struct ieee80211_mgmt *mgmt;
    223 	int ret;
    224 	u8 *pos;
    225 
    226 	if (hapd->driver->send_frame == NULL)
    227 		return -1;
    228 
    229 	mgmt = os_zalloc(sizeof(*mgmt) + 100);
    230 	if (mgmt == NULL)
    231 		return -1;
    232 
    233 	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
    234 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
    235 		" with minor reason code %u (stype=%u (%s))",
    236 		MAC2STR(addr), minor_reason_code, stype,
    237 		fc2str(mgmt->frame_control));
    238 
    239 	os_memcpy(mgmt->da, addr, ETH_ALEN);
    240 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
    241 	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
    242 	if (stype == WLAN_FC_STYPE_DEAUTH) {
    243 		mgmt->u.deauth.reason_code =
    244 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
    245 		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
    246 	} else {
    247 		mgmt->u.disassoc.reason_code =
    248 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
    249 		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
    250 	}
    251 
    252 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
    253 	*pos++ = 4 + 3 + 1;
    254 	WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
    255 	pos += 4;
    256 
    257 	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
    258 	WPA_PUT_LE16(pos, 1);
    259 	pos += 2;
    260 	*pos++ = minor_reason_code;
    261 
    262 	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
    263 				       pos - (u8 *) mgmt, 1);
    264 	os_free(mgmt);
    265 
    266 	return ret < 0 ? -1 : 0;
    267 }
    268 #endif /* CONFIG_P2P_MANAGER */
    269 
    270 
    271 int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
    272 				      const char *txtaddr)
    273 {
    274 	u8 addr[ETH_ALEN];
    275 	struct sta_info *sta;
    276 	const char *pos;
    277 	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
    278 
    279 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
    280 		txtaddr);
    281 
    282 	if (hwaddr_aton(txtaddr, addr))
    283 		return -1;
    284 
    285 	pos = os_strstr(txtaddr, " reason=");
    286 	if (pos)
    287 		reason = atoi(pos + 8);
    288 
    289 	pos = os_strstr(txtaddr, " test=");
    290 	if (pos) {
    291 		struct ieee80211_mgmt mgmt;
    292 		int encrypt;
    293 		if (hapd->driver->send_frame == NULL)
    294 			return -1;
    295 		pos += 6;
    296 		encrypt = atoi(pos);
    297 		os_memset(&mgmt, 0, sizeof(mgmt));
    298 		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
    299 						  WLAN_FC_STYPE_DEAUTH);
    300 		os_memcpy(mgmt.da, addr, ETH_ALEN);
    301 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
    302 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
    303 		mgmt.u.deauth.reason_code = host_to_le16(reason);
    304 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
    305 					     IEEE80211_HDRLEN +
    306 					     sizeof(mgmt.u.deauth),
    307 					     encrypt) < 0)
    308 			return -1;
    309 		return 0;
    310 	}
    311 
    312 #ifdef CONFIG_P2P_MANAGER
    313 	pos = os_strstr(txtaddr, " p2p=");
    314 	if (pos) {
    315 		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
    316 					      atoi(pos + 5), addr);
    317 	}
    318 #endif /* CONFIG_P2P_MANAGER */
    319 
    320 	hostapd_drv_sta_deauth(hapd, addr, reason);
    321 	sta = ap_get_sta(hapd, addr);
    322 	if (sta)
    323 		ap_sta_deauthenticate(hapd, sta, reason);
    324 	else if (addr[0] == 0xff)
    325 		hostapd_free_stas(hapd);
    326 
    327 	return 0;
    328 }
    329 
    330 
    331 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
    332 				    const char *txtaddr)
    333 {
    334 	u8 addr[ETH_ALEN];
    335 	struct sta_info *sta;
    336 	const char *pos;
    337 	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
    338 
    339 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
    340 		txtaddr);
    341 
    342 	if (hwaddr_aton(txtaddr, addr))
    343 		return -1;
    344 
    345 	pos = os_strstr(txtaddr, " reason=");
    346 	if (pos)
    347 		reason = atoi(pos + 8);
    348 
    349 	pos = os_strstr(txtaddr, " test=");
    350 	if (pos) {
    351 		struct ieee80211_mgmt mgmt;
    352 		int encrypt;
    353 		if (hapd->driver->send_frame == NULL)
    354 			return -1;
    355 		pos += 6;
    356 		encrypt = atoi(pos);
    357 		os_memset(&mgmt, 0, sizeof(mgmt));
    358 		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
    359 						  WLAN_FC_STYPE_DISASSOC);
    360 		os_memcpy(mgmt.da, addr, ETH_ALEN);
    361 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
    362 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
    363 		mgmt.u.disassoc.reason_code = host_to_le16(reason);
    364 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
    365 					     IEEE80211_HDRLEN +
    366 					     sizeof(mgmt.u.deauth),
    367 					     encrypt) < 0)
    368 			return -1;
    369 		return 0;
    370 	}
    371 
    372 #ifdef CONFIG_P2P_MANAGER
    373 	pos = os_strstr(txtaddr, " p2p=");
    374 	if (pos) {
    375 		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
    376 					      atoi(pos + 5), addr);
    377 	}
    378 #endif /* CONFIG_P2P_MANAGER */
    379 
    380 	hostapd_drv_sta_disassoc(hapd, addr, reason);
    381 	sta = ap_get_sta(hapd, addr);
    382 	if (sta)
    383 		ap_sta_disassociate(hapd, sta, reason);
    384 	else if (addr[0] == 0xff)
    385 		hostapd_free_stas(hapd);
    386 
    387 	return 0;
    388 }
    389 
    390 
    391 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
    392 			      size_t buflen)
    393 {
    394 	struct hostapd_iface *iface = hapd->iface;
    395 	int len = 0, ret;
    396 	size_t i;
    397 
    398 	ret = os_snprintf(buf + len, buflen - len,
    399 			  "state=%s\n"
    400 			  "phy=%s\n"
    401 			  "freq=%d\n"
    402 			  "num_sta_non_erp=%d\n"
    403 			  "num_sta_no_short_slot_time=%d\n"
    404 			  "num_sta_no_short_preamble=%d\n"
    405 			  "olbc=%d\n"
    406 			  "num_sta_ht_no_gf=%d\n"
    407 			  "num_sta_no_ht=%d\n"
    408 			  "num_sta_ht_20_mhz=%d\n"
    409 			  "num_sta_ht40_intolerant=%d\n"
    410 			  "olbc_ht=%d\n"
    411 			  "ht_op_mode=0x%x\n",
    412 			  hostapd_state_text(iface->state),
    413 			  iface->phy,
    414 			  iface->freq,
    415 			  iface->num_sta_non_erp,
    416 			  iface->num_sta_no_short_slot_time,
    417 			  iface->num_sta_no_short_preamble,
    418 			  iface->olbc,
    419 			  iface->num_sta_ht_no_gf,
    420 			  iface->num_sta_no_ht,
    421 			  iface->num_sta_ht_20mhz,
    422 			  iface->num_sta_ht40_intolerant,
    423 			  iface->olbc_ht,
    424 			  iface->ht_op_mode);
    425 	if (ret < 0 || (size_t) ret >= buflen - len)
    426 		return len;
    427 	len += ret;
    428 
    429 	if (!iface->cac_started || !iface->dfs_cac_ms) {
    430 		ret = os_snprintf(buf + len, buflen - len,
    431 				  "cac_time_seconds=%d\n"
    432 				  "cac_time_left_seconds=N/A\n",
    433 				  iface->dfs_cac_ms / 1000);
    434 	} else {
    435 		/* CAC started and CAC time set - calculate remaining time */
    436 		struct os_reltime now;
    437 		unsigned int left_time;
    438 
    439 		os_reltime_age(&iface->dfs_cac_start, &now);
    440 		left_time = iface->dfs_cac_ms / 1000 - now.sec;
    441 		ret = os_snprintf(buf + len, buflen - len,
    442 				  "cac_time_seconds=%u\n"
    443 				  "cac_time_left_seconds=%u\n",
    444 				  iface->dfs_cac_ms / 1000,
    445 				  left_time);
    446 	}
    447 	if (ret < 0 || (size_t) ret >= buflen - len)
    448 		return len;
    449 	len += ret;
    450 
    451 	ret = os_snprintf(buf + len, buflen - len,
    452 			  "channel=%u\n"
    453 			  "secondary_channel=%d\n"
    454 			  "ieee80211n=%d\n"
    455 			  "ieee80211ac=%d\n"
    456 			  "vht_oper_chwidth=%d\n"
    457 			  "vht_oper_centr_freq_seg0_idx=%d\n"
    458 			  "vht_oper_centr_freq_seg1_idx=%d\n",
    459 			  iface->conf->channel,
    460 			  iface->conf->secondary_channel,
    461 			  iface->conf->ieee80211n,
    462 			  iface->conf->ieee80211ac,
    463 			  iface->conf->vht_oper_chwidth,
    464 			  iface->conf->vht_oper_centr_freq_seg0_idx,
    465 			  iface->conf->vht_oper_centr_freq_seg1_idx);
    466 	if (ret < 0 || (size_t) ret >= buflen - len)
    467 		return len;
    468 	len += ret;
    469 
    470 	for (i = 0; i < iface->num_bss; i++) {
    471 		struct hostapd_data *bss = iface->bss[i];
    472 		ret = os_snprintf(buf + len, buflen - len,
    473 				  "bss[%d]=%s\n"
    474 				  "bssid[%d]=" MACSTR "\n"
    475 				  "ssid[%d]=%s\n"
    476 				  "num_sta[%d]=%d\n",
    477 				  (int) i, bss->conf->iface,
    478 				  (int) i, MAC2STR(bss->own_addr),
    479 				  (int) i,
    480 				  wpa_ssid_txt(bss->conf->ssid.ssid,
    481 					       bss->conf->ssid.ssid_len),
    482 				  (int) i, bss->num_sta);
    483 		if (ret < 0 || (size_t) ret >= buflen - len)
    484 			return len;
    485 		len += ret;
    486 	}
    487 
    488 	return len;
    489 }
    490 
    491 
    492 int hostapd_parse_csa_settings(const char *pos,
    493 			       struct csa_settings *settings)
    494 {
    495 	char *end;
    496 
    497 	os_memset(settings, 0, sizeof(*settings));
    498 	settings->cs_count = strtol(pos, &end, 10);
    499 	if (pos == end) {
    500 		wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
    501 		return -1;
    502 	}
    503 
    504 	settings->freq_params.freq = atoi(end);
    505 	if (settings->freq_params.freq == 0) {
    506 		wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
    507 		return -1;
    508 	}
    509 
    510 #define SET_CSA_SETTING(str) \
    511 	do { \
    512 		const char *pos2 = os_strstr(pos, " " #str "="); \
    513 		if (pos2) { \
    514 			pos2 += sizeof(" " #str "=") - 1; \
    515 			settings->freq_params.str = atoi(pos2); \
    516 		} \
    517 	} while (0)
    518 
    519 	SET_CSA_SETTING(center_freq1);
    520 	SET_CSA_SETTING(center_freq2);
    521 	SET_CSA_SETTING(bandwidth);
    522 	SET_CSA_SETTING(sec_channel_offset);
    523 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
    524 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
    525 	settings->block_tx = !!os_strstr(pos, " blocktx");
    526 #undef SET_CSA_SETTING
    527 
    528 	return 0;
    529 }
    530