]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
TWT: Support sending TWT Setup and Teardown Action frames
authorBen Greear <greearb@candelatech.com>
Sat, 6 Mar 2021 16:18:38 +0000 (08:18 -0800)
committerJouni Malinen <j@w1.fi>
Sun, 7 Mar 2021 20:07:37 +0000 (22:07 +0200)
This adds new control interface commands TWT_SETUP and TWT_TEARDOWN. For
now, these are only for testing purposes to be able to trigger
transmission of the TWT Action frames without configuring any local
behavior for TWT in the driver.

Signed-off-by: Ben Greear <greearb@candelatech.com>
src/common/ieee802_11_defs.h
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/ctrl_iface.c
wpa_supplicant/twt.c [new file with mode: 0644]
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h

index 9518545c8af52fbd7b108a2b1f0add010937de5f..710186e5d36af68580dca09bd3c3f35c1680b1b2 100644 (file)
 #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
 #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
index fd74c16409cca434db08edbde4738646afbf39f0..f539ce1348ec3a306fe244aa8f0c14d87f6eccbb 100644 (file)
@@ -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
index ce70c4ddce2a4e6bf18518c07882da94aff72e46..271f2aab31180daaf625af7243c0d448e452cbfb 100644 (file)
@@ -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
index 7fc7e7d69b24c2cb1b5b9c517ef7ceec6bd599be..a32cd5c6a1dbd192f95ad10745c09ae6cbd297c4 100644 (file)
@@ -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 (file)
index 0000000..d77cea2
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * wpa_supplicant - TWT
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
+ *
+ * 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 */
index b98a833dac80efc0b4dd91d6df1e1395d4386268..ae10f08a4af6cb6916ff3988dbaacad98a07e15a 100644 (file)
@@ -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=<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=<token>] [exponent=<exponent>] [mantissa=<mantissa>] [min_twt=<Min TWT>] [setup_cmd=<setup-cmd>] [twt=<u64>] [requestor=0|1] [trigger=0|1] [implicit=0|1] [flow_type=0|1] [flow_id=<3-bit-id>] [protection=0|1] [twt_channel=<twt chanel id>] = Send TWT Setup frame"
+       },
+       { "twt_teardown",
+         wpa_cli_cmd_twt_teardown, NULL, cli_cmd_flag_none,
+         "[flags=<value>] = Send TWT Teardown frame"
+       },
        { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
          "= flush ERP keys" },
        { "mac_rand_scan",
index b6c339ab7117164cf783a634a8abb2783fc6a0fa..8705b55b9b113319a7ec0a0f2ff93ccfc9df50cb 100644 (file)
@@ -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);