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