Cumulative patch from commit 2c0efd9e49b15da163cee659409eee85390620c3
2c0efd9 P2P: Fix stopping on search after SD callback
db3168d OpenSSL: Use SSL_cache_hit() when available
68ae477 OpenSSL: Use library wrapper functions to access cert store
abe96d0 P2P: Clean up Listen channel optimization debug prints
d2ca6ba Fix hostapd obss_interval documentation
d027c7b Fix 20/40 MHz co-ex report processing with obss_interval=0
93eca61 P2PS: Do not remove pending interface on p2p_stop_find
ae2dd83 P2PS: Allow PD retry in SEARCH and LISTEN_ONLY also
87d5ef5 P2PS: Add commands to control interface redir list
0cf12b3 P2PS: Send P2P_FIND_STOPPED event during P2P SD also
306aaf4 P2PS: Start WPS registrar upon GO formation
9e96e46 P2PS: PD Response processing
ab8ee77 P2PS: Provision Discovery fail event
1300cc8 P2PS: PD Request processing and PD Response building
5fefce2 P2PS: Callback to send P2PS provisioning events
9a58e52 P2PS: Callback to create pending group after sending PD Response
895d94d P2PS: Callback to remove stale persistent groups
f309c18 P2PS: ASP provisioning commands to control interface
6d90851 P2PS: Process P2PS provisioning commands
369678a P2PS: Add P2PS attributes into PD Request if requested
59fec34 P2PS: Allow p2p_build_ssid() to use pre-set SSID
d4b43b5 P2PS: Add support to send ASP-RESP events
6df08d0 P2PS: Logic to parse GAS requests for ASP services
5a4102c P2PS: Add support to send ASP service requests
095b3c4 P2PS: Add Application Service Info to device found events
4660e73 P2PS: Add Advertised Service Info into Probe Response frames
9e7321e P2PS: Parse Probe Request frames for matching ASP hashes
ae9d45f P2PS: Extend add/del services logic to support ASP
ea8e033 P2P: Allow p2p_get_group_num_members() to be called with NULL
4f88fc0 P2PS: WPS changes needed for P2PS default PIN
1a94b0a P2PS: Add service hash to Probe Request frames
5177509 P2PS: Add option to specify seek strings into P2P_FIND
5f18501 P2PS: Helper functions to build new P2P attributes
60d1148 P2PS: Add parsing of new P2P attributes
b9348be P2PS: Add new P2P identifier assignments from P2P spec v1.5
c3d6c71 Add helper functions for escaping and unescaping UTF-8
66eaf8a Fix driver-offloaded offchannel TX done processing
c5e154c P2P: Add P2P state into p2p_send_action_cb() debug entry
f2dc06e P2P: Ignore remain-on-channel callback event if not waiting for one
6a6569b HS 2.0R2: Add password to DB in case of machine managed subscription
f0d0a5d Improve BSS selection with default noise floor values
7f7bfba Add an option allow canned EAP-Success for wired IEEE 802.1X
49fcc32 EAP-MSCHAPv2 peer: Add option to disable password retry query
66bc683 hostapd: Simplify vlan_add_dynamic error paths
99805a0 Interworking: Convert wpa_printf() to wpa_msg()
b42f539 Add a variable to handle extra CFLAGS values
e6dd819 Work around Linux packet socket regression
7650f9e Fix resource leaks on rsn_preauth_init() error paths
a565e03 dhcp_snoop: Make IPv4 addresses human readable in debug log
2dd4f3a Fix STA re-bind to another VLAN on reauthentication
4437f8f Free old eap_user_file data on configuration change
1180dd6 WPA auth: Disconnect STA if MSK cannot be fetched
40aaa64 WPA auth: Clear temporary MSK storage from stack explicitly
01b481a Convert couple of remaining printf to wpa_printf in ap_list
bfaefd5 EAP-PEAP server: Fix Phase 2 TLV length in error case
745d936 mesh: Create new station entry on popen frames
41bff86 mesh: Always free the station if peering failed
871ff0b mesh: Sync plink state with kernel
ba42261 Simplify eapol_sm_notify_pmkid_attempt()
993a865 Add eap_session_id to wpa_supplicant STATUS output
f19c907 OpenSSL: Implement aes_wrap() and aes_unwrap()
fee31f7 OpenSSL: Remove support for versions older than 0.9.8
8bf3030 OpenSSL: Use a common helper function for HMAC
983c6a6 OpenSSL: Replace internal HMAC-MD5 implementation
Change-Id: I5743003f14efae324537f7dc2c5e6ada892a33a7
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index c8ef46b..6fcefdd 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -181,8 +181,6 @@
OBJS += src/ap/ctrl_iface_ap.c
endif
-OBJS += src/crypto/md5.c
-
L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
ifdef CONFIG_IAPP
@@ -677,7 +675,9 @@
AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c
endif
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += src/crypto/aes-wrap.c
+endif
ifdef NEED_AES_EAX
AESOBJS += src/crypto/aes-eax.c
NEED_AES_CTR=y
@@ -692,9 +692,11 @@
AESOBJS += src/crypto/aes-omac1.c
endif
ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
NEED_AES_DEC=y
AESOBJS += src/crypto/aes-unwrap.c
endif
+endif
ifdef NEED_AES_CBC
NEED_AES_DEC=y
AESOBJS += src/crypto/aes-cbc.c
@@ -735,6 +737,10 @@
OBJS += $(SHA1OBJS)
endif
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/md5.c
+endif
+
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
OBJS += src/crypto/md5-internal.c
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 894b652..eace68c 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -6,6 +6,7 @@
CFLAGS = -MMD -O2 -Wall -g
endif
+CFLAGS += $(EXTRA_CFLAGS)
CFLAGS += -I$(abspath ../src)
CFLAGS += -I$(abspath ../src/utils)
@@ -170,8 +171,6 @@
OBJS += ../src/ap/ctrl_iface_ap.o
endif
-OBJS += ../src/crypto/md5.o
-
CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
ifdef CONFIG_IAPP
@@ -671,7 +670,9 @@
AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
endif
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += ../src/crypto/aes-wrap.o
+endif
ifdef NEED_AES_EAX
AESOBJS += ../src/crypto/aes-eax.o
NEED_AES_CTR=y
@@ -686,9 +687,11 @@
AESOBJS += ../src/crypto/aes-omac1.o
endif
ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
NEED_AES_DEC=y
AESOBJS += ../src/crypto/aes-unwrap.o
endif
+endif
ifdef NEED_AES_CBC
NEED_AES_DEC=y
AESOBJS += ../src/crypto/aes-cbc.o
@@ -728,6 +731,10 @@
OBJS += $(SHA1OBJS)
endif
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/md5.o
+endif
+
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
OBJS += ../src/crypto/md5-internal.o
@@ -954,7 +961,7 @@
$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
@$(E) " LD " $@
-NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) ../src/crypto/md5.o
+NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
NOBJS += ../src/utils/common.o
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 7cbb46b..e3cad7c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -216,7 +216,7 @@
FILE *f;
char buf[512], *pos, *start, *pos2;
int line = 0, ret = 0, num_methods;
- struct hostapd_eap_user *user = NULL, *tail = NULL;
+ struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
if (!fname)
return 0;
@@ -494,7 +494,7 @@
done:
if (tail == NULL) {
- tail = conf->eap_user = user;
+ tail = new_user = user;
} else {
tail->next = user;
tail = user;
@@ -510,6 +510,18 @@
fclose(f);
+ if (ret == 0) {
+ user = conf->eap_user;
+ while (user) {
+ struct hostapd_eap_user *prev;
+
+ prev = user;
+ user = user->next;
+ hostapd_config_free_eap_user(prev);
+ }
+ conf->eap_user = new_user;
+ }
+
return ret;
}
#endif /* EAP_SERVER */
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index b370f21..1e56959 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -494,9 +494,9 @@
# If set non-zero, require stations to perform scans of overlapping
# channels to test for stations which would be affected by 40 MHz traffic.
-# This parameter sets the interval in seconds between these scans. This
-# is useful only for testing that stations properly set the OBSS interval,
-# since the other parameters in the OBSS scan parameters IE are set to 0.
+# This parameter sets the interval in seconds between these scans. Setting this
+# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if
+# no co-existence issues with neighboring devices are found.
#obss_interval=0
##### IEEE 802.11ac related configuration #####################################
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 4d77d0e..8a2abf1 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -103,6 +103,28 @@
}
+static void db_update_session_machine_managed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *sessionid,
+ const int pw_mm)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+ pw_mm ? "1" : "0", sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update session machine_managed: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
const char *realm, const char *sessionid,
xml_node_t *node)
@@ -1378,6 +1400,11 @@
debug_print(ctx, 1, "Request DB subscription registration on success "
"notification");
+ if (machine_managed) {
+ db_update_session_password(ctx, user, realm, session_id, pw);
+ db_update_session_machine_managed(ctx, user, realm, session_id,
+ machine_managed);
+ }
db_add_session_pps(ctx, user, realm, session_id, pps);
hs20_eventlog_node(ctx, user, realm, session_id,
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 287d520..04a56a9 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -111,8 +111,8 @@
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
else
- printf("AP: could not remove AP " MACSTR " from hash table\n",
- MAC2STR(ap->addr));
+ wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+ " from hash table", MAC2STR(ap->addr));
}
@@ -182,7 +182,8 @@
if (!ap) {
ap = ap_ap_add(iface, mgmt->bssid);
if (!ap) {
- printf("Failed to allocate AP information entry\n");
+ wpa_printf(MSG_INFO,
+ "Failed to allocate AP information entry");
return;
}
new_ap = 1;
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index a706024..3a77225 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -42,6 +42,17 @@
static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+static const char * ipaddr_str(u32 addr)
+{
+ static char buf[17];
+
+ os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ return buf;
+}
+
+
static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -109,16 +120,17 @@
return;
wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
- " @ IPv4 address %X/%d",
- MAC2STR(sta->addr), ntohl(b->your_ip), prefixlen);
+ " @ IPv4 address %s/%d",
+ MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+ prefixlen);
if (sta->ipaddr == b->your_ip)
return;
if (sta->ipaddr != 0) {
wpa_printf(MSG_DEBUG,
- "dhcp_snoop: Removing IPv4 address %X from the ip neigh table",
- sta->ipaddr);
+ "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+ ipaddr_str(be_to_host32(sta->ipaddr)));
hostapd_drv_br_delete_ip_neigh(hapd, 4,
(u8 *) &sta->ipaddr);
}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 3f299f3..4b0653d 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -292,7 +292,8 @@
iface->conf->secondary_channel = 0;
ieee802_11_set_beacons(iface);
}
- if (!iface->num_sta_ht40_intolerant) {
+ if (!iface->num_sta_ht40_intolerant &&
+ iface->conf->obss_interval) {
unsigned int delay_time;
delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
iface->conf->obss_interval;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index bb43218..eebaa3c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -781,13 +781,6 @@
if (sta->vlan_id == old_vlanid)
return 0;
- /*
- * During 1x reauth, if the vlan id changes, then remove the old id and
- * proceed furthur to add the new one.
- */
- if (old_vlanid > 0)
- vlan_remove_dynamic(hapd, old_vlanid);
-
iface = hapd->conf->iface;
if (sta->ssid->vlan[0])
iface = sta->ssid->vlan;
@@ -815,7 +808,8 @@
HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
"binding station to (vlan_id=%d)",
sta->vlan_id);
- return -1;
+ ret = -1;
+ goto done;
} else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
if (vlan == NULL) {
@@ -824,7 +818,8 @@
HOSTAPD_LEVEL_DEBUG, "could not add "
"dynamic VLAN interface for vlan_id=%d",
sta->vlan_id);
- return -1;
+ ret = -1;
+ goto done;
}
iface = vlan->ifname;
@@ -878,6 +873,12 @@
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
"entry to vlan_id=%d", sta->vlan_id);
}
+
+done:
+ /* During 1x reauth, if the vlan id changes, then remove the old id. */
+ if (old_vlanid > 0)
+ vlan_remove_dynamic(hapd, old_vlanid);
+
return ret;
#else /* CONFIG_NO_VLAN */
return 0;
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 4e4a352..2af2cbc 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -891,7 +891,7 @@
struct hostapd_vlan *vlan,
int vlan_id)
{
- struct hostapd_vlan *n;
+ struct hostapd_vlan *n = NULL;
char *ifname, *pos;
if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
@@ -904,28 +904,24 @@
if (ifname == NULL)
return NULL;
pos = os_strchr(ifname, '#');
- if (pos == NULL) {
- os_free(ifname);
- return NULL;
- }
+ if (pos == NULL)
+ goto free_ifname;
*pos++ = '\0';
n = os_zalloc(sizeof(*n));
- if (n == NULL) {
- os_free(ifname);
- return NULL;
- }
+ if (n == NULL)
+ goto free_ifname;
n->vlan_id = vlan_id;
n->dynamic_vlan = 1;
os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
pos);
- os_free(ifname);
if (hostapd_vlan_if_add(hapd, n->ifname)) {
os_free(n);
- return NULL;
+ n = NULL;
+ goto free_ifname;
}
n->next = hapd->conf->vlan;
@@ -935,6 +931,8 @@
ifconfig_up(n->ifname);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+free_ifname:
+ os_free(ifname);
return n;
}
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 1905dc9..668cb42 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1881,7 +1881,10 @@
} else {
wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
sm->wpa_auth->cb.get_msk);
+ sm->Disconnect = TRUE;
+ return;
}
+ os_memset(msk, 0, sizeof(msk));
sm->req_replay_counter_used = 0;
/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 97a4537..2e51935 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1034,6 +1034,14 @@
P2P_ATTR_OPERATING_CHANNEL = 17,
P2P_ATTR_INVITATION_FLAGS = 18,
P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
+ P2P_ATTR_SERVICE_HASH = 21,
+ P2P_ATTR_SESSION_INFORMATION_DATA = 22,
+ P2P_ATTR_CONNECTION_CAPABILITY = 23,
+ P2P_ATTR_ADVERTISEMENT_ID = 24,
+ P2P_ATTR_ADVERTISED_SERVICE = 25,
+ P2P_ATTR_SESSION_ID = 26,
+ P2P_ATTR_FEATURE_CAPABILITY = 27,
+ P2P_ATTR_PERSISTENT_GROUP = 28,
P2P_ATTR_VENDOR_SPECIFIC = 221
};
@@ -1078,6 +1086,7 @@
P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
P2P_SC_FAIL_REJECTED_BY_USER = 11,
+ P2P_SC_SUCCESS_DEFERRED = 12,
};
enum p2p_role_indication {
@@ -1116,6 +1125,7 @@
P2P_SERV_UPNP = 2,
P2P_SERV_WS_DISCOVERY = 3,
P2P_SERV_WIFI_DISPLAY = 4,
+ P2P_SERV_P2PS = 11,
P2P_SERV_VENDOR_SPECIFIC = 255
};
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 59a3412..c8e302a 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -164,6 +164,7 @@
#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
/* parameters: <src addr> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
@@ -177,6 +178,9 @@
#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
+#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
+#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
+
#define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index f79055c..f158ef4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -31,17 +31,9 @@
#include "sha384.h"
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < 0x00907000
-#define DES_key_schedule des_key_schedule
-#define DES_cblock des_cblock
-#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
-#define DES_ecb_encrypt(input, output, ks, enc) \
- des_ecb_encrypt((input), (output), *(ks), (enc))
-#endif /* openssl < 0.9.7 */
-
static BIGNUM * get_group5_prime(void)
{
-#if OPENSSL_VERSION_NUMBER < 0x00908000 || defined(OPENSSL_IS_BORINGSSL)
+#ifdef OPENSSL_IS_BORINGSSL
static const unsigned char RFC3526_PRIME_1536[] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
@@ -61,20 +53,11 @@
0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
-#else /* openssl < 0.9.8 */
+#else /* OPENSSL_IS_BORINGSSL */
return get_rfc3526_prime_1536(NULL);
-#endif /* openssl < 0.9.8 */
+#endif /* OPENSSL_IS_BORINGSSL */
}
-#if OPENSSL_VERSION_NUMBER < 0x00908000
-#ifndef OPENSSL_NO_SHA256
-#ifndef OPENSSL_FIPS
-#define NO_SHA256_WRAPPER
-#endif
-#endif
-
-#endif /* openssl < 0.9.8 */
-
#ifdef OPENSSL_NO_SHA256
#define NO_SHA256_WRAPPER
#endif
@@ -314,6 +297,33 @@
}
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+ AES_KEY actx;
+ int res;
+
+ if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+ OPENSSL_cleanse(&actx, sizeof(actx));
+ return res <= 0 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+ u8 *plain)
+{
+ AES_KEY actx;
+ int res;
+
+ if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+ return -1;
+ res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+ OPENSSL_cleanse(&actx, sizeof(actx));
+ return res <= 0 ? -1 : 0;
+}
+
+
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
@@ -688,43 +698,26 @@
}
-int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
- int iterations, u8 *buf, size_t buflen)
-{
-#if OPENSSL_VERSION_NUMBER < 0x00908000
- if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
- (unsigned char *) ssid,
- ssid_len, iterations, buflen, buf) != 1)
- return -1;
-#else /* openssl < 0.9.8 */
- if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
- ssid_len, iterations, buflen, buf) != 1)
- return -1;
-#endif /* openssl < 0.9.8 */
- return 0;
-}
-
-
-int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *mac)
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+ size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac,
+ unsigned int mdlen)
{
HMAC_CTX ctx;
size_t i;
- unsigned int mdlen;
int res;
HMAC_CTX_init(&ctx);
#if OPENSSL_VERSION_NUMBER < 0x00909000
- HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);
+ HMAC_Init_ex(&ctx, key, key_len, type, NULL);
#else /* openssl < 0.9.9 */
- if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1)
+ if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
return -1;
#endif /* openssl < 0.9.9 */
for (i = 0; i < num_elem; i++)
HMAC_Update(&ctx, addr[i], len[i]);
- mdlen = 20;
#if OPENSSL_VERSION_NUMBER < 0x00909000
HMAC_Final(&ctx, mac, &mdlen);
res = 1;
@@ -737,6 +730,43 @@
}
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+ mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+ ssid_len, iterations, buflen, buf) != 1)
+ return -1;
+ return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+ len, mac, 20);
+}
+
+
int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac)
{
@@ -749,32 +779,8 @@
int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
- HMAC_CTX ctx;
- size_t i;
- unsigned int mdlen;
- int res;
-
- HMAC_CTX_init(&ctx);
-#if OPENSSL_VERSION_NUMBER < 0x00909000
- HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
-#else /* openssl < 0.9.9 */
- if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1)
- return -1;
-#endif /* openssl < 0.9.9 */
-
- for (i = 0; i < num_elem; i++)
- HMAC_Update(&ctx, addr[i], len[i]);
-
- mdlen = 32;
-#if OPENSSL_VERSION_NUMBER < 0x00909000
- HMAC_Final(&ctx, mac, &mdlen);
- res = 1;
-#else /* openssl < 0.9.9 */
- res = HMAC_Final(&ctx, mac, &mdlen);
-#endif /* openssl < 0.9.9 */
- HMAC_CTX_cleanup(&ctx);
-
- return res == 1 ? 0 : -1;
+ return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+ len, mac, 32);
}
@@ -792,23 +798,8 @@
int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
- HMAC_CTX ctx;
- size_t i;
- unsigned int mdlen;
- int res;
-
- HMAC_CTX_init(&ctx);
- if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha384(), NULL) != 1)
- return -1;
-
- for (i = 0; i < num_elem; i++)
- HMAC_Update(&ctx, addr[i], len[i]);
-
- mdlen = 32;
- res = HMAC_Final(&ctx, mac, &mdlen);
- HMAC_CTX_cleanup(&ctx);
-
- return res == 1 ? 0 : -1;
+ return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+ len, mac, 32);
}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index e3ca068..d8c8c56 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -28,12 +28,6 @@
#include "crypto.h"
#include "tls.h"
-#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-#define OPENSSL_d2i_TYPE const unsigned char **
-#else
-#define OPENSSL_d2i_TYPE unsigned char **
-#endif
-
#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data)
#define OPENSSL_SUPPORTS_CTX_APP_DATA
#endif
@@ -90,6 +84,7 @@
struct tls_connection {
struct tls_context *context;
+ SSL_CTX *ssl_ctx;
SSL *ssl;
BIO *ssl_in, *ssl_out;
#ifndef OPENSSL_NO_ENGINE
@@ -400,7 +395,8 @@
goto err;
}
- cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
+ cert = d2i_X509(NULL,
+ (const unsigned char **) &priv->cert->pbCertEncoded,
priv->cert->cbCertEncoded);
if (cert == NULL) {
wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
@@ -500,7 +496,8 @@
}
while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
- cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
+ cert = d2i_X509(NULL,
+ (const unsigned char **) &ctx->pbCertEncoded,
ctx->cbCertEncoded);
if (cert == NULL) {
wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
@@ -774,7 +771,7 @@
#endif /* CONFIG_FIPS */
SSL_load_error_strings();
SSL_library_init();
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
EVP_add_digest(EVP_sha256());
#endif /* OPENSSL_NO_SHA256 */
/* TODO: if /dev/urandom is available, PRNG is seeded
@@ -1045,6 +1042,7 @@
conn = os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
+ conn->ssl_ctx = ssl_ctx;
conn->ssl = SSL_new(ssl);
if (conn->ssl == NULL) {
tls_show_errors(MSG_INFO, __func__,
@@ -1613,7 +1611,7 @@
X509_LOOKUP *lookup;
int ret = 0;
- lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+ lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
X509_LOOKUP_file());
if (lookup == NULL) {
tls_show_errors(MSG_WARNING, __func__,
@@ -1644,18 +1642,19 @@
size_t ca_cert_blob_len, const char *ca_path)
{
SSL_CTX *ssl_ctx = _ssl_ctx;
+ X509_STORE *store;
/*
* Remove previously configured trusted CA certificates before adding
* new ones.
*/
- X509_STORE_free(ssl_ctx->cert_store);
- ssl_ctx->cert_store = X509_STORE_new();
- if (ssl_ctx->cert_store == NULL) {
+ store = X509_STORE_new();
+ if (store == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
"certificate store", __func__);
return -1;
}
+ SSL_CTX_set_cert_store(ssl_ctx, store);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
conn->ca_cert_verify = 1;
@@ -1699,7 +1698,8 @@
}
if (ca_cert_blob) {
- X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
+ X509 *cert = d2i_X509(NULL,
+ (const unsigned char **) &ca_cert_blob,
ca_cert_blob_len);
if (cert == NULL) {
tls_show_errors(MSG_WARNING, __func__,
@@ -1707,7 +1707,8 @@
return -1;
}
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+ cert)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
"Failed to add ca_cert_blob to "
@@ -2138,7 +2139,7 @@
#ifdef PKCS12_FUNCS
PKCS12 *p12;
- p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
+ p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
if (p12 == NULL) {
tls_show_errors(MSG_INFO, __func__,
"Failed to use PKCS#12 blob");
@@ -2219,20 +2220,21 @@
#ifndef OPENSSL_NO_ENGINE
X509 *cert;
SSL_CTX *ssl_ctx = _ssl_ctx;
+ X509_STORE *store;
if (tls_engine_get_cert(conn, ca_cert_id, &cert))
return -1;
/* start off the same as tls_connection_ca_cert */
- X509_STORE_free(ssl_ctx->cert_store);
- ssl_ctx->cert_store = X509_STORE_new();
- if (ssl_ctx->cert_store == NULL) {
+ store = X509_STORE_new();
+ if (store == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
"certificate store", __func__);
X509_free(cert);
return -1;
}
- if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ SSL_CTX_set_cert_store(ssl_ctx, store);
+ if (!X509_STORE_add_cert(store, cert)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
"Failed to add CA certificate from engine "
@@ -2900,7 +2902,11 @@
int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
{
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ return conn ? SSL_cache_hit(conn->ssl) : 0;
+#else
return conn ? conn->ssl->hit : 0;
+#endif
}
@@ -3141,7 +3147,7 @@
return 0;
}
- store = SSL_CTX_get_cert_store(s->ctx);
+ store = SSL_CTX_get_cert_store(conn->ssl_ctx);
if (conn->peer_issuer) {
debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 62cd4a1..35433f3 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -934,6 +934,15 @@
}
+static int eap_peer_sm_allow_canned(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ return config && config->phase1 &&
+ os_strstr(config->phase1, "allow_canned_success=1");
+}
+
+
static void eap_peer_sm_step_received(struct eap_sm *sm)
{
int duplicate = eap_peer_req_is_duplicate(sm);
@@ -947,6 +956,17 @@
(sm->reqId == sm->lastId ||
eap_success_workaround(sm, sm->reqId, sm->lastId)))
SM_ENTER(EAP, SUCCESS);
+ else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess &&
+ !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */
+ else if (sm->workaround && sm->lastId == -1 && sm->rxFailure &&
+ !sm->rxReq && sm->methodState != METHOD_CONT &&
+ eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */
+ else if (sm->workaround && sm->rxSuccess && !sm->rxFailure &&
+ !sm->rxReq && sm->methodState != METHOD_CONT &&
+ eap_peer_sm_allow_canned(sm))
+ SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */
else if (sm->methodState != METHOD_CONT &&
((sm->rxFailure &&
sm->decision != DECISION_UNCOND_SUCC) ||
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 903412d..2b1a1d5 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -418,6 +418,16 @@
*
* EAP-WSC (WPS) uses following options: pin=Device_Password and
* uuid=Device_UUID
+ *
+ * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+ * used to configure a mode that allows EAP-Success (and EAP-Failure)
+ * without going through authentication step. Some switches use such
+ * sequence when forcing the port to be authorized/unauthorized or as a
+ * fallback option if the authentication server is unreachable. By
+ * default, wpa_supplicant discards such frames to protect against
+ * potential attacks by rogue devices, but this option can be used to
+ * disable that protection for cases where the server/authenticator does
+ * not need to be authenticated.
*/
char *phase1;
@@ -425,7 +435,9 @@
* phase2 - Phase2 (inner authentication with TLS tunnel) parameters
*
* String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
- * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+ * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+ * be used to disable MSCHAPv2 password retry in authentication failure
+ * cases.
*/
char *phase2;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 430c501..9e486e7 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -472,6 +472,13 @@
pos += 2;
msg = pos;
}
+ if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+ config && config->phase2 &&
+ os_strstr(config->phase2, "mschapv2_retry=0")) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+ retry = 0;
+ }
wpa_msg(sm->msg_ctx, MSG_WARNING,
"EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
"%d)",
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
index 040d1e7..b61057e 100644
--- a/src/eap_peer/eap_vendor_test.c
+++ b/src/eap_peer/eap_vendor_test.c
@@ -1,6 +1,6 @@
/*
* EAP peer method: Test method for vendor specific (expanded) EAP type
- * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,31 +14,36 @@
#include "common.h"
#include "eap_i.h"
-#ifdef TEST_PENDING_REQUEST
#include "eloop.h"
-#endif /* TEST_PENDING_REQUEST */
#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
#define EAP_VENDOR_TYPE 0xfcfbfaf9
-/* #define TEST_PENDING_REQUEST */
-
struct eap_vendor_test_data {
enum { INIT, CONFIRM, SUCCESS } state;
int first_try;
+ int test_pending_req;
};
static void * eap_vendor_test_init(struct eap_sm *sm)
{
struct eap_vendor_test_data *data;
+ const u8 *password;
+ size_t password_len;
+
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->state = INIT;
data->first_try = 1;
+
+ password = eap_get_config_password(sm, &password_len);
+ data->test_pending_req = password && password_len == 7 &&
+ os_memcmp(password, "pending", 7) == 0;
+
return data;
}
@@ -50,7 +55,6 @@
}
-#ifdef TEST_PENDING_REQUEST
static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
{
struct eap_sm *sm = eloop_ctx;
@@ -58,7 +62,6 @@
"request");
eap_notify_pending(sm);
}
-#endif /* TEST_PENDING_REQUEST */
static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
@@ -98,8 +101,7 @@
}
if (data->state == CONFIRM) {
-#ifdef TEST_PENDING_REQUEST
- if (data->first_try) {
+ if (data->test_pending_req && data->first_try) {
data->first_try = 0;
wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
"pending request");
@@ -108,7 +110,6 @@
NULL);
return NULL;
}
-#endif /* TEST_PENDING_REQUEST */
}
ret->ignore = FALSE;
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 98d608b..faa0fd2 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -344,12 +344,14 @@
size_t mlen;
mlen = 6; /* Result TLV */
- if (data->crypto_binding != NO_BINDING)
+ if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+ data->crypto_binding != NO_BINDING) {
mlen += 60; /* Cryptobinding TLV */
#ifdef EAP_SERVER_TNC
- if (data->soh_response)
- mlen += wpabuf_len(data->soh_response);
+ if (data->soh_response)
+ mlen += wpabuf_len(data->soh_response);
#endif /* EAP_SERVER_TNC */
+ }
buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
EAP_CODE_REQUEST, id);
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 621318e..f615051 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1634,21 +1634,15 @@
/**
* eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
* @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
- * @attempt: Whether PMKSA caching is tried
*
- * Notify EAPOL state machines whether PMKSA caching is used.
+ * Notify EAPOL state machines if PMKSA caching is used.
*/
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
{
if (sm == NULL)
return;
- if (attempt) {
- wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
- sm->cached_pmk = TRUE;
- } else {
- wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
- sm->cached_pmk = FALSE;
- }
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+ sm->cached_pmk = TRUE;
}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index d8ae9d4..03341a3 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -307,7 +307,7 @@
const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len);
void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
void eapol_sm_notify_cached(struct eapol_sm *sm);
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm);
void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
@@ -380,13 +380,20 @@
{
return -1;
}
+static inline const u8 *
+eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+ return NULL;
+}
static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
{
}
static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
{
}
-#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+}
#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
PortControl portControl)
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
index 7537f93..2a45245 100644
--- a/src/l2_packet/l2_packet.h
+++ b/src/l2_packet/l2_packet.h
@@ -68,6 +68,19 @@
void *rx_callback_ctx, int l2_hdr);
/**
+ * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround
+ *
+ * This version of l2_packet_init() can be used to enable a workaround for Linux
+ * packet socket in case of a station interface in a bridge.
+ */
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr);
+
+/**
* l2_packet_deinit - Deinitialize l2_packet interface
* @l2: Pointer to internal l2_packet data from l2_packet_init()
*/
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index d87c32b..aa83648 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -256,6 +256,18 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 != NULL) {
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 89ff7db..68b2008 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Layer2 packet handling with Linux packet sockets
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -27,6 +27,9 @@
void *rx_callback_ctx;
int l2_hdr; /* whether to include layer 2 (Ethernet) header data
* buffers */
+
+ /* For working around Linux packet socket behavior and regression. */
+ int fd_br_rx;
};
/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
@@ -130,6 +133,36 @@
}
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+
+ if (l2->fd_br_rx >= 0) {
+ wpa_printf(MSG_DEBUG, "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
+ l2->ifname);
+ eloop_unregister_read_sock(l2->fd_br_rx);
+ close(l2->fd_br_rx);
+ l2->fd_br_rx = -1;
+ }
+}
+
+
+static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+ struct sockaddr_ll ll;
+ socklen_t fromlen;
+
+ os_memset(&ll, 0, sizeof(ll));
+ fromlen = sizeof(ll);
+ res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+ &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
+ strerror(errno));
+ return;
+ }
+
+ l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
@@ -150,6 +183,7 @@
l2->rx_callback = rx_callback;
l2->rx_callback_ctx = rx_callback_ctx;
l2->l2_hdr = l2_hdr;
+ l2->fd_br_rx = -1;
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
htons(protocol));
@@ -197,6 +231,87 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ struct sock_filter ethertype_sock_filter_insns[] = {
+ /* Load ethertype */
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
+ /* Jump over next statement if ethertype does not match */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
+ /* Ethertype match - return all */
+ BPF_STMT(BPF_RET | BPF_K, ~0),
+ /* No match - drop */
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ const struct sock_fprog ethertype_sock_filter = {
+ .len = ARRAY_SIZE(ethertype_sock_filter_insns),
+ .filter = ethertype_sock_filter_insns,
+ };
+ struct sockaddr_ll ll;
+
+ l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+ if (!l2)
+ return NULL;
+
+ /*
+ * The Linux packet socket behavior has changed over the years and there
+ * is an inconvenient regression in it that breaks RX for a specific
+ * protocol from interfaces in a bridge when that interface is not in
+ * fully operation state (i.e., when in station mode and not completed
+ * authorization). To work around this, register ETH_P_ALL version of
+ * the packet socket bound to the real netdev and use socket filter to
+ * match the ethertype value. This version is less efficient, but
+ * required for functionality with many kernel version. If the main
+ * packet socket is found to be working, this less efficient version
+ * gets closed automatically.
+ */
+
+ l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+ htons(ETH_P_ALL));
+ if (l2->fd_br_rx < 0) {
+ wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
+ __func__, strerror(errno));
+ /* try to continue without the workaround RX socket */
+ return l2;
+ }
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = PF_PACKET;
+ ll.sll_ifindex = if_nametoindex(ifname);
+ ll.sll_protocol = htons(ETH_P_ALL);
+ if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
+ __func__, strerror(errno));
+ /* try to continue without the workaround RX socket */
+ close(l2->fd_br_rx);
+ l2->fd_br_rx = -1;
+ return l2;
+ }
+
+ if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
+ ðertype_sock_filter, sizeof(struct sock_fprog))) {
+ wpa_printf(MSG_DEBUG,
+ "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+ strerror(errno));
+ /* try to continue without the workaround RX socket */
+ close(l2->fd_br_rx);
+ l2->fd_br_rx = -1;
+ return l2;
+ }
+
+ eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+
+ return l2;
+}
+
+
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 == NULL)
@@ -206,7 +321,12 @@
eloop_unregister_read_sock(l2->fd);
close(l2->fd);
}
-
+
+ if (l2->fd_br_rx >= 0) {
+ eloop_unregister_read_sock(l2->fd_br_rx);
+ close(l2->fd_br_rx);
+ }
+
os_free(l2);
}
diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c
index 39a62a0..7167781 100644
--- a/src/l2_packet/l2_packet_ndis.c
+++ b/src/l2_packet/l2_packet_ndis.c
@@ -450,6 +450,18 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 == NULL)
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
index 0501925..307fc6d 100644
--- a/src/l2_packet/l2_packet_none.c
+++ b/src/l2_packet/l2_packet_none.c
@@ -91,6 +91,18 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 == NULL)
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index 76dcccc..e26ca20 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -231,6 +231,18 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 == NULL)
diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c
index b6e5088..74085a3 100644
--- a/src/l2_packet/l2_packet_winpcap.c
+++ b/src/l2_packet/l2_packet_winpcap.c
@@ -248,6 +248,18 @@
}
+struct l2_packet_data * l2_packet_init_bridge(
+ const char *br_ifname, const char *ifname, const u8 *own_addr,
+ unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+ rx_callback_ctx, l2_hdr);
+}
+
+
static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct l2_packet_data *l2 = eloop_ctx;
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index f3e31a8..602aa36 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -13,6 +13,8 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
@@ -151,6 +153,19 @@
}
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+ return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+ if (p2p && intended_addr)
+ os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev = NULL;
@@ -770,6 +785,12 @@
dev->oper_ssid_len = msg.ssid[1];
}
+ if (msg.adv_service_instance && msg.adv_service_instance_len) {
+ wpabuf_free(dev->info.p2ps_instance);
+ dev->info.p2ps_instance = wpabuf_alloc_copy(
+ msg.adv_service_instance, msg.adv_service_instance_len);
+ }
+
if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
*msg.ds_params >= 1 && *msg.ds_params <= 14) {
int ds_freq;
@@ -829,7 +850,9 @@
p2p_update_peer_vendor_elems(dev, ies, ies_len);
- if (dev->flags & P2P_DEV_REPORTED && !wfd_changed)
+ if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+ (!msg.adv_service_instance ||
+ (dev->flags & P2P_DEV_P2PS_REPORTED)))
return 0;
p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
@@ -865,6 +888,9 @@
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+ if (msg.adv_service_instance)
+ dev->flags |= P2P_DEV_P2PS_REPORTED;
+
return 0;
}
@@ -899,6 +925,7 @@
wpabuf_free(dev->info.wfd_subelems);
wpabuf_free(dev->info.vendor_elems);
wpabuf_free(dev->go_neg_conf);
+ wpabuf_free(dev->info.p2ps_instance);
os_free(dev);
}
@@ -1083,10 +1110,44 @@
}
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+ u8 buf[SHA256_MAC_LEN];
+ char str_buf[256];
+ const u8 *adv_array;
+ size_t i, adv_len;
+
+ if (!str || !hash)
+ return 0;
+
+ if (!str[0]) {
+ os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+ return 1;
+ }
+
+ adv_array = (u8 *) str_buf;
+ adv_len = os_strlen(str);
+
+ for (i = 0; str[i] && i < adv_len; i++) {
+ if (str[i] >= 'A' && str[i] <= 'Z')
+ str_buf[i] = str[i] - 'A' + 'a';
+ else
+ str_buf[i] = str[i];
+ }
+
+ if (sha256_vector(1, &adv_array, &adv_len, buf))
+ return 0;
+
+ os_memcpy(hash, buf, P2PS_HASH_LEN);
+ return 1;
+}
+
+
int p2p_find(struct p2p_data *p2p, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
- const u8 *dev_id, unsigned int search_delay)
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_count, const char **seek)
{
int res;
@@ -1113,6 +1174,47 @@
} else
p2p->find_dev_id = NULL;
+ if (seek_count == 0 || !seek) {
+ /* Not an ASP search */
+ p2p->p2ps_seek = 0;
+ } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+ /*
+ * An empty seek string means no hash values, but still an ASP
+ * search.
+ */
+ p2p->p2ps_seek_count = 0;
+ p2p->p2ps_seek = 1;
+ } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+ u8 buf[P2PS_HASH_LEN];
+ int i;
+
+ p2p->p2ps_seek_count = seek_count;
+ for (i = 0; i < seek_count; i++) {
+ if (!p2ps_gen_hash(p2p, seek[i], buf))
+ continue;
+
+ /* If asking for wildcard, don't do others */
+ if (os_memcmp(buf, p2p->wild_card_hash,
+ P2PS_HASH_LEN) == 0) {
+ p2p->p2ps_seek_count = 0;
+ break;
+ }
+
+ os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf,
+ P2PS_HASH_LEN);
+ }
+ p2p->p2ps_seek = 1;
+ } else {
+ p2p->p2ps_seek_count = 0;
+ p2p->p2ps_seek = 1;
+ }
+
+ /* Special case to perform wildcard search */
+ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+ p2p->p2ps_seek_count = 1;
+ os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+ }
+
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
p2p_clear_timeout(p2p);
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -1163,8 +1265,11 @@
p2p_dbg(p2p, "Stopping find");
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
p2p_clear_timeout(p2p);
- if (p2p->state == P2P_SEARCH)
+ if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+ p2p->p2ps_seek_count = 0;
+
p2p_set_state(p2p, P2P_IDLE);
p2p_free_req_dev_types(p2p);
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
@@ -1637,7 +1742,14 @@
int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
{
- p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len);
+ if (p2p->ssid_set) {
+ os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+ params->ssid_len = p2p->ssid_len;
+ } else {
+ p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len);
+ }
+ p2p->ssid_set = 0;
+
p2p_random(params->passphrase, p2p->cfg->passphrase_len);
return 0;
}
@@ -2036,6 +2148,9 @@
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+ if (p2p->query_count)
+ extra += MAX_SVC_ADV_IE_LEN;
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -2070,10 +2185,38 @@
p2p_buf_add_device_info(buf, p2p, NULL);
p2p_buf_update_ie_hdr(buf, len);
+ if (p2p->query_count) {
+ p2p_buf_add_service_instance(buf, p2p, p2p->query_count,
+ p2p->query_hash,
+ p2p->p2ps_adv_list);
+ }
+
return buf;
}
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+ struct p2ps_advertisement *adv_data;
+
+ p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+ /* Wildcard always matches if we have actual services */
+ if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+ return p2p->p2ps_adv_list != NULL;
+
+ adv_data = p2p->p2ps_adv_list;
+ while (adv_data) {
+ p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]);
+ if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+ return 1;
+ adv_data = adv_data->next;
+ }
+
+ return 0;
+}
+
+
static enum p2p_probe_req_status
p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
const u8 *bssid, const u8 *ie, size_t ie_len)
@@ -2084,13 +2227,6 @@
struct p2p_message msg;
struct wpabuf *ies;
- if (!p2p->in_listen || !p2p->drv_in_listen) {
- /* not in Listen state - ignore Probe Request */
- p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
- p2p->in_listen, p2p->drv_in_listen);
- return P2P_PREQ_NOT_LISTEN;
- }
-
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
/* Ignore invalid Probe Request frames */
@@ -2141,6 +2277,64 @@
return P2P_PREQ_NOT_P2P;
}
+ p2p->p2ps_svc_found = 0;
+
+ if (msg.service_hash && msg.service_hash_count) {
+ const u8 *hash = msg.service_hash;
+ u8 *dest = p2p->query_hash;
+ u8 i;
+
+ p2p->query_count = 0;
+ for (i = 0; i < msg.service_hash_count; i++) {
+ if (p2p_service_find_asp(p2p, hash)) {
+ p2p->p2ps_svc_found = 1;
+
+ if (!os_memcmp(hash, p2p->wild_card_hash,
+ P2PS_HASH_LEN)) {
+ /* We found match(es) but wildcard
+ * will return all */
+ p2p->query_count = 1;
+ os_memcpy(p2p->query_hash, hash,
+ P2PS_HASH_LEN);
+ break;
+ }
+
+ /* Save each matching hash */
+ if (p2p->query_count < P2P_MAX_QUERY_HASH) {
+ os_memcpy(dest, hash, P2PS_HASH_LEN);
+ dest += P2PS_HASH_LEN;
+ p2p->query_count++;
+ } else {
+ /* We found match(es) but too many to
+ * return all */
+ p2p->query_count = 0;
+ break;
+ }
+ }
+ hash += P2PS_HASH_LEN;
+ }
+
+ p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found);
+
+ /* Probed hash unknown */
+ if (!p2p->p2ps_svc_found) {
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+ } else {
+ /* This is not a P2PS Probe Request */
+ p2p->query_count = 0;
+ p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+ p2p->in_listen, p2p->drv_in_listen);
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_LISTEN;
+ }
+ }
+
if (msg.device_id &&
os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
/* Device ID did not match */
@@ -2238,6 +2432,7 @@
p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+ p2p->query_count = 0;
if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
p2p->go_neg_peer &&
@@ -2395,6 +2590,132 @@
}
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
+{
+ struct p2ps_advertisement *adv_data;
+
+ if (!p2p)
+ return NULL;
+
+ adv_data = p2p->p2ps_adv_list;
+ while (adv_data) {
+ if (adv_data->id == adv_id)
+ return adv_data;
+ adv_data = adv_data->next;
+ }
+
+ return NULL;
+}
+
+
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
+{
+ struct p2ps_advertisement *adv_data;
+ struct p2ps_advertisement **prior;
+
+ if (!p2p)
+ return -1;
+
+ adv_data = p2p->p2ps_adv_list;
+ prior = &p2p->p2ps_adv_list;
+ while (adv_data) {
+ if (adv_data->id == adv_id) {
+ p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+ *prior = adv_data->next;
+ os_free(adv_data);
+ return 0;
+ }
+ prior = &adv_data->next;
+ adv_data = adv_data->next;
+ }
+
+ return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state, u16 config_methods,
+ const char *svc_info)
+{
+ struct p2ps_advertisement *adv_data, *tmp, **prev;
+ u8 buf[P2PS_HASH_LEN];
+ size_t adv_data_len, adv_len, info_len = 0;
+
+ if (!p2p || !adv_str || !adv_str[0])
+ return -1;
+
+ if (!(config_methods & p2p->cfg->config_methods)) {
+ p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+ config_methods, p2p->cfg->config_methods);
+ return -1;
+ }
+
+ if (!p2ps_gen_hash(p2p, adv_str, buf))
+ return -1;
+
+ if (svc_info)
+ info_len = os_strlen(svc_info);
+ adv_len = os_strlen(adv_str);
+ adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+ info_len + 1;
+
+ adv_data = os_zalloc(adv_data_len);
+ if (!adv_data)
+ return -1;
+
+ os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+ adv_data->id = adv_id;
+ adv_data->state = svc_state;
+ adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+ adv_data->auto_accept = (u8) auto_accept;
+ os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+ if (svc_info && info_len) {
+ adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+ os_memcpy(adv_data->svc_info, svc_info, info_len);
+ }
+
+ /*
+ * Group Advertisements by service string. They do not need to be
+ * sorted, but groups allow easier Probe Response instance grouping
+ */
+ tmp = p2p->p2ps_adv_list;
+ prev = &p2p->p2ps_adv_list;
+ while (tmp) {
+ if (tmp->id == adv_data->id) {
+ if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+ os_free(adv_data);
+ return -1;
+ }
+ adv_data->next = tmp->next;
+ *prev = adv_data;
+ os_free(tmp);
+ goto inserted;
+ } else {
+ if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+ adv_data->next = tmp->next;
+ tmp->next = adv_data;
+ goto inserted;
+ }
+ }
+ prev = &tmp->next;
+ tmp = tmp->next;
+ }
+
+ /* No svc_name match found */
+ adv_data->next = p2p->p2ps_adv_list;
+ p2p->p2ps_adv_list = adv_data;
+
+inserted:
+ p2p_dbg(p2p,
+ "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
+ adv_id, adv_data->config_methods, svc_state, adv_str);
+
+ return 0;
+}
+
+
int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
{
struct p2p_message msg;
@@ -2508,6 +2829,8 @@
p2p->cfg->num_pref_chan = 0;
}
+ p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
p2p->min_disc_int = 1;
p2p->max_disc_int = 3;
p2p->max_disc_tu = -1;
@@ -2541,6 +2864,8 @@
void p2p_deinit(struct p2p_data *p2p)
{
+ struct p2ps_advertisement *adv, *prev;
+
#ifdef CONFIG_WIFI_DISPLAY
wpabuf_free(p2p->wfd_ie_beacon);
wpabuf_free(p2p->wfd_ie_probe_req);
@@ -2569,10 +2894,19 @@
os_free(p2p->cfg->serial_number);
os_free(p2p->cfg->pref_chan);
os_free(p2p->groups);
+ os_free(p2p->p2ps_prov);
wpabuf_free(p2p->sd_resp);
os_free(p2p->after_scan_tx);
p2p_remove_wps_vendor_extensions(p2p);
os_free(p2p->no_go_freq.range);
+
+ adv = p2p->p2ps_adv_list;
+ while (adv) {
+ prev = adv;
+ adv = adv->next;
+ os_free(prev);
+ }
+
os_free(p2p);
}
@@ -2837,13 +3171,15 @@
if (p2p->sd_peer)
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p->sd_peer = NULL;
- p2p_continue_find(p2p);
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
return;
}
if (p2p->sd_peer == NULL) {
p2p_dbg(p2p, "No SD peer entry known");
- p2p_continue_find(p2p);
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
return;
}
@@ -2874,9 +3210,6 @@
{
struct p2p_device *dev;
- if (p2p->state != P2P_IDLE)
- return;
-
/*
* Retry the prov disc req attempt only for the peer that the user had
* requested.
@@ -2950,6 +3283,51 @@
}
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+ if (p2p->after_scan_tx_in_progress) {
+ p2p->after_scan_tx_in_progress = 0;
+ if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+ p2p_run_after_scan(p2p))
+ return 1;
+ if (p2p->state == P2P_SEARCH) {
+ p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+ p2p_continue_find(p2p);
+ }
+ }
+
+ return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+ p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+ success);
+
+ if (p2p->send_action_in_progress) {
+ p2p->send_action_in_progress = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success)
+ goto continue_search;
+
+ if (!p2p->cfg->prov_disc_resp_cb ||
+ p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+ goto continue_search;
+
+ p2p_dbg(p2p,
+ "Post-Provision Discovery operations started - do not try to continue other P2P operations");
+ return;
+
+continue_search:
+ p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len)
@@ -2992,6 +3370,7 @@
void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
{
+ u8 dev_capab;
u8 *len;
#ifdef CONFIG_WIFI_DISPLAY
@@ -3004,8 +3383,15 @@
p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
len = p2p_buf_add_ie_hdr(ies);
- p2p_buf_add_capability(ies, p2p->dev_capab &
- ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+
+ dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+ /* P2PS requires Probe Request frames to include SD bit */
+ if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+ dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+ p2p_buf_add_capability(ies, dev_capab, 0);
+
if (dev_id)
p2p_buf_add_device_id(ies, dev_id);
if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -3015,6 +3401,10 @@
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
p2p->ext_listen_interval);
+
+ if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+ p2p_buf_add_service_hash(ies, p2p);
+
/* TODO: p2p_buf_add_operating_channel() if GO */
p2p_buf_update_ie_hdr(ies, len);
}
@@ -3209,9 +3599,9 @@
int success;
p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
- " src=" MACSTR " bssid=" MACSTR " result=%d",
+ " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
- MAC2STR(bssid), result);
+ MAC2STR(bssid), result, p2p_state_txt(p2p->state));
success = result == P2P_SEND_ACTION_SUCCESS;
state = p2p->pending_action_state;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
@@ -3221,16 +3611,7 @@
p2p->send_action_in_progress = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
- if (p2p->after_scan_tx_in_progress) {
- p2p->after_scan_tx_in_progress = 0;
- if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
- p2p_run_after_scan(p2p))
- break;
- if (p2p->state == P2P_SEARCH) {
- p2p_dbg(p2p, "Continue find after after_scan_tx completion");
- p2p_continue_find(p2p);
- }
- }
+ p2p_check_after_scan_tx_continuation(p2p);
break;
case P2P_PENDING_GO_NEG_REQUEST:
p2p_go_neg_req_cb(p2p, success);
@@ -3250,6 +3631,9 @@
case P2P_PENDING_PD:
p2p_prov_disc_cb(p2p, success);
break;
+ case P2P_PENDING_PD_RESPONSE:
+ p2p_prov_disc_resp_cb(p2p, success);
+ break;
case P2P_PENDING_INVITATION_REQUEST:
p2p_invitation_req_cb(p2p, success);
break;
@@ -3456,6 +3840,9 @@
static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
{
+ u32 adv_id = 0;
+ u8 *adv_mac = NULL;
+
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
/*
@@ -3484,12 +3871,18 @@
for_join = 1;
}
+ if (p2p->p2ps_prov) {
+ adv_id = p2p->p2ps_prov->adv_id;
+ adv_mac = p2p->p2ps_prov->adv_mac;
+ }
+
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
p2p->pending_pd_devaddr,
for_join ?
P2P_PROV_DISC_TIMEOUT_JOIN :
- P2P_PROV_DISC_TIMEOUT);
+ P2P_PROV_DISC_TIMEOUT,
+ adv_id, adv_mac, NULL);
p2p_reset_pending_pd(p2p);
}
}
@@ -3638,6 +4031,8 @@
return "PBC";
case WPS_NFC:
return "NFC";
+ case WPS_P2PS:
+ return "P2PS";
}
return "??";
@@ -3714,7 +4109,7 @@
"country=%c%c\n"
"oper_freq=%d\n"
"req_config_methods=0x%x\n"
- "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
"status=%d\n"
"invitation_reqs=%u\n",
(int) (now.sec - dev->last_seen.sec),
@@ -3740,6 +4135,8 @@
"[PD_PEER_DISPLAY]" : "",
dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
"[PD_PEER_KEYPAD]" : "",
+ dev->flags & P2P_DEV_PD_PEER_P2PS ?
+ "[PD_PEER_P2PS]" : "",
dev->flags & P2P_DEV_USER_REJECTED ?
"[USER_REJECTED]" : "",
dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
@@ -4171,15 +4568,18 @@
if (p2p_channel_to_freq(reg_class, channel) < 0)
return -1;
- p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
- reg_class, channel);
-
/*
* Listen channel was set in configuration or set by control interface;
* cannot override it.
*/
- if (p2p->cfg->channel_forced && forced == 0)
+ if (p2p->cfg->channel_forced && forced == 0) {
+ p2p_dbg(p2p,
+ "Listen channel was previously configured - do not override based on optimization");
return -1;
+ }
+
+ p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
+ reg_class, channel);
if (p2p->state == P2P_IDLE) {
p2p->cfg->reg_class = reg_class;
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index fa886f7..b1c89d7 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -11,6 +11,16 @@
#include "wps/wps_defs.h"
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
+
/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
@@ -52,7 +62,8 @@
};
enum p2p_wps_method {
- WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+ WPS_P2PS
};
/**
@@ -142,6 +153,93 @@
unsigned int peer_config_timeout;
};
+struct p2ps_provision {
+ /**
+ * status - Remote returned provisioning status code
+ */
+ int status;
+
+ /**
+ * adv_id - P2PS Advertisement ID
+ */
+ u32 adv_id;
+
+ /**
+ * session_id - P2PS Session ID
+ */
+ u32 session_id;
+
+ /**
+ * method - WPS Method (to be) used to establish session
+ */
+ u16 method;
+
+ /**
+ * conncap - Connection Capabilities negotiated between P2P peers
+ */
+ u8 conncap;
+
+ /**
+ * role - Info about the roles to be used for this connection
+ */
+ u8 role;
+
+ /**
+ * session_mac - MAC address of the peer that started the session
+ */
+ u8 session_mac[ETH_ALEN];
+
+ /**
+ * adv_mac - MAC address of the peer advertised the service
+ */
+ u8 adv_mac[ETH_ALEN];
+
+ /**
+ * info - Vendor defined extra Provisioning information
+ */
+ char info[0];
+};
+
+struct p2ps_advertisement {
+ struct p2ps_advertisement *next;
+
+ /**
+ * svc_info - Pointer to (internal) Service defined information
+ */
+ char *svc_info;
+
+ /**
+ * id - P2PS Advertisement ID
+ */
+ u32 id;
+
+ /**
+ * config_methods - WPS Methods which are allowed for this service
+ */
+ u16 config_methods;
+
+ /**
+ * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+ */
+ u8 state;
+
+ /**
+ * auto_accept - Automatically Accept provisioning request if possible.
+ */
+ u8 auto_accept;
+
+ /**
+ * hash - 6 octet Service Name has to match against incoming Probe Requests
+ */
+ u8 hash[P2PS_HASH_LEN];
+
+ /**
+ * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+ */
+ char svc_name[0];
+};
+
+
struct p2p_data;
enum p2p_scan_type {
@@ -238,6 +336,11 @@
* IE(s) from the frame that was used to discover the peer.
*/
struct wpabuf *vendor_elems;
+
+ /**
+ * p2ps_instance - P2PS Application Service Info
+ */
+ struct wpabuf *p2ps_instance;
};
enum p2p_prov_disc_status {
@@ -245,6 +348,7 @@
P2P_PROV_DISC_TIMEOUT,
P2P_PROV_DISC_REJECTED,
P2P_PROV_DISC_TIMEOUT_JOIN,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
};
struct p2p_channel {
@@ -705,6 +809,9 @@
* @ctx: Callback context from cb_ctx
* @peer: Source address of the response
* @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+ * @adv_id: If non-zero, then the adv_id of the PD Request
+ * @adv_mac: P2P Device Address of the advertizer
+ * @deferred_session_resp: Deferred session response sent by advertizer
*
* This callback is used to indicate either a failure or no response
* to an earlier provision discovery request.
@@ -713,7 +820,9 @@
* is not used or failures do not need to be indicated.
*/
void (*prov_disc_fail)(void *ctx, const u8 *peer,
- enum p2p_prov_disc_status status);
+ enum p2p_prov_disc_status status,
+ u32 adv_id, const u8 *adv_mac,
+ const char *deferred_session_resp);
/**
* invitation_process - Optional callback for processing Invitations
@@ -835,6 +944,83 @@
* or 0 if not.
*/
int (*is_p2p_in_progress)(void *ctx);
+
+ /**
+ * Determine if we have a persistent group we share with remote peer
+ * @ctx: Callback context from cb_ctx
+ * @addr: Peer device address to search for
+ * @ssid: Persistent group SSID or %NULL if any
+ * @ssid_len: Length of @ssid
+ * @go_dev_addr: Buffer for returning intended GO P2P Device Address
+ * @ret_ssid: Buffer for returning group SSID
+ * @ret_ssid_len: Buffer for returning length of @ssid
+ * Returns: 1 if a matching persistent group was found, 0 otherwise
+ */
+ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
+ size_t ssid_len, u8 *go_dev_addr,
+ u8 *ret_ssid, size_t *ret_ssid_len);
+
+ /**
+ * Get information about a possible local GO role
+ * @ctx: Callback context from cb_ctx
+ * @intended_addr: Buffer for returning intended GO interface address
+ * @ssid: Buffer for returning group SSID
+ * @ssid_len: Buffer for returning length of @ssid
+ * @group_iface: Buffer for returning whether a separate group interface
+ * would be used
+ * Returns: 1 if GO info found, 0 otherwise
+ *
+ * This is used to compose New Group settings (SSID, and intended
+ * address) during P2PS provisioning if results of provisioning *might*
+ * result in our being an autonomous GO.
+ */
+ int (*get_go_info)(void *ctx, u8 *intended_addr,
+ u8 *ssid, size_t *ssid_len, int *group_iface);
+
+ /**
+ * remove_stale_groups - Remove stale P2PS groups
+ *
+ * Because P2PS stages *potential* GOs, and remote devices can remove
+ * credentials unilaterally, we need to make sure we don't let stale
+ * unusable groups build up.
+ */
+ int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+ const u8 *ssid, size_t ssid_len);
+
+ /**
+ * p2ps_prov_complete - P2PS provisioning complete
+ *
+ * When P2PS provisioning completes (successfully or not) we must
+ * transmit all of the results to the upper layers.
+ */
+ void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+ const u8 *adv_mac, const u8 *ses_mac,
+ const u8 *grp_mac, u32 adv_id, u32 ses_id,
+ u8 conncap, int passwd_id,
+ const u8 *persist_ssid,
+ size_t persist_ssid_size, int response_done,
+ int prov_start, const char *session_info);
+
+ /**
+ * prov_disc_resp_cb - Callback for indicating completion of PD Response
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if operation was started, 0 otherwise
+ *
+ * This callback can be used to perform any pending actions after
+ * provisioning. It is mainly used for P2PS pending group creation.
+ */
+ int (*prov_disc_resp_cb)(void *ctx);
+
+ /**
+ * p2ps_group_capability - Determine group capability
+ *
+ * This function can be used to determine group capability based on
+ * information from P2PS PD exchange and the current state of ongoing
+ * groups and driver capabilities.
+ *
+ * P2PS_SETUP_* bitmap is used as the parameters and return value.
+ */
+ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
};
@@ -941,12 +1127,15 @@
* requested device types.
* @dev_id: Device ID to search for or %NULL to find all devices
* @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
* Returns: 0 on success, -1 on failure
*/
int p2p_find(struct p2p_data *p2p, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
- const u8 *dev_id, unsigned int search_delay);
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_count, const char **seek_string);
/**
* p2p_notify_scan_trigger_status - Indicate scan trigger status
@@ -1058,6 +1247,7 @@
* p2p_prov_disc_req - Send Provision Discovery Request
* @p2p: P2P module context from p2p_init()
* @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
* @config_methods: WPS Config Methods value (only one bit set)
* @join: Whether this is used by a client joining an active group
* @force_freq: Forced TX frequency for the frame (mainly for the join case)
@@ -1073,7 +1263,8 @@
* indicated with the p2p_config::prov_disc_resp() callback.
*/
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
- u16 config_methods, int join, int force_freq,
+ struct p2ps_provision *p2ps_prov, u16 config_methods,
+ int join, int force_freq,
int user_initiated_pd);
/**
@@ -1816,6 +2007,13 @@
unsigned int p2p_get_group_num_members(struct p2p_group *group);
/**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
* p2p_iterate_group_members - Iterate group members
* @group: P2P group context from p2p_group_init()
* @next: iteration pointer, must be a pointer to a void * that is set to %NULL
@@ -2030,4 +2228,14 @@
void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info);
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index e9b683d..92c9206 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -164,15 +164,18 @@
if (peer->wps_method == WPS_PBC)
methods |= WPS_CONFIG_PUSHBUTTON;
else if (peer->wps_method == WPS_PIN_DISPLAY ||
- peer->wps_method == WPS_PIN_KEYPAD)
+ peer->wps_method == WPS_PIN_KEYPAD) {
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods |= WPS_CONFIG_P2PS;
+ }
} else if (p2p->cfg->config_methods) {
methods |= p2p->cfg->config_methods &
(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
- WPS_CONFIG_KEYPAD);
+ WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
} else {
methods |= WPS_CONFIG_PUSHBUTTON;
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods |= WPS_CONFIG_P2PS;
}
wpabuf_put_be16(buf, methods);
@@ -342,6 +345,256 @@
}
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+ if (!p2p)
+ return;
+
+ /* Service Hash */
+ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+ wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+ wpabuf_put_data(buf, p2p->query_hash,
+ p2p->p2ps_seek_count * P2PS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+ p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+}
+
+
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
+{
+ size_t info_len = 0;
+
+ if (info && info[0])
+ info_len = os_strlen(info);
+
+ /* Session Information Data Info */
+ wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
+ wpabuf_put_le16(buf, (u16) info_len);
+
+ if (info) {
+ wpabuf_put_data(buf, info, info_len);
+ wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
+ }
+}
+
+
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
+{
+ /* Connection Capability Info */
+ wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, connection_cap);
+ wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+ connection_cap);
+}
+
+
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+ if (!buf || !mac)
+ return;
+
+ /* Advertisement ID Info */
+ wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
+ wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+ wpabuf_put_le32(buf, id);
+ wpabuf_put_data(buf, mac, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
+ id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+ u8 hash_count, const u8 *hash,
+ struct p2ps_advertisement *adv_list)
+{
+ struct p2ps_advertisement *adv;
+ struct wpabuf *tmp_buf;
+ u8 *tag_len = NULL, *ie_len = NULL;
+ size_t svc_len = 0, remaining = 0, total_len = 0;
+
+ if (!adv_list || !hash)
+ return;
+
+ /* Allocate temp buffer, allowing for overflow of 1 instance */
+ tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+ if (!tmp_buf)
+ return;
+
+ for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+ adv = adv->next) {
+ u8 count = hash_count;
+ const u8 *test = hash;
+
+ while (count--) {
+ /* Check for wildcard */
+ if (os_memcmp(test, p2p->wild_card_hash,
+ P2PS_HASH_LEN) == 0) {
+ total_len = MAX_SVC_ADV_LEN + 1;
+ goto wild_hash;
+ }
+
+ if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0)
+ goto hash_match;
+
+ test += P2PS_HASH_LEN;
+ }
+
+ /* No matches found - Skip this Adv Instance */
+ continue;
+
+hash_match:
+ if (!tag_len) {
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ remaining = 255 - 4;
+ if (!ie_len) {
+ wpabuf_put_u8(tmp_buf,
+ P2P_ATTR_ADVERTISED_SERVICE);
+ ie_len = wpabuf_put(tmp_buf, sizeof(u16));
+ remaining -= (sizeof(u8) + sizeof(u16));
+ }
+ }
+
+ svc_len = os_strlen(adv->svc_name);
+
+ if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) {
+ /* Can't fit... return wildcard */
+ total_len = MAX_SVC_ADV_LEN + 1;
+ break;
+ }
+
+ if (remaining <= (sizeof(adv->id) +
+ sizeof(adv->config_methods))) {
+ size_t front = remaining;
+ size_t back = (sizeof(adv->id) +
+ sizeof(adv->config_methods)) - front;
+ u8 holder[sizeof(adv->id) +
+ sizeof(adv->config_methods)];
+
+ /* This works even if front or back == 0 */
+ WPA_PUT_LE32(holder, adv->id);
+ WPA_PUT_BE16(&holder[sizeof(adv->id)],
+ adv->config_methods);
+ wpabuf_put_data(tmp_buf, holder, front);
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ wpabuf_put_data(tmp_buf, &holder[front], back);
+ remaining = 255 - (sizeof(adv->id) +
+ sizeof(adv->config_methods)) - back;
+ } else {
+ wpabuf_put_le32(tmp_buf, adv->id);
+ wpabuf_put_be16(tmp_buf, adv->config_methods);
+ remaining -= (sizeof(adv->id) +
+ sizeof(adv->config_methods));
+ }
+
+ /* We are guaranteed at least one byte for svc_len */
+ wpabuf_put_u8(tmp_buf, svc_len);
+ remaining -= sizeof(u8);
+
+ if (remaining < svc_len) {
+ size_t front = remaining;
+ size_t back = svc_len - front;
+
+ wpabuf_put_data(tmp_buf, adv->svc_name, front);
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+ /* In rare cases, we must split across 3 attributes */
+ if (back > 255 - 4) {
+ wpabuf_put_data(tmp_buf,
+ &adv->svc_name[front], 255 - 4);
+ back -= 255 - 4;
+ front += 255 - 4;
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ }
+
+ wpabuf_put_data(tmp_buf, &adv->svc_name[front], back);
+ remaining = 255 - 4 - back;
+ } else {
+ wpabuf_put_data(tmp_buf, adv->svc_name, svc_len);
+ remaining -= svc_len;
+ }
+
+ /* adv_id config_methods svc_string */
+ total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len;
+ }
+
+ if (tag_len)
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+
+ if (ie_len)
+ WPA_PUT_LE16(ie_len, (u16) total_len);
+
+wild_hash:
+ /* If all fit, return matching instances, otherwise the wildcard */
+ if (total_len <= MAX_SVC_ADV_LEN) {
+ wpabuf_put_buf(buf, tmp_buf);
+ } else {
+ char *wild_card = P2PS_WILD_HASH_STR;
+ u8 wild_len;
+
+ /* Insert wildcard instance */
+ tag_len = p2p_buf_add_ie_hdr(buf);
+ wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE);
+ ie_len = wpabuf_put(buf, sizeof(u16));
+
+ wild_len = (u8) os_strlen(wild_card);
+ wpabuf_put_le32(buf, 0);
+ wpabuf_put_be16(buf, 0);
+ wpabuf_put_u8(buf, wild_len);
+ wpabuf_put_data(buf, wild_card, wild_len);
+
+ WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len);
+ p2p_buf_update_ie_hdr(buf, tag_len);
+ }
+
+ wpabuf_free(tmp_buf);
+}
+
+
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+ if (!buf || !mac)
+ return;
+
+ /* Session ID Info */
+ wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
+ wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+ wpabuf_put_le32(buf, id);
+ wpabuf_put_data(buf, mac, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
+ id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
+{
+ if (!buf || !len || !mask)
+ return;
+
+ /* Feature Capability */
+ wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
+ wpabuf_put_le16(buf, len);
+ wpabuf_put_data(buf, mask, len);
+ wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
+}
+
+
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ /* P2P Group ID */
+ wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
+ wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpabuf_put_data(buf, ssid, ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+ MAC2STR(dev_addr));
+}
+
+
static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
const char *val)
{
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index c654c5a..98abf9d 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -107,6 +107,8 @@
return DEV_PW_PUSHBUTTON;
case WPS_NFC:
return DEV_PW_NFC_CONNECTION_HANDOVER;
+ case WPS_P2PS:
+ return DEV_PW_P2PS_DEFAULT;
default:
return DEV_PW_DEFAULT;
}
@@ -124,6 +126,8 @@
return "PBC";
case WPS_NFC:
return "NFC";
+ case WPS_P2PS:
+ return "P2PS";
default:
return "??";
}
@@ -218,10 +222,12 @@
config_method = WPS_CONFIG_DISPLAY;
else if (dev->wps_method == WPS_PBC)
config_method = WPS_CONFIG_PUSHBUTTON;
+ else if (dev->wps_method == WPS_P2PS)
+ config_method = WPS_CONFIG_P2PS;
else
return -1;
return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
- config_method, 0, 0, 1);
+ NULL, config_method, 0, 0, 1);
}
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
@@ -488,8 +494,8 @@
}
-static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
- u8 *status)
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ u8 *status)
{
struct p2p_channels tmp, intersection;
@@ -743,6 +749,16 @@
goto fail;
}
break;
+ case DEV_PW_P2PS_DEFAULT:
+ p2p_dbg(p2p, "Peer using P2PS pin");
+ if (dev->wps_method != WPS_P2PS) {
+ p2p_dbg(p2p,
+ "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
@@ -1102,6 +1118,15 @@
goto fail;
}
break;
+ case DEV_PW_P2PS_DEFAULT:
+ p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
+ if (dev->wps_method != WPS_P2PS) {
+ p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index da8588a..41ca99f 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -981,10 +981,22 @@
unsigned int p2p_get_group_num_members(struct p2p_group *group)
{
+ if (!group)
+ return 0;
+
return group->num_members;
}
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+ if (!group || !group->cfg)
+ return 1;
+
+ return group->num_members >= group->cfg->max_clients;
+}
+
+
const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
{
struct p2p_group_member *iter = *next;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 62711e7..6af19ce 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -16,6 +16,13 @@
enum p2p_role_indication;
+/*
+ * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
+ * must equal 248 or less. Must have a minimum size of 19.
+ */
+#define MAX_SVC_ADV_LEN 600
+#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
+
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
@@ -98,6 +105,8 @@
#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
#define P2P_DEV_NO_PREF_CHAN BIT(18)
#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
+#define P2P_DEV_P2PS_REPORTED BIT(20)
+#define P2P_DEV_PD_PEER_P2PS BIT(21)
unsigned int flags;
int status; /* enum p2p_status_code */
@@ -354,6 +363,7 @@
P2P_PENDING_GO_NEG_CONFIRM,
P2P_PENDING_SD,
P2P_PENDING_PD,
+ P2P_PENDING_PD_RESPONSE,
P2P_PENDING_INVITATION_REQUEST,
P2P_PENDING_INVITATION_RESPONSE,
P2P_PENDING_DEV_DISC_REQUEST,
@@ -492,6 +502,16 @@
u8 pending_channel;
u8 pending_channel_forced;
+ /* ASP Support */
+ struct p2ps_advertisement *p2ps_adv_list;
+ struct p2ps_provision *p2ps_prov;
+ u8 wild_card_hash[P2PS_HASH_LEN];
+ u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
+ u8 query_count;
+ u8 p2ps_seek;
+ u8 p2ps_seek_count;
+ u8 p2ps_svc_found;
+
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie_beacon;
struct wpabuf *wfd_ie_probe_req;
@@ -586,6 +606,31 @@
/* SSID IE */
const u8 *ssid;
+
+ /* P2PS */
+ u8 service_hash_count;
+ const u8 *service_hash;
+
+ const u8 *session_info;
+ size_t session_info_len;
+
+ const u8 *conn_cap;
+
+ const u8 *adv_id;
+ const u8 *adv_mac;
+
+ const u8 *adv_service_instance;
+ size_t adv_service_instance_len;
+
+ const u8 *session_id;
+ const u8 *session_mac;
+
+ const u8 *feature_cap;
+ size_t feature_cap_len;
+
+ const u8 *persistent_dev;
+ const u8 *persistent_ssid;
+ size_t persistent_ssid_len;
};
@@ -698,6 +743,18 @@
void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
u8 oper_class, u8 channel,
enum p2p_role_indication role);
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+ u8 count, const u8 *hash,
+ struct p2ps_advertisement *adv_list);
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
+ const u8 *mask);
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len);
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr);
@@ -793,6 +850,8 @@
unsigned int force_freq, unsigned int pref_freq,
int go);
void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ u8 *status);
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 52ba19e..fd6a461 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -281,6 +281,112 @@
data[0], data[1], data[2], data[3], data[4],
data[5]);
break;
+ case P2P_ATTR_SERVICE_HASH:
+ if (len < P2PS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Service Hash (length %u)",
+ len);
+ return -1;
+ }
+ msg->service_hash_count = len / P2PS_HASH_LEN;
+ msg->service_hash = data;
+ wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len);
+ break;
+ case P2P_ATTR_SESSION_INFORMATION_DATA:
+ msg->session_info = data;
+ msg->session_info_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p",
+ len, data);
+ break;
+ case P2P_ATTR_CONNECTION_CAPABILITY:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Connection Capability (length %u)",
+ len);
+ return -1;
+ }
+ msg->conn_cap = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+ *msg->conn_cap);
+ break;
+ case P2P_ATTR_ADVERTISEMENT_ID:
+ if (len < 10) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Advertisement ID (length %u)",
+ len);
+ return -1;
+ }
+ msg->adv_id = data;
+ msg->adv_mac = &data[sizeof(u32)];
+ wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x",
+ WPA_GET_LE32(data));
+ break;
+ case P2P_ATTR_ADVERTISED_SERVICE:
+ if (len < 8) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Service Instance (length %u)",
+ len);
+ return -1;
+ }
+ msg->adv_service_instance = data;
+ msg->adv_service_instance_len = len;
+ if (len <= 255 + 8) {
+ char str[256];
+ u8 namelen;
+
+ namelen = data[6];
+ if (namelen > len - 7)
+ break;
+ os_memcpy(str, &data[7], namelen);
+ str[namelen] = '\0';
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s",
+ WPA_GET_LE32(data), str);
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p",
+ data);
+ }
+ break;
+ case P2P_ATTR_SESSION_ID:
+ if (len < sizeof(u32) + ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Session ID Info (length %u)",
+ len);
+ return -1;
+ }
+ msg->session_id = data;
+ msg->session_mac = &data[sizeof(u32)];
+ wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR,
+ WPA_GET_LE32(data), MAC2STR(msg->session_mac));
+ break;
+ case P2P_ATTR_FEATURE_CAPABILITY:
+ if (!len) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Feature Capability (length %u)",
+ len);
+ return -1;
+ }
+ msg->feature_cap = data;
+ msg->feature_cap_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len);
+ break;
+ case P2P_ATTR_PERSISTENT_GROUP:
+ {
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Persistent Group Info (length %u)",
+ len);
+ return -1;
+ }
+
+ msg->persistent_dev = data;
+ msg->persistent_ssid_len = len - ETH_ALEN;
+ msg->persistent_ssid = &data[ETH_ALEN];
+ wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s",
+ MAC2STR(msg->persistent_dev),
+ wpa_ssid_txt(msg->persistent_ssid,
+ msg->persistent_ssid_len));
+ break;
+ }
default:
wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
"(length %d)", id, len);
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index e101367..328b1e0 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,14 +40,132 @@
}
+static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+{
+ int found;
+ u8 intended_addr[ETH_ALEN];
+ u8 ssid[32];
+ size_t ssid_len;
+ int group_iface;
+
+ if (!p2p->cfg->get_go_info)
+ return;
+
+ found = p2p->cfg->get_go_info(
+ p2p->cfg->cb_ctx, intended_addr, ssid,
+ &ssid_len, &group_iface);
+ if (found) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+ ssid, ssid_len);
+ p2p_buf_add_intended_addr(buf, intended_addr);
+ } else {
+ if (!p2p->ssid_set) {
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ p2p->ssid_set = 1;
+ }
+
+ /* Add pre-composed P2P Group ID */
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+ p2p->ssid, p2p->ssid_len);
+
+ if (group_iface)
+ p2p_buf_add_intended_addr(
+ buf, p2p->intended_addr);
+ else
+ p2p_buf_add_intended_addr(
+ buf, p2p->cfg->dev_addr);
+ }
+}
+
+
+static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
+ struct wpabuf *buf, u16 config_methods)
+{
+ struct p2ps_provision *prov = p2p->p2ps_prov;
+ u8 feat_cap_mask[] = { 1, 0 };
+ int shared_group = 0;
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 go_dev_addr[ETH_ALEN];
+
+ /* If we might be explicite group owner, add GO details */
+ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+ P2PS_SETUP_NEW))
+ p2ps_add_new_group_info(p2p, buf);
+
+ if (prov->status >= 0)
+ p2p_buf_add_status(buf, (u8) prov->status);
+ else
+ prov->method = config_methods;
+
+ if (p2p->cfg->get_persistent_group) {
+ shared_group = p2p->cfg->get_persistent_group(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
+ go_dev_addr, ssid, &ssid_len);
+ }
+
+ /* Add Operating Channel if conncap includes GO */
+ if (shared_group ||
+ (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+ P2PS_SETUP_NEW))) {
+ u8 tmp;
+
+ p2p_go_select_channel(p2p, dev, &tmp);
+
+ if (p2p->op_reg_class && p2p->op_channel)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ }
+
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
+
+ if (prov->info[0])
+ p2p_buf_add_session_info(buf, prov->info);
+
+ p2p_buf_add_connection_capability(buf, prov->conncap);
+
+ p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
+
+ if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+ /* Add Config Timeout */
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+ }
+
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+
+ p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
+
+ p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+ feat_cap_mask);
+
+ if (shared_group)
+ p2p_buf_add_persistent_group_info(buf, go_dev_addr,
+ ssid, ssid_len);
+}
+
+
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
- u8 dialog_token,
- u16 config_methods,
- struct p2p_device *go)
+ struct p2p_device *dev,
+ int join)
{
struct wpabuf *buf;
u8 *len;
size_t extra = 0;
+ u8 dialog_token = dev->dialog_token;
+ u16 config_methods = dev->req_config_methods;
+ struct p2p_device *go = join ? dev : NULL;
+ u8 group_capab;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_prov_disc_req)
@@ -57,6 +175,10 @@
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+ if (p2p->p2ps_prov)
+ extra += os_strlen(p2p->p2ps_prov->info) + 1 +
+ sizeof(struct p2ps_provision);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -64,10 +186,23 @@
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
+
+ group_capab = 0;
+ if (p2p->p2ps_prov) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
p2p_buf_add_capability(buf, p2p->dev_capab &
- ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
p2p_buf_add_device_info(buf, p2p, NULL);
- if (go) {
+ if (p2p->p2ps_prov) {
+ p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
+ } else if (go) {
p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
go->oper_ssid, go->oper_ssid_len);
}
@@ -89,13 +224,19 @@
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+ struct p2p_device *dev,
u8 dialog_token,
+ enum p2p_status_code status,
u16 config_methods,
+ u32 adv_id,
const u8 *group_id,
- size_t group_id_len)
+ size_t group_id_len,
+ const u8 *persist_ssid,
+ size_t persist_ssid_len)
{
struct wpabuf *buf;
size_t extra = 0;
+ int persist = 0;
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
@@ -121,12 +262,103 @@
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
- buf = wpabuf_alloc(100 + extra);
+ buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+ /* Add P2P IE for P2PS */
+ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
+ u8 feat_cap_mask[] = { 1, 0 };
+ u8 *len = p2p_buf_add_ie_hdr(buf);
+ struct p2ps_provision *prov = p2p->p2ps_prov;
+ u8 group_capab;
+
+ if (!status && prov->status != -1)
+ status = prov->status;
+
+ p2p_buf_add_status(buf, status);
+ group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+
+ if (persist_ssid && p2p->cfg->get_persistent_group &&
+ (status == P2P_SC_SUCCESS ||
+ status == P2P_SC_SUCCESS_DEFERRED)) {
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 go_dev_addr[ETH_ALEN];
+
+ persist = p2p->cfg->get_persistent_group(
+ p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ persist_ssid, persist_ssid_len, go_dev_addr,
+ ssid, &ssid_len);
+ if (persist)
+ p2p_buf_add_persistent_group_info(
+ buf, go_dev_addr, ssid, ssid_len);
+ }
+
+ if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
+ p2ps_add_new_group_info(p2p, buf);
+
+ /* Add Operating Channel if conncap indicates GO */
+ if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
+ u8 tmp;
+
+ if (dev)
+ p2p_go_select_channel(p2p, dev, &tmp);
+
+ if (p2p->op_reg_class && p2p->op_channel)
+ p2p_buf_add_operating_channel(
+ buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(
+ buf, p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ }
+
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->cfg->channels);
+
+ if (!persist && (status == P2P_SC_SUCCESS ||
+ status == P2P_SC_SUCCESS_DEFERRED))
+ p2p_buf_add_connection_capability(buf, prov->conncap);
+
+ p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
+
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+
+ p2p_buf_add_session_id(buf, prov->session_id,
+ prov->session_mac);
+
+ p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+ feat_cap_mask);
+ p2p_buf_update_ie_hdr(buf, len);
+ } else if (status != P2P_SC_SUCCESS || adv_id) {
+ u8 *len = p2p_buf_add_ie_hdr(buf);
+
+ p2p_buf_add_status(buf, status);
+
+ if (p2p->p2ps_prov)
+ p2p_buf_add_advertisement_id(buf, adv_id,
+ p2p->p2ps_prov->adv_mac);
+
+ p2p_buf_update_ie_hdr(buf, len);
+ }
+
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
@@ -142,14 +374,50 @@
}
+static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
+ u32 session_id, u16 method,
+ const u8 *session_mac, const u8 *adv_mac)
+{
+ struct p2ps_provision *tmp;
+
+ if (!p2p->p2ps_prov) {
+ p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
+ if (!p2p->p2ps_prov)
+ return -1;
+ } else {
+ os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
+ }
+
+ tmp = p2p->p2ps_prov;
+ tmp->adv_id = adv_id;
+ tmp->session_id = session_id;
+ tmp->method = method;
+ os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
+ os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
+ tmp->info[0] = '\0';
+
+ return 0;
+}
+
+
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_message msg;
struct p2p_device *dev;
int freq;
- int reject = 1;
+ enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
struct wpabuf *resp;
+ u32 adv_id = 0;
+ struct p2ps_advertisement *p2ps_adv = NULL;
+ u8 conncap = P2PS_SETUP_NEW;
+ u8 auto_accept = 0;
+ u32 session_id = 0;
+ u8 session_mac[ETH_ALEN];
+ u8 adv_mac[ETH_ALEN];
+ u8 group_mac[ETH_ALEN];
+ int passwd_id = DEV_PW_DEFAULT;
+ u16 config_methods;
if (p2p_parse(data, len, &msg))
return;
@@ -175,12 +443,13 @@
if (!(msg.wps_config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
- WPS_CONFIG_PUSHBUTTON))) {
+ WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
goto out;
}
- if (msg.group_id) {
+ /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+ if (!msg.adv_id && msg.group_id) {
size_t i;
for (i = 0; i < p2p->num_groups; i++) {
if (p2p_group_is_group_id_match(p2p->groups[i],
@@ -194,28 +463,203 @@
}
}
- if (dev)
+ if (dev) {
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
- P2P_DEV_PD_PEER_KEYPAD);
+ P2P_DEV_PD_PEER_KEYPAD |
+ P2P_DEV_PD_PEER_P2PS);
+
+ /* Remove stale persistent groups */
+ if (p2p->cfg->remove_stale_groups) {
+ p2p->cfg->remove_stale_groups(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+ msg.persistent_dev,
+ msg.persistent_ssid, msg.persistent_ssid_len);
+ }
+ }
if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to show a PIN on display", MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ passwd_id = DEV_PW_USER_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to write its PIN using keypad",
MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+ p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+ MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_P2PS;
+ passwd_id = DEV_PW_P2PS_DEFAULT;
}
- reject = 0;
+ reject = P2P_SC_SUCCESS;
+
+ os_memset(session_mac, 0, ETH_ALEN);
+ os_memset(adv_mac, 0, ETH_ALEN);
+ os_memset(group_mac, 0, ETH_ALEN);
+
+ if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
+ (msg.status || msg.conn_cap)) {
+ u8 remote_conncap;
+
+ if (msg.intended_addr)
+ os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+
+ os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
+ os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+
+ session_id = WPA_GET_LE32(msg.session_id);
+ adv_id = WPA_GET_LE32(msg.adv_id);
+
+ if (!msg.status)
+ p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+
+ p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
+
+ if (msg.conn_cap)
+ conncap = *msg.conn_cap;
+ remote_conncap = conncap;
+
+ if (p2ps_adv) {
+ auto_accept = p2ps_adv->auto_accept;
+ conncap = p2p->cfg->p2ps_group_capability(
+ p2p->cfg->cb_ctx, conncap, auto_accept);
+
+ p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+ auto_accept, remote_conncap, conncap);
+
+ if (p2ps_adv->config_methods &&
+ !(msg.wps_config_methods &
+ p2ps_adv->config_methods)) {
+ p2p_dbg(p2p,
+ "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+ p2ps_adv->config_methods,
+ msg.wps_config_methods);
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if (!p2ps_adv->state) {
+ p2p_dbg(p2p, "P2PS state unavailable");
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else if (!conncap) {
+ p2p_dbg(p2p, "Conncap resolution failed");
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ p2p_dbg(p2p, "Keypad - always defer");
+ auto_accept = 0;
+ }
+
+ if (auto_accept || reject != P2P_SC_SUCCESS) {
+ struct p2ps_provision *tmp;
+
+ if (reject == P2P_SC_SUCCESS && !conncap) {
+ reject =
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (p2ps_setup_p2ps_prov(
+ p2p, adv_id, session_id,
+ msg.wps_config_methods,
+ session_mac, adv_mac) < 0) {
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ goto out;
+ }
+
+ tmp = p2p->p2ps_prov;
+ if (conncap) {
+ tmp->conncap = conncap;
+ tmp->status = P2P_SC_SUCCESS;
+ } else {
+ tmp->conncap = auto_accept;
+ tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (reject != P2P_SC_SUCCESS)
+ goto out;
+ }
+ } else if (!msg.status) {
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+
+ if (!msg.status && !auto_accept &&
+ (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+ struct p2ps_provision *tmp;
+
+ if (!conncap) {
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+
+ if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+ msg.wps_config_methods,
+ session_mac, adv_mac) < 0) {
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ goto out;
+ }
+ tmp = p2p->p2ps_prov;
+ reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ tmp->status = reject;
+ }
+
+ if (msg.status) {
+ if (*msg.status &&
+ *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+ reject = *msg.status;
+ } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ u16 method = p2p->p2ps_prov->method;
+
+ conncap = p2p->cfg->p2ps_group_capability(
+ p2p->cfg->cb_ctx, remote_conncap,
+ p2p->p2ps_prov->conncap);
+
+ p2p_dbg(p2p,
+ "Conncap: local:%d remote:%d result:%d",
+ p2p->p2ps_prov->conncap,
+ remote_conncap, conncap);
+
+ /*
+ * Ensure that if we asked for PIN originally,
+ * our method is consistent with original
+ * request.
+ */
+ if (method & WPS_CONFIG_DISPLAY)
+ method = WPS_CONFIG_KEYPAD;
+ else if (method & WPS_CONFIG_KEYPAD)
+ method = WPS_CONFIG_DISPLAY;
+
+ /* Reject this "Deferred Accept* if incompatible
+ * conncap or method */
+ if (!conncap ||
+ !(msg.wps_config_methods & method))
+ reject =
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ else
+ reject = P2P_SC_SUCCESS;
+
+ p2p->p2ps_prov->status = reject;
+ p2p->p2ps_prov->conncap = conncap;
+ }
+ }
+ }
out:
- resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
- reject ? 0 : msg.wps_config_methods,
- msg.group_id, msg.group_id_len);
+ if (reject == P2P_SC_SUCCESS ||
+ reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ config_methods = msg.wps_config_methods;
+ else
+ config_methods = 0;
+ resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
+ config_methods, adv_id,
+ msg.group_id, msg.group_id_len,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len);
if (resp == NULL) {
p2p_parse_free(&msg);
return;
@@ -232,7 +676,7 @@
p2p_parse_free(&msg);
return;
}
- p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
@@ -242,7 +686,91 @@
wpabuf_free(resp);
- if (!reject && p2p->cfg->prov_disc_req) {
+ if (!p2p->cfg->p2ps_prov_complete) {
+ /* Don't emit anything */
+ } else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
+ *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+ reject = *msg.status;
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+ sa, adv_mac, session_mac,
+ NULL, adv_id, session_id,
+ 0, 0, msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ p2p->p2ps_prov->status = reject;
+ p2p->p2ps_prov->conncap = conncap;
+
+ if (reject != P2P_SC_SUCCESS)
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+ sa, adv_mac, session_mac,
+ NULL, adv_id,
+ session_id, conncap, 0,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len, 0,
+ 0, NULL);
+ else
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
+ *msg.status,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id,
+ session_id, conncap,
+ passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len, 0,
+ 0, NULL);
+ } else if (msg.status && p2p->p2ps_prov) {
+ p2p->p2ps_prov->status = P2P_SC_SUCCESS;
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
+ adv_mac, session_mac, group_mac,
+ adv_id, session_id, conncap,
+ passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (msg.status) {
+ } else if (auto_accept && reject == P2P_SC_SUCCESS) {
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id, session_id,
+ conncap, passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ (!msg.session_info || !msg.session_info_len)) {
+ p2p->p2ps_prov->method = msg.wps_config_methods;
+
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id, session_id,
+ conncap, passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 1, NULL);
+ } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ size_t buf_len = msg.session_info_len;
+ char *buf = os_malloc(2 * buf_len + 1);
+
+ if (buf) {
+ p2p->p2ps_prov->method = msg.wps_config_methods;
+
+ utf8_escape((char *) msg.session_info, buf_len,
+ buf, 2 * buf_len + 1);
+
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
+ adv_mac, session_mac, group_mac, adv_id,
+ session_id, conncap, passwd_id,
+ msg.persistent_ssid, msg.persistent_ssid_len,
+ 0, 1, buf);
+
+ os_free(buf);
+ }
+ }
+
+ if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
const u8 *dev_addr = sa;
if (msg.p2p_device_addr)
dev_addr = msg.p2p_device_addr;
@@ -265,11 +793,48 @@
struct p2p_message msg;
struct p2p_device *dev;
u16 report_config_methods = 0, req_config_methods;
+ u8 status = P2P_SC_SUCCESS;
int success = 0;
+ u32 adv_id = 0;
+ u8 conncap = P2PS_SETUP_NEW;
+ u8 adv_mac[ETH_ALEN];
+ u8 group_mac[ETH_ALEN];
+ int passwd_id = DEV_PW_DEFAULT;
if (p2p_parse(data, len, &msg))
return;
+ /* Parse the P2PS members present */
+ if (msg.status)
+ status = *msg.status;
+
+ if (msg.intended_addr)
+ os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+ else
+ os_memset(group_mac, 0, ETH_ALEN);
+
+ if (msg.adv_mac)
+ os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+ else
+ os_memset(adv_mac, 0, ETH_ALEN);
+
+ if (msg.adv_id)
+ adv_id = WPA_GET_LE32(msg.adv_id);
+
+ if (msg.conn_cap) {
+ conncap = *msg.conn_cap;
+
+ /* Switch bits to local relative */
+ switch (conncap) {
+ case P2PS_SETUP_GROUP_OWNER:
+ conncap = P2PS_SETUP_CLIENT;
+ break;
+ case P2PS_SETUP_CLIENT:
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ break;
+ }
+ }
+
p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
" with config methods 0x%x",
MAC2STR(sa), msg.wps_config_methods);
@@ -313,23 +878,109 @@
msg.wps_config_methods, req_config_methods);
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
- P2P_PROV_DISC_REJECTED);
+ P2P_PROV_DISC_REJECTED,
+ adv_id, adv_mac, NULL);
p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
goto out;
}
report_config_methods = req_config_methods;
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
- P2P_DEV_PD_PEER_KEYPAD);
+ P2P_DEV_PD_PEER_KEYPAD |
+ P2P_DEV_PD_PEER_P2PS);
if (req_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to show a PIN on display", MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to write our PIN using keypad",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ passwd_id = DEV_PW_USER_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+ p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_P2PS;
+ passwd_id = DEV_PW_P2PS_DEFAULT;
+ }
+
+ if ((msg.conn_cap || msg.persistent_dev) &&
+ msg.adv_id &&
+ (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+ p2p->p2ps_prov) {
+ if (p2p->cfg->p2ps_prov_complete) {
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, status, sa, adv_mac,
+ p2p->p2ps_prov->session_mac,
+ group_mac, adv_id, p2p->p2ps_prov->session_id,
+ conncap, passwd_id, msg.persistent_ssid,
+ msg.persistent_ssid_len, 1, 0, NULL);
+ }
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ }
+
+ if (status != P2P_SC_SUCCESS &&
+ status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+ if (p2p->cfg->p2ps_prov_complete)
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, status, sa, adv_mac,
+ p2p->p2ps_prov->session_mac,
+ group_mac, adv_id, p2p->p2ps_prov->session_id,
+ 0, 0, NULL, 0, 1, 0, NULL);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ }
+
+ if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ if (p2p->cfg->remove_stale_groups) {
+ p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ NULL, NULL, 0);
+ }
+
+ if (msg.session_info && msg.session_info_len) {
+ size_t info_len = msg.session_info_len;
+ char *deferred_sess_resp = os_malloc(2 * info_len + 1);
+
+ if (!deferred_sess_resp) {
+ p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ goto out;
+ }
+ utf8_escape((char *) msg.session_info, info_len,
+ deferred_sess_resp, 2 * info_len + 1);
+
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(
+ p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
+ adv_id, adv_mac,
+ deferred_sess_resp);
+ os_free(deferred_sess_resp);
+ } else
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(
+ p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
+ adv_id, adv_mac, NULL);
+ } else if (msg.wps_config_methods != dev->req_config_methods ||
+ status != P2P_SC_SUCCESS) {
+ p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_REJECTED, 0,
+ NULL, NULL);
+ p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ goto out;
}
/* Store the provisioning info */
@@ -388,9 +1039,33 @@
/* TODO: use device discoverability request through GO */
}
- req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
- dev->req_config_methods,
- join ? dev : NULL);
+ if (p2p->p2ps_prov) {
+ if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
+ if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
+ dev->req_config_methods = WPS_CONFIG_KEYPAD;
+ else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
+ dev->req_config_methods = WPS_CONFIG_DISPLAY;
+ else
+ dev->req_config_methods = WPS_CONFIG_P2PS;
+ } else {
+ /* Order of preference, based on peer's capabilities */
+ if (p2p->p2ps_prov->method)
+ dev->req_config_methods =
+ p2p->p2ps_prov->method;
+ else if (dev->info.config_methods & WPS_CONFIG_P2PS)
+ dev->req_config_methods = WPS_CONFIG_P2PS;
+ else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
+ dev->req_config_methods = WPS_CONFIG_DISPLAY;
+ else
+ dev->req_config_methods = WPS_CONFIG_KEYPAD;
+ }
+ p2p_dbg(p2p,
+ "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
+ p2p->p2ps_prov->method, p2p->p2ps_prov->status,
+ dev->req_config_methods);
+ }
+
+ req = p2p_build_prov_disc_req(p2p, dev, join);
if (req == NULL)
return -1;
@@ -413,6 +1088,7 @@
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ struct p2ps_provision *p2ps_prov,
u16 config_methods, int join, int force_freq,
int user_initiated_pd)
{
@@ -424,17 +1100,28 @@
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
" not yet known", MAC2STR(peer_addr));
+ os_free(p2ps_prov);
return -1;
}
p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
" (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
- if (config_methods == 0)
+ if (config_methods == 0 && !p2ps_prov) {
+ os_free(p2ps_prov);
return -1;
+ }
+
+ if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ /* Use cached method from deferred provisioning */
+ p2ps_prov->method = p2p->p2ps_prov->method;
+ }
/* Reset provisioning info */
dev->wps_prov_info = 0;
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = p2ps_prov;
dev->req_config_methods = config_methods;
if (join)
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 6356912..c6534af 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -172,6 +172,7 @@
{
struct eapol_config eapol_conf;
struct eapol_ctx *ctx;
+ int ret;
if (sm->preauth_eapol)
return -1;
@@ -197,14 +198,16 @@
wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
"packet processing (bridge) for "
"pre-authentication");
- return -2;
+ ret = -2;
+ goto fail;
}
}
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
- return -4;
+ ret = -4;
+ goto fail;
}
ctx->ctx = sm->ctx->ctx;
ctx->msg_ctx = sm->ctx->ctx;
@@ -222,7 +225,8 @@
os_free(ctx);
wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
"state machines for pre-authentication");
- return -3;
+ ret = -3;
+ goto fail;
}
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
eapol_conf.accept_802_1x_keys = 0;
@@ -247,6 +251,15 @@
rsn_preauth_timeout, sm, NULL);
return 0;
+
+fail:
+ if (sm->l2_preauth_br) {
+ l2_packet_deinit(sm->l2_preauth_br);
+ sm->l2_preauth_br = NULL;
+ }
+ l2_packet_deinit(sm->l2_preauth);
+ sm->l2_preauth = NULL;
+ return ret;
}
diff --git a/src/utils/common.c b/src/utils/common.c
index 93f1722..5fd795f 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -976,3 +976,89 @@
*context = end;
return pos;
}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+ char *outp, size_t out_size)
+{
+ size_t res_size = 0;
+
+ if (!inp || !outp)
+ return 0;
+
+ if (!in_size)
+ in_size = os_strlen(inp);
+
+ /* Advance past leading single quote */
+ if (*inp == '\'' && in_size) {
+ inp++;
+ in_size--;
+ }
+
+ while (in_size--) {
+ if (res_size >= out_size)
+ return 0;
+
+ switch (*inp) {
+ case '\'':
+ /* Terminate on bare single quote */
+ *outp = '\0';
+ return res_size;
+
+ case '\\':
+ if (!in_size--)
+ return 0;
+ inp++;
+ /* fall through */
+
+ default:
+ *outp++ = *inp++;
+ res_size++;
+ }
+ }
+
+ /* NUL terminate if space allows */
+ if (res_size < out_size)
+ *outp = '\0';
+
+ return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+ char *outp, size_t out_size)
+{
+ size_t res_size = 0;
+
+ if (!inp || !outp)
+ return 0;
+
+ /* inp may or may not be NUL terminated, but must be if 0 size
+ * is specified */
+ if (!in_size)
+ in_size = os_strlen(inp);
+
+ while (in_size--) {
+ if (res_size++ >= out_size)
+ return 0;
+
+ switch (*inp) {
+ case '\\':
+ case '\'':
+ if (res_size++ >= out_size)
+ return 0;
+ *outp++ = '\\';
+ /* fall through */
+
+ default:
+ *outp++ = *inp++;
+ break;
+ }
+ }
+
+ /* NUL terminate if space allows */
+ if (res_size < out_size)
+ *outp = '\0';
+
+ return res_size;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 82a51e5..576e8e7 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -548,6 +548,10 @@
int random_mac_addr_keep_oui(u8 *addr);
char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+ char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+ char *outp, size_t out_size);
/*
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 222d485..c1ede6a 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -535,6 +535,9 @@
#ifdef CONFIG_WPS_NFC
methods |= WPS_CONFIG_NFC_INTERFACE;
#endif /* CONFIG_WPS_NFC */
+#ifdef CONFIG_P2P
+ methods |= WPS_CONFIG_P2PS;
+#endif /* CONFIG_P2P */
} else {
if (os_strstr(str, "ethernet"))
methods |= WPS_CONFIG_ETHERNET;
@@ -560,6 +563,8 @@
methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
if (os_strstr(str, "physical_push_button"))
methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ if (os_strstr(str, "p2ps"))
+ methods |= WPS_CONFIG_P2PS;
}
return methods;
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index da005a4..25cd14a 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -154,7 +154,8 @@
DEV_PW_REKEY = 0x0003,
DEV_PW_PUSHBUTTON = 0x0004,
DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
- DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
+ DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
+ DEV_PW_P2PS_DEFAULT = 0x0008
};
/* Message Type */
@@ -244,6 +245,7 @@
#define WPS_CONFIG_KEYPAD 0x0100
#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_P2PS 0x1000
#define WPS_CONFIG_VIRT_DISPLAY 0x2008
#define WPS_CONFIG_PHY_DISPLAY 0x4008
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 8ee1ea9..48b7e12 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -2578,6 +2578,7 @@
if (wps->dev_pw_id < 0x10 &&
wps->dev_pw_id != DEV_PW_DEFAULT &&
+ wps->dev_pw_id != DEV_PW_P2PS_DEFAULT &&
wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 96a969e..579582b 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -1127,7 +1127,9 @@
AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
endif
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += src/crypto/aes-unwrap.c
+endif
ifdef NEED_AES_EAX
AESOBJS += src/crypto/aes-eax.c
NEED_AES_CTR=y
@@ -1148,8 +1150,10 @@
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += src/crypto/aes-wrap.c
endif
+endif
ifdef NEED_AES_CBC
NEED_AES_ENC=y
AESOBJS += src/crypto/aes-cbc.c
@@ -1195,8 +1199,10 @@
MD5OBJS =
ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
MD5OBJS += src/crypto/md5.c
endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += src/crypto/md5-internal.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 21486c4..95fbe78 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -10,6 +10,7 @@
export BINDIR ?= /usr/local/sbin/
PKG_CONFIG ?= pkg-config
+CFLAGS += $(EXTRA_CFLAGS)
CFLAGS += -I$(abspath ../src)
CFLAGS += -I$(abspath ../src/utils)
@@ -1142,7 +1143,9 @@
AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
endif
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += ../src/crypto/aes-unwrap.o
+endif
ifdef NEED_AES_EAX
AESOBJS += ../src/crypto/aes-eax.o
NEED_AES_CTR=y
@@ -1166,8 +1169,10 @@
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
AESOBJS += ../src/crypto/aes-wrap.o
endif
+endif
ifdef NEED_AES_CBC
NEED_AES_ENC=y
AESOBJS += ../src/crypto/aes-cbc.o
@@ -1208,8 +1213,10 @@
endif
ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
MD5OBJS += ../src/crypto/md5.o
endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += ../src/crypto/md5-internal.o
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index caa480c..c59ccc3 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1701,6 +1701,8 @@
#ifdef CONFIG_HS20
const u8 *hs20;
#endif /* CONFIG_HS20 */
+ const u8 *sess_id;
+ size_t sess_id_len;
if (os_strcmp(params, "-DRIVER") == 0)
return wpa_drv_status(wpa_s, buf, buflen);
@@ -1933,6 +1935,24 @@
pos += res;
}
+ sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
+ if (sess_id) {
+ char *start = pos;
+
+ ret = os_snprintf(pos, end - pos, "eap_session_id=");
+ if (os_snprintf_error(end - pos, ret))
+ return start - buf;
+ pos += ret;
+ ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
+ if (ret <= 0)
+ return start - buf;
+ pos += ret;
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (os_snprintf_error(end - pos, ret))
+ return start - buf;
+ pos += ret;
+ }
+
res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
if (res >= 0)
pos += res;
@@ -4463,6 +4483,8 @@
u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
char *pos;
unsigned int search_delay;
+ const char *seek[P2P_MAX_QUERY_HASH + 1];
+ u8 seek_count = 0;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_INFO,
@@ -4497,8 +4519,180 @@
} else
search_delay = wpas_p2p_search_delay(wpa_s);
+ /* Must be searched for last, because it adds nul termination */
+ pos = os_strstr(cmd, " seek=");
+ while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
+ char *term;
+
+ term = os_strchr(pos + 1, ' ');
+ seek[seek_count++] = pos + 6;
+ pos = os_strstr(pos + 6, " seek=");
+
+ if (term)
+ *term = '\0';
+ }
+
+ if (!seek_count)
+ return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL,
+ _dev_type, _dev_id,
+ search_delay, 0, NULL);
+
+ if (seek_count > P2P_MAX_QUERY_HASH) {
+ seek[0] = NULL;
+ return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL,
+ _dev_type, _dev_id,
+ search_delay, 1, seek);
+ }
+
return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
- _dev_id, search_delay);
+ _dev_id, search_delay, seek_count, seek);
+}
+
+
+static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
+{
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+ size_t info_len = 0;
+ char *info = NULL;
+ u8 role = P2PS_SETUP_NONE;
+ long long unsigned val;
+
+ pos = os_strstr(cmd, "info=");
+ if (pos) {
+ pos += 5;
+ info_len = os_strlen(pos);
+
+ if (info_len) {
+ info = os_malloc(info_len + 1);
+ if (info) {
+ info_len = utf8_unescape(pos, info_len,
+ info, info_len + 1);
+ } else
+ info_len = 0;
+ }
+ }
+
+ p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
+ if (p2ps_prov == NULL) {
+ os_free(info);
+ return NULL;
+ }
+
+ if (info) {
+ os_memcpy(p2ps_prov->info, info, info_len);
+ p2ps_prov->info[info_len] = '\0';
+ os_free(info);
+ }
+
+ pos = os_strstr(cmd, "status=");
+ if (pos)
+ p2ps_prov->status = atoi(pos + 7);
+ else
+ p2ps_prov->status = -1;
+
+ pos = os_strstr(cmd, "adv_id=");
+ if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
+ goto invalid_args;
+ p2ps_prov->adv_id = val;
+
+ pos = os_strstr(cmd, "method=");
+ if (pos)
+ p2ps_prov->method = strtol(pos + 7, NULL, 16);
+ else
+ p2ps_prov->method = 0;
+
+ pos = os_strstr(cmd, "session=");
+ if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
+ goto invalid_args;
+ p2ps_prov->session_id = val;
+
+ pos = os_strstr(cmd, "adv_mac=");
+ if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
+ goto invalid_args;
+
+ pos = os_strstr(cmd, "session_mac=");
+ if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
+ goto invalid_args;
+
+ /* force conncap with tstCap (no sanity checks) */
+ pos = os_strstr(cmd, "tstCap=");
+ if (pos) {
+ role = strtol(pos + 7, NULL, 16);
+ } else {
+ pos = os_strstr(cmd, "role=");
+ if (pos) {
+ role = strtol(pos + 5, NULL, 16);
+ if (role != P2PS_SETUP_CLIENT &&
+ role != P2PS_SETUP_GROUP_OWNER)
+ role = P2PS_SETUP_NONE;
+ }
+ }
+ p2ps_prov->role = role;
+
+ return p2ps_prov;
+
+invalid_args:
+ os_free(p2ps_prov);
+ return NULL;
+}
+
+
+static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+
+ /* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
+
+ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+
+ p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+ if (!p2ps_prov)
+ return -1;
+
+ if (p2ps_prov->status < 0) {
+ os_free(p2ps_prov);
+ return -1;
+ }
+
+ return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+ p2ps_prov);
+}
+
+
+static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct p2ps_provision *p2ps_prov;
+ char *pos;
+
+ /* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
+ * session=<ses_id> mac=<ses_mac> [info=<infodata>]
+ */
+
+ wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+
+ p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+ if (!p2ps_prov)
+ return -1;
+
+ return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+ p2ps_prov);
}
@@ -4520,7 +4714,7 @@
int pd;
int ht40, vht;
- /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
+ /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
* [persistent|persistent=<network id>]
* [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
* [ht40] [vht] */
@@ -4584,6 +4778,8 @@
*pos++ = '\0';
if (os_strncmp(pos, "display", 7) == 0)
wps_method = WPS_PIN_DISPLAY;
+ else if (os_strncmp(pos, "p2ps", 4) == 0)
+ wps_method = WPS_P2PS;
}
if (!wps_pin_str_valid(pin)) {
os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
@@ -4650,7 +4846,7 @@
else if (os_strstr(pos, " auto") != NULL)
use = WPAS_P2P_PD_AUTO;
- return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
+ return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
}
@@ -4703,6 +4899,40 @@
} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
#endif /* CONFIG_WIFI_DISPLAY */
+ } else if (os_strncmp(pos, "asp ", 4) == 0) {
+ char *svc_str;
+ char *svc_info = NULL;
+ u32 id;
+
+ pos += 4;
+ if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
+ return -1;
+
+ svc_str = pos + 1;
+
+ pos = os_strchr(svc_str, ' ');
+
+ if (pos)
+ *pos++ = '\0';
+
+ /* All remaining data is the svc_info string */
+ if (pos && pos[0] && pos[0] != ' ') {
+ len = os_strlen(pos);
+
+ /* Unescape in place */
+ len = utf8_unescape(pos, len, pos, len);
+ if (len > 0xff)
+ return -1;
+
+ svc_info = pos;
+ }
+
+ ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
+ svc_str, svc_info);
} else {
len = os_strlen(pos);
if (len & 1)
@@ -4865,6 +5095,106 @@
}
+static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
+ u8 replace, char *cmd)
+{
+ char *pos;
+ char *adv_str;
+ u32 auto_accept, adv_id, svc_state, config_methods;
+ char *svc_info = NULL;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ /* Auto-Accept value is mandatory, and must be one of the
+ * single values (0, 1, 2, 4) */
+ auto_accept = atoi(cmd);
+ switch (auto_accept) {
+ case P2PS_SETUP_NONE: /* No auto-accept */
+ case P2PS_SETUP_NEW:
+ case P2PS_SETUP_CLIENT:
+ case P2PS_SETUP_GROUP_OWNER:
+ break;
+ default:
+ return -1;
+ }
+
+ /* Advertisement ID is mandatory */
+ cmd = pos;
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
+ if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
+ return -1;
+
+ /* Only allow replacements if exist, and adds if not */
+ if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
+ if (!replace)
+ return -1;
+ } else {
+ if (replace)
+ return -1;
+ }
+
+ /* svc_state between 0 - 0xff is mandatory */
+ if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+
+ /* config_methods is mandatory */
+ pos++;
+ if (sscanf(pos, "%x", &config_methods) != 1)
+ return -1;
+
+ if (!(config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+
+ pos++;
+ adv_str = pos;
+
+ /* Advertisement string is mandatory */
+ if (!pos[0] || pos[0] == ' ')
+ return -1;
+
+ /* Terminate svc string */
+ pos = os_strchr(pos, ' ');
+ if (pos != NULL)
+ *pos++ = '\0';
+
+ /* Service and Response Information are optional */
+ if (pos && pos[0]) {
+ size_t len;
+
+ /* Note the bare ' included, which cannot exist legally
+ * in unescaped string. */
+ svc_info = os_strstr(pos, "svc_info='");
+
+ if (svc_info) {
+ svc_info += 9;
+ len = os_strlen(svc_info);
+ utf8_unescape(svc_info, len, svc_info, len);
+ }
+ }
+
+ return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
+ (u8) svc_state, (u16) config_methods,
+ svc_info);
+}
+
+
static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
@@ -4878,6 +5208,8 @@
return p2p_ctrl_service_add_bonjour(wpa_s, pos);
if (os_strcmp(cmd, "upnp") == 0)
return p2p_ctrl_service_add_upnp(wpa_s, pos);
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
return -1;
}
@@ -4925,6 +5257,17 @@
}
+static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u32 adv_id;
+
+ if (sscanf(cmd, "%x", &adv_id) != 1)
+ return -1;
+
+ return wpas_p2p_service_del_asp(wpa_s, adv_id);
+}
+
+
static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
{
char *pos;
@@ -4938,6 +5281,25 @@
return p2p_ctrl_service_del_bonjour(wpa_s, pos);
if (os_strcmp(cmd, "upnp") == 0)
return p2p_ctrl_service_del_upnp(wpa_s, pos);
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_del_asp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "asp") == 0)
+ return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
+
wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
return -1;
}
@@ -6201,6 +6563,7 @@
p2p_wpa_s->p2p_disable_ip_addr_req = 0;
os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+ p2p_wpa_s->global->pending_p2ps_group = 0;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS_TESTING
@@ -7634,13 +7997,19 @@
#endif /* CONFIG_MESH */
#ifdef CONFIG_P2P
} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
- if (p2p_ctrl_find(wpa_s, buf + 9))
+ if (p2p_ctrl_find(wpa_s, buf + 8))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_FIND") == 0) {
if (p2p_ctrl_find(wpa_s, ""))
reply_len = -1;
} else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
wpas_p2p_stop_find(wpa_s);
+ } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
+ if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
+ if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
+ reply_len = -1;
} else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
reply_size);
@@ -7686,6 +8055,9 @@
} else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
+ if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
reply_len = -1;
@@ -8279,6 +8651,7 @@
"P2P_SERV_DISC_EXTERNAL ",
"P2P_SERVICE_ADD ",
"P2P_SERVICE_DEL ",
+ "P2P_SERVICE_REP ",
"P2P_REJECT ",
"P2P_INVITE ",
"P2P_PEER ",
@@ -8292,6 +8665,8 @@
"NFC_GET_HANDOVER_SEL ",
"NFC_GET_HANDOVER_REQ ",
"NFC_REPORT_HANDOVER ",
+ "P2P_ASP_PROVISION ",
+ "P2P_ASP_PROVISION_RESP ",
NULL
};
int found = 0;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 9c880a2..24822bf 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -131,7 +131,7 @@
wpa_s = wpa_s->p2p_dev;
wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
- NULL, 0);
+ NULL, 0, 0, NULL);
os_free(req_dev_types);
return reply;
@@ -730,7 +730,7 @@
wpa_s = wpa_s->p2p_dev;
if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
- WPAS_P2P_PD_FOR_GO_NEG) < 0)
+ WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
return wpas_dbus_error_unknown_error(message,
"Failed to send provision discovery request");
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index f1f8864..539832c 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -240,7 +240,7 @@
ie.pmkid + i * PMKID_LEN,
NULL, NULL, 0);
if (pmksa_set == 0) {
- eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
break;
}
}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 5ea046f..5b66211 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -250,8 +250,8 @@
struct wpabuf *extra = NULL;
int all = wpa_s->fetch_all_anqp;
- wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
- MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+ MAC2STR(bss->bssid));
wpa_s->interworking_gas_bss = bss;
info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
@@ -312,14 +312,14 @@
res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
interworking_anqp_resp_cb, wpa_s);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
NULL);
} else
- wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
- "%u", res);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query started with dialog token %u", res);
return ret;
}
@@ -966,8 +966,8 @@
if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
return -1;
- wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
- MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+ " (3GPP)", MAC2STR(bss->bssid));
if (already_connected(wpa_s, cred, bss)) {
wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
@@ -1024,13 +1024,13 @@
break;
}
if (res < 0) {
- wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
- eap_type);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Selected EAP method (%d) not supported", eap_type);
goto fail;
}
if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
- wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+ wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
goto fail;
}
@@ -1503,8 +1503,8 @@
{
struct wpa_ssid *ssid;
- wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
- "roaming consortium match", MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+ " based on roaming consortium match", MAC2STR(bss->bssid));
if (already_connected(wpa_s, cred, bss)) {
wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
@@ -1532,8 +1532,8 @@
goto fail;
if (cred->eap_method == NULL) {
- wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
- "credential using roaming consortium");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No EAP method set for credential using roaming consortium");
goto fail;
}
@@ -1572,8 +1572,9 @@
return -1;
if (disallowed_bssid(wpa_s, bss->bssid) ||
disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
- wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS "
- MACSTR, MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Reject connection to disallowed BSS "
+ MACSTR, MAC2STR(bss->bssid));
return -1;
}
@@ -1586,27 +1587,26 @@
* We currently support only HS 2.0 networks and those are
* required to use WPA2-Enterprise.
*/
- wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
- "RSN");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Network does not use RSN");
return -1;
}
cred_rc = interworking_credentials_available_roaming_consortium(
wpa_s, bss, 0, excl);
if (cred_rc) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
- "consortium matching credential priority %d "
- "sp_priority %d",
- cred_rc->priority, cred_rc->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
+ cred_rc->priority, cred_rc->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
if (cred) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
- "matching credential priority %d sp_priority %d",
- cred->priority, cred->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
+ cred->priority, cred->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
@@ -1614,22 +1614,22 @@
cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
excl);
if (cred_3gpp) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
- "credential priority %d sp_priority %d",
- cred_3gpp->priority, cred_3gpp->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
if (!cred_rc && !cred && !cred_3gpp) {
- wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No full credential matches - consider options without BW(etc.) limits");
cred_rc = interworking_credentials_available_roaming_consortium(
wpa_s, bss, 1, excl);
if (cred_rc) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
- "consortium matching credential priority %d "
- "sp_priority %d (ignore BW)",
- cred_rc->priority, cred_rc->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
+ cred_rc->priority, cred_rc->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
@@ -1637,10 +1637,9 @@
cred = interworking_credentials_available_realm(wpa_s, bss, 1,
excl);
if (cred) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
- "list matching credential priority %d "
- "sp_priority %d (ignore BW)",
- cred->priority, cred->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
+ cred->priority, cred->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
@@ -1648,10 +1647,9 @@
cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
1, excl);
if (cred_3gpp) {
- wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
- "matching credential priority %d "
- "sp_priority %d (ignore BW)",
- cred_3gpp->priority, cred_3gpp->sp_priority);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
+ cred_3gpp->priority, cred_3gpp->sp_priority);
if (allow_excluded && excl && !(*excl))
excl = NULL;
}
@@ -1669,16 +1667,18 @@
}
if (cred == NULL) {
- wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
- "found for " MACSTR, MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, 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));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Could not parse NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
return -1;
}
@@ -1691,15 +1691,15 @@
}
if (!eap) {
- wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
- "and EAP method found for " MACSTR,
- MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: No matching credentials and EAP method found for "
+ MACSTR, MAC2STR(bss->bssid));
nai_realm_free(realm, count);
return -1;
}
- wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
- MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
+ MAC2STR(bss->bssid));
if (already_connected(wpa_s, cred, bss)) {
wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
@@ -1927,10 +1927,12 @@
#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
compare:
#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
- wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
- MACSTR, MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Parsing 3GPP info from " MACSTR,
+ MAC2STR(bss->bssid));
ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
- wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+ wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
+ ret ? "" : "not ");
if (ret) {
if (cred_no_required_oi_match(cred, bss))
continue;
@@ -1982,12 +1984,13 @@
if (wpa_s->conf->cred == NULL)
return NULL;
- wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
- MACSTR, MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
realm = nai_realm_parse(bss->anqp->nai_realm, &count);
if (realm == NULL) {
- wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
- "Realm list from " MACSTR, MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Could not parse NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
return NULL;
}
@@ -2026,6 +2029,9 @@
}
}
break;
+ } else {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: realm-find-eap returned false");
}
}
}
@@ -2166,8 +2172,9 @@
realm = os_strchr(nai, '@');
if (realm)
realm++;
- wpa_printf(MSG_DEBUG, "Interworking: Search for match "
- "with SIM/USIM domain %s", realm);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Search for match with SIM/USIM domain %s",
+ realm);
if (realm &&
domain_name_list_contains(domain_names, realm, 1))
return 1;
@@ -2180,8 +2187,9 @@
return ret;
for (i = 0; i < cred->num_domain; i++) {
- wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
- "home SP FQDN %s", cred->domain[i]);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Search for match with home SP FQDN %s",
+ cred->domain[i]);
if (domain_name_list_contains(domain_names, cred->domain[i], 1))
return 1;
}
@@ -2357,14 +2365,16 @@
&excluded);
if (!cred)
continue;
+
if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
/*
* We currently support only HS 2.0 networks and those
* are required to use WPA2-Enterprise.
*/
- wpa_printf(MSG_DEBUG, "Interworking: Credential match "
- "with " MACSTR " but network does not use "
- "RSN", MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Credential match with " MACSTR
+ " but network does not use RSN",
+ MAC2STR(bss->bssid));
continue;
}
if (!excluded)
@@ -2455,8 +2465,8 @@
* have matching APs.
*/
if (interworking_find_network_match(wpa_s)) {
- wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
- "match for enabled network configurations");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Possible BSS match for enabled network configurations");
if (wpa_s->auto_select) {
interworking_reconnect(wpa_s);
return;
@@ -2464,8 +2474,8 @@
}
if (wpa_s->auto_network_select) {
- wpa_printf(MSG_DEBUG, "Interworking: Continue "
- "scanning after ANQP fetch");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Continue scanning after ANQP fetch");
wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
0);
return;
@@ -2516,9 +2526,10 @@
os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
continue;
- wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
- "already fetched BSSID " MACSTR " and " MACSTR,
- MAC2STR(other->bssid), MAC2STR(bss->bssid));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Share ANQP data with already fetched BSSID "
+ MACSTR " and " MACSTR,
+ MAC2STR(other->bssid), MAC2STR(bss->bssid));
other->anqp->users++;
return other->anqp;
}
@@ -2661,8 +2672,9 @@
if (freq <= 0)
return -1;
- wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
- MAC2STR(dst), (unsigned int) num_ids);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query Request to " MACSTR " for %u id(s)",
+ MAC2STR(dst), (unsigned int) num_ids);
#ifdef CONFIG_HS20
if (subtypes != 0) {
@@ -2680,12 +2692,13 @@
res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
- } else
- wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
- "%u", res);
+ } else {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Query started with dialog token %u", res);
+ }
return ret;
}
@@ -2801,22 +2814,23 @@
pos, slen);
break;
default:
- wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
- "vendor type %u", type);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "HS20: Unsupported ANQP vendor type %u",
+ type);
break;
}
break;
#endif /* CONFIG_HS20 */
default:
- wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
- "vendor-specific ANQP OUI %06x",
- WPA_GET_BE24(pos));
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Unsupported vendor-specific ANQP OUI %06x",
+ WPA_GET_BE24(pos));
return;
}
break;
default:
- wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
- "%u", info_id);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Unsupported ANQP Info ID %u", info_id);
break;
}
}
@@ -2848,8 +2862,8 @@
pos = wpabuf_head(adv_proto);
if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
- wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
- "Protocol in response");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Unexpected Advertisement Protocol in response");
if (wpa_s->fetch_osu_icon_in_progress)
hs20_icon_fetch_failed(wpa_s);
anqp_result = "INVALID_FRAME";
@@ -2878,7 +2892,7 @@
unsigned int left = end - pos;
if (left < 4) {
- wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
+ wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
anqp_result = "INVALID_FRAME";
goto out_parse_done;
}
@@ -2888,8 +2902,9 @@
pos += 2;
left -= 4;
if (left < slen) {
- wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
- "for Info ID %u", info_id);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "ANQP: Invalid element length for Info ID %u",
+ info_id);
anqp_result = "INVALID_FRAME";
goto out_parse_done;
}
@@ -2909,8 +2924,8 @@
static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
- wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
- "ANQP fetch");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Scan results available - start ANQP fetch");
interworking_start_fetch_anqp(wpa_s);
}
@@ -2924,8 +2939,8 @@
wpa_s->auto_select = !!auto_select;
wpa_s->fetch_all_anqp = 0;
wpa_s->fetch_osu_info = 0;
- wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
- "selection");
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "Interworking: Start scan for network selection");
wpa_s->scan_res_handler = interworking_scan_res_handler;
wpa_s->normal_scans = 0;
wpa_s->scan_req = MANUAL_SCAN_REQ;
@@ -2986,8 +3001,8 @@
if (freq <= 0)
return -1;
- wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
- MAC2STR(dst), freq);
+ wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+ MAC2STR(dst), freq);
wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
@@ -3013,12 +3028,12 @@
res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+ wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
wpabuf_free(buf);
ret = -1;
} else
- wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
- "%u", res);
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "GAS: Query started with dialog token %u", res);
return ret;
}
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 4a259ff..1d6f2be 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -193,6 +193,11 @@
sta->my_lid = llid;
sta->peer_lid = 0;
+
+ /*
+ * We do not use wpa_mesh_set_plink_state() here because there is no
+ * entry in kernel yet.
+ */
sta->plink_state = PLINK_LISTEN;
}
@@ -348,9 +353,9 @@
/* configure peering state in ours and driver's station entry */
-static void
-wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta,
- enum mesh_plink_state state)
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+ struct sta_info *sta,
+ enum mesh_plink_state state)
{
struct hostapd_sta_add_params params;
int ret;
@@ -379,14 +384,7 @@
eloop_cancel_timeout(plink_timer, wpa_s, sta);
- if (sta->mpm_close_reason == WLAN_REASON_MESH_CLOSE_RCVD) {
- ap_free_sta(hapd, sta);
- return;
- }
-
- wpa_mesh_set_plink_state(wpa_s, sta, PLINK_LISTEN);
- sta->my_lid = sta->peer_lid = sta->mpm_close_reason = 0;
- sta->mpm_retries = 0;
+ ap_free_sta(hapd, sta);
}
@@ -417,7 +415,7 @@
/* confirm timer */
if (!reason)
reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
- sta->plink_state = PLINK_HOLDING;
+ wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
@@ -521,27 +519,34 @@
mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
}
-
-void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
- struct ieee802_11_elems *elems)
+/*
+ * Initialize a sta_info structure for a peer and upload it into the driver
+ * in preparation for beginning authentication or peering. This is done when a
+ * Beacon (secure or open mesh) or a peering open frame (for open mesh) is
+ * received from the peer for the first time.
+ */
+static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
+ const u8 *addr,
+ struct ieee802_11_elems *elems)
{
struct hostapd_sta_add_params params;
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
struct hostapd_data *data = wpa_s->ifmsh->bss[0];
struct sta_info *sta;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- int ret = 0;
+ int ret;
sta = ap_get_sta(data, addr);
if (!sta) {
sta = ap_sta_add(data, addr);
if (!sta)
- return;
+ return NULL;
}
/* initialize sta */
- if (copy_supp_rates(wpa_s, sta, elems))
- return;
+ if (copy_supp_rates(wpa_s, sta, elems)) {
+ ap_free_sta(data, sta);
+ return NULL;
+ }
mesh_mpm_init_link(wpa_s, sta);
@@ -575,9 +580,26 @@
wpa_msg(wpa_s, MSG_ERROR,
"Driver failed to insert " MACSTR ": %d",
MAC2STR(addr), ret);
- return;
+ ap_free_sta(data, sta);
+ return NULL;
}
+ return sta;
+}
+
+
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+ struct ieee802_11_elems *elems)
+{
+ struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+ struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+ struct sta_info *sta;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ sta = mesh_mpm_add_peer(wpa_s, addr, elems);
+ if (!sta)
+ return;
+
if (ssid && ssid->no_auto_peer) {
wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
MACSTR " because of no_auto_peer", MAC2STR(addr));
@@ -930,6 +952,15 @@
wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
sta = ap_get_sta(hapd, mgmt->sa);
+
+ /*
+ * If this is an open frame from an unknown STA, and this is an
+ * open mesh, then go ahead and add the peer before proceeding.
+ */
+ if (!sta && action_field == PLINK_OPEN &&
+ !(mconf->security & MESH_CONF_SEC_AMPE))
+ sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
+
if (!sta) {
wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer");
return;
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
index 2f7f6a7..7ebaef0 100644
--- a/wpa_supplicant/mesh_mpm.h
+++ b/wpa_supplicant/mesh_mpm.h
@@ -15,6 +15,9 @@
void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh);
void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
void mesh_mpm_free_sta(struct sta_info *sta);
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+ struct sta_info *sta,
+ enum mesh_plink_state state);
#ifdef CONFIG_MESH
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index aee325a..da4cb03 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -41,7 +41,7 @@
mesh_rsn_auth_sae_sta(wpa_s, sta);
} else {
/* block the STA if exceeded the number of attempts */
- sta->plink_state = PLINK_BLOCKED;
+ wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
sta->sae->state = SAE_NOTHING;
}
sta->sae_auth_retry++;
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 7a86347..63af83a 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -354,15 +354,18 @@
*/
void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
{
- wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
- "notification");
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
+ wpa_s->pending_action_tx,
+ !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
+ wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
+ wpa_s->roc_waiting_drv_freq);
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
wpa_s->action_tx_wait_time)
wpa_drv_send_action_cancel_wait(wpa_s);
-
- if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 65c1b48..9e1d665 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -123,6 +123,8 @@
static void wpas_stop_listen(void *ctx);
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type);
/*
@@ -475,6 +477,287 @@
}
+/* Determine total number of clients in active groups where we are the GO */
+static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
+{
+ unsigned int count = 0;
+ struct wpa_ssid *s;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+ wpa_s, s, s->disabled, s->p2p_group,
+ s->mode);
+ if (!s->disabled && s->p2p_group &&
+ s->mode == WPAS_MODE_P2P_GO) {
+ count += p2p_get_group_num_members(
+ wpa_s->p2p_group);
+ }
+ }
+ }
+
+ return count;
+}
+
+
+/* Find an interface for a P2P group where we are the GO */
+static struct wpa_supplicant *
+wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *save = NULL;
+ struct wpa_ssid *s;
+
+ if (!wpa_s)
+ return NULL;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled || !s->p2p_group ||
+ s->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ /* Prefer a group with connected clients */
+ if (p2p_get_group_num_members(wpa_s->p2p_group))
+ return wpa_s;
+ save = wpa_s;
+ }
+ }
+
+ /* No group with connected clients, so pick the one without (if any) */
+ return save;
+}
+
+
+/* Find an active P2P group where we are the GO */
+static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
+ u8 *bssid)
+{
+ struct wpa_ssid *s, *empty = NULL;
+
+ if (!wpa_s)
+ return 0;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled || !s->p2p_group ||
+ s->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
+ if (p2p_get_group_num_members(wpa_s->p2p_group))
+ return s;
+ empty = s;
+ }
+ }
+
+ return empty;
+}
+
+
+/* Find a persistent group where we are the GO */
+static struct wpa_ssid *
+wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *s;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
+ return s;
+ }
+
+ return NULL;
+}
+
+
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+{
+ struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+ struct wpa_ssid *s;
+ u8 conncap = P2PS_SETUP_NONE;
+ unsigned int owned_members = 0;
+ unsigned int owner = 0;
+ unsigned int client = 0;
+ struct wpa_supplicant *go_wpa_s;
+ struct wpa_ssid *persistent_go;
+ int p2p_no_group_iface;
+
+ wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+
+ /*
+ * For non-concurrent capable devices:
+ * If persistent_go, then no new.
+ * If GO, then no client.
+ * If client, then no GO.
+ */
+ go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+ persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+ p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+
+ wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
+ go_wpa_s, persistent_go);
+
+ for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
+ tmp_wpa_s = tmp_wpa_s->next) {
+ for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+ tmp_wpa_s, s, s->disabled,
+ s->p2p_group, s->mode);
+ if (!s->disabled && s->p2p_group) {
+ if (s->mode == WPAS_MODE_P2P_GO) {
+ owned_members +=
+ p2p_get_group_num_members(
+ tmp_wpa_s->p2p_group);
+ owner++;
+ } else
+ client++;
+ }
+ }
+ }
+
+ /* If not concurrent, restrict our choices */
+ if (p2p_no_group_iface) {
+ wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
+
+ if (client)
+ return P2PS_SETUP_NONE;
+
+ if (go_wpa_s) {
+ if (role == P2PS_SETUP_CLIENT ||
+ incoming == P2PS_SETUP_GROUP_OWNER ||
+ p2p_client_limit_reached(go_wpa_s->p2p_group))
+ return P2PS_SETUP_NONE;
+
+ return P2PS_SETUP_GROUP_OWNER;
+ }
+
+ if (persistent_go) {
+ if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
+ if (!incoming)
+ return P2PS_SETUP_GROUP_OWNER |
+ P2PS_SETUP_CLIENT;
+ if (incoming == P2PS_SETUP_NEW) {
+ u8 r;
+
+ if (os_get_random(&r, sizeof(r)) < 0 ||
+ (r & 1))
+ return P2PS_SETUP_CLIENT;
+ return P2PS_SETUP_GROUP_OWNER;
+ }
+ }
+ }
+ }
+
+ /* If a required role has been specified, handle it here */
+ if (role && role != P2PS_SETUP_NEW) {
+ switch (incoming) {
+ case P2PS_SETUP_NONE:
+ case P2PS_SETUP_NEW:
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+ conncap = role;
+ goto grp_owner;
+
+ case P2PS_SETUP_GROUP_OWNER:
+ /*
+ * Must be a complimentary role - cannot be a client to
+ * more than one peer.
+ */
+ if (incoming == role || client)
+ return P2PS_SETUP_NONE;
+
+ return P2PS_SETUP_CLIENT;
+
+ case P2PS_SETUP_CLIENT:
+ /* Must be a complimentary role */
+ if (incoming != role) {
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ goto grp_owner;
+ }
+
+ default:
+ return P2PS_SETUP_NONE;
+ }
+ }
+
+ /*
+ * For now, we only will support ownership of one group, and being a
+ * client of one group. Therefore, if we have either an existing GO
+ * group, or an existing client group, we will not do a new GO
+ * negotiation, but rather try to re-use the existing groups.
+ */
+ switch (incoming) {
+ case P2PS_SETUP_NONE:
+ case P2PS_SETUP_NEW:
+ if (client)
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ else if (!owned_members)
+ conncap = P2PS_SETUP_NEW;
+ else if (incoming == P2PS_SETUP_NONE)
+ conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
+ else
+ conncap = P2PS_SETUP_CLIENT;
+ break;
+
+ case P2PS_SETUP_CLIENT:
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ break;
+
+ case P2PS_SETUP_GROUP_OWNER:
+ if (!client)
+ conncap = P2PS_SETUP_CLIENT;
+ break;
+
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+ if (client)
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ else {
+ u8 r;
+
+ if (os_get_random(&r, sizeof(r)) < 0 ||
+ (r & 1))
+ conncap = P2PS_SETUP_CLIENT;
+ else
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ }
+ break;
+
+ default:
+ return P2PS_SETUP_NONE;
+ }
+
+grp_owner:
+ if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
+ (!incoming && (conncap & P2PS_SETUP_NEW))) {
+ if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
+ conncap &= ~P2PS_SETUP_GROUP_OWNER;
+ wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
+ owner, owned_members, conncap);
+
+ s = wpas_p2p_get_persistent_go(wpa_s);
+
+ if (!s && !owner && p2p_no_group_iface) {
+ p2p_set_intended_addr(wpa_s->global->p2p,
+ wpa_s->own_addr);
+ } else if (!s && !owner) {
+ if (wpas_p2p_add_group_interface(wpa_s,
+ WPA_IF_P2P_GO) < 0) {
+ wpa_printf(MSG_ERROR,
+ "P2P: Failed to allocate a new interface for the group");
+ return P2PS_SETUP_NONE;
+ }
+ wpa_s->global->pending_group_iface_for_p2ps = 1;
+ p2p_set_intended_addr(wpa_s->global->p2p,
+ wpa_s->pending_interface_addr);
+ }
+ }
+
+ return conncap;
+}
+
+
static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
enum p2p_group_removal_reason removal_reason)
{
@@ -1268,6 +1551,8 @@
#endif /* CONFIG_WPS_NFC */
} else {
u16 dev_pw_id = DEV_PW_DEFAULT;
+ if (wpa_s->p2p_wps_method == WPS_P2PS)
+ dev_pw_id = DEV_PW_P2PS_DEFAULT;
if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
@@ -1348,6 +1633,16 @@
}
+static void p2p_config_write(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->parent->conf->update_config &&
+ wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
static void p2p_go_configured(void *ctx, void *data)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -1371,6 +1666,16 @@
params->persistent_group, "");
wpa_s->group_formation_reported = 1;
+ if (wpa_s->parent->p2ps_join_addr_valid) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2PS: Setting default PIN for " MACSTR,
+ MAC2STR(wpa_s->parent->p2ps_join_addr));
+ wpa_supplicant_ap_wps_pin(wpa_s,
+ wpa_s->parent->p2ps_join_addr,
+ "12345670", NULL, 0, 0);
+ wpa_s->parent->p2ps_join_addr_valid = 0;
+ }
+
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
if (params->persistent_group) {
network_id = wpas_p2p_store_persistent_group(
@@ -1639,6 +1944,7 @@
wpa_s->pending_interface_name);
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
wpa_s->pending_interface_name[0] = '\0';
+ wpa_s->global->pending_group_iface_for_p2ps = 0;
}
@@ -1684,6 +1990,7 @@
group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
P2P_GROUP_INTERFACE_CLIENT;
wpa_s->global->p2p_group_formation = group_wpa_s;
+ wpa_s->global->pending_group_iface_for_p2ps = 0;
wpas_p2p_clone_config(group_wpa_s, wpa_s);
@@ -1847,6 +2154,52 @@
WFD_SUBELEM_DEVICE_INFO);
#endif /* CONFIG_WIFI_DISPLAY */
+ if (info->p2ps_instance) {
+ char str[256];
+ const u8 *buf = wpabuf_head(info->p2ps_instance);
+ size_t len = wpabuf_len(info->p2ps_instance);
+
+ while (len) {
+ u32 id;
+ u16 methods;
+ u8 str_len;
+
+ if (len < 4 + 2 + 1)
+ break;
+ id = WPA_GET_LE32(buf);
+ buf += sizeof(u32);
+ methods = WPA_GET_BE16(buf);
+ buf += sizeof(u16);
+ str_len = *buf++;
+ if (str_len > len - 4 - 2 - 1)
+ break;
+ os_memcpy(str, buf, str_len);
+ str[str_len] = '\0';
+ buf += str_len;
+ len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
+
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_DEVICE_FOUND MACSTR
+ " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s'"
+ " config_methods=0x%x"
+ " dev_capab=0x%x"
+ " group_capab=0x%x"
+ " adv_id=%x asp_svc=%s%s",
+ MAC2STR(addr),
+ MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(
+ info->pri_dev_type,
+ devtype, sizeof(devtype)),
+ info->device_name, methods,
+ info->dev_capab, info->group_capab,
+ id, str,
+ info->vendor_elems ?
+ " vendor_elems=1" : "");
+ }
+ goto done;
+ }
+
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
" p2p_dev_addr=" MACSTR
" pri_dev_type=%s name='%s' config_methods=0x%x "
@@ -1861,6 +2214,7 @@
info->vendor_elems ? " vendor_elems=1" : "",
new_device);
+done:
os_free(wfd_dev_info_hex);
#endif /* CONFIG_NO_STDOUT_DEBUG */
@@ -2187,8 +2541,8 @@
}
-static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id)
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id, u8 status)
{
u8 *len_pos;
@@ -2200,12 +2554,35 @@
wpabuf_put_u8(resp, srv_proto);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
- wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+ wpabuf_put_u8(resp, status);
/* Response Data: empty */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+ P2P_SD_PROTO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
+}
+
+
+static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+ P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+}
+
+
static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id)
{
@@ -2513,6 +2890,148 @@
#endif /* CONFIG_WIFI_DISPLAY */
+static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
+ const u8 *needle, size_t needle_len)
+{
+ const u8 *haystack = (const u8 *) adv_data->svc_info;
+ size_t haystack_len, i;
+
+ /* Allow search term to be empty */
+ if (!needle || !needle_len)
+ return 1;
+
+ if (!haystack)
+ return 0;
+
+ haystack_len = os_strlen(adv_data->svc_info);
+ for (i = 0; i < haystack_len; i++) {
+ if (haystack_len - i < needle_len)
+ break;
+ if (os_memcmp(haystack + i, needle, needle_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2ps_advertisement *adv_data;
+ const u8 *svc = &query[1];
+ const u8 *info = NULL;
+ size_t svc_len = query[0];
+ size_t info_len = 0;
+ int prefix = 0;
+ u8 *count_pos = NULL;
+ u8 *len_pos = NULL;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
+
+ if (!wpa_s->global->p2p) {
+ wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
+ return;
+ }
+
+ /* Info block is optional */
+ if (svc_len + 1 < query_len) {
+ info = &svc[svc_len];
+ info_len = *info++;
+ }
+
+ /* Range check length of svc string and info block */
+ if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
+ wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
+ wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
+ return;
+ }
+
+ /* Detect and correct for prefix search */
+ if (svc_len && svc[svc_len - 1] == '*') {
+ prefix = 1;
+ svc_len--;
+ }
+
+ for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
+ adv_data; adv_data = adv_data->next) {
+ /* If not a prefix match, reject length mismatches */
+ if (!prefix && svc_len != os_strlen(adv_data->svc_name))
+ continue;
+
+ /* Search each service for request */
+ if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
+ find_p2ps_substr(adv_data, info, info_len)) {
+ size_t len = os_strlen(adv_data->svc_name);
+ size_t svc_info_len = 0;
+
+ if (adv_data->svc_info)
+ svc_info_len = os_strlen(adv_data->svc_info);
+
+ if (len > 0xff || svc_info_len > 0xffff)
+ return;
+
+ /* Length & Count to be filled as we go */
+ if (!len_pos && !count_pos) {
+ if (wpabuf_tailroom(resp) <
+ len + svc_info_len + 16)
+ return;
+
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_P2PS);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ count_pos = wpabuf_put(resp, 1);
+ *count_pos = 0;
+ } else if (wpabuf_tailroom(resp) <
+ len + svc_info_len + 10)
+ return;
+
+ if (svc_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Add Svc: %s info: %s",
+ adv_data->svc_name,
+ adv_data->svc_info);
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
+ adv_data->svc_name);
+ }
+
+ /* Advertisement ID */
+ wpabuf_put_le32(resp, adv_data->id);
+
+ /* Config Methods */
+ wpabuf_put_be16(resp, adv_data->config_methods);
+
+ /* Service Name */
+ wpabuf_put_u8(resp, (u8) len);
+ wpabuf_put_data(resp, adv_data->svc_name, len);
+
+ /* Service State */
+ wpabuf_put_u8(resp, adv_data->state);
+
+ /* Service Information */
+ wpabuf_put_le16(resp, (u16) svc_info_len);
+ wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
+
+ /* Update length and count */
+ (*count_pos)++;
+ WPA_PUT_LE16(len_pos,
+ (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+ }
+ }
+
+ /* Return error if no matching svc found */
+ if (count_pos == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
+ wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
+ }
+}
+
+
static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
u16 update_indic, const u8 *tlvs, size_t tlvs_len)
{
@@ -2610,6 +3129,10 @@
pos, tlv_end - pos);
break;
#endif /* CONFIG_WIFI_DISPLAY */
+ case P2P_SERV_P2PS:
+ wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
default:
wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
"protocol %u", srv_proto);
@@ -2631,6 +3154,80 @@
}
+static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u8 srv_trans_id,
+ const u8 *pos, const u8 *tlv_end)
+{
+ u8 left = *pos++;
+ u32 adv_id;
+ u8 svc_status;
+ u16 config_methods;
+ char svc_str[256];
+
+ while (left-- && pos < tlv_end) {
+ char *buf = NULL;
+ size_t buf_len;
+ u8 svc_len;
+
+ /* Sanity check fixed length+svc_str */
+ if (pos + 6 >= tlv_end)
+ break;
+ svc_len = pos[6];
+ if (pos + svc_len + 10 > tlv_end)
+ break;
+
+ /* Advertisement ID */
+ adv_id = WPA_GET_LE32(pos);
+ pos += sizeof(u32);
+
+ /* Config Methods */
+ config_methods = WPA_GET_BE16(pos);
+ pos += sizeof(u16);
+
+ /* Service Name */
+ pos++; /* svc_len */
+ os_memcpy(svc_str, pos, svc_len);
+ svc_str[svc_len] = '\0';
+ pos += svc_len;
+
+ /* Service Status */
+ svc_status = *pos++;
+
+ /* Service Information Length */
+ buf_len = WPA_GET_LE16(pos);
+ pos += sizeof(u16);
+
+ /* Sanity check buffer length */
+ if (buf_len > (unsigned int) (tlv_end - pos))
+ break;
+
+ if (buf_len) {
+ buf = os_zalloc(2 * buf_len + 1);
+ if (buf) {
+ utf8_escape((const char *) pos, buf_len, buf,
+ 2 * buf_len + 1);
+ }
+ }
+
+ pos += buf_len;
+
+ if (buf) {
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+ MACSTR " %x %x %x %x %s '%s'",
+ MAC2STR(sa), srv_trans_id, adv_id,
+ svc_status, config_methods, svc_str,
+ buf);
+ os_free(buf);
+ } else {
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+ MACSTR " %x %x %x %x %s",
+ MAC2STR(sa), srv_trans_id, adv_id,
+ svc_status, config_methods, svc_str);
+ }
+ }
+}
+
+
static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
const u8 *tlvs, size_t tlvs_len)
{
@@ -2689,6 +3286,11 @@
wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
pos, tlv_end - pos);
+ if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
+ wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
+ pos, tlv_end);
+ }
+
pos = tlv_end;
}
@@ -2725,6 +3327,39 @@
}
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+ const char *svc_str, const char *info_substr)
+{
+ struct wpabuf *tlvs;
+ size_t plen, svc_len, substr_len = 0;
+ u64 ret;
+
+ svc_len = os_strlen(svc_str);
+ if (info_substr)
+ substr_len = os_strlen(info_substr);
+
+ if (svc_len > 0xff || substr_len > 0xff)
+ return 0;
+
+ plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
+ tlvs = wpabuf_alloc(2 + plen);
+ if (tlvs == NULL)
+ return 0;
+
+ wpabuf_put_le16(tlvs, plen);
+ wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
+ wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+ wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
+ wpabuf_put_data(tlvs, svc_str, svc_len);
+ wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
+ wpabuf_put_data(tlvs, info_substr, substr_len);
+ ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+
+ return ret;
+}
+
+
#ifdef CONFIG_WIFI_DISPLAY
static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
@@ -2866,6 +3501,35 @@
}
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+ if (adv_id == 0)
+ return 1;
+
+ if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
+ return 1;
+
+ return 0;
+}
+
+
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+ return p2p_service_del_asp(wpa_s->global->p2p, adv_id);
+}
+
+
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
+ int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info)
+{
+ return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
+ adv_str, svc_state, config_methods,
+ svc_info);
+}
+
+
int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *query, struct wpabuf *resp)
{
@@ -3055,7 +3719,9 @@
static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
- enum p2p_prov_disc_status status)
+ enum p2p_prov_disc_status status,
+ u32 adv_id, const u8 *adv_mac,
+ const char *deferred_session_resp)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -3075,9 +3741,21 @@
return;
}
- wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
- " p2p_dev_addr=" MACSTR " status=%d",
- MAC2STR(peer), status);
+ if (adv_id && adv_mac && deferred_session_resp) {
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
+ " deferred_session_resp='%s'",
+ MAC2STR(peer), status, adv_id,
+ deferred_session_resp);
+ } else if (adv_id && adv_mac) {
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
+ MAC2STR(peer), status, adv_id);
+ } else {
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=%d",
+ MAC2STR(peer), status);
+ }
wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
status, 0, 0);
@@ -4012,6 +4690,337 @@
}
+static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
+ size_t ssid_len, u8 *go_dev_addr,
+ u8 *ret_ssid, size_t *ret_ssid_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+
+ s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
+ if (s) {
+ os_memcpy(ret_ssid, s->ssid, s->ssid_len);
+ *ret_ssid_len = s->ssid_len;
+ os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_get_go_info(void *ctx, u8 *intended_addr,
+ u8 *ssid, size_t *ssid_len, int *group_iface)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+ u8 bssid[ETH_ALEN];
+
+ s = wpas_p2p_group_go_ssid(wpa_s, bssid);
+ if (!s) {
+ s = wpas_p2p_get_persistent_go(wpa_s);
+ if (s)
+ os_memcpy(bssid, s->bssid, ETH_ALEN);
+ }
+
+ *group_iface = wpas_p2p_create_iface(wpa_s);
+ if (!s)
+ return 0;
+
+ os_memcpy(intended_addr, bssid, ETH_ALEN);
+ os_memcpy(ssid, s->ssid, s->ssid_len);
+ *ssid_len = s->ssid_len;
+
+ return 1;
+}
+
+
+static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+ int save_config = 0;
+ size_t i;
+
+ /* Start with our first choice of Persistent Groups */
+ while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
+ if (go && ssid && ssid_len &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+
+ /* Remove stale persistent group */
+ if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
+ wpa_config_remove_network(wpa_s->conf, s->id);
+ save_config = 1;
+ continue;
+ }
+
+ for (i = 0; i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+ peer, ETH_ALEN) != 0)
+ continue;
+
+ os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+ s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+ (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+ break;
+ }
+ s->num_p2p_clients--;
+ save_config = 1;
+ }
+
+ if (save_config)
+ p2p_config_write(wpa_s);
+
+ /* Return TRUE if valid SSID remains */
+ return s != NULL;
+}
+
+
+static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
+ const u8 *adv_mac, const u8 *ses_mac,
+ const u8 *grp_mac, u32 adv_id, u32 ses_id,
+ u8 conncap, int passwd_id,
+ const u8 *persist_ssid,
+ size_t persist_ssid_size, int response_done,
+ int prov_start, const char *session_info)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ u8 mac[ETH_ALEN];
+ struct wpa_ssid *persistent_go, *stale, *s;
+ int save_config = 0;
+ struct wpa_supplicant *go_wpa_s;
+
+ if (!dev)
+ return;
+
+ os_memset(mac, 0, ETH_ALEN);
+ if (!adv_mac)
+ adv_mac = mac;
+ if (!ses_mac)
+ ses_mac = mac;
+ if (!grp_mac)
+ grp_mac = mac;
+
+ if (prov_start) {
+ if (session_info == NULL) {
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_START MACSTR
+ " adv_id=%x conncap=%x"
+ " adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " dev_passwd_id=%d",
+ MAC2STR(dev), adv_id, conncap,
+ MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac),
+ passwd_id);
+ } else {
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_START MACSTR
+ " adv_id=%x conncap=%x"
+ " adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " dev_passwd_id=%d info='%s'",
+ MAC2STR(dev), adv_id, conncap,
+ MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac),
+ passwd_id, session_info);
+ }
+ return;
+ }
+
+ go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+ persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+ if (status && status != P2P_SC_SUCCESS_DEFERRED) {
+ if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+ wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+ if (persistent_go && !persistent_go->num_p2p_clients) {
+ /* remove empty persistent GO */
+ wpa_config_remove_network(wpa_s->conf,
+ persistent_go->id);
+ }
+
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+ " status=%d"
+ " adv_id=%x adv_mac=" MACSTR
+ " session=%x mac=" MACSTR,
+ MAC2STR(dev), status,
+ adv_id, MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac));
+ return;
+ }
+
+ /* Clean up stale persistent groups with this device */
+ s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+ persist_ssid_size);
+ for (;;) {
+ stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
+ if (!stale)
+ break;
+
+ if (s && s->ssid_len == stale->ssid_len &&
+ os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
+ os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
+ break;
+
+ /* Remove stale persistent group */
+ if (stale->mode != WPAS_MODE_P2P_GO ||
+ stale->num_p2p_clients <= 1) {
+ wpa_config_remove_network(wpa_s->conf, stale->id);
+ } else {
+ size_t i;
+
+ for (i = 0; i < stale->num_p2p_clients; i++) {
+ if (os_memcmp(stale->p2p_client_list +
+ i * ETH_ALEN,
+ dev, ETH_ALEN) == 0) {
+ os_memmove(stale->p2p_client_list +
+ i * ETH_ALEN,
+ stale->p2p_client_list +
+ (i + 1) * ETH_ALEN,
+ (stale->num_p2p_clients -
+ i - 1) * ETH_ALEN);
+ break;
+ }
+ }
+ stale->num_p2p_clients--;
+ }
+ save_config = 1;
+ }
+
+ if (save_config)
+ p2p_config_write(wpa_s);
+
+ if (s) {
+ if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+ wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+ if (persistent_go && s != persistent_go &&
+ !persistent_go->num_p2p_clients) {
+ /* remove empty persistent GO */
+ wpa_config_remove_network(wpa_s->conf,
+ persistent_go->id);
+ /* Save config */
+ }
+
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+ " status=%d"
+ " adv_id=%x adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " persist=%d",
+ MAC2STR(dev), status,
+ adv_id, MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac), s->id);
+ return;
+ }
+
+ if (conncap == P2PS_SETUP_GROUP_OWNER) {
+ const char *go_ifname = NULL;
+ if (!go_wpa_s) {
+ wpa_s->global->pending_p2ps_group = 1;
+
+ if (wpa_s->conf->p2p_no_group_iface)
+ go_ifname = wpa_s->ifname;
+ else if (wpa_s->pending_interface_name[0])
+ go_ifname = wpa_s->pending_interface_name;
+
+ if (!go_ifname) {
+ wpas_p2ps_prov_complete(
+ wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
+ dev, adv_mac, ses_mac,
+ NULL, adv_id, ses_id, 0, 0,
+ NULL, 0, 0, 0, NULL);
+ return;
+ }
+
+ /* If PD Resp complete, start up the GO */
+ if (response_done && persistent_go) {
+ wpas_p2p_group_add_persistent(
+ wpa_s, persistent_go,
+ 0, 0, 0, 0, 0, NULL,
+ persistent_go->mode ==
+ WPAS_MODE_P2P_GO ?
+ P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+ 0);
+ } else if (response_done) {
+ wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ }
+
+ if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+ os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
+ wpa_s->p2ps_join_addr_valid = 1;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2PS: Saving PIN for " MACSTR,
+ MAC2STR(dev));
+ }
+ } else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+ go_ifname = go_wpa_s->ifname;
+
+ wpa_dbg(go_wpa_s, MSG_DEBUG,
+ "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
+ wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670",
+ NULL, 0, 0);
+
+ os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
+ wpa_s->p2ps_join_addr_valid = 1;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2PS: Saving PIN for " MACSTR, MAC2STR(dev));
+ }
+
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+ " status=%d conncap=%x"
+ " adv_id=%x adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " dev_passwd_id=%d go=%s",
+ MAC2STR(dev), status, conncap,
+ adv_id, MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac),
+ passwd_id, go_ifname);
+ return;
+ }
+
+ if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+ wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+ if (persistent_go && !persistent_go->num_p2p_clients) {
+ /* remove empty persistent GO */
+ wpa_config_remove_network(wpa_s->conf, persistent_go->id);
+ }
+
+ if (conncap == P2PS_SETUP_CLIENT) {
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+ " status=%d conncap=%x"
+ " adv_id=%x adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " dev_passwd_id=%d join=" MACSTR,
+ MAC2STR(dev), status, conncap,
+ adv_id, MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac),
+ passwd_id, MAC2STR(grp_mac));
+ } else {
+ wpa_msg_global(wpa_s, MSG_INFO,
+ P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+ " status=%d conncap=%x"
+ " adv_id=%x adv_mac=" MACSTR
+ " session=%x mac=" MACSTR
+ " dev_passwd_id=%d",
+ MAC2STR(dev), status, conncap,
+ adv_id, MAC2STR(adv_mac),
+ ses_id, MAC2STR(ses_mac),
+ passwd_id);
+ }
+}
+
+
static int _wpas_p2p_in_progress(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -4019,6 +5028,33 @@
}
+static int wpas_prov_disc_resp_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *persistent_go;
+
+ if (!wpa_s->global->pending_p2ps_group)
+ return 0;
+
+ wpa_s->global->pending_p2ps_group = 0;
+
+ if (wpas_p2p_get_go_group(wpa_s))
+ return 0;
+ persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+ if (persistent_go) {
+ wpas_p2p_group_add_persistent(
+ wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+ persistent_go->mode == WPAS_MODE_P2P_GO ?
+ P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+ } else {
+ wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ }
+
+ return 1;
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
@@ -4066,6 +5102,12 @@
p2p.presence_resp = wpas_presence_resp;
p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
+ p2p.get_persistent_group = wpas_get_persistent_group;
+ p2p.get_go_info = wpas_get_go_info;
+ p2p.remove_stale_groups = wpas_remove_stale_groups;
+ p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
+ p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
+ p2p.p2ps_group_capability = p2ps_group_capability;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -4485,7 +5527,7 @@
wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
MAC2STR(wpa_s->pending_join_dev_addr), join);
if (p2p_prov_disc_req(wpa_s->global->p2p,
- wpa_s->pending_join_dev_addr,
+ wpa_s->pending_join_dev_addr, NULL,
wpa_s->pending_pd_config_methods, join,
0, wpa_s->user_initiated_pd) < 0) {
wpa_s->p2p_auto_pd = 0;
@@ -4614,7 +5656,8 @@
}
if (p2p_prov_disc_req(wpa_s->global->p2p,
- wpa_s->pending_join_dev_addr, method, 1,
+ wpa_s->pending_join_dev_addr,
+ NULL, method, 1,
freq, wpa_s->user_initiated_pd) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
"Discovery Request before joining an "
@@ -4971,6 +6014,7 @@
wpa_s->global->add_psk = NULL;
wpa_s->global->p2p_fail_on_wps_complete = 0;
+ wpa_s->global->pending_p2ps_group = 0;
if (go_intent < 0)
go_intent = wpa_s->conf->p2p_go_intent;
@@ -5089,7 +6133,11 @@
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
- if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
+ wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+ wpa_s->roc_waiting_drv_freq, freq, duration);
+ if (wpa_s->off_channel_freq &&
+ wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
wpa_s->pending_listen_duration);
wpa_s->pending_listen_freq = 0;
@@ -5841,13 +6889,25 @@
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
- enum wpas_p2p_prov_disc_use use)
+ enum wpas_p2p_prov_disc_use use,
+ struct p2ps_provision *p2ps_prov)
{
u16 config_methods;
+ wpa_s->global->pending_p2ps_group = 0;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->pending_pd_use = NORMAL_PD;
- if (os_strncmp(config_method, "display", 7) == 0)
+ if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
+ p2ps_prov->conncap = p2ps_group_capability(
+ wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+ wpa_printf(MSG_DEBUG,
+ "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
+ __func__, p2ps_prov->conncap,
+ p2ps_prov->adv_id, p2ps_prov->conncap,
+ p2ps_prov->status, p2ps_prov->info);
+
+ config_methods = 0;
+ } else if (os_strncmp(config_method, "display", 7) == 0)
config_methods = WPS_CONFIG_DISPLAY;
else if (os_strncmp(config_method, "keypad", 6) == 0)
config_methods = WPS_CONFIG_KEYPAD;
@@ -5856,6 +6916,7 @@
config_methods = WPS_CONFIG_PUSHBUTTON;
else {
wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+ os_free(p2ps_prov);
return -1;
}
@@ -5876,10 +6937,12 @@
return 0;
}
- if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
+ os_free(p2ps_prov);
return -1;
+ }
- return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+ return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
0, 1);
}
@@ -5908,7 +6971,8 @@
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
- const u8 *dev_id, unsigned int search_delay)
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_cnt, const char **seek_string)
{
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->p2p_long_listen = 0;
@@ -5921,7 +6985,7 @@
return p2p_find(wpa_s->global->p2p, timeout, type,
num_req_dev_types, req_dev_types, dev_id,
- search_delay);
+ search_delay, seek_cnt, seek_string);
}
@@ -5968,7 +7032,8 @@
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
{
wpas_p2p_stop_find_oper(wpa_s);
- wpas_p2p_remove_pending_group_interface(wpa_s);
+ if (!wpa_s->global->pending_group_iface_for_p2ps)
+ wpas_p2p_remove_pending_group_interface(wpa_s);
}
@@ -8102,7 +9167,7 @@
if (cand) {
wpa_dbg(wpa_s, MSG_DEBUG,
- "P2P: Update Listen channel to %u baased on operating channel",
+ "P2P: Update Listen channel to %u based on operating channel",
cand);
p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 9f5a83b..1cf1573 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -15,6 +15,7 @@
struct p2p_peer_info;
struct p2p_channels;
struct wps_event_fail;
+struct p2ps_provision;
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
const char *conf_p2p_dev);
@@ -41,11 +42,13 @@
enum wpas_p2p_prov_disc_use {
WPAS_P2P_PD_FOR_GO_NEG,
WPAS_P2P_PD_FOR_JOIN,
- WPAS_P2P_PD_AUTO
+ WPAS_P2P_PD_AUTO,
+ WPAS_P2P_PD_FOR_ASP
};
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
- enum wpas_p2p_prov_disc_use use);
+ enum wpas_p2p_prov_disc_use use,
+ struct p2ps_provision *p2ps_prov);
void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
const u8 *data, size_t data_len,
enum p2p_send_action_result result);
@@ -55,7 +58,8 @@
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
- const u8 *dev_id, unsigned int search_delay);
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_cnt, const char **seek_string);
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
@@ -65,6 +69,8 @@
void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+ const char *svc_str, const char *info_substr);
u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
u8 version, const char *query);
u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
@@ -83,6 +89,11 @@
const char *service);
int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service);
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
+ u32 adv_id, const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info);
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 0653cc2..c1f3efc 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1594,11 +1594,12 @@
*/
#define GREAT_SNR 30
+#define IS_5GHZ(n) (n > 4000)
+
/* Compare function for sorting scan results. Return >0 if @b is considered
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
{
-#define IS_5GHZ(n) (n > 4000)
#define MIN(a,b) a < b ? a : b
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
@@ -1626,18 +1627,18 @@
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
return -1;
- if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
- !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
+ if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
} else {
- /* Not suitable information to calculate SNR, so use level */
+ /* Level is not in dBm, so we can't calculate
+ * SNR. Just use raw level (units unknown). */
snr_a = wa->level;
snr_b = wb->level;
}
- /* best/max rate preferred if SNR close enough */
- if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+ /* if SNR is close, decide by max rate or frequency band */
+ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
maxrate_a = wpa_scan_get_max_rate(wa);
maxrate_b = wpa_scan_get_max_rate(wb);
@@ -1647,8 +1648,6 @@
return IS_5GHZ(wa->freq) ? -1 : 1;
}
- /* use freq for channel preference */
-
/* all things being equal, use SNR; if SNRs are
* identical, use quality values since some drivers may only report
* that value and leave the signal level zero */
@@ -1656,7 +1655,6 @@
return wb->qual - wa->qual;
return snr_b - snr_a;
#undef MIN
-#undef IS_5GHZ
}
@@ -1721,15 +1719,15 @@
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *r = scan_res->res[i];
u8 *pos;
- if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
- == WPA_SCAN_LEVEL_DBM) {
+ if (r->flags & WPA_SCAN_LEVEL_DBM) {
int snr = r->level - r->noise;
+ int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
+
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
- "noise=%d level=%d snr=%d%s flags=0x%x "
- "age=%u",
+ "noise=%d%s 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,
+ r->noise, noise_valid ? "" : "~", r->level,
+ snr, snr >= GREAT_SNR ? "*" : "", r->flags,
r->age);
} else {
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
@@ -1802,6 +1800,14 @@
}
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurments. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+
/**
* wpa_supplicant_get_scan_results - Get scan results
* @wpa_s: Pointer to wpa_supplicant data
@@ -1835,6 +1841,17 @@
}
filter_scan_res(wpa_s, scan_res);
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *scan_res_item = scan_res->res[i];
+
+ if (scan_res_item->flags & WPA_SCAN_NOISE_INVALID) {
+ scan_res_item->noise =
+ IS_5GHZ(scan_res_item->freq) ?
+ DEFAULT_NOISE_FLOOR_5GHZ :
+ DEFAULT_NOISE_FLOOR_2GHZ;
+ }
+ }
+
#ifdef CONFIG_WPS
if (wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index c2b0990..6c05707 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -297,7 +297,7 @@
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
wpa_s->current_ssid,
try_opportunistic) == 0)
- eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 911effe..2f06c35 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1825,6 +1825,20 @@
}
+static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv);
+}
+
+
static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1909,11 +1923,9 @@
{
char cmd[4096];
- if (argc != 2 && argc != 4) {
+ if (argc < 2) {
printf("Invalid P2P_SERV_DISC_REQ command: needs two "
- "arguments (address and TLVs) or four arguments "
- "(address, \"upnp\", version, search target "
- "(SSDP ST:)\n");
+ "or more arguments (address and TLVs)\n");
return -1;
}
@@ -1975,27 +1987,25 @@
static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[4096];
- int res;
+ if (argc < 3) {
+ printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n");
+ return -1;
+ }
- if (argc != 3 && argc != 4) {
- printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+ return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc < 5 || argc > 6) {
+ printf("Invalid P2P_SERVICE_REP command: needs 5-6 "
"arguments\n");
return -1;
}
- if (argc == 4)
- res = os_snprintf(cmd, sizeof(cmd),
- "P2P_SERVICE_ADD %s %s %s %s",
- argv[0], argv[1], argv[2], argv[3]);
- else
- res = os_snprintf(cmd, sizeof(cmd),
- "P2P_SERVICE_ADD %s %s %s",
- argv[0], argv[1], argv[2]);
- if (os_snprintf_error(sizeof(cmd), res))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
}
@@ -2882,6 +2892,12 @@
"[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
"= stop P2P Devices search" },
+ { "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL,
+ cli_cmd_flag_none,
+ "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" },
+ { "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL,
+ cli_cmd_flag_none,
+ "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" },
{ "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
cli_cmd_flag_none,
"<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
@@ -2918,8 +2934,12 @@
"= remove all stored service entries" },
{ "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
cli_cmd_flag_none,
- "<bonjour|upnp> <query|version> <response|service> = add a local "
+ "<bonjour|upnp|asp> <query|version> <response|service> = add a local "
"service" },
+ { "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL,
+ cli_cmd_flag_none,
+ "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace "
+ "local ASP service" },
{ "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
cli_cmd_flag_none,
"<bonjour|upnp> <query|version> [|service] = remove a local "
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 434847d..6ad09a8 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1906,7 +1906,7 @@
(ssid->proto & WPA_PROTO_RSN);
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
ssid, try_opportunistic) == 0)
- eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+ eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
@@ -3086,11 +3086,9 @@
if (wpa_s->bridge_ifname[0]) {
wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
"interface '%s'", wpa_s->bridge_ifname);
- wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
- wpa_s->own_addr,
- ETH_P_EAPOL,
- wpa_supplicant_rx_eapol_bridge,
- wpa_s, 1);
+ wpa_s->l2_br = l2_packet_init_bridge(
+ wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+ ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
if (wpa_s->l2_br == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
"connection for the bridge interface '%s'",
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 23c2299..8964b3f 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -940,9 +940,20 @@
# * 2 = require cryptobinding
# EAP-WSC (WPS) uses following options: pin=<Device Password> or
# pbc=1.
+#
+# For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+# used to configure a mode that allows EAP-Success (and EAP-Failure)
+# without going through authentication step. Some switches use such
+# sequence when forcing the port to be authorized/unauthorized or as a
+# fallback option if the authentication server is unreachable. By default,
+# wpa_supplicant discards such frames to protect against potential attacks
+# by rogue devices, but this option can be used to disable that protection
+# for cases where the server/authenticator does not need to be
+# authenticated.
# phase2: Phase2 (inner authentication with TLS tunnel) parameters
# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
-# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be
+# used to disable MSCHAPv2 password retry in authentication failure cases.
#
# TLS-based methods can use the following parameters to control TLS behavior
# (these are normally in the phase1 parameter, but can be used also in the
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7d22000..c80a620 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -275,6 +275,8 @@
unsigned int p2p_per_sta_psk:1;
unsigned int p2p_fail_on_wps_complete:1;
unsigned int p2p_24ghz_social_channels:1;
+ unsigned int pending_p2ps_group:1;
+ unsigned int pending_group_iface_for_p2ps:1;
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
@@ -771,7 +773,7 @@
int force_long_sd;
u16 pending_pd_config_methods;
enum {
- NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
+ NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
} pending_pd_use;
/*
@@ -810,6 +812,7 @@
unsigned int p2p_nfc_tag_enabled:1;
unsigned int p2p_peer_oob_pk_hash_known:1;
unsigned int p2p_disable_ip_addr_req:1;
+ unsigned int p2ps_join_addr_valid:1;
int p2p_persistent_go_freq;
int p2p_persistent_id;
int p2p_go_intent;
@@ -829,6 +832,7 @@
/* group common frequencies */
int *p2p_group_common_freqs;
unsigned int p2p_group_common_freqs_num;
+ u8 p2ps_join_addr[ETH_ALEN];
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;