diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 9518545c8..710186e5d 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -446,6 +446,7 @@ #define WLAN_EID_WHITE_SPACE_MAP 205 #define WLAN_EID_FTM_PARAMETERS 206 #define WLAN_EID_S1G_BCN_COMPAT 213 +#define WLAN_EID_TWT 216 #define WLAN_EID_S1G_CAPABILITIES 217 #define WLAN_EID_VENDOR_SPECIFIC 221 #define WLAN_EID_S1G_OPERATION 232 @@ -603,6 +604,10 @@ #define WLAN_ACTION_ROBUST_AV_STREAMING 19 #define WLAN_ACTION_UNPROTECTED_DMG 20 #define WLAN_ACTION_VHT 21 +#define WLAN_ACTION_S1G 22 +#define WLAN_ACTION_S1G_RELAY 23 +#define WLAN_ACTION_FLOW_CONTROL 24 +#define WLAN_ACTION_CTRL_RESP_MCS_NEG 25 #define WLAN_ACTION_FILS 26 #define WLAN_ACTION_PROTECTED_FTM 34 #define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126 @@ -820,6 +825,19 @@ enum nai_realm_eap_cred_type { NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 }; +/* Unprotected S1G Action field values for WLAN_ACTION_S1G */ +#define S1G_ACT_AID_SWITCH_REQUEST 0 +#define S1G_ACT_AID_SWITCH_RESPONSE 1 +#define S1G_ACT_SYNC_CONTROL 2 +#define S1G_ACT_STA_INFO_ANNOUNCE 3 +#define S1G_ACT_EDCA_PARAM_SET 4 +#define S1G_ACT_EL_OPERATION 5 +#define S1G_ACT_TWT_SETUP 6 +#define S1G_ACT_TWT_TEARDOWN 7 +#define S1G_ACT_SECT_GROUP_ID_LIST 8 +#define S1G_ACT_SECT_ID_FEEDBACK 9 +#define S1G_ACT_TWT_INFORMATION 11 + /* * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for * measurement requests diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index fd74c1640..f539ce134 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -103,6 +103,7 @@ OBJS += src/utils/crc32.c OBJS += wmm_ac.c OBJS += op_classes.c OBJS += rrm.c +OBJS += twt.c OBJS += robust_av.c OBJS_p = wpa_passphrase.c OBJS_p += src/utils/common.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index ce70c4ddc..271f2aab3 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -95,6 +95,7 @@ OBJS += ../src/utils/ip_addr.o OBJS += ../src/utils/crc32.o OBJS += op_classes.o OBJS += rrm.o +OBJS += twt.o OBJS += robust_av.o OBJS_p = wpa_passphrase.o OBJS_p += ../src/utils/common.o diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 7fc7e7d69..a32cd5c6a 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -9792,6 +9792,96 @@ static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s) #endif /* CONFIG_SME */ } + +static int wpas_ctrl_iface_send_twt_setup(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + u8 dtok = 1; + int exponent = 10; + int mantissa = 8192; + u8 min_twt = 255; + unsigned long long twt = 0; + bool requestor = true; + int setup_cmd = 0; + bool trigger = true; + bool implicit = true; + bool flow_type = true; + int flow_id = 0; + bool protection = false; + u8 twt_channel = 0; + const char *tok_s; + + tok_s = os_strstr(cmd, " dialog="); + if (tok_s) + dtok = atoi(tok_s + os_strlen(" dialog=")); + + tok_s = os_strstr(cmd, " exponent="); + if (tok_s) + exponent = atoi(tok_s + os_strlen(" exponent=")); + + tok_s = os_strstr(cmd, " mantissa="); + if (tok_s) + mantissa = atoi(tok_s + os_strlen(" mantissa=")); + + tok_s = os_strstr(cmd, " min_twt="); + if (tok_s) + min_twt = atoi(tok_s + os_strlen(" min_twt=")); + + tok_s = os_strstr(cmd, " setup_cmd="); + if (tok_s) + setup_cmd = atoi(tok_s + os_strlen(" setup_cmd=")); + + tok_s = os_strstr(cmd, " twt="); + if (tok_s) + sscanf(tok_s + os_strlen(" twt="), "%llu", &twt); + + tok_s = os_strstr(cmd, " requestor="); + if (tok_s) + requestor = atoi(tok_s + os_strlen(" requestor=")); + + tok_s = os_strstr(cmd, " trigger="); + if (tok_s) + trigger = atoi(tok_s + os_strlen(" trigger=")); + + tok_s = os_strstr(cmd, " implicit="); + if (tok_s) + implicit = atoi(tok_s + os_strlen(" implicit=")); + + tok_s = os_strstr(cmd, " flow_type="); + if (tok_s) + flow_type = atoi(tok_s + os_strlen(" flow_type=")); + + tok_s = os_strstr(cmd, " flow_id="); + if (tok_s) + flow_id = atoi(tok_s + os_strlen(" flow_id=")); + + tok_s = os_strstr(cmd, " protection="); + if (tok_s) + protection = atoi(tok_s + os_strlen(" protection=")); + + tok_s = os_strstr(cmd, " twt_channel="); + if (tok_s) + twt_channel = atoi(tok_s + os_strlen(" twt_channel=")); + + return wpas_twt_send_setup(wpa_s, dtok, exponent, mantissa, min_twt, + setup_cmd, twt, requestor, trigger, implicit, + flow_type, flow_id, protection, twt_channel); +} + + +static int wpas_ctrl_iface_send_twt_teardown(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + u8 flags = 0x1; + const char *tok_s; + + tok_s = os_strstr(cmd, " flags="); + if (tok_s) + flags = atoi(tok_s + os_strlen(" flags=")); + + return wpas_twt_send_teardown(wpa_s, flags); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -11285,6 +11375,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, sme_event_unprot_disconnect( wpa_s, wpa_s->bssid, NULL, WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + } else if (os_strncmp(buf, "TWT_SETUP ", 10) == 0) { + if (wpas_ctrl_iface_send_twt_setup(wpa_s, buf + 9)) + reply_len = -1; + } else if (os_strcmp(buf, "TWT_SETUP") == 0) { + if (wpas_ctrl_iface_send_twt_setup(wpa_s, "")) + reply_len = -1; + } else if (os_strncmp(buf, "TWT_TEARDOWN ", 13) == 0) { + if (wpas_ctrl_iface_send_twt_teardown(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strcmp(buf, "TWT_TEARDOWN") == 0) { + if (wpas_ctrl_iface_send_twt_teardown(wpa_s, "")) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) diff --git a/wpa_supplicant/twt.c b/wpa_supplicant/twt.c new file mode 100644 index 000000000..d77cea227 --- /dev/null +++ b/wpa_supplicant/twt.c @@ -0,0 +1,143 @@ +/* + * wpa_supplicant - TWT + * Copyright (c) 2003-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" + + +#ifdef CONFIG_TESTING_OPTIONS + +/** + * wpas_twt_send_setup - Send TWT Setup frame (Request) to our AP + * @wpa_s: Pointer to wpa_supplicant + * @dtok: Dialog token + * @exponent: Wake-interval exponent + * @mantissa: Wake-interval mantissa + * @min_twt: Minimum TWT wake duration in units of 256 usec + * @setup_cmd: 0 == request, 1 == suggest, etc. Table 9-297 + * Returns: 0 in case of success, negative error code otherwise + * + */ +int wpas_twt_send_setup(struct wpa_supplicant *wpa_s, u8 dtok, int exponent, + int mantissa, u8 min_twt, int setup_cmd, u64 twt, + bool requestor, bool trigger, bool implicit, + bool flow_type, u8 flow_id, bool protection, + u8 twt_channel) +{ + struct wpabuf *buf; + u16 req_type = 0; + int ret = 0; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, + "TWT: No connection - cannot send TWT Setup frame"); + return -ENOTCONN; + } + + /* 3 = Action category + Action code + Dialog token */ + /* 17 = TWT element */ + buf = wpabuf_alloc(3 + 17); + if (!buf) { + wpa_printf(MSG_DEBUG, + "TWT: Failed to allocate TWT Setup frame (Request)"); + return -ENOMEM; + } + + wpa_printf(MSG_DEBUG, + "TWT: Setup request, dtok: %d exponent: %d mantissa: %d min-twt: %d", + dtok, exponent, mantissa, min_twt); + + wpabuf_put_u8(buf, WLAN_ACTION_S1G); + wpabuf_put_u8(buf, S1G_ACT_TWT_SETUP); + wpabuf_put_u8(buf, dtok); + + wpabuf_put_u8(buf, WLAN_EID_TWT); + wpabuf_put_u8(buf, 15); /* len */ + + wpabuf_put_u8(buf, BIT(4)); /* Control field: + * B4 = TWT Information Frame Disabled */ + + if (requestor) + req_type |= BIT(0); /* This STA is a TWT Requesting STA */ + /* TWT Setup Command field */ + req_type |= (setup_cmd & 0x7) << 1; + if (trigger) + req_type |= BIT(4); /* TWT SP includes trigger frames */ + if (implicit) + req_type |= BIT(5); /* Implicit TWT */ + if (flow_type) + req_type |= BIT(6); /* Flow Type: Unannounced TWT */ + req_type |= (flow_id & 0x7) << 7; + req_type |= (exponent & 0x1f) << 10; /* TWT Wake Interval Exponent */ + if (protection) + req_type |= BIT(15); + wpabuf_put_le16(buf, req_type); + wpabuf_put_le64(buf, twt); + wpabuf_put_u8(buf, min_twt); /* Nominal Minimum TWT Wake Duration */ + wpabuf_put_le16(buf, mantissa); /* TWT Wake Interval Mantissa */ + wpabuf_put_u8(buf, twt_channel); /* TWT Channel */ + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_printf(MSG_DEBUG, "TWT: Failed to send TWT Setup Request"); + ret = -ECANCELED; + } + + wpabuf_free(buf); + return ret; +} + + +/** + * wpas_twt_send_teardown - Send TWT teardown request to our AP + * @wpa_s: Pointer to wpa_supplicant + * @flags: The byte that goes inside the TWT Teardown element + * Returns: 0 in case of success, negative error code otherwise + * + */ +int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags) +{ + struct wpabuf *buf; + int ret = 0; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, + "TWT: No connection - cannot send TWT Teardown frame"); + return -ENOTCONN; + } + + /* 3 = Action category + Action code + flags */ + buf = wpabuf_alloc(3); + if (!buf) { + wpa_printf(MSG_DEBUG, + "TWT: Failed to allocate TWT Teardown frame"); + return -ENOMEM; + } + + wpa_printf(MSG_DEBUG, "TWT: Teardown request, flags: 0x%x", flags); + + wpabuf_put_u8(buf, WLAN_ACTION_S1G); + wpabuf_put_u8(buf, S1G_ACT_TWT_TEARDOWN); + wpabuf_put_u8(buf, flags); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_printf(MSG_DEBUG, "TWT: Failed to send TWT Teardown frame"); + ret = -ECANCELED; + } + + wpabuf_free(buf); + return ret; +} + +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index b98a833da..ae10f08a4 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2926,6 +2926,20 @@ static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_twt_setup(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TWT_SETUP", 0, argc, argv); +} + + +static int wpa_cli_cmd_twt_teardown(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TWT_TEARDOWN", 0, argc, argv); +} + + static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "ERP_FLUSH"); @@ -3805,6 +3819,14 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, "[ssid=] [lci] [civic] = Trigger request to AP for neighboring AP report (with optional given SSID in hex or enclosed in double quotes, default: current SSID; with optional LCI and location civic request)" }, + { "twt_setup", + wpa_cli_cmd_twt_setup, NULL, cli_cmd_flag_none, + "[dialog=] [exponent=] [mantissa=] [min_twt=] [setup_cmd=] [twt=] [requestor=0|1] [trigger=0|1] [implicit=0|1] [flow_type=0|1] [flow_id=<3-bit-id>] [protection=0|1] [twt_channel=] = Send TWT Setup frame" + }, + { "twt_teardown", + wpa_cli_cmd_twt_teardown, NULL, cli_cmd_flag_none, + "[flags=] = Send TWT Teardown frame" + }, { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none, "= flush ERP keys" }, { "mac_rand_scan", diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index b6c339ab7..8705b55b9 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1514,6 +1514,14 @@ void add_freq(int *freqs, int *num_freqs, int freq); int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, u8 *op_class, u8 *chan, u8 *phy_type); + +int wpas_twt_send_setup(struct wpa_supplicant *wpa_s, u8 dtok, int exponent, + int mantissa, u8 min_twt, int setup_cmd, u64 twt, + bool requestor, bool trigger, bool implicit, + bool flow_type, u8 flow_id, bool protection, + u8 twt_channel); +int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags); + void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, const u8 *report, size_t report_len);