Merge remote-tracking branch 'goog/mirror-m-wireless-internal-release' into master_merge
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index f0e4604..e6f8c6a 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,49 @@
ChangeLog for hostapd
+2015-03-15 - v2.4
+ * allow OpenSSL cipher configuration to be set for internal EAP server
+ (openssl_ciphers parameter)
+ * fixed number of small issues based on hwsim test case failures and
+ static analyzer reports
+ * fixed Accounting-Request to not include duplicated Acct-Session-Id
+ * add support for Acct-Multi-Session-Id in RADIUS Accounting messages
+ * add support for PMKSA caching with SAE
+ * add support for generating BSS Load element (bss_load_update_period)
+ * fixed channel switch from VHT to HT
+ * add INTERFACE-ENABLED and INTERFACE-DISABLED ctrl_iface events
+ * add support for learning STA IPv4/IPv6 addresses and configuring
+ ProxyARP support
+ * dropped support for the madwifi driver interface
+ * add support for Suite B (128-bit and 192-bit level) key management and
+ cipher suites
+ * fixed a regression with driver=wired
+ * extend EAPOL-Key msg 1/4 retry workaround for changing SNonce
+ * add BSS_TM_REQ ctrl_iface command to send BSS Transition Management
+ Request frames and BSS-TM-RESP event to indicate response to such
+ frame
+ * add support for EAP Re-Authentication Protocol (ERP)
+ * fixed AP IE in EAPOL-Key 3/4 when both WPA and FT was enabled
+ * fixed a regression in HT 20/40 coex Action frame parsing
+ * set stdout to be line-buffered
+ * add support for vendor specific VHT extension to enable 256 QAM rates
+ (VHT-MCS 8 and 9) on 2.4 GHz band
+ * RADIUS DAS:
+ - extend Disconnect-Request processing to allow matching of multiple
+ sessions
+ - support Acct-Multi-Session-Id as an identifier
+ - allow PMKSA cache entry to be removed without association
+ * expire hostapd STA entry if kernel does not have a matching entry
+ * allow chanlist to be used to specify a subset of channels for ACS
+ * improve ACS behavior on 2.4 GHz band and allow channel bias to be
+ configured with acs_chan_bias parameter
+ * do not reply to a Probe Request frame that includes DSS Parameter Set
+ element in which the channel does not match the current operating
+ channel
+ * add UPDATE_BEACON ctrl_iface command; this can be used to force Beacon
+ frame contents to be updated and to start beaconing on an interface
+ that used start_disabled=1
+ * fixed some RADIUS server failover cases
+
2014-10-09 - v2.3
* fixed number of minor issues identified in static analyzer warnings
* fixed DFS and channel switch operation for multi-BSS cases
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index f6019ac..3e4e16b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1619,6 +1619,7 @@
wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
hapd->conf->iface);
hostapd_bss_deinit_no_free(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
hostapd_cleanup(hapd);
}
@@ -1631,6 +1632,8 @@
if (iface == NULL)
return;
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
#ifdef CONFIG_IEEE80211N
#ifdef NEED_AP_MLME
hostapd_stop_setup_timers(iface);
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index e8babb5..309215e 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -88,8 +88,8 @@
int sec_chan)
{
int ok, j, first;
- int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
- 184, 192 };
+ int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+ 149, 157, 184, 192 };
size_t k;
if (pri_chan == sec_chan || !sec_chan)
diff --git a/src/common/version.h b/src/common/version.h
index c662270..5ddf617 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
#define VERSION_STR_POSTFIX ""
#endif /* VERSION_STR_POSTFIX */
-#define VERSION_STR "2.4-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.5-devel" VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index de81d53..5534eab 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -207,8 +207,10 @@
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len, u8 *mic)
{
- u8 *buf, *pos;
- size_t buf_len;
+ const u8 *addr[9];
+ size_t len[9];
+ size_t i, num_elem = 0;
+ u8 zero_mic[16];
if (kck_len != 16) {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
@@ -216,48 +218,58 @@
return -1;
}
- buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
+ addr[num_elem] = sta_addr;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
- pos = buf;
- os_memcpy(pos, sta_addr, ETH_ALEN);
- pos += ETH_ALEN;
- os_memcpy(pos, ap_addr, ETH_ALEN);
- pos += ETH_ALEN;
- *pos++ = transaction_seqnum;
+ addr[num_elem] = ap_addr;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+
+ addr[num_elem] = &transaction_seqnum;
+ len[num_elem] = 1;
+ num_elem++;
+
if (rsnie) {
- os_memcpy(pos, rsnie, rsnie_len);
- pos += rsnie_len;
+ addr[num_elem] = rsnie;
+ len[num_elem] = rsnie_len;
+ num_elem++;
}
if (mdie) {
- os_memcpy(pos, mdie, mdie_len);
- pos += mdie_len;
+ addr[num_elem] = mdie;
+ len[num_elem] = mdie_len;
+ num_elem++;
}
if (ftie) {
- struct rsn_ftie *_ftie;
- os_memcpy(pos, ftie, ftie_len);
- if (ftie_len < 2 + sizeof(*_ftie)) {
- os_free(buf);
+ if (ftie_len < 2 + sizeof(struct rsn_ftie))
return -1;
- }
- _ftie = (struct rsn_ftie *) (pos + 2);
- os_memset(_ftie->mic, 0, sizeof(_ftie->mic));
- pos += ftie_len;
+
+ /* IE hdr and mic_control */
+ addr[num_elem] = ftie;
+ len[num_elem] = 2 + 2;
+ num_elem++;
+
+ /* MIC field with all zeros */
+ os_memset(zero_mic, 0, sizeof(zero_mic));
+ addr[num_elem] = zero_mic;
+ len[num_elem] = sizeof(zero_mic);
+ num_elem++;
+
+ /* Rest of FTIE */
+ addr[num_elem] = ftie + 2 + 2 + 16;
+ len[num_elem] = ftie_len - (2 + 2 + 16);
+ num_elem++;
}
if (ric) {
- os_memcpy(pos, ric, ric_len);
- pos += ric_len;
+ addr[num_elem] = ric;
+ len[num_elem] = ric_len;
+ num_elem++;
}
- wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf);
- if (omac1_aes_128(kck, buf, pos - buf, mic)) {
- os_free(buf);
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+ if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
return -1;
- }
-
- os_free(buf);
return 0;
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 03bd1a7..4074b87 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1477,6 +1477,7 @@
int above_threshold;
int current_signal;
int avg_signal;
+ int avg_beacon_signal;
int current_noise;
int current_txrate;
enum chan_width chanwidth;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d743868..aaff9ab 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1187,6 +1187,7 @@
static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
};
struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -1215,6 +1216,13 @@
else
sig_change->avg_signal = 0;
+ if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+ sig_change->avg_beacon_signal =
+ (s8)
+ nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+ else
+ sig_change->avg_beacon_signal = 0;
+
if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
sinfo[NL80211_STA_INFO_TX_BITRATE],
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index e0d1d23..13c446f 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -335,6 +335,33 @@
}
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 ft_byte;
+
+ if ((int) ftidx / 8 >= ext_features_len)
+ return 0;
+
+ ft_byte = ext_features[ftidx / 8];
+ return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct wpa_driver_capa *capa = info->capa;
+
+ if (tb == NULL)
+ return;
+
+ if (ext_feature_isset(nla_data(tb), nla_len(tb),
+ NL80211_EXT_FEATURE_VHT_IBSS))
+ capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+}
+
+
static void wiphy_info_feature_flags(struct wiphy_info_data *info,
struct nlattr *tb)
{
@@ -509,6 +536,7 @@
info->device_ap_sme = 1;
wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+ wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
wiphy_info_probe_resp_offload(capa,
tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index b37bd5a..ae16ba9 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -25,10 +25,30 @@
*
*/
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
#include <linux/types.h>
#define NL80211_GENL_NAME "nl80211"
+#define NL80211_MULTICAST_GROUP_CONFIG "config"
+#define NL80211_MULTICAST_GROUP_SCAN "scan"
+#define NL80211_MULTICAST_GROUP_REG "regulatory"
+#define NL80211_MULTICAST_GROUP_MLME "mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR "vendor"
+#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
+
/**
* DOC: Station handling
*
@@ -173,8 +193,8 @@
* %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
*
* @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
- * either a dump request on a %NL80211_ATTR_WIPHY or a specific get
- * on an %NL80211_ATTR_IFINDEX is supported.
+ * either a dump request for all interfaces or a specific get with a
+ * single %NL80211_ATTR_IFINDEX is supported.
* @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
* %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
* @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
@@ -252,7 +272,18 @@
* %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
- * regulatory domain.
+ * regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ * has a private regulatory domain, it will be returned. Otherwise, the
+ * global regdomain will be returned.
+ * A device will have a private regulatory domain if it uses the
+ * regulatory_hint() API. Even when a private regdomain is used the channel
+ * information will still be mended according to further hints from
+ * the regulatory core to help with compliance. A dump version of this API
+ * is now available which will returns the global regdomain as well as
+ * all private regdomains of present wiphys (for those that have it).
+ * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ * its private regdomain is the only valid one for it. The regulatory
+ * core is not used to help with compliance in this case.
* @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
* after being queried by the kernel. CRDA replies by sending a regulatory
* domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -306,7 +337,9 @@
* if passed, define which channels should be scanned; if not
* passed, all channels allowed for the current regulatory domain
* are used. Extra IEs can also be passed from the userspace by
- * using the %NL80211_ATTR_IE attribute.
+ * using the %NL80211_ATTR_IE attribute. The first cycle of the
+ * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ * is supplied.
* @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
* scheduled scan is not running. The caller may assume that as soon
* as the call returns, it is safe to start a new scheduled scan again.
@@ -774,6 +807,10 @@
* peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
* when this command completes.
*
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ * as an event to indicate changes for devices with wiphy-specific regdom
+ * management.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -958,6 +995,8 @@
NL80211_CMD_TDLS_CHANNEL_SWITCH,
NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+ NL80211_CMD_WIPHY_REG_CHANGE,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1655,6 +1694,13 @@
* @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
* creation then the new interface will be owned by the netlink socket
* that created it and will be destroyed when the socket is closed.
+ * If set during scheduled scan start then the new scan req will be
+ * owned by the netlink socket that created it and the scheduled scan will
+ * be stopped when the socket is closed.
+ * If set during configuration of regulatory indoor operation then the
+ * regulatory indoor configuration would be owned by the netlink socket
+ * that configured the indoor setting, and the indoor operation would be
+ * cleared when the socket is closed.
*
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
@@ -1688,6 +1734,32 @@
*
* @NL80211_ATTR_MAC_MASK: MAC address mask
*
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ * is self-managing its regulatory information and any regulatory domain
+ * obtained from it is coming from the device's wiphy and not the global
+ * cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ * array. The feature flags are identified by their bit index (see &enum
+ * nl80211_ext_feature_index). The bit index is ordered starting at the
+ * least-significant bit of the first byte in the array, ie. bit index 0
+ * is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ * of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ * may return a survey entry without a channel indicating global radio
+ * statistics (only some values are valid and make sense.)
+ * For devices that don't return such an entry even then, the information
+ * should be contained in the result as the sum of the respective counters
+ * over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
+ * WoWLAN net-detect scan) is started, u32 in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2045,6 +2117,18 @@
NL80211_ATTR_MAC_MASK,
+ NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+ NL80211_ATTR_EXT_FEATURES,
+
+ NL80211_ATTR_SURVEY_RADIO_STATS,
+
+ NL80211_ATTR_NETNS_FD,
+
+ NL80211_ATTR_SCHED_SCAN_DELAY,
+
+ NL80211_ATTR_REG_INDOOR,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2085,7 +2169,7 @@
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_HT_RATES 77
-#define NL80211_MAX_SUPP_REG_RULES 32
+#define NL80211_MAX_SUPP_REG_RULES 64
#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0
#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
@@ -2225,8 +2309,15 @@
* @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
* @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
* @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
- * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ * same as 160 for purposes of the bitrates
* @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * a quarter of the base (20 MHz) rate
* @__NL80211_RATE_INFO_AFTER_LAST: internal use
*/
enum nl80211_rate_info {
@@ -2241,6 +2332,8 @@
NL80211_RATE_INFO_80_MHZ_WIDTH,
NL80211_RATE_INFO_80P80_MHZ_WIDTH,
NL80211_RATE_INFO_160_MHZ_WIDTH,
+ NL80211_RATE_INFO_10_MHZ_WIDTH,
+ NL80211_RATE_INFO_5_MHZ_WIDTH,
/* keep last */
__NL80211_RATE_INFO_AFTER_LAST,
@@ -2285,18 +2378,24 @@
*
* @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
- * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
- * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station)
- * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ * (u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ * (u64, to this station)
* @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
* @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
* containing info as possible, see &enum nl80211_rate_info
- * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
- * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
- * station)
- * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
- * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ * (u32, to this station)
* @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
* @NL80211_STA_INFO_LLID: the station's mesh LLID
* @NL80211_STA_INFO_PLID: the station's mesh PLID
@@ -2320,6 +2419,16 @@
* Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
* @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
* 802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ * (u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ * for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ * This is a nested attribute where each the inner attribute number is the
+ * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ * each one of those is again nested with &enum nl80211_tid_stats
+ * attributes carrying the actual values.
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -2352,6 +2461,10 @@
NL80211_STA_INFO_CHAIN_SIGNAL,
NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+ NL80211_STA_INFO_RX_DROP_MISC,
+ NL80211_STA_INFO_BEACON_RX,
+ NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+ NL80211_STA_INFO_TID_STATS,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -2359,6 +2472,31 @@
};
/**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ * attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ * transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ * MSDUs (u64)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+ __NL80211_TID_STATS_INVALID,
+ NL80211_TID_STATS_RX_MSDU,
+ NL80211_TID_STATS_TX_MSDU,
+ NL80211_TID_STATS_TX_MSDU_RETRIES,
+ NL80211_TID_STATS_TX_MSDU_FAILED,
+
+ /* keep last */
+ NUM_NL80211_TID_STATS,
+ NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
* enum nl80211_mpath_flags - nl80211 mesh path flags
*
* @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
@@ -2772,16 +2910,18 @@
* @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
* @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
* @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
- * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
- * spent on this channel
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ * was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
* channel was sensed busy (either due to activity or energy detect)
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
* channel was sensed busy
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
- * receiving data
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
- * transmitting data
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ * receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ * transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ * (on this channel or globally)
* @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
* currently defined
* @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
@@ -2791,17 +2931,25 @@
NL80211_SURVEY_INFO_FREQUENCY,
NL80211_SURVEY_INFO_NOISE,
NL80211_SURVEY_INFO_IN_USE,
- NL80211_SURVEY_INFO_CHANNEL_TIME,
- NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
- NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
- NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
- NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
+ NL80211_SURVEY_INFO_TIME,
+ NL80211_SURVEY_INFO_TIME_BUSY,
+ NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+ NL80211_SURVEY_INFO_TIME_RX,
+ NL80211_SURVEY_INFO_TIME_TX,
+ NL80211_SURVEY_INFO_TIME_SCAN,
/* keep last */
__NL80211_SURVEY_INFO_AFTER_LAST,
NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
};
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX
+
/**
* enum nl80211_mntr_flags - monitor configuration flags
*
@@ -2966,7 +3114,8 @@
*
* @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
* established peering with for longer than this time (in seconds), then
- * remove it from the STA's list of peers. Default is 30 minutes.
+ * remove it from the STA's list of peers. You may set this to 0 to disable
+ * the removal of the STA. Default is 30 minutes.
*
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
@@ -3238,6 +3387,9 @@
/**
* enum nl80211_bss_status - BSS "status"
* @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ * Note that this is no longer used since cfg80211 no longer
+ * keeps track of whether or not authentication was done with
+ * a given BSS.
* @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
* @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
*
@@ -3565,6 +3717,8 @@
* @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
* the chip into a special state -- works best with chips that have
* support for low-power operation already (flag)
+ * Note that this mode is incompatible with all of the others, if
+ * any others are even supported by the device.
* @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
* is detected is implementation-specific (flag)
* @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
@@ -3621,9 +3775,12 @@
* @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
* is detected. This is a nested attribute that contains the
* same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
- * specifies how the scan is performed (e.g. the interval and the
- * channels to scan) as well as the scan results that will
- * trigger a wake (i.e. the matchsets).
+ * specifies how the scan is performed (e.g. the interval, the
+ * channels to scan and the initial delay) as well as the scan
+ * results that will trigger a wake (i.e. the matchsets). This
+ * attribute is also sent in a response to
+ * @NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ * supported by the driver (u32).
* @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
* containing an array with information about what triggered the
* wake up. If no elements are present in the array, it means
@@ -4194,6 +4351,21 @@
};
/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_VHT_IBSS,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+ MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
* enum nl80211_probe_resp_offload_support_attr - optional supported
* protocols for probe-response offloading by the driver/FW.
* To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
diff --git a/src/utils/bitfield.c b/src/utils/bitfield.c
index f90e4be..8dcec39 100644
--- a/src/utils/bitfield.c
+++ b/src/utils/bitfield.c
@@ -76,11 +76,11 @@
int bitfield_get_first_zero(struct bitfield *bf)
{
size_t i;
- for (i = 0; i <= (bf->max_bits + 7) / 8; i++) {
+ for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
if (bf->bits[i] != 0xff)
break;
}
- if (i > (bf->max_bits + 7) / 8)
+ if (i == (bf->max_bits + 7) / 8)
return -1;
i = i * 8 + first_zero(bf->bits[i]);
if (i >= bf->max_bits)
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 2ec99ad..322104b 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -270,6 +270,7 @@
ifdef CONFIG_P2P
OBJS += p2p_supplicant.c
+OBJS += p2p_supplicant_sd.c
OBJS += src/p2p/p2p.c
OBJS += src/p2p/p2p_utils.c
OBJS += src/p2p/p2p_parse.c
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index f09e7a0..1ac79b4 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,83 @@
ChangeLog for wpa_supplicant
+2015-03-15 - v2.4
+ * allow OpenSSL cipher configuration to be set for internal EAP server
+ (openssl_ciphers parameter)
+ * fixed number of small issues based on hwsim test case failures and
+ static analyzer reports
+ * P2P:
+ - add new=<0/1> flag to P2P-DEVICE-FOUND events
+ - add passive channels in invitation response from P2P Client
+ - enable nl80211 P2P_DEVICE support by default
+ - fix regresssion in disallow_freq preventing search on social
+ channels
+ - fix regressions in P2P SD query processing
+ - try to re-invite with social operating channel if no common channels
+ in invitation
+ - allow cross connection on parent interface (this fixes number of
+ use cases with nl80211)
+ - add support for P2P services (P2PS)
+ - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
+ be configured
+ * increase postponing of EAPOL-Start by one second with AP/GO that
+ supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
+ of identity frames)
+ * add support for PMKSA caching with SAE
+ * add support for control mesh BSS (IEEE 802.11s) operations
+ * fixed number of issues with D-Bus P2P commands
+ * fixed regression in ap_scan=2 special case for WPS
+ * fixed macsec_validate configuration
+ * add a workaround for incorrectly behaving APs that try to use
+ EAPOL-Key descriptor version 3 when the station supports PMF even if
+ PMF is not enabled on the AP
+ * allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
+ of disabling these can be configured to work around issues with broken
+ servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
+ * add support for Suite B (128-bit and 192-bit level) key management and
+ cipher suites
+ * add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
+ * improved BSS Transition Management processing
+ * add support for neighbor report
+ * add support for link measurement
+ * fixed expiration of BSS entry with all-zeros BSSID
+ * add optional LAST_ID=x argument to LIST_NETWORK to allow all
+ configured networks to be listed even with huge number of network
+ profiles
+ * add support for EAP Re-Authentication Protocol (ERP)
+ * fixed EAP-IKEv2 fragmentation reassembly
+ * improved PKCS#11 configuration for OpenSSL
+ * set stdout to be line-buffered
+ * add TDLS channel switch configuration
+ * add support for MAC address randomization in scans with nl80211
+ * enable HT for IBSS if supported by the driver
+ * add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
+ * add support for domain_suffix_match with GnuTLS
+ * add OCSP stapling client support with GnuTLS
+ * include peer certificate in EAP events even without a separate probe
+ operation; old behavior can be restored with cert_in_cb=0
+ * add peer ceritficate alt subject name to EAP events
+ (CTRL-EVENT-EAP-PEER-ALT)
+ * add domain_match network profile parameter (similar to
+ domain_suffix_match, but full match is required)
+ * enable AP/GO mode HT Tx STBC automatically based on driver support
+ * add ANQP-QUERY-DONE event to provide information on ANQP parsing
+ status
+ * allow passive scanning to be forced with passive_scan=1
+ * add a workaround for Linux packet socket behavior when interface is in
+ bridge
+ * increase 5 GHz band preference in BSS selection (estimate SNR, if info
+ not available from driver; estimate maximum throughput based on common
+ HT/VHT/specific TX rate support)
+ * add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
+ implement Interworking network selection behavior in upper layers
+ software components
+ * add optional reassoc_same_bss_optim=1 (disabled by default)
+ optimization to avoid unnecessary Authentication frame exchange
+ * extend TDLS frame padding workaround to cover all packets
+ * allow wpa_supplicant to recover nl80211 functionality if the cfg80211
+ module gets removed and reloaded without restarting wpa_supplicant
+ * allow hostapd DFS implementation to be used in wpa_supplicant AP mode
+
2014-10-09 - v2.3
* fixed number of minor issues identified in static analyzer warnings
* fixed wfd_dev_info to be more careful and not read beyond the buffer
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 0f82af9..976b984 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -276,6 +276,7 @@
ifdef CONFIG_P2P
OBJS += p2p_supplicant.o
+OBJS += p2p_supplicant_sd.o
OBJS += ../src/p2p/p2p.o
OBJS += ../src/p2p/p2p_utils.o
OBJS += ../src/p2p/p2p_parse.o
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index ea5b2b4..7ecf7a8 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -707,6 +707,9 @@
hostapd_interface_free(wpa_s->ap_iface);
wpa_s->ap_iface = NULL;
wpa_drv_deinit_ap(wpa_s);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+ " reason=%d locally_generated=1",
+ MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
}
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8e6cd20..a01a910 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4052,6 +4052,31 @@
}
+#ifdef CONFIG_P2P
+static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
+ long offset, char *buf, size_t buflen,
+ int pretty_print)
+{
+ void *val = ((u8 *) config) + (long) offset;
+ int res;
+ char addr[INET_ADDRSTRLEN];
+
+ if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
+ return -1;
+
+ if (pretty_print)
+ res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
+ else
+ res = os_snprintf(buf, buflen, "%s", addr);
+
+ if (os_snprintf_error(buflen, res))
+ res = -1;
+
+ return res;
+}
+#endif /* CONFIG_P2P */
+
+
#ifdef OFFSET
#undef OFFSET
#endif /* OFFSET */
@@ -4067,7 +4092,8 @@
#define STR(f) _STR(f), NULL, NULL
#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
-#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4, \
+ OFFSET(f), NULL, NULL
static const struct global_parse_data global_fields[] = {
#ifdef CONFIG_CTRL_IFACE
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b4aefb6..4ebc3a1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6476,6 +6476,14 @@
pos += ret;
}
+ if (si.avg_beacon_signal) {
+ ret = os_snprintf(pos, end - pos,
+ "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
return pos - buf;
}
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 84766db..fe91236 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -17,6 +17,7 @@
<arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
<arg>-i <replaceable>ifname</replaceable></arg>
<arg>-t</arg>
+ <arg>-q</arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -57,6 +58,13 @@
manager supports it). By default the main status window is
shown.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>-q</term>
+
+ <listitem><para>Run program in the quiet mode - do not display tray
+ icon pop-up messages.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b200ca0..bb61808 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -2388,1227 +2388,6 @@
}
-/*
- * DNS Header section is used only to calculate compression pointers, so the
- * contents of this data does not matter, but the length needs to be reserved
- * in the virtual packet.
- */
-#define DNS_HEADER_LEN 12
-
-/*
- * 27-octet in-memory packet from P2P specification containing two implied
- * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
- */
-#define P2P_SD_IN_MEMORY_LEN 27
-
-static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
- u8 **spos, const u8 *end)
-{
- while (*spos < end) {
- u8 val = ((*spos)[0] & 0xc0) >> 6;
- int len;
-
- if (val == 1 || val == 2) {
- /* These are reserved values in RFC 1035 */
- wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
- "sequence starting with 0x%x", val);
- return -1;
- }
-
- if (val == 3) {
- u16 offset;
- u8 *spos_tmp;
-
- /* Offset */
- if (*spos + 2 > end) {
- wpa_printf(MSG_DEBUG, "P2P: No room for full "
- "DNS offset field");
- return -1;
- }
-
- offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
- if (offset >= *spos - start) {
- wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
- "pointer offset %u", offset);
- return -1;
- }
-
- (*spos) += 2;
- spos_tmp = start + offset;
- return p2p_sd_dns_uncompress_label(upos, uend, start,
- &spos_tmp,
- *spos - 2);
- }
-
- /* Label */
- len = (*spos)[0] & 0x3f;
- if (len == 0)
- return 0;
-
- (*spos)++;
- if (*spos + len > end) {
- wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
- "sequence - no room for label with length "
- "%u", len);
- return -1;
- }
-
- if (*upos + len + 2 > uend)
- return -2;
-
- os_memcpy(*upos, *spos, len);
- *spos += len;
- *upos += len;
- (*upos)[0] = '.';
- (*upos)++;
- (*upos)[0] = '\0';
- }
-
- return 0;
-}
-
-
-/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
- * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
- * not large enough */
-static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
- size_t msg_len, size_t offset)
-{
- /* 27-octet in-memory packet from P2P specification */
- const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
- "\x04_udp\xC0\x11\x00\x0C\x00\x01";
- u8 *tmp, *end, *spos;
- char *upos, *uend;
- int ret = 0;
-
- if (buf_len < 2)
- return -1;
- if (offset > msg_len)
- return -1;
-
- tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
- if (tmp == NULL)
- return -1;
- spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
- end = spos + msg_len;
- spos += offset;
-
- os_memset(tmp, 0, DNS_HEADER_LEN);
- os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
- os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
-
- upos = buf;
- uend = buf + buf_len;
-
- ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
- if (ret) {
- os_free(tmp);
- return ret;
- }
-
- if (upos == buf) {
- upos[0] = '.';
- upos[1] = '\0';
- } else if (upos[-1] == '.')
- upos[-1] = '\0';
-
- os_free(tmp);
- return 0;
-}
-
-
-static struct p2p_srv_bonjour *
-wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
- const struct wpabuf *query)
-{
- struct p2p_srv_bonjour *bsrv;
- size_t len;
-
- len = wpabuf_len(query);
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (len == wpabuf_len(bsrv->query) &&
- os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
- len) == 0)
- return bsrv;
- }
- return NULL;
-}
-
-
-static struct p2p_srv_upnp *
-wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
-{
- struct p2p_srv_upnp *usrv;
-
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (version == usrv->version &&
- os_strcmp(service, usrv->service) == 0)
- return usrv;
- }
- return NULL;
-}
-
-
-static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
- u8 srv_trans_id, u8 status)
-{
- u8 *len_pos;
-
- if (wpabuf_tailroom(resp) < 5)
- return;
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, srv_proto);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- 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)
-{
- struct p2p_srv_bonjour *bsrv;
- u8 *len_pos;
-
- wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
-
- if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
- return;
- }
-
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (wpabuf_tailroom(resp) <
- 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
- wpabuf_head(bsrv->resp),
- wpabuf_len(bsrv->resp));
- /* Response Data */
- wpabuf_put_buf(resp, bsrv->query); /* Key */
- wpabuf_put_buf(resp, bsrv->resp); /* Value */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
-}
-
-
-static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
- size_t query_len)
-{
- char str_rx[256], str_srv[256];
-
- if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
- return 0; /* Too short to include DNS Type and Version */
- if (os_memcmp(query + query_len - 3,
- wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
- 3) != 0)
- return 0; /* Mismatch in DNS Type or Version */
- if (query_len == wpabuf_len(bsrv->query) &&
- os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
- return 1; /* Binary match */
-
- if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
- 0))
- return 0; /* Failed to uncompress query */
- if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
- wpabuf_head(bsrv->query),
- wpabuf_len(bsrv->query) - 3, 0))
- return 0; /* Failed to uncompress service */
-
- return os_strcmp(str_rx, str_srv) == 0;
-}
-
-
-static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
-{
- struct p2p_srv_bonjour *bsrv;
- u8 *len_pos;
- int matches = 0;
-
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
- query, query_len);
- if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
- srv_trans_id);
- return;
- }
-
- if (query_len == 0) {
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- return;
- }
-
- dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list) {
- if (!match_bonjour_query(bsrv, query, query_len))
- continue;
-
- if (wpabuf_tailroom(resp) <
- 5 + query_len + wpabuf_len(bsrv->resp))
- return;
-
- matches++;
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
-
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
- wpabuf_head(bsrv->resp),
- wpabuf_len(bsrv->resp));
-
- /* Response Data */
- wpabuf_put_data(resp, query, query_len); /* Key */
- wpabuf_put_buf(resp, bsrv->resp); /* Value */
-
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
-
- if (matches == 0) {
- wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
- "available");
- if (wpabuf_tailroom(resp) < 5)
- return;
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
-
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
- /* Response Data: empty */
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
-}
-
-
-static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id)
-{
- struct p2p_srv_upnp *usrv;
- u8 *len_pos;
-
- wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
-
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
- wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
- return;
- }
-
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
- return;
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_UPNP);
- wpabuf_put_u8(resp, srv_trans_id);
-
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- /* Response Data */
- wpabuf_put_u8(resp, usrv->version);
- wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
- usrv->service);
- wpabuf_put_str(resp, usrv->service);
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
- 2);
- }
-}
-
-
-static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
-{
- struct p2p_srv_upnp *usrv;
- u8 *len_pos;
- u8 version;
- char *str;
- int count = 0;
-
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
- query, query_len);
-
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
- wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
- srv_trans_id);
- return;
- }
-
- if (query_len == 0) {
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- return;
- }
-
- if (wpabuf_tailroom(resp) < 5)
- return;
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_UPNP);
- wpabuf_put_u8(resp, srv_trans_id);
-
- version = query[0];
- str = os_malloc(query_len);
- if (str == NULL)
- return;
- os_memcpy(str, query + 1, query_len - 1);
- str[query_len - 1] = '\0';
-
- dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list) {
- if (version != usrv->version)
- continue;
-
- if (os_strcmp(str, "ssdp:all") != 0 &&
- os_strstr(usrv->service, str) == NULL)
- continue;
-
- if (wpabuf_tailroom(resp) < 2)
- break;
- if (count == 0) {
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- /* Response Data */
- wpabuf_put_u8(resp, version);
- } else
- wpabuf_put_u8(resp, ',');
-
- count++;
-
- wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
- usrv->service);
- if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
- break;
- wpabuf_put_str(resp, usrv->service);
- }
- os_free(str);
-
- if (count == 0) {
- wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
- "available");
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
- /* Response Data: empty */
- }
-
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-
-
-#ifdef CONFIG_WIFI_DISPLAY
-static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
- struct wpabuf *resp, u8 srv_trans_id,
- const u8 *query, size_t query_len)
-{
- const u8 *pos;
- u8 role;
- u8 *len_pos;
-
- wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
-
- if (!wpa_s->global->wifi_display) {
- wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
- wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
- srv_trans_id);
- return;
- }
-
- if (query_len < 1) {
- wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
- "Role");
- return;
- }
-
- if (wpabuf_tailroom(resp) < 5)
- return;
-
- pos = query;
- role = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
-
- /* TODO: role specific handling */
-
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
- wpabuf_put_u8(resp, srv_trans_id);
- wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
-
- while (pos < query + query_len) {
- if (*pos < MAX_WFD_SUBELEMS &&
- wpa_s->global->wfd_subelem[*pos] &&
- wpabuf_tailroom(resp) >=
- wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
- wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
- "subelement %u", *pos);
- wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
- }
- pos++;
- }
-
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-#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)
-{
- struct wpa_supplicant *wpa_s = ctx;
- const u8 *pos = tlvs;
- const u8 *end = tlvs + tlvs_len;
- const u8 *tlv_end;
- u16 slen;
- struct wpabuf *resp;
- u8 srv_proto, srv_trans_id;
- size_t buf_len;
- char *buf;
-
- wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
- tlvs, tlvs_len);
- buf_len = 2 * tlvs_len + 1;
- buf = os_malloc(buf_len);
- if (buf) {
- wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
- wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
- MACSTR " %u %u %s",
- freq, MAC2STR(sa), dialog_token, update_indic,
- buf);
- os_free(buf);
- }
-
- if (wpa_s->p2p_sd_over_ctrl_iface) {
- wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
- update_indic, tlvs, tlvs_len);
- return; /* to be processed by an external program */
- }
-
- resp = wpabuf_alloc(10000);
- if (resp == NULL)
- return;
-
- while (pos + 1 < end) {
- wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (pos + slen > end || slen < 2) {
- wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
- "length");
- wpabuf_free(resp);
- return;
- }
- tlv_end = pos + slen;
-
- srv_proto = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
- srv_proto);
- srv_trans_id = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
- srv_trans_id);
-
- wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
- pos, tlv_end - pos);
-
-
- if (wpa_s->force_long_sd) {
- wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
- "response");
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- goto done;
- }
-
- switch (srv_proto) {
- case P2P_SERV_ALL_SERVICES:
- wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
- "for all services");
- if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
- dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
- wpa_printf(MSG_DEBUG, "P2P: No service "
- "discovery protocols available");
- wpas_sd_add_proto_not_avail(
- resp, P2P_SERV_ALL_SERVICES,
- srv_trans_id);
- break;
- }
- wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
- wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
- break;
- case P2P_SERV_BONJOUR:
- wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
- case P2P_SERV_UPNP:
- wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
- pos, tlv_end - pos);
- break;
-#ifdef CONFIG_WIFI_DISPLAY
- case P2P_SERV_WIFI_DISPLAY:
- wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
- 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);
- wpas_sd_add_proto_not_avail(resp, srv_proto,
- srv_trans_id);
- break;
- }
-
- pos = tlv_end;
- }
-
-done:
- wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
- update_indic, tlvs, tlvs_len);
-
- wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
-
- wpabuf_free(resp);
-}
-
-
-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)
-{
- struct wpa_supplicant *wpa_s = ctx;
- const u8 *pos = tlvs;
- const u8 *end = tlvs + tlvs_len;
- const u8 *tlv_end;
- u16 slen;
- size_t buf_len;
- char *buf;
-
- wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
- tlvs, tlvs_len);
- if (tlvs_len > 1500) {
- /* TODO: better way for handling this */
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- P2P_EVENT_SERV_DISC_RESP MACSTR
- " %u <long response: %u bytes>",
- MAC2STR(sa), update_indic,
- (unsigned int) tlvs_len);
- } else {
- buf_len = 2 * tlvs_len + 1;
- buf = os_malloc(buf_len);
- if (buf) {
- wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
- MAC2STR(sa), update_indic, buf);
- os_free(buf);
- }
- }
-
- while (pos < end) {
- u8 srv_proto, srv_trans_id, status;
-
- wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (pos + slen > end || slen < 3) {
- wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
- "length");
- return;
- }
- tlv_end = pos + slen;
-
- srv_proto = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
- srv_proto);
- srv_trans_id = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
- srv_trans_id);
- status = *pos++;
- wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
- status);
-
- 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;
- }
-
- wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
-}
-
-
-u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
- const struct wpabuf *tlvs)
-{
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return 0;
- return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
- u8 version, const char *query)
-{
- struct wpabuf *tlvs;
- u64 ret;
-
- tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
- if (tlvs == NULL)
- return 0;
- wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
- wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
- wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
- wpabuf_put_u8(tlvs, version);
- wpabuf_put_str(tlvs, query);
- ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
- wpabuf_free(tlvs);
- return ret;
-}
-
-
-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,
- const struct wpabuf *tlvs)
-{
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return 0;
- return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-#define MAX_WFD_SD_SUBELEMS 20
-
-static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
- const char *subelems)
-{
- u8 *len;
- const char *pos;
- int val;
- int count = 0;
-
- len = wpabuf_put(tlvs, 2);
- wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
- wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
-
- wpabuf_put_u8(tlvs, role);
-
- pos = subelems;
- while (*pos) {
- val = atoi(pos);
- if (val >= 0 && val < 256) {
- wpabuf_put_u8(tlvs, val);
- count++;
- if (count == MAX_WFD_SD_SUBELEMS)
- break;
- }
- pos = os_strchr(pos + 1, ',');
- if (pos == NULL)
- break;
- pos++;
- }
-
- WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
-}
-
-
-u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
- const u8 *dst, const char *role)
-{
- struct wpabuf *tlvs;
- u64 ret;
- const char *subelems;
- u8 id = 1;
-
- subelems = os_strchr(role, ' ');
- if (subelems == NULL)
- return 0;
- subelems++;
-
- tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
- if (tlvs == NULL)
- return 0;
-
- if (os_strstr(role, "[source]"))
- wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
- if (os_strstr(role, "[pri-sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
- if (os_strstr(role, "[sec-sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
- if (os_strstr(role, "[source+sink]"))
- wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
-
- ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
- wpabuf_free(tlvs);
- return ret;
-}
-
-#endif /* CONFIG_WIFI_DISPLAY */
-
-
-int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
-{
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return -1;
- return p2p_sd_cancel_request(wpa_s->global->p2p,
- (void *) (uintptr_t) req);
-}
-
-
-void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
- const u8 *dst, u8 dialog_token,
- const struct wpabuf *resp_tlvs)
-{
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return;
- p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
- resp_tlvs);
-}
-
-
-void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
-{
- if (wpa_s->global->p2p)
- p2p_sd_service_update(wpa_s->global->p2p);
-}
-
-
-static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
-{
- dl_list_del(&bsrv->list);
- wpabuf_free(bsrv->query);
- wpabuf_free(bsrv->resp);
- os_free(bsrv);
-}
-
-
-static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
-{
- dl_list_del(&usrv->list);
- os_free(usrv->service);
- os_free(usrv);
-}
-
-
-void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
-{
- struct p2p_srv_bonjour *bsrv, *bn;
- struct p2p_srv_upnp *usrv, *un;
-
- dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
- struct p2p_srv_bonjour, list)
- wpas_p2p_srv_bonjour_free(bsrv);
-
- dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
- struct p2p_srv_upnp, list)
- wpas_p2p_srv_upnp_free(usrv);
-
- wpas_p2p_sd_service_update(wpa_s);
-}
-
-
-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)
-{
- struct p2p_srv_bonjour *bsrv;
-
- bsrv = os_zalloc(sizeof(*bsrv));
- if (bsrv == NULL)
- return -1;
- bsrv->query = query;
- bsrv->resp = resp;
- dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
-
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
-}
-
-
-int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
- const struct wpabuf *query)
-{
- struct p2p_srv_bonjour *bsrv;
-
- bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
- if (bsrv == NULL)
- return -1;
- wpas_p2p_srv_bonjour_free(bsrv);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
-}
-
-
-int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
-{
- struct p2p_srv_upnp *usrv;
-
- if (wpas_p2p_service_get_upnp(wpa_s, version, service))
- return 0; /* Already listed */
- usrv = os_zalloc(sizeof(*usrv));
- if (usrv == NULL)
- return -1;
- usrv->version = version;
- usrv->service = os_strdup(service);
- if (usrv->service == NULL) {
- os_free(usrv);
- return -1;
- }
- dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
-
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
-}
-
-
-int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
- const char *service)
-{
- struct p2p_srv_upnp *usrv;
-
- usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
- if (usrv == NULL)
- return -1;
- wpas_p2p_srv_upnp_free(usrv);
- wpas_p2p_sd_service_update(wpa_s);
- return 0;
-}
-
-
static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
const u8 *peer, const char *params,
unsigned int generated_pin)
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index b786178..1d3c67b 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -94,6 +94,10 @@
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);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
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/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
new file mode 100644
index 0000000..cb68c03
--- /dev/null
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -0,0 +1,1237 @@
+/*
+ * wpa_supplicant - P2P service discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "p2p/p2p.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+ u8 **spos, const u8 *end)
+{
+ while (*spos < end) {
+ u8 val = ((*spos)[0] & 0xc0) >> 6;
+ int len;
+
+ if (val == 1 || val == 2) {
+ /* These are reserved values in RFC 1035 */
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence starting with 0x%x", val);
+ return -1;
+ }
+
+ if (val == 3) {
+ u16 offset;
+ u8 *spos_tmp;
+
+ /* Offset */
+ if (*spos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "P2P: No room for full "
+ "DNS offset field");
+ return -1;
+ }
+
+ offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+ if (offset >= *spos - start) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+ "pointer offset %u", offset);
+ return -1;
+ }
+
+ (*spos) += 2;
+ spos_tmp = start + offset;
+ return p2p_sd_dns_uncompress_label(upos, uend, start,
+ &spos_tmp,
+ *spos - 2);
+ }
+
+ /* Label */
+ len = (*spos)[0] & 0x3f;
+ if (len == 0)
+ return 0;
+
+ (*spos)++;
+ if (*spos + len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence - no room for label with length "
+ "%u", len);
+ return -1;
+ }
+
+ if (*upos + len + 2 > uend)
+ return -2;
+
+ os_memcpy(*upos, *spos, len);
+ *spos += len;
+ *upos += len;
+ (*upos)[0] = '.';
+ (*upos)++;
+ (*upos)[0] = '\0';
+ }
+
+ return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+ size_t msg_len, size_t offset)
+{
+ /* 27-octet in-memory packet from P2P specification */
+ const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+ "\x04_udp\xC0\x11\x00\x0C\x00\x01";
+ u8 *tmp, *end, *spos;
+ char *upos, *uend;
+ int ret = 0;
+
+ if (buf_len < 2)
+ return -1;
+ if (offset > msg_len)
+ return -1;
+
+ tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+ if (tmp == NULL)
+ return -1;
+ spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+ end = spos + msg_len;
+ spos += offset;
+
+ os_memset(tmp, 0, DNS_HEADER_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+ upos = buf;
+ uend = buf + buf_len;
+
+ ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+ if (ret) {
+ os_free(tmp);
+ return ret;
+ }
+
+ if (upos == buf) {
+ upos[0] = '.';
+ upos[1] = '\0';
+ } else if (upos[-1] == '.')
+ upos[-1] = '\0';
+
+ os_free(tmp);
+ return 0;
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+ size_t len;
+
+ len = wpabuf_len(query);
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (len == wpabuf_len(bsrv->query) &&
+ os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+ len) == 0)
+ return bsrv;
+ }
+ return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version == usrv->version &&
+ os_strcmp(service, usrv->service) == 0)
+ return usrv;
+ }
+ return NULL;
+}
+
+
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id, u8 status)
+{
+ u8 *len_pos;
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, srv_proto);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ 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)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (wpabuf_tailroom(resp) <
+ 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+ size_t query_len)
+{
+ char str_rx[256], str_srv[256];
+
+ if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+ return 0; /* Too short to include DNS Type and Version */
+ if (os_memcmp(query + query_len - 3,
+ wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+ 3) != 0)
+ return 0; /* Mismatch in DNS Type or Version */
+ if (query_len == wpabuf_len(bsrv->query) &&
+ os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+ return 1; /* Binary match */
+
+ if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+ 0))
+ return 0; /* Failed to uncompress query */
+ if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+ wpabuf_head(bsrv->query),
+ wpabuf_len(bsrv->query) - 3, 0))
+ return 0; /* Failed to uncompress service */
+
+ return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+ int matches = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+ query, query_len);
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (!match_bonjour_query(bsrv, query, query_len))
+ continue;
+
+ if (wpabuf_tailroom(resp) <
+ 5 + query_len + wpabuf_len(bsrv->resp))
+ return;
+
+ matches++;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+
+ /* Response Data */
+ wpabuf_put_data(resp, query, query_len); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+ }
+
+ if (matches == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+ "available");
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ return;
+ }
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, usrv->version);
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ wpabuf_put_str(resp, usrv->service);
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+ u8 version;
+ char *str;
+ int count = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+ query, query_len);
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ version = query[0];
+ str = os_malloc(query_len);
+ if (str == NULL)
+ return;
+ os_memcpy(str, query + 1, query_len - 1);
+ str[query_len - 1] = '\0';
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version != usrv->version)
+ continue;
+
+ if (os_strcmp(str, "ssdp:all") != 0 &&
+ os_strstr(usrv->service, str) == NULL)
+ continue;
+
+ if (wpabuf_tailroom(resp) < 2)
+ break;
+ if (count == 0) {
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, version);
+ } else
+ wpabuf_put_u8(resp, ',');
+
+ count++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+ break;
+ wpabuf_put_str(resp, usrv->service);
+ }
+ os_free(str);
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+ "available");
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ const u8 *pos;
+ u8 role;
+ u8 *len_pos;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+ if (!wpa_s->global->wifi_display) {
+ wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+ "Role");
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ pos = query;
+ role = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+ /* TODO: role specific handling */
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+ wpabuf_put_u8(resp, srv_trans_id);
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+ while (pos < query + query_len) {
+ if (*pos < MAX_WFD_SUBELEMS &&
+ wpa_s->global->wfd_subelem[*pos] &&
+ wpabuf_tailroom(resp) >=
+ wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+ wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+ "subelement %u", *pos);
+ wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+ }
+ pos++;
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#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);
+ }
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ struct wpabuf *resp;
+ u8 srv_proto, srv_trans_id;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+ tlvs, tlvs_len);
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+ MACSTR " %u %u %s",
+ freq, MAC2STR(sa), dialog_token, update_indic,
+ buf);
+ os_free(buf);
+ }
+
+ if (wpa_s->p2p_sd_over_ctrl_iface) {
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+ return; /* to be processed by an external program */
+ }
+
+ resp = wpabuf_alloc(10000);
+ if (resp == NULL)
+ return;
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+ "length");
+ wpabuf_free(resp);
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+ pos, tlv_end - pos);
+
+
+ if (wpa_s->force_long_sd) {
+ wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+ "response");
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ goto done;
+ }
+
+ switch (srv_proto) {
+ case P2P_SERV_ALL_SERVICES:
+ wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+ "for all services");
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+ dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: No service "
+ "discovery protocols available");
+ wpas_sd_add_proto_not_avail(
+ resp, P2P_SERV_ALL_SERVICES,
+ srv_trans_id);
+ break;
+ }
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ break;
+ case P2P_SERV_BONJOUR:
+ wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ case P2P_SERV_UPNP:
+ wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+#ifdef CONFIG_WIFI_DISPLAY
+ case P2P_SERV_WIFI_DISPLAY:
+ wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+ 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);
+ wpas_sd_add_proto_not_avail(resp, srv_proto,
+ srv_trans_id);
+ break;
+ }
+
+ pos = tlv_end;
+ }
+
+done:
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+
+ wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+ wpabuf_free(resp);
+}
+
+
+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);
+ }
+ }
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+ tlvs, tlvs_len);
+ if (tlvs_len > 1500) {
+ /* TODO: better way for handling this */
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR
+ " %u <long response: %u bytes>",
+ MAC2STR(sa), update_indic,
+ (unsigned int) tlvs_len);
+ } else {
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+ MAC2STR(sa), update_indic, buf);
+ os_free(buf);
+ }
+ }
+
+ while (pos < end) {
+ u8 srv_proto, srv_trans_id, status;
+
+ wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+ "length");
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+ status = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+ status);
+
+ 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;
+ }
+
+ wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+ return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query)
+{
+ struct wpabuf *tlvs;
+ u64 ret;
+
+ tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+ if (tlvs == NULL)
+ return 0;
+ wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+ wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+ wpabuf_put_u8(tlvs, version);
+ wpabuf_put_str(tlvs, query);
+ ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+
+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,
+ const struct wpabuf *tlvs)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+ return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+ const char *subelems)
+{
+ u8 *len;
+ const char *pos;
+ int val;
+ int count = 0;
+
+ len = wpabuf_put(tlvs, 2);
+ wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+ wpabuf_put_u8(tlvs, role);
+
+ pos = subelems;
+ while (*pos) {
+ val = atoi(pos);
+ if (val >= 0 && val < 256) {
+ wpabuf_put_u8(tlvs, val);
+ count++;
+ if (count == MAX_WFD_SD_SUBELEMS)
+ break;
+ }
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *role)
+{
+ struct wpabuf *tlvs;
+ u64 ret;
+ const char *subelems;
+ u8 id = 1;
+
+ subelems = os_strchr(role, ' ');
+ if (subelems == NULL)
+ return 0;
+ subelems++;
+
+ tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+ if (tlvs == NULL)
+ return 0;
+
+ if (os_strstr(role, "[source]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+ if (os_strstr(role, "[pri-sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+ if (os_strstr(role, "[sec-sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+ if (os_strstr(role, "[source+sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+ ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+ return p2p_sd_cancel_request(wpa_s->global->p2p,
+ (void *) (uintptr_t) req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+ resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p)
+ p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+ dl_list_del(&bsrv->list);
+ wpabuf_free(bsrv->query);
+ wpabuf_free(bsrv->resp);
+ os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+ dl_list_del(&usrv->list);
+ os_free(usrv->service);
+ os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_srv_bonjour *bsrv, *bn;
+ struct p2p_srv_upnp *usrv, *un;
+
+ dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list)
+ wpas_p2p_srv_bonjour_free(bsrv);
+
+ dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list)
+ wpas_p2p_srv_upnp_free(usrv);
+
+ wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+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)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = os_zalloc(sizeof(*bsrv));
+ if (bsrv == NULL)
+ return -1;
+ bsrv->query = query;
+ bsrv->resp = resp;
+ dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv == NULL)
+ return -1;
+ wpas_p2p_srv_bonjour_free(bsrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+ return 0; /* Already listed */
+ usrv = os_zalloc(sizeof(*usrv));
+ if (usrv == NULL)
+ return -1;
+ usrv->version = version;
+ usrv->service = os_strdup(service);
+ if (usrv->service == NULL) {
+ os_free(usrv);
+ return -1;
+ }
+ dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+ if (usrv == NULL)
+ return -1;
+ wpas_p2p_srv_upnp_free(usrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 5a0af0d..2b40bbf 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -608,35 +608,57 @@
"uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
"no_keep_alive",
/* global configuration parameters */
- "eapol_version", "ap_scan", "disable_scan_offload",
- "fast_reauth", "opensc_engine_path", "pkcs11_engine_path",
- "pkcs11_module_path", "openssl_ciphers",
- "pcsc_reader", "pcsc_pin",
- "driver_param", "dot11RSNAConfigPMKLifetime",
+#ifdef CONFIG_CTRL_IFACE
+ "ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+ "eapol_version", "ap_scan", "bgscan",
+#ifdef CONFIG_MESH
+ "user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+ "disable_scan_offload", "fast_reauth", "opensc_engine_path",
+ "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+ "pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+ "dot11RSNAConfigPMKLifetime",
"dot11RSNAConfigPMKReauthThreshold",
"dot11RSNAConfigSATimeout",
- "update_config", "load_dynamic_eap", "uuid", "device_name",
- "manufacturer", "model_name", "model_number", "serial_number",
- "device_type", "os_version", "config_methods",
- "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type",
+#ifndef CONFIG_NO_CONFIG_WRITE
+ "update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ "load_dynamic_eap",
+#ifdef CONFIG_WPS
+ "uuid", "device_name", "manufacturer", "model_name",
+ "model_number", "serial_number", "device_type", "os_version",
+ "config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ "sec_device_type",
"p2p_listen_reg_class", "p2p_listen_channel",
- "p2p_oper_reg_class", "p2p_oper_channel",
- "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
- "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
- "p2p_no_go_freq",
- "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
- "p2p_go_vht",
- "p2p_ignore_shared_freq", "country", "bss_max_count",
- "bss_expiration_age", "bss_expiration_scan_count",
- "filter_ssids", "filter_rssi", "max_num_sta",
- "disassoc_low_ack", "hs20", "interworking", "hessid",
- "access_network_type", "pbc_in_m1", "autoscan",
- "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey",
- "wps_nfc_dev_pw", "ext_password_backend",
+ "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+ "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+ "p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
+ "p2p_no_go_freq", "p2p_add_cli_chan",
+ "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+ "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+ "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+ "ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+ "country", "bss_max_count", "bss_expiration_age",
+ "bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+ "max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+ "hs20",
+#endif /* CONFIG_HS20 */
+ "interworking", "hessid", "access_network_type", "pbc_in_m1",
+ "autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
+ "wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
- "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
- "ignore_old_scan_res", "freq_list", "external_sim",
- "tdls_external_control", "p2p_search_delay"
+ "sae_groups", "dtim_period", "beacon_int",
+ "ap_vendor_elements", "ignore_old_scan_res", "freq_list",
+ "scan_cur_freq", "sched_scan_interval",
+ "tdls_external_control", "osu_dir", "wowlan_triggers",
+ "p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+ "preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+ "reassoc_same_bss_optim"
};
int i, num_fields = ARRAY_SIZE(fields);
@@ -670,6 +692,74 @@
}
+static char ** wpa_cli_complete_get(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ const char *fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+ "ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+ "eapol_version", "ap_scan",
+#ifdef CONFIG_MESH
+ "user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+ "disable_scan_offload", "fast_reauth", "opensc_engine_path",
+ "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+ "pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+ "dot11RSNAConfigPMKLifetime",
+ "dot11RSNAConfigPMKReauthThreshold",
+ "dot11RSNAConfigSATimeout",
+#ifndef CONFIG_NO_CONFIG_WRITE
+ "update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+#ifdef CONFIG_WPS
+ "device_name", "manufacturer", "model_name", "model_number",
+ "serial_number", "config_methods", "wps_cred_processing",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ "p2p_listen_reg_class", "p2p_listen_channel",
+ "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+ "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+ "p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
+ "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+ "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+ "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+ "ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+ "bss_max_count", "bss_expiration_age",
+ "bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+ "max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+ "hs20",
+#endif /* CONFIG_HS20 */
+ "interworking", "access_network_type", "pbc_in_m1", "autoscan",
+ "wps_nfc_dev_pw_id", "ext_password_backend",
+ "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+ "dtim_period", "beacon_int", "ignore_old_scan_res",
+ "scan_cur_freq", "sched_scan_interval",
+ "tdls_external_control", "osu_dir", "wowlan_triggers",
+ "p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+ "preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+ "reassoc_same_bss_optim"
+ };
+ int i, num_fields = ARRAY_SIZE(fields);
+
+ if (arg == 1) {
+ char **res = os_calloc(num_fields + 1, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; i < num_fields; i++) {
+ res[i] = os_strdup(fields[i]);
+ if (res[i] == NULL)
+ return res;
+ }
+ return res;
+ }
+
+ return NULL;
+}
+
+
static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "LOGOFF");
@@ -2624,7 +2714,7 @@
{ "dump", wpa_cli_cmd_dump, NULL,
cli_cmd_flag_none,
"= dump config variables" },
- { "get", wpa_cli_cmd_get, NULL,
+ { "get", wpa_cli_cmd_get, wpa_cli_complete_get,
cli_cmd_flag_none,
"<name> = get information" },
{ "logon", wpa_cli_cmd_logon, NULL,