internal Registrar. This allows station Enrollee from which the password
token was received to run through WPS protocol to provision the
credential.
+
+"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
+contents of a Handover Select Message for connection handover when this
+does not depend on the contents of the Handover Request Message. The
+first argument selects the format of the output data and the second
+argument selects which type of connection handover is requested (WPS =
+Wi-Fi handover as specified in WSC 2.0).
+
+"nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
+<carrier from handover select>" is used to report completed NFC
+connection handover. The first parameter indicates whether the local
+device initiated or responded to the connection handover and the carrier
+records are the selected carrier from the handover request and select
+messages as a hexdump.
"wps_nfc_dev_pw_id value", line);
errors++;
}
+ bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
wpabuf_free(bss->wps_nfc_dh_pubkey);
bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
wpabuf_free(bss->wps_nfc_dh_privkey);
bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
wpabuf_free(bss->wps_nfc_dev_pw);
bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+ bss->wps_nfc_pw_from_config = 1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P_MANAGER
return -1;
}
+
+
+static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ struct wpabuf *buf;
+ int res;
+ char *pos;
+ int ndef;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ if (os_strcmp(pos, "WPS-CR") == 0)
+ buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
+ else
+ buf = NULL;
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
+ char *cmd)
+{
+ /*
+ * Since NFC connection handover provided full WPS Credential, there is
+ * no need for additional operations within hostapd. Just report this in
+ * debug log.
+ */
+ wpa_printf(MSG_DEBUG, "NFC: Connection handover reported: %s", cmd);
+ return 0;
+}
+
#endif /* CONFIG_WPS_NFC */
} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
reply_len = hostapd_ctrl_iface_wps_nfc_token(
hapd, buf + 14, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
+ reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
+ hapd, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
+ if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
+ reply_len = -1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_WNM
}
return wpa_ctrl_command(ctrl, cmd);
}
+
+
+static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[64];
+ int res;
+
+ if (argc != 2) {
+ printf("Invalid 'nfc_get_handover_sel' command - two arguments "
+ "are required.\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long NFC_GET_HANDOVER_SEL command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
#endif /* CONFIG_WPS_NFC */
{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+ { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
#endif /* CONFIG_WPS_NFC */
{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
{ "wps_config", hostapd_cli_cmd_wps_config },
--- /dev/null
+#!/usr/bin/python
+#
+# Example nfcpy to hostapd wrapper for WPS NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+logging.basicConfig()
+
+import wpactrl
+
+wpas_ctrl = '/var/run/hostapd'
+
+def wpas_connect():
+ ifaces = []
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError, error:
+ print "Could not find hostapd: ", error
+ return None
+
+ if len(ifaces) < 1:
+ print "No hostapd control interface found"
+ return None
+
+ for ctrl in ifaces:
+ try:
+ wpas = wpactrl.WPACtrl(ctrl)
+ return wpas
+ except wpactrl.error, error:
+ print "Error: ", error
+ pass
+ return None
+
+
+def wpas_tag_read(message):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return
+ print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+
+
+def wpas_get_config_token():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
+
+
+def wpas_get_password_token():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
+
+
+def wpas_get_handover_sel():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
+
+
+def wpas_report_handover(req, sel):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
+ str(req).encode("hex") + " " +
+ str(sel).encode("hex"))
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+ def __init__(self):
+ super(HandoverServer, self).__init__()
+
+ def process_request(self, request):
+ print "HandoverServer - request received"
+ print "Parsed handover request: " + request.pretty()
+
+ sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+ for carrier in request.carriers:
+ print "Remote carrier type: " + carrier.type
+ if carrier.type == "application/vnd.wfa.wsc":
+ print "WPS carrier type match - add WPS carrier record"
+ self.received_carrier = carrier.record
+ data = wpas_get_handover_sel()
+ if data is None:
+ print "Could not get handover select carrier record from hostapd"
+ continue
+ print "Handover select carrier record from hostapd:"
+ print data.encode("hex")
+ self.sent_carrier = data
+
+ message = nfc.ndef.Message(data);
+ sel.add_carrier(message[0], "active", message[1:])
+
+ print "Handover select:"
+ print sel.pretty()
+ print str(sel).encode("hex")
+
+ print "Sending handover select"
+ return sel
+
+
+def wps_handover_resp(peer):
+ print "Trying to handle WPS handover"
+
+ srv = HandoverServer()
+
+ nfc.llcp.activate(peer);
+
+ try:
+ print "Trying handover";
+ srv.start()
+ print "Wait for disconnect"
+ while nfc.llcp.connected():
+ time.sleep(0.1)
+ print "Disconnected after handover"
+ except nfc.llcp.ConnectRefused:
+ print "Handover connection refused"
+ nfc.llcp.shutdown()
+ return
+
+ if srv.sent_carrier:
+ wpas_report_handover(srv.received_carrier, srv.sent_carrier)
+
+ print "Remove peer"
+ nfc.llcp.shutdown()
+ print "Done with handover"
+
+
+def wps_tag_read(tag):
+ if len(tag.ndef.message):
+ message = nfc.ndef.Message(tag.ndef.message)
+ print "message type " + message.type
+
+ for record in message:
+ print "record type " + record.type
+ if record.type == "application/vnd.wfa.wsc":
+ print "WPS tag - send to hostapd"
+ wpas_tag_read(tag.ndef.message)
+ break
+ else:
+ print "Empty tag"
+
+ print "Remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def wps_write_config_tag(clf):
+ print "Write WPS config token"
+ data = wpas_get_config_token()
+ if (data == None):
+ print "Could not get WPS config token from hostapd"
+ return
+
+ print "Touch an NFC tag"
+ while True:
+ tag = clf.poll()
+ if tag == None:
+ time.sleep(0.1)
+ continue
+ break
+
+ print "Tag found - writing"
+ tag.ndef.message = data
+ print "Done - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def wps_write_password_tag(clf):
+ print "Write WPS password token"
+ data = wpas_get_password_token()
+ if (data == None):
+ print "Could not get WPS password token from hostapd"
+ return
+
+ print "Touch an NFC tag"
+ while True:
+ tag = clf.poll()
+ if tag == None:
+ time.sleep(0.1)
+ continue
+ break
+
+ print "Tag found - writing"
+ tag.ndef.message = data
+ print "Done - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def find_peer(clf):
+ while True:
+ if nfc.llcp.connected():
+ print "LLCP connected"
+ general_bytes = nfc.llcp.startup({})
+ peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
+ if isinstance(peer, nfc.DEP):
+ print "listen -> DEP";
+ if peer.general_bytes.startswith("Ffm"):
+ print "Found DEP"
+ return peer
+ print "mismatch in general_bytes"
+ print peer.general_bytes
+
+ peer = clf.poll(general_bytes)
+ if isinstance(peer, nfc.DEP):
+ print "poll -> DEP";
+ if peer.general_bytes.startswith("Ffm"):
+ print "Found DEP"
+ return peer
+ print "mismatch in general_bytes"
+ print peer.general_bytes
+
+ if peer:
+ print "Found tag"
+ return peer
+
+
+def main():
+ clf = nfc.ContactlessFrontend()
+
+ try:
+ if len(sys.argv) > 1 and sys.argv[1] == "write-config":
+ wps_write_config_tag(clf)
+ raise SystemExit
+
+ if len(sys.argv) > 1 and sys.argv[1] == "write-password":
+ wps_write_password_tag(clf)
+ raise SystemExit
+
+ while True:
+ print "Waiting for a tag or peer to be touched"
+
+ tag = find_peer(clf)
+ if isinstance(tag, nfc.DEP):
+ wps_handover_resp(tag)
+ continue
+
+ if tag.ndef:
+ wps_tag_read(tag)
+ continue
+
+ print "Not an NDEF tag - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+ except KeyboardInterrupt:
+ raise SystemExit
+ finally:
+ clf.close()
+
+ raise SystemExit
+
+if __name__ == '__main__':
+ main()
char *model_url;
char *upc;
struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int wps_nfc_pw_from_config;
int wps_nfc_dev_pw_id;
struct wpabuf *wps_nfc_dh_pubkey;
struct wpabuf *wps_nfc_dh_privkey;
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
}
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code)
+{
+ switch (reason_code) {
+ case MAX_CLIENT_REACHED:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ case BLOCKED_CLIENT:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ }
+}
+
+
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal)
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset);
break;
+ case EVENT_CONNECT_FAILED_REASON:
+ if (!data)
+ break;
+ hostapd_event_connect_failed_reason(
+ hapd, data->connect_failed_reason.addr,
+ data->connect_failed_reason.code);
+ break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code);
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal);
iface->conf->channel +
iface->conf->secondary_channel * 4);
iface->conf->secondary_channel = 0;
- iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
}
res = ieee80211n_allowed_ht40_channel_pair(iface);
hapd->iface->freq);
}
if (hapd->public_action_cb2) {
- hapd->public_action_cb2(hapd->public_action_cb_ctx,
+ hapd->public_action_cb2(hapd->public_action_cb2_ctx,
(u8 *) mgmt, len,
hapd->iface->freq);
}
new_op_mode = 0;
if (iface->num_sta_no_ht)
new_op_mode = OP_MODE_MIXED;
- else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
- && iface->num_sta_ht_20mhz)
+ else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
else if (iface->olbc_ht)
new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
}
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
+{
+ /*
+ * Handover Select carrier record for WPS uses the same format as
+ * configuration token.
+ */
+ return hostapd_wps_nfc_config_token(hapd, ndef);
+}
+
+
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
{
+ if (hapd->conf->wps_nfc_pw_from_config) {
+ return wps_nfc_token_build(ndef,
+ hapd->conf->wps_nfc_dev_pw_id,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->conf->wps_nfc_dev_pw);
+ }
+
return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
&hapd->conf->wps_nfc_dh_pubkey,
&hapd->conf->wps_nfc_dh_privkey,
const struct wpabuf *data);
struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
int ndef);
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
/* EIDs defined by IEEE 802.11h - END */
#define WLAN_EID_ERP_INFO 42
#define WLAN_EID_HT_CAP 45
+#define WLAN_EID_QOS 46
#define WLAN_EID_RSN 48
#define WLAN_EID_EXT_SUPP_RATES 50
#define WLAN_EID_MOBILITY_DOMAIN 54
#define AP_STA_CONNECTED "AP-STA-CONNECTED "
#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
+#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
/* BSS command information masks */
#define IEEE80211_CAP_IBSS 0x0002
#define IEEE80211_CAP_PRIVACY 0x0010
+/* DMG (60 GHz) IEEE 802.11ad */
+/* type - bits 0..1 */
+#define IEEE80211_CAP_DMG_MASK 0x0003
+#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */
+#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */
+#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */
+
#define WPA_SCAN_QUAL_INVALID BIT(0)
#define WPA_SCAN_NOISE_INVALID BIT(1)
#define WPA_SCAN_LEVEL_INVALID BIT(2)
* struct wpa_scan_results - Scan results
* @res: Array of pointers to allocated variable length scan result entries
* @num: Number of entries in the scan result array
+ * @fetch_time: Time when the results were fetched from the driver
*/
struct wpa_scan_results {
struct wpa_scan_res **res;
size_t num;
+ struct os_time fetch_time;
};
/**
u32 flags; /* bitmask of WPA_STA_* flags */
int set; /* Set STA parameters instead of add */
u8 qosinfo;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
};
struct hostapd_freq_params {
*
* This event can be used to request a WNM operation to be performed.
*/
- EVENT_WNM
+ EVENT_WNM,
+
+ /**
+ * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
+ *
+ * This event indicates that the driver reported a connection failure
+ * with the specified client (for example, max client reached, etc.) in
+ * AP mode.
+ */
+ EVENT_CONNECT_FAILED_REASON
};
int ht_enabled;
int ch_offset;
} ch_switch;
+
+ /**
+ * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+ * @addr: Remote client address
+ * @code: Reason code for connection failure
+ */
+ struct connect_failed_reason {
+ u8 addr[ETH_ALEN];
+ enum {
+ MAX_CLIENT_REACHED,
+ BLOCKED_CLIENT
+ } code;
+ } connect_failed_reason;
};
/**
E2S(EAPOL_TX_STATUS);
E2S(CH_SWITCH);
E2S(WNM);
+ E2S(CONNECT_FAILED_REASON);
}
return "UNKNOWN";
}
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+ u32 reason;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+ if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.connect_failed_reason.addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+ reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+ switch (reason) {
+ case NL80211_CONN_FAIL_MAX_CLIENTS:
+ wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+ data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+ break;
+ case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+ wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+ " tried to connect",
+ MAC2STR(data.connect_failed_reason.addr));
+ data.connect_failed_reason.code = BLOCKED_CLIENT;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+ "%u", reason);
+ return;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
int wds)
{
case NL80211_CMD_TDLS_OPER:
nl80211_tdls_oper_event(drv, tb);
break;
+ case NL80211_CMD_CONN_FAILED:
+ nl80211_connect_failed_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
if (!msg)
return -ENOMEM;
+ wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
+ params->set ? "Set" : "Add", MAC2STR(params->addr));
nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
NL80211_CMD_NEW_STATION);
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
params->supp_rates);
+ wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates,
+ params->supp_rates_len);
if (!params->set) {
+ wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ wpa_printf(MSG_DEBUG, " * listen_interval=%u",
+ params->listen_interval);
NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
params->listen_interval);
}
if (params->ht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * ht_capabilities",
+ (u8 *) params->ht_capabilities,
+ sizeof(*params->ht_capabilities));
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
sizeof(*params->ht_capabilities),
params->ht_capabilities);
}
if (params->vht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * vht_capabilities",
+ (u8 *) params->vht_capabilities,
+ sizeof(*params->vht_capabilities));
NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY,
sizeof(*params->vht_capabilities),
params->vht_capabilities);
}
+ wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
+
+ if (params->ext_capab) {
+ wpa_hexdump(MSG_DEBUG, " * ext_capab",
+ params->ext_capab, params->ext_capab_len);
+ NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+ params->ext_capab_len, params->ext_capab);
+ }
+
os_memset(&upd, 0, sizeof(upd));
upd.mask = sta_flags_nl80211(params->flags);
upd.set = upd.mask;
+ wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x",
+ upd.set, upd.mask);
NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
if (params->flags & WPA_STA_WMM) {
if (!wme)
goto nla_put_failure;
+ wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo);
NLA_PUT_U8(wme, NL80211_STA_WME_UAPSD_QUEUES,
params->qosinfo & WMM_QOSINFO_STA_AC_MASK);
NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP,
- (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) &
+ (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
WMM_QOSINFO_STA_SP_MASK);
if (nla_put_nested(msg, NL80211_ATTR_STA_WME, wme) < 0)
goto nla_put_failure;
if (drv->pending_p2p_scan && drv->p2p) {
#ifdef CONFIG_P2P
size_t i;
+ struct os_time now;
+ os_get_time(&now);
for (i = 0; i < drv->num_scanres; i++) {
struct wpa_scan_res *bss = drv->scanres[i];
if (p2p_scan_res_handler(drv->p2p, bss->bssid,
- bss->freq, bss->age,
- bss->level,
+ bss->freq, &now, bss->level,
(const u8 *) (bss + 1),
bss->ie_len) > 0)
return;
* %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
* %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
* %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
- * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
* The channel to use can be set on the interface or be given using the
* %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
* @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
* requests to connect to a specified network but without separating
* auth and assoc steps. For this, you need to specify the SSID in a
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
- * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
+ * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
* Background scan period can optionally be
* command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
* more background information, see
* http://wireless.kernel.org/en/users/Documentation/WoWLAN.
+ * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
+ * from the driver reporting the wakeup reason. In this case, the
+ * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
+ * for the wakeup, if it was caused by wireless. If it is not present
+ * in the wakeup notification, the wireless device didn't cause the
+ * wakeup but reports that it was woken up.
*
* @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
* the necessary information for supporting GTK rekey offload. This
* @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
* for IBSS or MESH vif.
*
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ * This is to be used with the drivers advertising the support of MAC
+ * address based access control. List of MAC addresses is passed in
+ * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ * is not already done. The new list will replace any existing list. Driver
+ * will clear its ACL when the list of MAC addresses passed is empty. This
+ * command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ * ACL list during %NL80211_CMD_STOP_AP.
+ *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ * a radar is detected or the channel availability scan (CAC) has finished
+ * or was aborted, or a radar was detected, usermode will be notified with
+ * this event. This command is also used to notify userspace about radars
+ * while operating on this channel.
+ * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ * event.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
NL80211_CMD_SET_MCAST_RATE,
+ NL80211_CMD_SET_MAC_ACL,
+
+ NL80211_CMD_RADAR_DETECT,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
* @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
* used for the association (&enum nl80211_mfp, represented as a u32);
* this attribute can be used
- * with %NL80211_CMD_ASSOCIATE request
+ * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests
*
* @NL80211_ATTR_STA_FLAGS2: Attribute containing a
* &struct nl80211_sta_flag_update.
* if not given in START_AP 0 is assumed, if not given in SET_BSS
* no change is made.
*
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ * defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ * carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ * MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ * number of MAC addresses that a device can support for MAC
+ * ACL.
+ *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ * contains a value of enum nl80211_radar_event (u32).
+ *
+ * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver
+ * has and handles. The format is the same as the IE contents. See
+ * 802.11-2012 8.4.2.29 for more information.
+ * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver
+ * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields.
+ *
+ * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to
+ * the driver, e.g., to enable TDLS power save (PU-APSD).
+ *
+ * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are
+ * advertised to the driver, e.g., to enable TDLS off channel operations
+ * and PU-APSD.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
NL80211_ATTR_P2P_CTWINDOW,
NL80211_ATTR_P2P_OPPPS,
+ NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+ NL80211_ATTR_ACL_POLICY,
+
+ NL80211_ATTR_MAC_ADDRS,
+
+ NL80211_ATTR_MAC_ACL_MAX,
+
+ NL80211_ATTR_RADAR_EVENT,
+
+ NL80211_ATTR_EXT_CAPA,
+ NL80211_ATTR_EXT_CAPA_MASK,
+
+ NL80211_ATTR_STA_CAPABILITY,
+ NL80211_ATTR_STA_EXT_CAPABILITY,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
* flag can't be changed, it is only valid while adding a station, and
* attempts to change it will silently be ignored (rather than rejected
* as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ * previously added station into associated state
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
NL80211_STA_FLAG_MFP,
NL80211_STA_FLAG_AUTHENTICATED,
NL80211_STA_FLAG_TDLS_PEER,
+ NL80211_STA_FLAG_ASSOCIATED,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station)
* @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
* @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
* containing info as possible, see &enum nl80211_rate_info
* @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
* @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
* @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ * non-peer STA
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
NL80211_STA_INFO_STA_FLAGS,
NL80211_STA_INFO_BEACON_LOSS,
NL80211_STA_INFO_T_OFFSET,
+ NL80211_STA_INFO_LOCAL_PM,
+ NL80211_STA_INFO_PEER_PM,
+ NL80211_STA_INFO_NONPEER_PM,
+ NL80211_STA_INFO_RX_BYTES64,
+ NL80211_STA_INFO_TX_BYTES64,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
* (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ * (enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ * this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel
+ * as the primary or any of the secondary channels isn't possible,
+ * this includes 80+80 channels
+ * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
+ * using this channel as the primary or any of the secondary channels
+ * isn't possible
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
NL80211_FREQUENCY_ATTR_NO_IBSS,
NL80211_FREQUENCY_ATTR_RADAR,
NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ NL80211_FREQUENCY_ATTR_DFS_STATE,
+ NL80211_FREQUENCY_ATTR_DFS_TIME,
+ NL80211_FREQUENCY_ATTR_NO_HT40_MINUS,
+ NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
+ NL80211_FREQUENCY_ATTR_NO_80MHZ,
+ NL80211_FREQUENCY_ATTR_NO_160MHZ,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
};
/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ * not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ * in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but will wake up for
+ * neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but may not wake up
+ * for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+ NL80211_MESH_POWER_UNKNOWN,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_LIGHT_SLEEP,
+ NL80211_MESH_POWER_DEEP_SLEEP,
+
+ __NL80211_MESH_POWER_AFTER_LAST,
+ NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_meshconf_params - mesh configuration parameters
*
* Mesh configuration parameters. These can be changed while the mesh is
* (in TUs) during which a mesh STA can send only one Action frame
* containing a PREQ element for root path confirmation.
*
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ * type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ NL80211_MESHCONF_POWER_MODE,
+ NL80211_MESHCONF_AWAKE_WINDOW,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
* corresponds to the lowest-order bit in the second byte of the mask.
* For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
* xx indicates "don't care") would be represented by a pattern of
- * twelve zero bytes, and a mask of "0xed,0x07".
+ * twelve zero bytes, and a mask of "0xed,0x01".
* Note that the pattern matching is done as though frames were not
* 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
* first (including SNAP header unpacking) and then matched.
+ * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * these fixed number of bytes of received packet
* @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
* @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
*/
__NL80211_WOWLAN_PKTPAT_INVALID,
NL80211_WOWLAN_PKTPAT_MASK,
NL80211_WOWLAN_PKTPAT_PATTERN,
+ NL80211_WOWLAN_PKTPAT_OFFSET,
NUM_NL80211_WOWLAN_PKTPAT,
MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
* @max_patterns: maximum number of patterns supported
* @min_pattern_len: minimum length of each pattern
* @max_pattern_len: maximum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
*
* This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
* that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
__u32 max_patterns;
__u32 min_pattern_len;
__u32 max_pattern_len;
+ __u32 max_pkt_offset;
} __attribute__((packed));
/**
* @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
* which are passed in an array of nested attributes, each nested attribute
* defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
- * Each pattern defines a wakeup packet. The matching is done on the MSDU,
- * i.e. as though the packet was an 802.3 packet, so the pattern matching
- * is done after the packet is converted to the MSDU.
+ * Each pattern defines a wakeup packet. Packet offset is associated with
+ * each pattern which is used while matching the pattern. The matching is
+ * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the
+ * pattern matching is done after the packet is converted to the MSDU.
*
* In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
* carrying a &struct nl80211_wowlan_pattern_support.
+ *
+ * When reporting wakeup. it is a u32 attribute containing the 0-based
+ * index of the pattern that caused the wakeup, in the patterns passed
+ * to the kernel when configuring.
* @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
* used when setting, used only to indicate that GTK rekeying is supported
* by the device (flag)
* @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
* @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
* (on devices that have rfkill in the device) (flag)
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
+ * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
+ * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+ * attribute contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
+ * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
+ * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
+ * contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section
+ * "TCP connection wakeup" for more details. This is a nested attribute
+ * containing the exact information for establishing and keeping alive
+ * the TCP connection.
+ * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
+ * wakeup packet was received on the TCP connection
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+ * TCP connection was lost or failed to be established
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
+ * the TCP connection ran out of tokens to use for data to send to the
+ * service
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ *
+ * These nested attributes are used to configure the wakeup triggers and
+ * to report the wakeup reason(s).
*/
enum nl80211_wowlan_triggers {
__NL80211_WOWLAN_TRIG_INVALID,
NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
+ NL80211_WOWLAN_TRIG_TCP_CONNECTION,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
/* keep last */
NUM_NL80211_WOWLAN_TRIG,
};
/**
+ * DOC: TCP connection wakeup
+ *
+ * Some devices can establish a TCP connection in order to be woken up by a
+ * packet coming in from outside their network segment, or behind NAT. If
+ * configured, the device will establish a TCP connection to the given
+ * service, and periodically send data to that service. The first data
+ * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK.
+ * The data packets can optionally include a (little endian) sequence
+ * number (in the TCP payload!) that is generated by the device, and, also
+ * optionally, a token from a list of tokens. This serves as a keep-alive
+ * with the service, and for NATed connections, etc.
+ *
+ * During this keep-alive period, the server doesn't send any data to the
+ * client. When receiving data, it is compared against the wakeup pattern
+ * (and mask) and if it matches, the host is woken up. Similarly, if the
+ * connection breaks or cannot be established to start with, the host is
+ * also woken up.
+ *
+ * Developer's note: ARP offload is required for this, otherwise TCP
+ * response packets might not go through correctly.
+ */
+
+/**
+ * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence
+ * @start: starting value
+ * @offset: offset of sequence number in packet
+ * @len: length of the sequence value to write, 1 through 4
+ *
+ * Note: don't confuse with the TCP sequence number(s), this is for the
+ * keepalive packet payload. The actual value is written into the packet
+ * in little endian.
+ */
+struct nl80211_wowlan_tcp_data_seq {
+ __u32 start, offset, len;
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config
+ * @offset: offset of token in packet
+ * @len: length of each token
+ * @token_stream: stream of data to be used for the tokens, the length must
+ * be a multiple of @len for this to make sense
+ */
+struct nl80211_wowlan_tcp_data_token {
+ __u32 offset, len;
+ __u8 token_stream[];
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token_feature - data token features
+ * @min_len: minimum token length
+ * @max_len: maximum token length
+ * @bufsize: total available token buffer size (max size of @token_stream)
+ */
+struct nl80211_wowlan_tcp_data_token_feature {
+ __u32 min_len, max_len, bufsize;
+};
+
+/**
+ * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters
+ * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address
+ * (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because
+ * route lookup when configured might be invalid by the time we suspend,
+ * and doing a route lookup when suspending is no longer possible as it
+ * might require ARP querying.
+ * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a
+ * socket and port will be allocated
+ * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16)
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte.
+ * For feature advertising, a u32 attribute holding the maximum length
+ * of the data payload.
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration
+ * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature
+ * advertising it is just a flag
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration,
+ * see &struct nl80211_wowlan_tcp_data_token and for advertising see
+ * &struct nl80211_wowlan_tcp_data_token_feature.
+ * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum
+ * interval in feature advertising (u32)
+ * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
+ * u32 attribute holding the maximum length
+ * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
+ * feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ * but on the TCP payload only.
+ * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
+ * @MAX_NL80211_WOWLAN_TCP: highest attribute number
+ */
+enum nl80211_wowlan_tcp_attrs {
+ __NL80211_WOWLAN_TCP_INVALID,
+ NL80211_WOWLAN_TCP_SRC_IPV4,
+ NL80211_WOWLAN_TCP_DST_IPV4,
+ NL80211_WOWLAN_TCP_DST_MAC,
+ NL80211_WOWLAN_TCP_SRC_PORT,
+ NL80211_WOWLAN_TCP_DST_PORT,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+ NL80211_WOWLAN_TCP_DATA_INTERVAL,
+ NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+ NL80211_WOWLAN_TCP_WAKE_MASK,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TCP,
+ MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+};
+
+/**
* enum nl80211_iface_limit_attrs - limit attributes
* @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
* @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
* the infrastructure network's beacon interval.
* @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
* different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ * of supported channel widths for radar detection.
* @NUM_NL80211_IFACE_COMB: number of attributes
* @MAX_NL80211_IFACE_COMB: highest attribute number
*
NL80211_IFACE_COMB_MAXNUM,
NL80211_IFACE_COMB_STA_AP_BI_MATCH,
NL80211_IFACE_COMB_NUM_CHANNELS,
+ NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
/* keep last */
NUM_NL80211_IFACE_COMB,
* setting
* @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
* powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ * transitions for AP clients. Without this flag (and if the driver
+ * doesn't have the AP SME in the device) the driver supports adding
+ * stations only when they're associated and adds them in associated
+ * state (to later be transitioned into authorized), with this flag
+ * they should be added before even sending the authentication reply
+ * and then transitioned into authenticated, associated and authorized
+ * states using station flags.
+ * Note that even for drivers that support this, the default is to add
+ * stations in authenticated/associated state, so to add unauthenticated
+ * stations the authenticated/associated bits have to be set in the mask.
+ * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
+ * (HT40, VHT 80/160 MHz) if this flag is set
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
+ /* bit 13 is reserved */
+ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14,
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
};
/**
* enum nl80211_connect_failed_reason - connection request failed reasons
* @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
* handled by the AP is reached.
- * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
*/
enum nl80211_connect_failed_reason {
NL80211_CONN_FAIL_MAX_CLIENTS,
NL80211_SCAN_FLAG_AP = 1<<2,
};
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ * listed in ACL, i.e. allow all the stations which are not listed
+ * in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ * in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ * now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ * the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ * change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ * over, channel becomes usable.
+ */
+enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+ NL80211_RADAR_CAC_FINISHED,
+ NL80211_RADAR_CAC_ABORTED,
+ NL80211_RADAR_NOP_FINISHED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * check (CAC) must be performed before using it for AP or IBSS.
+ * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * is therefore marked as not available.
+ * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+
+enum nl80211_dfs_state {
+ NL80211_DFS_USABLE,
+ NL80211_DFS_UNAVAILABLE,
+ NL80211_DFS_AVAILABLE,
+};
+
#endif /* __LINUX_NL80211_H */
}
+static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
+ u8 *kdf_out, size_t kdf_out_len,
+ const u8 *psk, const u8 *seed,
+ size_t seed_len, u8 method_type)
+{
+ u8 *pos, *data;
+ size_t data_len;
+ int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+ u8 *buf, size_t len);
+
+ gkdf = NULL;
+ switch (csuite_specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ gkdf = eap_gpsk_gkdf_cmac;
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ gkdf = eap_gpsk_gkdf_sha256;
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
+ "Session-Id derivation", csuite_specifier);
+ return -1;
+ }
+
+#define SID_LABEL "Method ID"
+ /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
+ data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
+ pos += strlen(SID_LABEL);
+#undef SID_LABEL
+ os_memcpy(pos, &method_type, 1);
+ pos += 1;
+ WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+ pos += 4;
+ WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+ pos += 2;
+ os_memcpy(pos, seed, seed_len); /* inputString */
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
+ data, data_len);
+
+ if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
+ os_free(data);
+ return -1;
+ }
+ os_free(data);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
+
+ return 0;
+}
+
+
+/**
+ * eap_gpsk_session_id - Derive EAP-GPSK Session ID
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @method_type: EAP Authentication Method Type
+ * @sid: Buffer for 17-byte Session ID
+ * @sid_len: Buffer for returning length of Session ID
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_peer, const u8 *rand_server,
+ const u8 *id_peer, size_t id_peer_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 method_type, u8 *sid, size_t *sid_len)
+{
+ u8 *seed, *pos;
+ u8 kdf_out[16];
+ size_t seed_len;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
+ vendor, specifier);
+
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+ /*
+ * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+ * (= seed)
+ * KS = 16, CSuite_Sel = 0x00000000 0x0001
+ * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+ * CSuite_Sel || inputString)
+ */
+ seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
+ seed = os_malloc(seed_len);
+ if (seed == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
+ "for Session-Id derivation");
+ return -1;
+ }
+
+ pos = seed;
+ os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_peer, id_peer_len);
+ pos += id_peer_len;
+ os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_server, id_server_len);
+ pos += id_server_len;
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+
+ ret = eap_gpsk_derive_mid_helper(specifier,
+ kdf_out, sizeof(kdf_out),
+ psk, seed, seed_len,
+ method_type);
+
+ sid[0] = method_type;
+ os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
+ *sid_len = 1 + sizeof(kdf_out);
+
+ os_free(seed);
+
+ return ret;
+}
+
+
/**
* eap_gpsk_mic_len - Get the length of the MIC
* @vendor: CSuite/Vendor
const u8 *id_server, size_t id_server_len,
u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
u8 *pk, size_t *pk_len);
+int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_peer, const u8 *rand_server,
+ const u8 *id_peer, size_t id_peer_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 method_type, u8 *sid, size_t *sid_len);
size_t eap_gpsk_mic_len(int vendor, int specifier);
int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
int specifier, const u8 *data, size_t len, u8 *mic);
eapol_set_bool(sm, EAPOL_eapFail, FALSE);
os_free(sm->eapKeyData);
sm->eapKeyData = NULL;
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = NULL;
sm->eapKeyAvailable = FALSE;
eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
sm->lastId = -1; /* new session - make sure this does not match with
os_free(sm->eapKeyData);
sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
&sm->eapKeyDataLen);
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = sm->m->getSessionId(sm, sm->eap_method_priv,
+ &sm->eapSessionIdLen);
+ if (sm->eapSessionId) {
+ wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+ sm->eapSessionId, sm->eapSessionIdLen);
+ }
}
}
sm->eapRespData = NULL;
os_free(sm->eapKeyData);
sm->eapKeyData = NULL;
+ os_free(sm->eapSessionId);
+ sm->eapSessionId = NULL;
/* This is not clearly specified in the EAP statemachines draft, but
* it seems necessary to make sure that some of the EAPOL variables get
/**
+ * eap_get_eapSessionId - Get Session-Id from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available
+ * only after a successful authentication. EAP state machine continues to manage
+ * the Session-Id and the caller must not change or free the returned data.
+ */
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapSessionId == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapSessionIdLen;
+ return sm->eapSessionId;
+}
+
+
+/**
* eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @len: Pointer to variable that will be set to number of bytes in the key
int eap_key_available(struct eap_sm *sm);
void eap_notify_success(struct eap_sm *sm);
void eap_notify_lower_layer_success(struct eap_sm *sm);
+const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
}
+static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *id;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+ id = os_malloc(*len);
+ if (id == NULL)
+ return NULL;
+
+ id[0] = data->eap_method;
+ os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+ os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
+
+ return id;
+}
+
+
static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_aka_data *data = priv;
eap->process = eap_aka_process;
eap->isKeyAvailable = eap_aka_isKeyAvailable;
eap->getKey = eap_aka_getKey;
+ eap->getSessionId = eap_aka_get_session_id;
eap->has_reauth_data = eap_aka_has_reauth_data;
eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
eap->init_for_reauth = eap_aka_init_for_reauth;
eap->process = eap_aka_process;
eap->isKeyAvailable = eap_aka_isKeyAvailable;
eap->getKey = eap_aka_getKey;
+ eap->getSessionId = eap_aka_get_session_id;
eap->has_reauth_data = eap_aka_has_reauth_data;
eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
eap->init_for_reauth = eap_aka_init_for_reauth;
int session_ticket_used;
u8 key_data[EAP_FAST_KEY_LEN];
+ u8 *session_id;
+ size_t id_len;
u8 emsk[EAP_EMSK_LEN];
int success;
pac = pac->next;
eap_fast_free_pac(prev);
}
+ os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
}
return NULL;
}
+ if (!data->anon_provisioning && data->phase2_success) {
+ os_free(data->session_id);
+ data->session_id = eap_peer_tls_derive_session_id(
+ sm, &data->ssl, EAP_TYPE_FAST, &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive "
+ "Session-Id");
+ wpabuf_free(resp);
+ return NULL;
+ }
+ }
+
pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
pos, _bind, cmk);
os_free(data);
return NULL;
}
+ os_free(data->session_id);
+ data->session_id = NULL;
if (data->phase2_priv && data->phase2_method &&
data->phase2_method->init_for_reauth)
data->phase2_method->init_for_reauth(sm, data->phase2_priv);
}
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *id;
+
+ if (!data->success)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_fast_data *data = priv;
eap->process = eap_fast_process;
eap->isKeyAvailable = eap_fast_isKeyAvailable;
eap->getKey = eap_fast_getKey;
+ eap->getSessionId = eap_fast_get_session_id;
eap->get_status = eap_fast_get_status;
#if 0
eap->has_reauth_data = eap_fast_has_reauth_data;
size_t sk_len;
u8 pk[EAP_GPSK_MAX_PK_LEN];
size_t pk_len;
- u8 session_id;
- int session_id_set;
+ u8 session_id[128];
+ size_t id_len;
u8 *id_peer;
size_t id_peer_len;
u8 *id_server;
return NULL;
}
+ if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
+ data->vendor, data->specifier,
+ data->rand_peer, data->rand_server,
+ data->id_peer, data->id_peer_len,
+ data->id_server, data->id_server_len,
+ EAP_TYPE_GPSK,
+ data->session_id, &data->id_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+ data->session_id, data->id_len);
+
/* No PD_Payload_1 */
wpabuf_put_be16(resp, 0);
}
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *sid;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ sid = os_malloc(data->id_len);
+ if (sid == NULL)
+ return NULL;
+ os_memcpy(sid, data->session_id, data->id_len);
+ *len = data->id_len;
+
+ return sid;
+}
+
+
int eap_peer_gpsk_register(void)
{
struct eap_method *eap;
eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
eap->getKey = eap_gpsk_getKey;
eap->get_emsk = eap_gpsk_get_emsk;
+ eap->getSessionId = eap_gpsk_get_session_id;
ret = eap_peer_method_register(eap);
if (ret)
* private data or this function may derive the key.
*/
u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * getSessionId - Get EAP method specific Session-Id
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store Session-Id length
+ * Returns: Session-Id or %NULL if not available
+ *
+ * This function can be used to get the Session-Id from the EAP method.
+ * The Session-Id may already be stored in the method-specific private
+ * data or this function may derive the Session-Id.
+ */
+ u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
};
Boolean eapKeyAvailable; /* peer to lower layer */
u8 *eapKeyData; /* peer to lower layer */
size_t eapKeyDataLen; /* peer to lower layer */
+ u8 *eapSessionId; /* peer to lower layer */
+ size_t eapSessionIdLen; /* peer to lower layer */
const struct eap_method *m; /* selected EAP method */
/* not defined in RFC 4137 */
Boolean changed;
}
+static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *sid;
+ size_t sid_len;
+ size_t offset;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
+ sid = os_malloc(sid_len);
+ if (sid) {
+ offset = 0;
+ sid[offset] = EAP_TYPE_IKEV2;
+ offset++;
+ os_memcpy(sid + offset, data->ikev2.i_nonce,
+ data->ikev2.i_nonce_len);
+ offset += data->ikev2.i_nonce_len;
+ os_memcpy(sid + offset, data->ikev2.r_nonce,
+ data->ikev2.r_nonce_len);
+ *len = sid_len;
+ wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
+ sid, sid_len);
+ }
+
+ return sid;
+}
+
+
int eap_peer_ikev2_register(void)
{
struct eap_method *eap;
eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
eap->getKey = eap_ikev2_getKey;
eap->get_emsk = eap_ikev2_get_emsk;
+ eap->getSessionId = eap_ikev2_get_session_id;
ret = eap_peer_method_register(eap);
if (ret)
int resuming; /* starting a resumed session */
int reauth; /* reauthentication */
u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
struct wpabuf *pending_phase2_req;
enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
os_free(data->phase2_types);
eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
+ os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
}
"derive key");
}
+ os_free(data->session_id);
+ data->session_id =
+ eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_PEAP,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-PEAP: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
+ "derive Session-Id");
+ }
+
if (sm->workaround && data->resuming) {
/*
* At least few RADIUS servers (Aegis v1.1.6;
struct eap_peap_data *data = priv;
os_free(data->key_data);
data->key_data = NULL;
+ os_free(data->session_id);
+ data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
os_free(data);
return NULL;
}
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL || !data->phase2_success)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
int eap_peer_peap_register(void)
{
struct eap_method *eap;
eap->has_reauth_data = eap_peap_has_reauth_data;
eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
eap->init_for_reauth = eap_peap_init_for_reauth;
+ eap->getSessionId = eap_peap_get_session_id;
ret = eap_peer_method_register(eap);
if (ret)
--- /dev/null
+/*
+ * EAP proxy definitions
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PROXY_H
+#define EAP_PROXY_H
+
+struct eap_proxy_sm;
+struct eapol_callbacks;
+struct eap_sm;
+struct eap_peer_config;
+
+enum eap_proxy_status {
+ EAP_PROXY_FAILURE = 0x00,
+ EAP_PROXY_SUCCESS
+};
+
+struct eap_proxy_sm *
+eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ void *msg_ctx);
+
+void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy);
+
+int eap_proxy_key_available(struct eap_proxy_sm *sm);
+
+const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len);
+
+struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm);
+
+int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm);
+
+enum eap_proxy_status
+eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
+ int eapReqDataLen);
+
+int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
+ int verbose);
+
+int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len);
+
+int eap_proxy_notify_config(struct eap_proxy_sm *sm,
+ struct eap_peer_config *config);
+
+#endif /* EAP_PROXY_H */
--- /dev/null
+/*
+ * EAP proxy - dummy implementation for build testing
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_proxy.h"
+
+struct eap_proxy_sm *
+eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+ void *msg_ctx)
+{
+ return NULL;
+}
+
+
+void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy)
+{
+}
+
+
+int eap_proxy_key_available(struct eap_proxy_sm *sm)
+{
+ return 0;
+}
+
+
+const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len)
+{
+ return NULL;
+}
+
+
+struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm)
+{
+ return NULL;
+}
+
+
+int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm)
+{
+ return 0;
+}
+
+
+enum eap_proxy_status
+eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
+ int eapReqDataLen)
+{
+ return EAP_PROXY_FAILURE;
+}
+
+
+int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ return 0;
+}
+
+
+int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len)
+{
+ return -1;
+}
+
+
+int eap_proxy_notify_config(struct eap_proxy_sm *sm,
+ struct eap_peer_config *config)
+{
+ return -1;
+}
struct eap_psk_data {
enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 rand_s[EAP_PSK_RAND_LEN];
u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
u8 *id_s, *id_p;
size_t id_s_len, id_p_len;
}
wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
EAP_PSK_RAND_LEN);
+ os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
os_free(data->id_s);
data->id_s_len = len - sizeof(*hdr1);
data->id_s = os_malloc(data->id_s_len);
}
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *id;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ *len = 1 + 2 * EAP_PSK_RAND_LEN;
+ id = os_malloc(*len);
+ if (id == NULL)
+ return NULL;
+
+ id[0] = EAP_TYPE_PSK;
+ os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+ os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+ return id;
+}
+
+
static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_psk_data *data = priv;
eap->process = eap_psk_process;
eap->isKeyAvailable = eap_psk_isKeyAvailable;
eap->getKey = eap_psk_getKey;
+ eap->getSessionId = eap_psk_get_session_id;
eap->get_emsk = eap_psk_get_emsk;
ret = eap_peer_method_register(eap);
}
+static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *id;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ *len = 1 + 2 * EAP_SAKE_RAND_LEN;
+ id = os_malloc(*len);
+ if (id == NULL)
+ return NULL;
+
+ id[0] = EAP_TYPE_SAKE;
+ os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
+ os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
+
+ return id;
+}
+
+
static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sake_data *data = priv;
eap->process = eap_sake_process;
eap->isKeyAvailable = eap_sake_isKeyAvailable;
eap->getKey = eap_sake_getKey;
+ eap->getSessionId = eap_sake_get_session_id;
eap->get_emsk = eap_sake_get_emsk;
ret = eap_peer_method_register(eap);
}
+static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *id;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+ id = os_malloc(*len);
+ if (id == NULL)
+ return NULL;
+
+ id[0] = EAP_TYPE_SIM;
+ os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+ os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
+ EAP_SIM_NONCE_MT_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
+
+ return id;
+}
+
+
static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_sim_data *data = priv;
eap->process = eap_sim_process;
eap->isKeyAvailable = eap_sim_isKeyAvailable;
eap->getKey = eap_sim_getKey;
+ eap->getSessionId = eap_sim_get_session_id;
eap->has_reauth_data = eap_sim_has_reauth_data;
eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
eap->init_for_reauth = eap_sim_init_for_reauth;
struct eap_tls_data {
struct eap_ssl_data ssl;
u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
void *ssl_ctx;
u8 eap_type;
};
return;
eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
+ os_free(data->session_id);
os_free(data);
}
} else {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
}
+
+ os_free(data->session_id);
+ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_TLS,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
+ }
}
struct eap_tls_data *data = priv;
os_free(data->key_data);
data->key_data = NULL;
+ os_free(data->session_id);
+ data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
os_free(data);
return NULL;
}
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
int eap_peer_tls_register(void)
{
struct eap_method *eap;
eap->process = eap_tls_process;
eap->isKeyAvailable = eap_tls_isKeyAvailable;
eap->getKey = eap_tls_getKey;
+ eap->getSessionId = eap_tls_get_session_id;
eap->get_status = eap_tls_get_status;
eap->has_reauth_data = eap_tls_has_reauth_data;
eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
/**
+ * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+ struct eap_ssl_data *data, u8 eap_type,
+ size_t *len)
+{
+ struct tls_keys keys;
+ u8 *out;
+
+ /*
+ * TLS library did not support session ID generation,
+ * so get the needed TLS session parameters
+ */
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ return NULL;
+
+ if (keys.client_random == NULL || keys.server_random == NULL ||
+ keys.master_key == NULL)
+ return NULL;
+
+ *len = 1 + keys.client_random_len + keys.server_random_len;
+ out = os_malloc(*len);
+ if (out == NULL)
+ return NULL;
+
+ /* Session-Id = EAP type || client.random || server.random */
+ out[0] = eap_type;
+ os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+ os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ return out;
+}
+
+
+/**
* eap_peer_tls_reassemble_fragment - Reassemble a received fragment
* @data: Data for TLS processing
* @in_data: Next incoming TLS segment
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, size_t len);
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+ struct eap_ssl_data *data, u8 eap_type,
+ size_t *len);
int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
EapType eap_type, int peap_version,
u8 id, const u8 *in_data, size_t in_len,
int resuming; /* starting a resumed session */
int reauth; /* reauthentication */
u8 *key_data;
+ u8 *session_id;
+ size_t id_len;
struct wpabuf *pending_phase2_req;
os_free(data->phase2_eap_types);
eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
+ os_free(data->session_id);
wpabuf_free(data->pending_phase2_req);
os_free(data);
}
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
data->key_data, EAP_TLS_KEY_LEN);
+ os_free(data->session_id);
+ data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+ EAP_TYPE_TTLS,
+ &data->id_len);
+ if (data->session_id) {
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
+ data->session_id, data->id_len);
+ } else {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
+ }
+
return 0;
}
struct eap_ttls_data *data = priv;
os_free(data->key_data);
data->key_data = NULL;
+ os_free(data->session_id);
+ data->session_id = NULL;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
os_free(data);
return NULL;
}
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *id;
+
+ if (data->session_id == NULL || !data->phase2_success)
+ return NULL;
+
+ id = os_malloc(data->id_len);
+ if (id == NULL)
+ return NULL;
+
+ *len = data->id_len;
+ os_memcpy(id, data->session_id, data->id_len);
+
+ return id;
+}
+
+
int eap_peer_ttls_register(void)
{
struct eap_method *eap;
eap->process = eap_ttls_process;
eap->isKeyAvailable = eap_ttls_isKeyAvailable;
eap->getKey = eap_ttls_getKey;
+ eap->getSessionId = eap_ttls_get_session_id;
eap->get_status = eap_ttls_get_status;
eap->has_reauth_data = eap_ttls_has_reauth_data;
eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
cfg.pin = dev_pw;
cfg.pin_len /= 2;
}
- if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) {
+ if (cfg.pin_len == 6 &&
+ os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
cfg.pin = NULL;
cfg.pin_len = 0;
nfc = 1;
*/
wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
wpabuf_put_buf(msg, pv);
- os_free(pv);
+ wpabuf_free(pv);
plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
WPA_PUT_BE16(phdr->payload_length, plen);
#include "crypto/md5.h"
#include "common/eapol_common.h"
#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
#include "eapol_supp_sm.h"
#define STATE_MACHINE_DATA struct eapol_sm
Boolean cached_pmk;
Boolean unicast_key_received, broadcast_key_received;
+#ifdef CONFIG_EAP_PROXY
+ Boolean use_eap_proxy;
+ struct eap_proxy_sm *eap_proxy;
+#endif /* CONFIG_EAP_PROXY */
};
sm->keyRun = TRUE;
sm->suppSuccess = TRUE;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ if (eap_proxy_key_available(sm->eap_proxy)) {
+ /* New key received - clear IEEE 802.1X EAPOL-Key replay
+ * counter */
+ sm->replay_counter_valid = FALSE;
+ }
+ return;
+ }
+#endif /* CONFIG_EAP_PROXY */
+
if (eap_key_available(sm->eap)) {
/* New key received - clear IEEE 802.1X EAPOL-Key replay
* counter */
struct wpabuf *resp;
wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Get EAP Response from EAP Proxy */
+ resp = eap_proxy_get_eapRespData(sm->eap_proxy);
+ if (resp == NULL) {
+ wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
+ "response data not available");
+ return;
+ }
+ } else
+#endif /* CONFIG_EAP_PROXY */
+
resp = eap_get_eapRespData(sm->eap);
if (resp == NULL) {
wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
SM_STEP_RUN(SUPP_PAE);
SM_STEP_RUN(KEY_RX);
SM_STEP_RUN(SUPP_BE);
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Drive the EAP proxy state machine */
+ if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
+ sm->changed = TRUE;
+ } else
+#endif /* CONFIG_EAP_PROXY */
if (eap_peer_sm_step(sm->eap))
sm->changed = TRUE;
if (!sm->changed)
len += ret;
}
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy)
+ len += eap_proxy_sm_get_status(sm->eap_proxy,
+ buf + len, buflen - len,
+ verbose);
+ else
+#endif /* CONFIG_EAP_PROXY */
len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
return len;
wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
"frame");
sm->eapolEap = TRUE;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ eap_proxy_packet_update(
+ sm->eap_proxy,
+ wpabuf_mhead_u8(sm->eapReqData),
+ wpabuf_len(sm->eapReqData));
+ wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
+ "EAP Req updated");
+ }
+#endif /* CONFIG_EAP_PROXY */
eapol_sm_step(sm);
}
break;
return;
sm->config = config;
+#ifdef CONFIG_EAP_PROXY
+ sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
+#endif /* CONFIG_EAP_PROXY */
if (conf == NULL)
return;
sm->conf.required_keys = conf->required_keys;
sm->conf.fast_reauth = conf->fast_reauth;
sm->conf.workaround = conf->workaround;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Using EAP Proxy, so skip EAP state machine update */
+ return;
+ }
+#endif /* CONFIG_EAP_PROXY */
if (sm->eap) {
eap_set_fast_reauth(sm->eap, conf->fast_reauth);
eap_set_workaround(sm->eap, conf->workaround);
const u8 *eap_key;
size_t eap_len;
+#ifdef CONFIG_EAP_PROXY
+ if (sm->use_eap_proxy) {
+ /* Get key from EAP proxy */
+ if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+ return -1;
+ }
+ eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
+ if (eap_key == NULL) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
+ "eapKeyData");
+ return -1;
+ }
+ goto key_fetched;
+ }
+#endif /* CONFIG_EAP_PROXY */
if (sm == NULL || !eap_key_available(sm->eap)) {
wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
return -1;
wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
return -1;
}
+#ifdef CONFIG_EAP_PROXY
+key_fetched:
+#endif /* CONFIG_EAP_PROXY */
if (len > eap_len) {
wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
"available (len=%lu)",
return NULL;
}
+#ifdef CONFIG_EAP_PROXY
+ sm->use_eap_proxy = FALSE;
+ sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
+ if (sm->eap_proxy == NULL) {
+ wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
+ }
+#endif /* CONFIG_EAP_PROXY */
+
/* Initialize EAPOL state machines */
sm->initialize = TRUE;
eapol_sm_step(sm);
eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
eap_peer_sm_deinit(sm->eap);
+#ifdef CONFIG_EAP_PROXY
+ eap_proxy_deinit(sm->eap_proxy);
+#endif /* CONFIG_EAP_PROXY */
os_free(sm->last_rx_key);
wpabuf_free(sm->eapReqData);
os_free(sm->ctx);
}
if (!probe_req) {
- dev->info.config_methods = msg->config_methods ?
+ u16 new_config_methods;
+ new_config_methods = msg->config_methods ?
msg->config_methods : msg->wps_config_methods;
+ if (new_config_methods &&
+ dev->info.config_methods != new_config_methods) {
+ wpa_printf(MSG_DEBUG, "P2P: Update peer " MACSTR
+ " config_methods 0x%x -> 0x%x",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->info.config_methods,
+ new_config_methods);
+ dev->info.config_methods = new_config_methods;
+ }
}
}
* P2P Device Address or P2P Interface Address)
* @level: Signal level (signal strength of the received frame from the peer)
* @freq: Frequency on which the Beacon or Probe Response frame was received
- * @age_ms: Age of the information in milliseconds
+ * @rx_time: Time when the result was received
* @ies: IEs from the Beacon or Probe Response frame
* @ies_len: Length of ies buffer in octets
* @scan_res: Whether this was based on scan results
* Info attributes.
*/
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
- unsigned int age_ms, int level, const u8 *ies,
+ struct os_time *rx_time, int level, const u8 *ies,
size_t ies_len, int scan_res)
{
struct p2p_device *dev;
struct p2p_message msg;
const u8 *p2p_dev_addr;
int i;
- struct os_time time_now, time_tmp_age, entry_ts;
+ struct os_time time_now;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ies, ies_len, &msg)) {
return -1;
}
- os_get_time(&time_now);
- time_tmp_age.sec = age_ms / 1000;
- time_tmp_age.usec = (age_ms % 1000) * 1000;
- os_time_sub(&time_now, &time_tmp_age, &entry_ts);
+ if (rx_time == NULL) {
+ os_get_time(&time_now);
+ rx_time = &time_now;
+ }
/*
* Update the device entry only if the new peer
* entry is newer than the one previously stored.
*/
- if (dev->last_seen.usec > 0 &&
- os_time_before(&entry_ts, &dev->last_seen)) {
+ if (dev->last_seen.sec > 0 &&
+ os_time_before(rx_time, &dev->last_seen)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not update peer "
+ "entry based on old frame (rx_time=%u.%06u "
+ "last_seen=%u.%06u)",
+ (unsigned int) rx_time->sec,
+ (unsigned int) rx_time->usec,
+ (unsigned int) dev->last_seen.sec,
+ (unsigned int) dev->last_seen.usec);
p2p_parse_free(&msg);
return -1;
}
- os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time));
+ os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_time));
dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
return 0;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
- "P2P: Peer found with Listen frequency %d MHz", freq);
+ "P2P: Peer found with Listen frequency %d MHz "
+ "(rx_time=%u.%06u)", freq, (unsigned int) rx_time->sec,
+ (unsigned int) rx_time->usec);
if (dev->flags & P2P_DEV_USER_REJECTED) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Do not report rejected device");
return 0;
}
+ if (dev->info.config_methods == 0 &&
+ (freq == 2412 || freq == 2437 || freq == 2462)) {
+ /*
+ * If we have only seen a Beacon frame from a GO, we do not yet
+ * know what WPS config methods it supports. Since some
+ * applications use config_methods value from P2P-DEVICE-FOUND
+ * events, postpone reporting this peer until we've fully
+ * discovered its capabilities.
+ *
+ * At least for now, do this only if the peer was detected on
+ * one of the social channels since that peer can be easily be
+ * found again and there are no limitations of having to use
+ * passive scan on this channels, so this can be done through
+ * Probe Response frame that includes the config_methods
+ * information.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report peer " MACSTR " with unknown "
+ "config methods", MAC2STR(addr));
+ return 0;
+ }
+
p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
type);
+ os_get_time(&p2p->find_start);
if (p2p->p2p_scan_running) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is "
"already running");
"now that previous scan was completed");
if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
p2p->num_req_dev_types, p2p->req_dev_types,
- p2p->find_dev_id, p2p->search_delay) < 0)
+ p2p->find_dev_id, p2p->search_delay) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
return 0;
+ }
return 1;
}
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
- unsigned int age, int level, const u8 *ies,
+ struct os_time *rx_time, int level, const u8 *ies,
size_t ies_len)
{
- p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1);
+ if (os_time_before(rx_time, &p2p->find_start)) {
+ /*
+ * The driver may have cached (e.g., in cfg80211 BSS table) the
+ * scan results for relatively long time. To avoid reporting
+ * stale information, update P2P peers only based on results
+ * that have based on frames received after the last p2p_find
+ * operation was started.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore old scan "
+ "result for " MACSTR " (rx_time=%u.%06u)",
+ MAC2STR(bssid), (unsigned int) rx_time->sec,
+ (unsigned int) rx_time->usec);
+ return 0;
+ }
+
+ p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1);
return 0;
}
* @p2p: P2P module context from p2p_init()
* @bssid: BSSID of the scan result
* @freq: Frequency of the channel on which the device was found in MHz
- * @age: Age of the scan result in milliseconds
+ * @rx_time: Time when the result was received
* @level: Signal level (signal strength of the received Beacon/Probe Response
* frame)
* @ies: Pointer to IEs from the scan result
* start of a pending operation, e.g., to start a pending GO negotiation.
*/
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
- unsigned int age, int level, const u8 *ies,
+ struct os_time *rx_time, int level, const u8 *ies,
size_t ies_len);
/**
u8 *find_dev_id;
u8 find_dev_id_buf[ETH_ALEN];
+ struct os_time find_start; /* time of last p2p_find start */
+
struct p2p_group **groups;
size_t num_groups;
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg);
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
- unsigned int age_ms, int level, const u8 *ies,
+ struct os_time *rx_time, int level, const u8 *ies,
size_t ies_len, int scan_res);
struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
"P2P: Invitation Request from unknown peer "
MACSTR, MAC2STR(sa));
- if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+ if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
0)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Invitation Request add device failed "
"P2P: Provision Discovery Request from "
"unknown peer " MACSTR, MAC2STR(sa));
- if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+ if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
0)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Provision Discovery Request add device "
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
size_t supp_rates_len;
+
+ struct ieee80211_ht_capabilities *ht_capabilities;
+
+ u8 qos_info;
+
+ u8 *ext_capab;
+ size_t ext_capab_len;
};
peer->initiator = 0;
os_free(peer->sm_tmr.buf);
peer->sm_tmr.buf = NULL;
+ os_free(peer->ht_capabilities);
+ peer->ht_capabilities = NULL;
+ os_free(peer->ext_capab);
+ peer->ext_capab = NULL;
peer->rsnie_i_len = peer->rsnie_p_len = 0;
peer->cipher = 0;
peer->tpk_set = peer->tpk_success = 0;
pos = rbuf;
if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) {
- /* Overwrite the reason code */
- reason_code = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+ if (reason_code != WLAN_REASON_DEAUTH_LEAVING) {
+ /* Overwrite the reason code */
+ reason_code = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+ }
goto skip_ies;
}
}
+static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->ht_capabilities ||
+ kde->ht_capabilities_len <
+ sizeof(struct ieee80211_ht_capabilities) ) {
+ wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
+ "received");
+ return 0;
+ }
+
+ if (!peer->ht_capabilities) {
+ peer->ht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+ if (peer->ht_capabilities == NULL)
+ return -1;
+ }
+
+ os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
+ sizeof(struct ieee80211_ht_capabilities));
+ wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
+ (u8 *) peer->ht_capabilities,
+ sizeof(struct ieee80211_ht_capabilities));
+
+ return 0;
+}
+
+
+static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->ext_capab) {
+ wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
+ "received");
+ return 0;
+ }
+
+ if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
+ /* Need to allocate buffer to fit the new information */
+ os_free(peer->ext_capab);
+ peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
+ if (peer->ext_capab == NULL)
+ return -1;
+ }
+
+ peer->ext_capab_len = kde->ext_capab_len - 2;
+ os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
+
+ return 0;
+}
+
+
static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
if (copy_supp_rates(&kde, peer) < 0)
goto error;
+ if (copy_peer_ht_capab(&kde, peer) < 0)
+ goto error;
+
+ if (copy_peer_ext_capab(&kde, peer) < 0)
+ goto error;
+
+ peer->qos_info = kde.qosinfo;
+
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
peer = wpa_tdls_add_peer(sm, src_addr, NULL);
skip_rsn_check:
/* add the peer to the driver as a "setup in progress" peer */
- wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0, NULL, 0,
+ NULL, 0);
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
#endif /* CONFIG_TDLS_TESTING */
}
- /* add supported rates and capabilities to the TDLS peer */
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability,
- peer->supp_rates, peer->supp_rates_len);
+ peer->supp_rates, peer->supp_rates_len,
+ peer->ht_capabilities, peer->qos_info,
+ peer->ext_capab, peer->ext_capab_len);
wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
}
if (copy_supp_rates(&kde, peer) < 0)
goto error;
+ if (copy_peer_ht_capab(&kde, peer) < 0)
+ goto error;
+
+ if (copy_peer_ext_capab(&kde, peer) < 0)
+ goto error;
+
+ peer->qos_info = kde.qosinfo;
+
if (!wpa_tdls_get_privacy(sm)) {
peer->rsnie_p_len = 0;
peer->cipher = WPA_CIPHER_NONE;
peer->initiator = 1;
/* add the peer to the driver as a "setup in progress" peer */
- wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0, NULL, 0,
+ NULL, 0);
if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
wpa_tdls_disable_link(sm, peer->addr);
}
+void wpa_tdls_teardown_peers(struct wpa_sm *sm)
+{
+ struct wpa_tdls_peer *peer;
+
+ peer = sm->tdls;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
+
+ while (peer) {
+ wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
+ MAC2STR(peer->addr));
+ if (sm->tdls_external_setup)
+ wpa_tdls_send_teardown(sm, peer->addr,
+ WLAN_REASON_DEAUTH_LEAVING);
+ else
+ wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+
+ peer = peer->next;
+ }
+}
+
+
static void wpa_tdls_remove_peers(struct wpa_sm *sm)
{
struct wpa_tdls_peer *peer, *tmp;
#include "common/defs.h"
#include "common/eapol_common.h"
#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
struct wpa_sm;
struct eapol_sm;
int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add,
u16 capability, const u8 *supp_rates,
- size_t supp_rates_len);
+ size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u8 qosinfo, const u8 *ext_capab,
+ size_t ext_capab_len);
#endif /* CONFIG_TDLS */
void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
const u8 *replay_ctr);
int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_teardown_peers(struct wpa_sm *sm);
void wpa_tdls_deinit(struct wpa_sm *sm);
void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
static inline int
wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
u16 capability, const u8 *supp_rates,
- size_t supp_rates_len)
+ size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
{
if (sm->ctx->tdls_peer_addset)
return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
capability, supp_rates,
- supp_rates_len);
+ supp_rates_len, ht_capab,
+ qosinfo, ext_capab,
+ ext_capab_len);
return -1;
}
#endif /* CONFIG_TDLS */
} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
ie->ext_supp_rates = pos;
ie->ext_supp_rates_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_HT_CAP) {
+ ie->ht_capabilities = pos + 2;
+ ie->ht_capabilities_len = pos[1];
+ } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
+ ie->qosinfo = pos[2];
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)
size_t supp_rates_len;
const u8 *ext_supp_rates;
size_t ext_supp_rates_len;
+ const u8 *ht_capabilities;
+ size_t ht_capabilities_len;
+ u8 qosinfo;
};
int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
* We ignore errors here since errors are normal if we
* are already running as non-root.
*/
+#ifdef ANDROID_SETGROUPS_OVERRIDE
+ gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
+#else /* ANDROID_SETGROUPS_OVERRIDE */
gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+#endif /* ANDROID_SETGROUPS_OVERRIDE */
struct __user_cap_header_struct header;
struct __user_cap_data_struct cap;
}
+struct wpabuf * ndef_build_wifi_hc(int begin)
+{
+ struct wpabuf *hc, *carrier;
+
+ carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
+ if (carrier == NULL)
+ return NULL;
+ wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
+ wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
+ wpabuf_put_str(carrier, wifi_handover_type);
+
+ hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
+ FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
+ "0", 1, carrier);
+ wpabuf_free(carrier);
+
+ return hc;
+}
+
+
struct wpabuf * ndef_build_wifi_hr(void)
{
struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
- struct wpabuf *carrier, *hc;
+ struct wpabuf *hc;
rn = wpabuf_alloc(2);
if (rn == NULL)
if (hr == NULL)
return NULL;
- carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
- if (carrier == NULL) {
- wpabuf_free(hr);
- return NULL;
- }
- wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
- wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
- wpabuf_put_str(carrier, wifi_handover_type);
-
- hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
- "0", 1, carrier);
- wpabuf_free(carrier);
+ hc = ndef_build_wifi_hc(0);
if (hc == NULL) {
wpabuf_free(hr);
return NULL;
}
os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
data->dev_password_len = cfg->pin_len;
+ wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
+ data->dev_password, data->dev_password_len);
}
#ifdef CONFIG_WPS_NFC
if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
+ /* Keep AP PIN as alternative Device Password */
+ data->alt_dev_pw_id = data->dev_pw_id;
+ data->alt_dev_password = data->dev_password;
+ data->alt_dev_password_len = data->dev_password_len;
+
data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
- os_free(data->dev_password);
data->dev_password =
os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
if (data->dev_password == NULL) {
wpabuf_head(cfg->wps->ap_nfc_dev_pw),
wpabuf_len(cfg->wps->ap_nfc_dev_pw));
data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
+ data->dev_password, data->dev_password_len);
}
#endif /* CONFIG_WPS_NFC */
wpabuf_free(data->dh_pubkey_r);
wpabuf_free(data->last_msg);
os_free(data->dev_password);
+ os_free(data->alt_dev_password);
os_free(data->new_psk);
wps_device_data_free(&data->peer_dev);
os_free(data->new_ap_settings);
struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
const struct wpabuf *pubkey,
const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
+ struct wpabuf *dev_pw);
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw);
/* ndef.c */
struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi_hc(int begin);
struct wpabuf * ndef_build_wifi_hr(void);
#ifdef CONFIG_WPS_STRICT
#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
+ struct wpabuf *dev_pw)
+{
+ struct wpabuf *ret;
+
+ if (pubkey == NULL || dev_pw == NULL)
+ return NULL;
+
+ ret = wps_build_nfc_pw_token(id, pubkey, dev_pw);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
struct wpabuf **privkey,
struct wpabuf **dev_pw)
{
- struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+ struct wpabuf *priv = NULL, *pub = NULL, *pw;
void *dh_ctx;
u16 val;
wpabuf_free(*dev_pw);
*dev_pw = pw;
- ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
- if (ndef && ret) {
- struct wpabuf *tmp;
- tmp = ndef_build_wifi(ret);
- wpabuf_free(ret);
- if (tmp == NULL)
- return NULL;
- ret = tmp;
- }
-
- return ret;
+ return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
}
+
#endif /* CONFIG_WPS_NFC */
}
+static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
+{
+ u16 id;
+
+ if (dev_pw_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
+ return -1;
+ }
+
+ id = WPA_GET_BE16(dev_pw_id);
+ if (wps->dev_pw_id == id) {
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
+ "ID from %u to %u", wps->dev_pw_id, id);
+
+ if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
+ wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
+ os_free(wps->dev_password);
+ wps->dev_pw_id = wps->alt_dev_pw_id;
+ wps->dev_password = wps->alt_dev_password;
+ wps->dev_password_len = wps->alt_dev_password_len;
+ wps->alt_dev_password = NULL;
+ wps->alt_dev_password_len = 0;
+ return 0;
+ }
+
+ return -1;
+}
+
+
static enum wps_process_res wps_process_m2(struct wps_data *wps,
const struct wpabuf *msg,
struct wps_parse_attr *attr)
if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
- wps_process_uuid_r(wps, attr->uuid_r)) {
+ wps_process_uuid_r(wps, attr->uuid_r) ||
+ wps_process_dev_pw_id(wps, attr->dev_password_id)) {
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
size_t dev_password_len;
u16 dev_pw_id;
int pbc;
+ u8 *alt_dev_password;
+ size_t alt_dev_password_len;
+ u16 alt_dev_pw_id;
/**
* request_type - Request Type attribute from (Re)AssocReq
} else {
pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
&pin_len);
+ if (pin && wps->dev_pw_id >= 0x10) {
+ wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device "
+ "Password ID, but PIN found");
+ /*
+ * See whether Enrollee is willing to use PIN instead.
+ */
+ wps->dev_pw_id = DEV_PW_DEFAULT;
+ }
}
if (pin == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
NEED_AES_CBC=y
endif
+ifdef CONFIG_EAP_PROXY
+L_CFLAGS += -DCONFIG_EAP_PROXY
+OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
+include eap_proxy_$(CONFIG_EAP_PROXY).mk
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
ifdef CONFIG_EAP_AKA_PRIME
# EAP-AKA'
ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
NEED_AES_CBC=y
endif
+ifdef CONFIG_EAP_PROXY
+CFLAGS += -DCONFIG_EAP_PROXY
+OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
+include eap_proxy_$(CONFIG_EAP_PROXY).mk
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
ifdef CONFIG_EAP_AKA_PRIME
# EAP-AKA'
ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
tokens during manufacturing (each station needs to have its own random
keys).
+The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
+NFC configuration token when wpa_supplicant is controlling an AP
+interface (AP or P2P GO). The output value from this command is a
+hexdump of the current AP configuration (WPS parameter requests this to
+include only the WPS attributes; NDEF parameter requests additional NDEF
+encapsulation to be included). This data needs to be written to an NFC
+tag with an external program. Once written, the NFC configuration token
+can be used to touch an NFC interface on a station to provision the
+credentials needed to access the network.
+
If the station includes NFC interface and reads an NFC tag with a MIME
media type "application/vnd.wfa.wsc", the NDEF message payload (with or
without NDEF encapsulation) can be delivered to wpa_supplicant using the
of NFC connection handover select. The payload may include multiple
carriers the the applicable ones are matched based on the media
type.
+
+"nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
+<carrier from handover select>" can be used as an alternative way for
+reporting completed NFC connection handover. The first parameter
+indicates whether the local device initiated or responded to the
+connection handover and the carrier records are the selected carrier
+from the handover request and select messages as a hexdump.
hapd->conf->ap_pin = NULL;
}
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ return hostapd_wps_nfc_config_token(hapd, ndef);
+}
+
+
+struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ return hostapd_wps_nfc_hs_cr(hapd, ndef);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
#endif /* CONFIG_WPS */
void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
int offset);
+struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef);
+struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef);
#endif /* AP_H */
}
-static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src)
+static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
+ struct os_time *fetch_time)
{
os_time_t usec;
dst->level = src->level;
dst->tsf = src->tsf;
- os_get_time(&dst->last_update);
+ dst->last_update.sec = fetch_time->sec;
+ dst->last_update.usec = fetch_time->usec;
dst->last_update.sec -= src->age / 1000;
usec = (src->age % 1000) * 1000;
if (dst->last_update.usec < usec) {
static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len,
- struct wpa_scan_res *res)
+ struct wpa_scan_res *res,
+ struct os_time *fetch_time)
{
struct wpa_bss *bss;
return NULL;
bss->id = wpa_s->bss_next_id++;
bss->last_update_idx = wpa_s->bss_update_idx;
- wpa_bss_copy_res(bss, res);
+ wpa_bss_copy_res(bss, res, fetch_time);
os_memcpy(bss->ssid, ssid, ssid_len);
bss->ssid_len = ssid_len;
bss->ie_len = res->ie_len;
static struct wpa_bss *
wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
- struct wpa_scan_res *res)
+ struct wpa_scan_res *res, struct os_time *fetch_time)
{
u32 changes;
changes = wpa_bss_compare_res(bss, res);
bss->scan_miss_count = 0;
bss->last_update_idx = wpa_s->bss_update_idx;
- wpa_bss_copy_res(bss, res);
+ wpa_bss_copy_res(bss, res, fetch_time);
/* Move the entry to the end of the list */
dl_list_del(&bss->list);
if (bss->ie_len + bss->beacon_ie_len >=
* wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
* @wpa_s: Pointer to wpa_supplicant data
* @res: Scan result
+ * @fetch_time: Time when the result was fetched from the driver
*
* This function updates a BSS table entry (or adds one) based on a scan result.
* This is called separately for each scan result between the calls to
* wpa_bss_update_start() and wpa_bss_update_end().
*/
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_res *res)
+ struct wpa_scan_res *res,
+ struct os_time *fetch_time)
{
const u8 *ssid, *p2p;
struct wpa_bss *bss;
* (to save memory) */
bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
if (bss == NULL)
- bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
+ bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
else
- bss = wpa_bss_update(wpa_s, bss, res);
+ bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
if (bss == NULL)
return;
/**
+ * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @idf: Smallest allowed identifier assigned for the entry
+ * @idf: Largest allowed identifier assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
+ * smallest id value to be fetched within the specified range without the
+ * caller having to know the exact id.
+ */
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+ unsigned int idf, unsigned int idl)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+ if (bss->id >= idf && bss->id <= idl)
+ return bss;
+ }
+ return NULL;
+}
+
+
+/**
* wpa_bss_get_ie - Fetch a specified information element from a BSS entry
* @bss: BSS table entry
* @ie: Information element identitifier (WLAN_EID_*)
void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
- struct wpa_scan_res *res);
+ struct wpa_scan_res *res,
+ struct os_time *fetch_time);
void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
int new_scan);
int wpa_bss_init(struct wpa_supplicant *wpa_s);
struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
const u8 *dev_addr);
struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
+struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
+ unsigned int idf, unsigned int idl);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
{ INT_RANGE(access_network_type, 0, 15), 0 },
{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
{ STR(autoscan), 0 },
- { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 },
- { BIN(wps_nfc_dh_pubkey), 0 },
- { BIN(wps_nfc_dh_privkey), 0 },
- { BIN(wps_nfc_dev_pw), 0 },
+ { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
+ CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
+ { BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
{ INT(p2p_go_max_inactivity), 0 },
{ INT_RANGE(auto_interworking, 0, 1), 0 },
"parse '%s'.", line, pos);
ret = -1;
}
+ if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
+ config->wps_nfc_pw_from_config = 1;
config->changed_parameters |= field->changed_flag;
break;
}
#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
+#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
/**
* struct wpa_config - wpa_supplicant configuration data
char *autoscan;
/**
+ * wps_nfc_pw_from_config - NFC Device Password was read from config
+ *
+ * This parameter can be determined whether the NFC Device Password was
+ * included in the configuration (1) or generated dynamically (0). Only
+ * the former case is re-written back to the configuration file.
+ */
+ int wps_nfc_pw_from_config;
+
+ /**
* wps_nfc_dev_pw_id - NFC Device Password ID for password token
*/
int wps_nfc_dev_pw_id;
#endif /* CONFIG_INTERWORKING */
if (config->pbc_in_m1)
fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
- if (config->wps_nfc_dev_pw_id)
- fprintf(f, "wps_nfc_dev_pw_id=%d\n",
- config->wps_nfc_dev_pw_id);
- write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey);
- write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey);
- write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+ if (config->wps_nfc_pw_from_config) {
+ if (config->wps_nfc_dev_pw_id)
+ fprintf(f, "wps_nfc_dev_pw_id=%d\n",
+ config->wps_nfc_dev_pw_id);
+ write_global_bin(f, "wps_nfc_dh_pubkey",
+ config->wps_nfc_dh_pubkey);
+ write_global_bin(f, "wps_nfc_dh_privkey",
+ config->wps_nfc_dh_privkey);
+ write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+ }
if (config->ext_password_backend)
fprintf(f, "ext_password_backend=%s\n",
}
+static int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_nfc_config_token(wpa_s, ndef);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
static int wpa_supplicant_ctrl_iface_wps_nfc_token(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
- char *reply, size_t max_len)
+ char *reply, size_t max_len,
+ int cr)
{
struct wpabuf *buf;
int res;
- buf = wpas_wps_nfc_handover_req(wpa_s);
+ buf = wpas_wps_nfc_handover_req(wpa_s, cr);
if (buf == NULL)
return -1;
if (os_strcmp(cmd, "NDEF") != 0)
return -1;
- if (os_strcmp(pos, "WPS") == 0) {
- return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply,
- max_len);
+ if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_req_wps(
+ wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
}
return -1;
static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
- char *reply, size_t max_len)
+ char *reply, size_t max_len,
+ int ndef, int cr)
{
struct wpabuf *buf;
int res;
- buf = wpas_wps_nfc_handover_sel(wpa_s);
+ buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr);
if (buf == NULL)
return -1;
size_t max_len)
{
char *pos;
+ int ndef;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
- if (os_strcmp(cmd, "NDEF") != 0)
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
return -1;
- if (os_strcmp(pos, "WPS") == 0) {
- return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply,
- max_len);
+ if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_wps(
+ wpa_s, reply, max_len, ndef,
+ os_strcmp(pos, "WPS-CR") == 0);
}
return -1;
return ret;
}
+
+static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *req, *sel;
+ int ret;
+ char *pos, *role, *type, *pos2;
+
+ role = cmd;
+ pos = os_strchr(role, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ type = pos;
+ pos = os_strchr(type, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ return -1;
+ *pos2++ = '\0';
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ req = wpabuf_alloc(len);
+ if (req == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+ wpabuf_free(req);
+ return -1;
+ }
+
+ len = os_strlen(pos2);
+ if (len & 0x01) {
+ wpabuf_free(req);
+ return -1;
+ }
+ len /= 2;
+
+ sel = wpabuf_alloc(len);
+ if (sel == NULL) {
+ wpabuf_free(req);
+ return -1;
+ }
+ if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+ wpabuf_free(req);
+ wpabuf_free(sel);
+ return -1;
+ }
+
+ if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
+ ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+ } else {
+ wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
+ "reported: role=%s type=%s", role, type);
+ ret = -1;
+ }
+ wpabuf_free(req);
+ wpabuf_free(sel);
+
+ return ret;
+}
+
#endif /* CONFIG_WPS_NFC */
return 0;
}
- id1 = atoi(cmd + 6);
- bss = wpa_bss_get_id(wpa_s, id1);
- id2 = atoi(ctmp + 1);
- if (id2 == 0)
+ if (*(cmd + 6) == '-')
+ id1 = 0;
+ else
+ id1 = atoi(cmd + 6);
+ ctmp++;
+ if (*ctmp >= '0' && *ctmp <= '9')
+ id2 = atoi(ctmp);
+ else
+ id2 = (unsigned int) -1;
+ bss = wpa_bss_get_id_range(wpa_s, id1, id2);
+ if (id2 == (unsigned int) -1)
bsslast = dl_list_last(&wpa_s->bss_id,
struct wpa_bss,
list_id);
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+ os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0 ||
os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
+ wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
wpa_s, buf + 14, reply, reply_size);
} else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
reply_len = -1;
+ } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
+ if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
+ reply_len = -1;
#endif /* CONFIG_WPS_NFC */
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
case WPAS_DBUS_BSS_PROP_RSN:
prop = "RSN";
break;
+ case WPAS_DBUS_BSS_PROP_WPS:
+ prop = "WPS";
+ break;
case WPAS_DBUS_BSS_PROP_IES:
prop = "IEs";
break;
}
+static int bss_is_dmg(struct wpa_bss *bss)
+{
+ return bss->freq > 45000;
+}
+
+
+/*
+ * Test whether BSS is in an ESS.
+ * This is done differently in DMG (60 GHz) and non-DMG bands
+ */
+static int bss_is_ess(struct wpa_bss *bss)
+{
+ if (bss_is_dmg(bss)) {
+ return (bss->caps & IEEE80211_CAP_DMG_MASK) ==
+ IEEE80211_CAP_DMG_AP;
+ }
+
+ return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+ IEEE80211_CAP_ESS);
+}
+
+
static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group)
continue;
}
- if (bss->caps & IEEE80211_CAP_IBSS) {
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) "
- "network");
+ if (!bss_is_ess(bss)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network");
continue;
}
wpas_wps_start_pbc(wpa_s, NULL, 0);
#endif /* CONFIG_WPS */
break;
+ case EVENT_CONNECT_FAILED_REASON:
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface || !data)
+ break;
+ hostapd_event_connect_failed_reason(
+ wpa_s->ap_iface->bss[0],
+ data->connect_failed_reason.addr,
+ data->connect_failed_reason.code);
+#endif /* CONFIG_AP */
+ break;
default:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
#!/usr/bin/python
#
# Example nfcpy to wpa_supplicant wrapper for WPS NFC operations
-# Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import os
import sys
import time
+import random
+import StringIO
import nfc
import nfc.ndef
import nfc.llcp
import nfc.handover
+import logging
+logging.basicConfig()
+
import wpactrl
wpas_ctrl = '/var/run/wpa_supplicant'
print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+def wpas_get_config_token():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
+
+
+def wpas_get_password_token():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
+
+
def wpas_get_handover_req():
wpas = wpas_connect()
if (wpas == None):
return None
- return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS").rstrip().decode("hex")
+ return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
-def wpas_put_handover_sel(message):
+def wpas_report_handover(req, sel):
wpas = wpas_connect()
if (wpas == None):
- return
- print wpas.request("NFC_RX_HANDOVER_SEL " + str(message).encode("hex"))
+ return None
+ return wpas.request("NFC_REPORT_HANDOVER INIT WPS " +
+ str(req).encode("hex") + " " +
+ str(sel).encode("hex"))
def wps_handover_init(peer):
data = wpas_get_handover_req()
if (data == None):
- print "Could not get handover request message from wpa_supplicant"
+ print "Could not get handover request carrier record from wpa_supplicant"
return
- print "Handover request from wpa_supplicant: " + data.encode("hex")
- message = nfc.ndef.Message(data)
- print "Parsed handover request: " + message.pretty()
+ print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
+ record = nfc.ndef.Record()
+ f = StringIO.StringIO(data)
+ record._read(f)
+ record = nfc.ndef.HandoverCarrierRecord(record)
+ print "Parsed handover request carrier record:"
+ print record.pretty()
+
+ message = nfc.ndef.HandoverRequestMessage(version="1.2")
+ message.nonce = random.randint(0, 0xffff)
+ message.add_carrier(record, "active")
+
+ print "Handover request:"
+ print message.pretty()
nfc.llcp.activate(peer);
- time.sleep(0.5)
client = nfc.handover.HandoverClient()
try:
print "Receiving handover response"
message = client._recv()
+ if message is None:
+ print "No response received"
+ nfc.llcp.shutdown()
+ client.close()
+ return
+ if message.type != "urn:nfc:wkt:Hs":
+ print "Response was not Hs - received: " + message.type
+ nfc.llcp.shutdown()
+ client.close()
+ return
+
+ print "Received message"
+ print message.pretty()
+ message = nfc.ndef.HandoverSelectMessage(message)
print "Handover select received"
print message.pretty()
- wpas_put_handover_sel(message)
+
+ for carrier in message.carriers:
+ print "Remote carrier type: " + carrier.type
+ if carrier.type == "application/vnd.wfa.wsc":
+ print "WPS carrier type match - send to wpa_supplicant"
+ wpas_report_handover(data, carrier.record)
+ wifi = nfc.ndef.WifiConfigRecord(carrier.record)
+ print wifi.pretty()
print "Remove peer"
nfc.llcp.shutdown()
time.sleep(0.1)
+def wps_write_config_tag(clf):
+ print "Write WPS config token"
+ data = wpas_get_config_token()
+ if (data == None):
+ print "Could not get WPS config token from wpa_supplicant"
+ return
+
+ print "Touch an NFC tag"
+ while True:
+ tag = clf.poll()
+ if tag == None:
+ time.sleep(0.1)
+ continue
+ break
+
+ print "Tag found - writing"
+ tag.ndef.message = data
+ print "Done - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def wps_write_password_tag(clf):
+ print "Write WPS password token"
+ data = wpas_get_password_token()
+ if (data == None):
+ print "Could not get WPS password token from wpa_supplicant"
+ return
+
+ print "Touch an NFC tag"
+ while True:
+ tag = clf.poll()
+ if tag == None:
+ time.sleep(0.1)
+ continue
+ break
+
+ print "Tag found - writing"
+ tag.ndef.message = data
+ print "Done - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def find_peer(clf):
+ while True:
+ if nfc.llcp.connected():
+ print "LLCP connected"
+ general_bytes = nfc.llcp.startup({})
+ peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
+ if isinstance(peer, nfc.DEP):
+ print "listen -> DEP";
+ if peer.general_bytes.startswith("Ffm"):
+ print "Found DEP"
+ return peer
+ print "mismatch in general_bytes"
+ print peer.general_bytes
+
+ peer = clf.poll(general_bytes)
+ if isinstance(peer, nfc.DEP):
+ print "poll -> DEP";
+ if peer.general_bytes.startswith("Ffm"):
+ print "Found DEP"
+ return peer
+ print "mismatch in general_bytes"
+ print peer.general_bytes
+
+ if peer:
+ print "Found tag"
+ return peer
+
+
def main():
clf = nfc.ContactlessFrontend()
try:
+ if len(sys.argv) > 1 and sys.argv[1] == "write-config":
+ wps_write_config_tag(clf)
+ raise SystemExit
+
+ if len(sys.argv) > 1 and sys.argv[1] == "write-password":
+ wps_write_password_tag(clf)
+ raise SystemExit
+
while True:
print "Waiting for a tag or peer to be touched"
- while True:
- general_bytes = nfc.llcp.startup({})
- tag = clf.poll(general_bytes)
- if tag == None:
- continue
-
- if isinstance(tag, nfc.DEP):
- wps_handover_init(tag)
- break
-
- if tag.ndef:
- wps_tag_read(tag)
- break
-
- if tag:
- print "Not an NDEF tag - remove tag"
- while tag.is_present:
- time.sleep(0.1)
- break
+ tag = find_peer(clf)
+ if isinstance(tag, nfc.DEP):
+ wps_handover_init(tag)
+ continue
+
+ if tag.ndef:
+ wps_tag_read(tag)
+ continue
+
+ print "Not an NDEF tag - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
except KeyboardInterrupt:
raise SystemExit
#endif
static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+static struct wpa_cred * interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+static struct wpa_cred * interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
static void interworking_reconnect(struct wpa_supplicant *wpa_s)
static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
struct wpa_bss *bss)
{
#ifdef INTERWORKING_3GPP
- struct wpa_cred *cred;
struct wpa_ssid *ssid;
const u8 *ie;
int eap_type;
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return -1;
- for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- char *sep;
- const char *imsi;
- int mnc_len;
-
-#ifdef PCSC_FUNCS
- if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
- wpa_s->imsi[0]) {
- imsi = wpa_s->imsi;
- mnc_len = wpa_s->mnc_len;
- goto compare;
- }
-#endif /* PCSC_FUNCS */
-
- if (cred->imsi == NULL || !cred->imsi[0] ||
- cred->milenage == NULL || !cred->milenage[0])
- continue;
-
- sep = os_strchr(cred->imsi, '-');
- if (sep == NULL ||
- (sep - cred->imsi != 5 && sep - cred->imsi != 6))
- continue;
- mnc_len = sep - cred->imsi - 3;
- imsi = cred->imsi;
-
-#ifdef PCSC_FUNCS
- compare:
-#endif /* PCSC_FUNCS */
- if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
- break;
- }
- if (cred == NULL)
- return -1;
-
ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
if (ie == NULL)
return -1;
int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
- struct wpa_cred *cred;
+ struct wpa_cred *cred, *cred_rc, *cred_3gpp;
struct wpa_ssid *ssid;
struct nai_realm *realm;
struct nai_realm_eap *eap = NULL;
return -1;
}
- cred = interworking_credentials_available_roaming_consortium(wpa_s,
- bss);
- if (cred)
- return interworking_connect_roaming_consortium(wpa_s, cred,
+ cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
+ bss);
+ if (cred_rc) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+ "consortium matching credential priority %d",
+ cred_rc->priority);
+ }
+
+ cred = interworking_credentials_available_realm(wpa_s, bss);
+ if (cred) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
+ "matching credential priority %d",
+ cred->priority);
+ }
+
+ cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+ if (cred_3gpp) {
+ wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
+ "credential priority %d", cred_3gpp->priority);
+ }
+
+ if (cred_rc &&
+ (cred == NULL || cred_rc->priority >= cred->priority) &&
+ (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+ return interworking_connect_roaming_consortium(wpa_s, cred_rc,
bss, ie);
+ if (cred_3gpp &&
+ (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+ return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
+ }
+
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+ "found for " MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
&count);
if (realm == NULL) {
wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
"Realm list from " MACSTR, MAC2STR(bss->bssid));
- count = 0;
+ return -1;
}
- for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
- for (i = 0; i < count; i++) {
- if (!nai_realm_match(&realm[i], cred->realm))
- continue;
- eap = nai_realm_find_eap(cred, &realm[i]);
- if (eap)
- break;
- }
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ eap = nai_realm_find_eap(cred, &realm[i]);
if (eap)
break;
}
if (!eap) {
- if (interworking_connect_3gpp(wpa_s, bss) == 0) {
- if (realm)
- nai_realm_free(realm, count);
- return 0;
- }
-
wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
"and EAP method found for " MACSTR,
MAC2STR(bss->bssid));
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+ void *timeout_ctx);
static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
int group_added);
static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
+ struct os_time time_tmp_age, entry_ts;
+ time_tmp_age.sec = bss->age / 1000;
+ time_tmp_age.usec = (bss->age % 1000) * 1000;
+ os_time_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
- bss->freq, bss->age, bss->level,
+ bss->freq, &entry_ts, bss->level,
(const u8 *) (bss + 1),
bss->ie_len) > 0)
break;
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+ if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
+ "timeout");
if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
} else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
- wpa_s->conf->p2p_oper_reg_class == 124) {
+ wpa_s->conf->p2p_oper_reg_class == 116 ||
+ wpa_s->conf->p2p_oper_reg_class == 117 ||
+ wpa_s->conf->p2p_oper_reg_class == 124 ||
+ wpa_s->conf->p2p_oper_reg_class == 126 ||
+ wpa_s->conf->p2p_oper_reg_class == 127) {
params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
{
+ int ret;
+
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
- return p2p_in_progress(wpa_s->global->p2p);
+ ret = p2p_in_progress(wpa_s->global->p2p);
+ if (ret == 0) {
+ /*
+ * Check whether there is an ongoing WPS provisioning step (or
+ * other parts of group formation) on another interface since
+ * p2p_in_progress() does not report this to avoid issues for
+ * scans during such provisioning step.
+ */
+ if (wpa_s->global->p2p_group_formation &&
+ wpa_s->global->p2p_group_formation != wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
+ "in group formation",
+ wpa_s->global->p2p_group_formation->ifname);
+ ret = 1;
+ }
+ }
+
+ return ret;
}
== WPA_SCAN_LEVEL_DBM) {
int snr = r->level - r->noise;
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
- "noise=%d level=%d snr=%d%s flags=0x%x",
+ "noise=%d level=%d snr=%d%s flags=0x%x "
+ "age=%u",
MAC2STR(r->bssid), r->freq, r->qual,
r->noise, r->level, snr,
- snr >= GREAT_SNR ? "*" : "", r->flags);
+ snr >= GREAT_SNR ? "*" : "", r->flags,
+ r->age);
} else {
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
- "noise=%d level=%d flags=0x%x",
+ "noise=%d level=%d flags=0x%x age=%u",
MAC2STR(r->bssid), r->freq, r->qual,
- r->noise, r->level, r->flags);
+ r->noise, r->level, r->flags, r->age);
}
pos = (u8 *) (r + 1);
if (r->ie_len)
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
}
+ if (scan_res->fetch_time.sec == 0) {
+ /*
+ * Make sure we have a valid timestamp if the driver wrapper
+ * does not set this.
+ */
+ os_get_time(&scan_res->fetch_time);
+ }
filter_scan_res(wpa_s, scan_res);
#ifdef CONFIG_WPS
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
- wpa_bss_update_scan_res(wpa_s, scan_res->res[i]);
+ wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
+ &scan_res->fetch_time);
wpa_bss_update_end(wpa_s, info, new_scan);
return scan_res;
}
+static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv);
+}
+
+
static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return ret;
}
+
+static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv);
+}
+
#endif /* CONFIG_WPS_NFC */
{ "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
cli_cmd_flag_none,
"[BSSID] = start Wi-Fi Protected Setup: NFC" },
+ { "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL,
+ cli_cmd_flag_none,
+ "<WPS|NDEF> = build configuration token" },
{ "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
cli_cmd_flag_none,
"<WPS|NDEF> = create password token" },
{ "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
cli_cmd_flag_none,
"<hexdump of payload> = report received NFC handover select" },
+ { "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL,
+ cli_cmd_flag_none,
+ "<role> <type> <hexdump of req> <hexdump of sel> = report completed "
+ "NFC handover" },
#endif /* CONFIG_WPS_NFC */
{ "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
cli_cmd_flag_sensitive,
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
struct wpa_ssid *ssid = wpa_s->current_ssid;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
- MACSTR " completed %s [id=%d id_str=%s]",
- MAC2STR(wpa_s->bssid), "(auth)",
+ MACSTR " completed (auth) [id=%d id_str=%s]",
+ MAC2STR(wpa_s->bssid),
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "");
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
zero_addr = 1;
}
+#ifdef CONFIG_TDLS
+ wpa_tdls_teardown_peers(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
if (addr) {
wpa_drv_deauthenticate(wpa_s, addr, reason_code);
os_memset(&event, 0, sizeof(event));
static int wpa_supplicant_tdls_peer_addset(
void *ctx, const u8 *peer, int add, u16 capability,
- const u8 *supp_rates, size_t supp_rates_len)
+ const u8 *supp_rates, size_t supp_rates_len,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
{
struct wpa_supplicant *wpa_s = ctx;
struct hostapd_sta_add_params params;
+ os_memset(¶ms, 0, sizeof(params));
+
params.addr = peer;
params.aid = 1;
params.capability = capability;
params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
- params.ht_capabilities = NULL;
+
+ /*
+ * TDLS Setup frames do not contain WMM IEs, hence need to depend on
+ * qosinfo to check if the peer is WMM capable.
+ */
+ if (qosinfo)
+ params.flags |= WPA_STA_WMM;
+
+ params.ht_capabilities = ht_capab;
+ params.qosinfo = qosinfo;
params.listen_interval = 0;
params.supp_rates = supp_rates;
params.supp_rates_len = supp_rates_len;
params.set = !add;
+ params.ext_capab = ext_capab;
+ params.ext_capab_len = ext_capab_len;
return wpa_drv_sta_add(wpa_s, ¶ms);
}
#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef)
+{
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ return wpas_ap_wps_nfc_config_token(wpa_s, ndef);
+#endif /* CONFIG_AP */
+ return NULL;
+}
+
+
struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
{
+ if (wpa_s->conf->wps_nfc_pw_from_config) {
+ return wps_nfc_token_build(ndef,
+ wpa_s->conf->wps_nfc_dev_pw_id,
+ wpa_s->conf->wps_nfc_dh_pubkey,
+ wpa_s->conf->wps_nfc_dev_pw);
+ }
+
return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
&wpa_s->conf->wps_nfc_dh_pubkey,
&wpa_s->conf->wps_nfc_dh_privkey,
}
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
{
+ if (cr)
+ return ndef_build_wifi_hc(1);
return ndef_build_wifi_hr();
}
-struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s)
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int cr)
{
- return NULL;
+ if (!cr)
+ return NULL;
+ return wpas_ap_wps_nfc_handover_sel(wpa_s, ndef);
}
return ret;
}
+
+int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported");
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req);
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel);
+ return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
+}
+
#endif /* CONFIG_WPS_NFC */
int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef);
struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
const struct wpabuf *data);
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s);
-struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr);
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+ int ndef, int cr);
int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
const struct wpabuf *data);
int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
const struct wpabuf *data);
+int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);