From 0c840c33f7abf5cdba060e5a9bcd4061b906d9a9 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 27 Sep 2011 17:28:46 +0300 Subject: [PATCH] Move GAS/ANQP build routines to a separate file from P2P GAS/ANQP is a generic protocol and in no way specific to P2P, so move routines used to build GAS/ANQP frames to a separate file that can be shared for other uses than just P2P service discovery. --- src/common/gas.c | 279 ++++++++++++++++++++++++++++++++++++++++ src/common/gas.h | 40 ++++++ src/p2p/p2p_sd.c | 99 +++----------- wpa_supplicant/Makefile | 5 + 4 files changed, 341 insertions(+), 82 deletions(-) create mode 100644 src/common/gas.c create mode 100644 src/common/gas.h diff --git a/src/common/gas.c b/src/common/gas.c new file mode 100644 index 000000000..c9f6c46f1 --- /dev/null +++ b/src/common/gas.c @@ -0,0 +1,279 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "gas.h" + + +static struct wpabuf * +gas_build_req(u8 action, u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + + return buf; +} + + +static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +{ + return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, + size); +} + + +struct wpabuf * gas_build_comeback_req(u8 dialog_token) +{ + return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); +} + + +static struct wpabuf * +gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, + u8 more, u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, status_code); + if (action == WLAN_PA_GAS_COMEBACK_RESP) + wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); + wpabuf_put_le16(buf, comeback_delay); + + return buf; +} + + +static struct wpabuf * +gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, + size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, + status_code, 0, 0, comeback_delay, size); +} + + +static struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, + status_code, frag_id, more, comeback_delay, + size); +} + + +/** + * gas_add_adv_proto_anqp - Add an Advertisement Protocol element + * @buf: Buffer to which the element is added + * @query_resp_len_limit: Query Response Length Limit in units of 256 octets + * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) + * + * + * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means + * that the maximum limit is determined by the maximum allowable number of + * fragments in the GAS Query Response Fragment ID. + */ +static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, + u8 pame_bi) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 2); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + /* Advertisement Protocol */ + wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); +} + + +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_req(dialog_token, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0, 0); + + wpabuf_put(buf, 2); /* Query Request Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, + 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_comeback_resp(dialog_token, status_code, + frag_id, more, comeback_delay, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +/** + * gas_anqp_set_len - Set Query Request/Response Length + * @buf: GAS message + * + * This function is used to update the Query Request/Response Length field once + * the payload has been filled. + */ +void gas_anqp_set_len(struct wpabuf *buf) +{ + u8 action; + size_t offset; + u8 *len; + + if (buf == NULL || wpabuf_len(buf) < 2) + return; + + action = *(wpabuf_head_u8(buf) + 1); + switch (action) { + case WLAN_PA_GAS_INITIAL_REQ: + offset = 3 + 4; + break; + case WLAN_PA_GAS_INITIAL_RESP: + offset = 7 + 4; + break; + case WLAN_PA_GAS_COMEBACK_RESP: + offset = 8 + 4; + break; + default: + return; + } + + if (wpabuf_len(buf) < offset + 2) + return; + + len = wpabuf_mhead_u8(buf) + offset; + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +/** + * gas_anqp_add_element - Add ANQP element header + * @buf: GAS message + * @info_id: ANQP Info ID + * Returns: Pointer to the Length field for gas_anqp_set_element_len() + */ +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) +{ + wpabuf_put_le16(buf, info_id); + return wpabuf_put(buf, 2); /* Length to be filled */ +} + + +/** + * gas_anqp_set_element_len - Update ANQP element Length field + * @buf: GAS message + * @len_pos: Length field position from gas_anqp_add_element() + * + * This function is called after the ANQP element payload has been added to the + * buffer. + */ +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) +{ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); +} diff --git a/src/common/gas.h b/src/common/gas.h new file mode 100644 index 000000000..4a498004a --- /dev/null +++ b/src/common/gas.h @@ -0,0 +1,40 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef GAS_H +#define GAS_H + +struct wpabuf * gas_build_comeback_req(u8 dialog_token); +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload); +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload); +void gas_anqp_set_len(struct wpabuf *buf); + +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); + +#endif /* GAS_H */ diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index 1c3738c00..43b3a61c0 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -16,6 +16,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/gas.h" #include "p2p_i.h" #include "p2p.h" @@ -90,52 +91,21 @@ static struct wpabuf * p2p_build_sd_query(u16 update_indic, struct wpabuf *tlvs) { struct wpabuf *buf; - u8 *len_pos, *len_pos2; + u8 *len_pos; - buf = wpabuf_alloc(1000 + wpabuf_len(tlvs)); + buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs)); if (buf == NULL) return NULL; - wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); - wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ); - wpabuf_put_u8(buf, 0); /* Dialog Token */ - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 2); /* Length */ - wpabuf_put_u8(buf, 0); /* QueryRespLenLimit | PAME-BI */ - /* Advertisement Protocol */ - wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); - - /* Query Request */ - len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */ - /* ANQP Query Request Frame */ - wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ - len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); - WPA_PUT_LE16(len_pos2, (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2); - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); - - return buf; -} - - -static struct wpabuf * p2p_build_gas_comeback_req(u8 dialog_token) -{ - struct wpabuf *buf; - - buf = wpabuf_alloc(3); - if (buf == NULL) - return NULL; - - wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); - wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_REQ); - wpabuf_put_u8(buf, dialog_token); + gas_anqp_set_len(buf); return buf; } @@ -146,7 +116,7 @@ static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, { struct wpabuf *req; - req = p2p_build_gas_comeback_req(dialog_token); + req = gas_build_comeback_req(dialog_token); if (req == NULL) return; @@ -166,43 +136,26 @@ static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, const struct wpabuf *tlvs) { struct wpabuf *buf; - u8 *len_pos, *len_pos2; + u8 *len_pos; - buf = wpabuf_alloc(1000 + (tlvs ? wpabuf_len(tlvs) : 0)); + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + 100 + (tlvs ? wpabuf_len(tlvs) : 0)); if (buf == NULL) return NULL; - wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); - wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP); - wpabuf_put_u8(buf, dialog_token); - wpabuf_put_le16(buf, status_code); - wpabuf_put_le16(buf, comeback_delay); - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 2); /* Length */ - wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */ - /* Advertisement Protocol */ - wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); - - /* Query Response */ - len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */ - if (tlvs) { /* ANQP Query Response Frame */ - wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ - len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, P2P_OUI_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); wpabuf_put_buf(buf, tlvs); - - WPA_PUT_LE16(len_pos2, - (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2); + gas_anqp_set_element_len(buf, len_pos); } - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); + gas_anqp_set_len(buf); return buf; } @@ -216,29 +169,12 @@ static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, u16 total_len) { struct wpabuf *buf; - u8 *len_pos; - buf = wpabuf_alloc(1000 + len); + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, 0, 100 + len); if (buf == NULL) return NULL; - wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); - wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_RESP); - wpabuf_put_u8(buf, dialog_token); - wpabuf_put_le16(buf, status_code); - wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); - wpabuf_put_le16(buf, 0); /* Comeback Delay */ - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 2); /* Length */ - wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */ - /* Advertisement Protocol */ - wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); - - /* Query Response */ - len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */ - if (frag_id == 0) { /* ANQP Query Response Frame */ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ @@ -250,8 +186,7 @@ static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, } wpabuf_put_data(buf, data, len); - - WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); + gas_anqp_set_len(buf); return buf; } diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 78ce532bf..f28632b00 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -201,6 +201,7 @@ OBJS += ../src/p2p/p2p_dev_disc.o OBJS += ../src/p2p/p2p_group.o OBJS += ../src/ap/p2p_hostapd.o CFLAGS += -DCONFIG_P2P +NEED_GAS=y NEED_80211_COMMON=y ifdef CONFIG_P2P_STRICT CFLAGS += -DCONFIG_P2P_STRICT @@ -1282,6 +1283,10 @@ CFLAGS += -DCONFIG_BGSCAN OBJS += bgscan.o endif +ifdef NEED_GAS +OBJS += ../src/common/gas.o +endif + OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o ifdef CONFIG_AUTHENTICATOR