[PATCH 17/19] P2PS: re-factor p2p_buf_add_service_instance function
Ilan Peer
ilan.peer
Wed Jun 10 01:43:47 PDT 2015
From: Max Stepanov <Max.Stepanov at intel.com>
Add auxiliary functions to write a single advertised service info record
to wpabuf and to find P2PS wildcard hash in a received hash attribute.
Re-factor p2p_buf_add_service_instance function to allow adding new
wildcard types in the future patches.
Signed-off-by: Max Stepanov <Max.Stepanov at intel.com>
Reviewed-by: Ilan Peer <ilan.peer at intel.com>
---
src/p2p/p2p_build.c | 322 ++++++++++++++++++++++++++++++++--------------------
1 file changed, 200 insertions(+), 122 deletions(-)
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 8cffa26..03bd458 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -404,150 +404,228 @@ void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
}
-void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
- u8 hash_count, const u8 *hash,
- struct p2ps_advertisement *adv_list)
+static int p2p_p2ps_wildcard_hash(struct p2p_data *p2p,
+ const u8 *hash, u8 hash_count)
{
- struct p2ps_advertisement *adv;
- struct wpabuf *tmp_buf;
- u8 *tag_len = NULL, *ie_len = NULL;
- size_t svc_len = 0, remaining = 0, total_len = 0;
+ u8 i;
+ const u8 *test;
- if (!adv_list || !hash)
- return;
+ for (i = 0, test = hash; i < hash_count; i++, test += P2PS_HASH_LEN)
+ if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+ return 1;
+ return 0;
+}
- /* Allocate temp buffer, allowing for overflow of 1 instance */
- tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
- if (!tmp_buf)
- return;
- for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
- adv = adv->next) {
- u8 count = hash_count;
- const u8 *test = hash;
-
- while (count--) {
- /* Check for wildcard */
- if (os_memcmp(test, p2p->wild_card_hash,
- P2PS_HASH_LEN) == 0) {
- total_len = MAX_SVC_ADV_LEN + 1;
- goto wild_hash;
- }
+static int p2p_buf_add_service_info(struct wpabuf *buf,
+ struct p2p_data *p2p,
+ u32 adv_id,
+ u16 config_methods,
+ const char *svc_name,
+ u8 **ie_len,
+ u8 **pos,
+ size_t *total_len,
+ u8 *attr_len)
+{
+ size_t svc_len;
+ size_t remaining;
+ size_t info_len;
- if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0)
- goto hash_match;
+ svc_len = os_strlen(svc_name);
+ info_len = sizeof(adv_id) + sizeof(config_methods) +
+ sizeof(u8) + svc_len;
- test += P2PS_HASH_LEN;
- }
+ if (info_len + *total_len > MAX_SVC_ADV_LEN) {
+ p2p_dbg(p2p,
+ "Unsuficient buffer, failed to add adverticed service info");
+ return -1;
+ }
- /* No matches found - Skip this Adv Instance */
- continue;
-
-hash_match:
- if (!tag_len) {
- tag_len = p2p_buf_add_ie_hdr(tmp_buf);
- remaining = 255 - 4;
- if (!ie_len) {
- wpabuf_put_u8(tmp_buf,
- P2P_ATTR_ADVERTISED_SERVICE);
- ie_len = wpabuf_put(tmp_buf, sizeof(u16));
- remaining -= (sizeof(u8) + sizeof(u16));
- }
- }
+ if (svc_len > 255) {
+ p2p_dbg(p2p,
+ "Invalid service name length (%zu bytes), failed to add advertised service info",
+ svc_len);
+ return -1;
+ }
- svc_len = os_strlen(adv->svc_name);
+ if (*ie_len) {
+ int ie_data_len = (*pos - *ie_len) - 1;
- if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) {
- /* Can't fit... return wildcard */
- total_len = MAX_SVC_ADV_LEN + 1;
- break;
+ if (ie_data_len < 0 || ie_data_len > 255) {
+ p2p_dbg(p2p,
+ "Invalid IE length, failed to add adveriticed service info");
+ return -1;
}
+ remaining = 255 - ie_data_len;
+ } else {
+ /* Adding new P2P IE header takes 6 extra bytes:
+ * - 2 byte IE header (1 byte IE id and 1 byte length)
+ * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
+ */
+ *ie_len = p2p_buf_add_ie_hdr(buf);
+ remaining = 255 - 4;
+ }
- if (remaining <= (sizeof(adv->id) +
- sizeof(adv->config_methods))) {
- size_t front = remaining;
- size_t back = (sizeof(adv->id) +
- sizeof(adv->config_methods)) - front;
- u8 holder[sizeof(adv->id) +
- sizeof(adv->config_methods)];
-
- /* This works even if front or back == 0 */
- WPA_PUT_LE32(holder, adv->id);
- WPA_PUT_BE16(&holder[sizeof(adv->id)],
- adv->config_methods);
- wpabuf_put_data(tmp_buf, holder, front);
- p2p_buf_update_ie_hdr(tmp_buf, tag_len);
- tag_len = p2p_buf_add_ie_hdr(tmp_buf);
- wpabuf_put_data(tmp_buf, &holder[front], back);
- remaining = 255 - (sizeof(adv->id) +
- sizeof(adv->config_methods)) - back;
- } else {
- wpabuf_put_le32(tmp_buf, adv->id);
- wpabuf_put_be16(tmp_buf, adv->config_methods);
- remaining -= (sizeof(adv->id) +
- sizeof(adv->config_methods));
- }
+ if (remaining < (sizeof(u32) + sizeof(u16) + sizeof(u8))) {
+ /* split adv_id, config_methods and svc_name_len
+ * between two IEs
+ */
+ size_t front = remaining;
+ size_t back = (sizeof(u32) + sizeof(u16) + sizeof(u8)) - front;
+ u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
- /* We are guaranteed at least one byte for svc_len */
- wpabuf_put_u8(tmp_buf, svc_len);
- remaining -= sizeof(u8);
-
- if (remaining < svc_len) {
- size_t front = remaining;
- size_t back = svc_len - front;
-
- wpabuf_put_data(tmp_buf, adv->svc_name, front);
- p2p_buf_update_ie_hdr(tmp_buf, tag_len);
- tag_len = p2p_buf_add_ie_hdr(tmp_buf);
-
- /* In rare cases, we must split across 3 attributes */
- if (back > 255 - 4) {
- wpabuf_put_data(tmp_buf,
- &adv->svc_name[front], 255 - 4);
- back -= 255 - 4;
- front += 255 - 4;
- p2p_buf_update_ie_hdr(tmp_buf, tag_len);
- tag_len = p2p_buf_add_ie_hdr(tmp_buf);
- }
+ WPA_PUT_LE32(holder, adv_id);
+ WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
+ holder[sizeof(u32) + sizeof(u16)] = svc_len;
- wpabuf_put_data(tmp_buf, &adv->svc_name[front], back);
- remaining = 255 - 4 - back;
- } else {
- wpabuf_put_data(tmp_buf, adv->svc_name, svc_len);
- remaining -= svc_len;
- }
+ if (front)
+ wpabuf_put_data(buf, holder, front);
+
+ p2p_buf_update_ie_hdr(buf, *ie_len);
+ *ie_len = p2p_buf_add_ie_hdr(buf);
- /* adv_id config_methods svc_string */
- total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len;
+ wpabuf_put_data(buf, &holder[front], back);
+ remaining = 255 - 4 -
+ (sizeof(u32) + sizeof(u16) + sizeof(u8)) - back;
+ } else {
+ wpabuf_put_le32(buf, adv_id);
+ wpabuf_put_be16(buf, config_methods);
+ wpabuf_put_u8(buf, svc_len);
+ remaining -= (sizeof(adv_id) +
+ sizeof(config_methods) +
+ sizeof(u8));
}
- if (tag_len)
- p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ if (remaining < svc_len) {
+ /* split svc_name between two or three IEs */
+ size_t front = remaining;
+ size_t back = svc_len - front;
- if (ie_len)
- WPA_PUT_LE16(ie_len, (u16) total_len);
+ if (front)
+ wpabuf_put_data(buf, svc_name, front);
-wild_hash:
- /* If all fit, return matching instances, otherwise the wildcard */
- if (total_len <= MAX_SVC_ADV_LEN) {
- wpabuf_put_buf(buf, tmp_buf);
+ p2p_buf_update_ie_hdr(buf, *ie_len);
+ *ie_len = p2p_buf_add_ie_hdr(buf);
+
+ /* In rare cases, we must split across 3 attributes */
+ if (back > 255 - 4) {
+ wpabuf_put_data(buf, &svc_name[front], 255 - 4);
+ back -= 255 - 4;
+ front += 255 - 4;
+ p2p_buf_update_ie_hdr(buf, *ie_len);
+ *ie_len = p2p_buf_add_ie_hdr(buf);
+ }
+
+ wpabuf_put_data(buf, &svc_name[front], back);
+ remaining = 255 - 4 - back;
} else {
- char *wild_card = P2PS_WILD_HASH_STR;
- u8 wild_len;
+ wpabuf_put_data(buf, svc_name, svc_len);
+ remaining -= svc_len;
+ }
- /* Insert wildcard instance */
- tag_len = p2p_buf_add_ie_hdr(buf);
- wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE);
- ie_len = wpabuf_put(buf, sizeof(u16));
+ p2p_buf_update_ie_hdr(buf, *ie_len);
+
+ /* set *ie_len to NULL if a new IE has to be added on the next call */
+ if (!remaining)
+ *ie_len = NULL;
+
+ /* set *pos to point to the next byte to update */
+ *pos = wpabuf_put(buf, 0);
+
+ *total_len += info_len;
+ WPA_PUT_LE16(attr_len, (u16) *total_len);
+ return 0;
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+ u8 hash_count, const u8 *hash,
+ struct p2ps_advertisement *adv_list)
+{
+ struct p2ps_advertisement *adv;
+ int p2ps_wildcard;
+ size_t total_len;
+ struct wpabuf *tmp_buf = NULL;
+ u8 *pos, *attr_len, *ie_len = NULL;
+
+ if (!adv_list || !hash || !hash_count)
+ return;
+
+ p2ps_wildcard = p2p_p2ps_wildcard_hash(p2p, hash, hash_count);
+ if (p2ps_wildcard)
+ goto end;
- wild_len = (u8) os_strlen(wild_card);
- wpabuf_put_le32(buf, 0);
- wpabuf_put_be16(buf, 0);
- wpabuf_put_u8(buf, wild_len);
- wpabuf_put_data(buf, wild_card, wild_len);
+ /* Allocate temp buffer, allowing for overflow of 1 instance */
+ tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+ if (!tmp_buf)
+ return;
+
+ /* An attribute data can be split into a number of IEs,
+ * start with a first IE and an attribute headers here.
+ */
+ ie_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+ total_len = 0;
+
+ wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
+ attr_len = wpabuf_put(tmp_buf, sizeof(u16));
+ WPA_PUT_LE16(attr_len, (u16)total_len);
+ p2p_buf_update_ie_hdr(tmp_buf, ie_len);
+ pos = wpabuf_put(tmp_buf, 0);
+
+ /* add advertised service info of matching services */
+ for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+ adv = adv->next) {
+ const u8 *test;
+ u8 i;
+
+ for (i = 0, test = hash; i < hash_count;
+ i++, test += P2PS_HASH_LEN) {
+ /* exact name hash match */
+ if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
+ p2p_buf_add_service_info(tmp_buf, p2p,
+ adv->id,
+ adv->config_methods,
+ adv->svc_name,
+ &ie_len, &pos,
+ &total_len,
+ attr_len)) {
+ /* We cannot return all services matching
+ * a probe request hash attibute. In this
+ * case drop currently written entries and
+ * return only a single wildcard advertised
+ * service info in a probe response.
+ */
+ p2ps_wildcard = 1;
+ goto end;
+ }
+ }
+ }
- WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len);
- p2p_buf_update_ie_hdr(buf, tag_len);
+end:
+ if (p2ps_wildcard) {
+ /* Add the attibute with P2PS wildcard if either a
+ * wildcard hash was present in a probe request hash
+ * attribute or we failed to add at least one matching
+ * advertisement.
+ */
+ ie_len = p2p_buf_add_ie_hdr(buf);
+ wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE);
+ attr_len = wpabuf_put(buf, sizeof(u16));
+ pos = wpabuf_put(buf, 0);
+ total_len = 0;
+
+ p2p_buf_add_service_info(buf, p2p,
+ 0, 0, P2PS_WILD_HASH_STR,
+ &ie_len, &pos, &total_len, attr_len);
+ } else if (tmp_buf) {
+ /* TODO: an empty attribute is returned if a device
+ * not able to match advertised services.
+ * The WFDS spec defines that if the device is not a GO
+ * it shall not send a P2Ps related Probe Response frame
+ * in this case.
+ */
+ wpabuf_put_buf(buf, tmp_buf);
}
wpabuf_free(tmp_buf);
--
1.9.1
More information about the Hostap
mailing list