1252190Srpaulo/* 2252190Srpaulo * Generic advertisement service (GAS) (IEEE 802.11u) 3252190Srpaulo * Copyright (c) 2009, Atheros Communications 4252190Srpaulo * Copyright (c) 2011-2012, Qualcomm Atheros 5252190Srpaulo * 6252190Srpaulo * This software may be distributed under the terms of the BSD license. 7252190Srpaulo * See README for more details. 8252190Srpaulo */ 9252190Srpaulo 10252190Srpaulo#include "includes.h" 11252190Srpaulo 12252190Srpaulo#include "common.h" 13252190Srpaulo#include "ieee802_11_defs.h" 14252190Srpaulo#include "gas.h" 15252190Srpaulo 16252190Srpaulo 17252190Srpaulostatic struct wpabuf * 18252190Srpaulogas_build_req(u8 action, u8 dialog_token, size_t size) 19252190Srpaulo{ 20252190Srpaulo struct wpabuf *buf; 21252190Srpaulo 22252190Srpaulo buf = wpabuf_alloc(100 + size); 23252190Srpaulo if (buf == NULL) 24252190Srpaulo return NULL; 25252190Srpaulo 26252190Srpaulo wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); 27252190Srpaulo wpabuf_put_u8(buf, action); 28252190Srpaulo wpabuf_put_u8(buf, dialog_token); 29252190Srpaulo 30252190Srpaulo return buf; 31252190Srpaulo} 32252190Srpaulo 33252190Srpaulo 34252190Srpaulostruct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) 35252190Srpaulo{ 36252190Srpaulo return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, 37252190Srpaulo size); 38252190Srpaulo} 39252190Srpaulo 40252190Srpaulo 41252190Srpaulostruct wpabuf * gas_build_comeback_req(u8 dialog_token) 42252190Srpaulo{ 43252190Srpaulo return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); 44252190Srpaulo} 45252190Srpaulo 46252190Srpaulo 47252190Srpaulostatic struct wpabuf * 48252190Srpaulogas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, 49252190Srpaulo u8 more, u16 comeback_delay, size_t size) 50252190Srpaulo{ 51252190Srpaulo struct wpabuf *buf; 52252190Srpaulo 53252190Srpaulo buf = wpabuf_alloc(100 + size); 54252190Srpaulo if (buf == NULL) 55252190Srpaulo return NULL; 56252190Srpaulo 57252190Srpaulo wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); 58252190Srpaulo wpabuf_put_u8(buf, action); 59252190Srpaulo wpabuf_put_u8(buf, dialog_token); 60252190Srpaulo wpabuf_put_le16(buf, status_code); 61252190Srpaulo if (action == WLAN_PA_GAS_COMEBACK_RESP) 62252190Srpaulo wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); 63252190Srpaulo wpabuf_put_le16(buf, comeback_delay); 64252190Srpaulo 65252190Srpaulo return buf; 66252190Srpaulo} 67252190Srpaulo 68252190Srpaulo 69252190Srpaulostruct wpabuf * 70252190Srpaulogas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, 71252190Srpaulo size_t size) 72252190Srpaulo{ 73252190Srpaulo return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, 74252190Srpaulo status_code, 0, 0, comeback_delay, size); 75252190Srpaulo} 76252190Srpaulo 77252190Srpaulo 78346981Scystruct wpabuf * 79252190Srpaulogas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, 80252190Srpaulo u16 comeback_delay, size_t size) 81252190Srpaulo{ 82252190Srpaulo return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, 83252190Srpaulo status_code, frag_id, more, comeback_delay, 84252190Srpaulo size); 85252190Srpaulo} 86252190Srpaulo 87252190Srpaulo 88252190Srpaulo/** 89252190Srpaulo * gas_add_adv_proto_anqp - Add an Advertisement Protocol element 90252190Srpaulo * @buf: Buffer to which the element is added 91252190Srpaulo * @query_resp_len_limit: Query Response Length Limit in units of 256 octets 92252190Srpaulo * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) 93252190Srpaulo * 94252190Srpaulo * 95252190Srpaulo * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means 96252190Srpaulo * that the maximum limit is determined by the maximum allowable number of 97252190Srpaulo * fragments in the GAS Query Response Fragment ID. 98252190Srpaulo */ 99252190Srpaulostatic void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, 100252190Srpaulo u8 pame_bi) 101252190Srpaulo{ 102252190Srpaulo /* Advertisement Protocol IE */ 103252190Srpaulo wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 104252190Srpaulo wpabuf_put_u8(buf, 2); /* Length */ 105252190Srpaulo wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | 106252190Srpaulo (pame_bi ? 0x80 : 0)); 107252190Srpaulo /* Advertisement Protocol */ 108252190Srpaulo wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); 109252190Srpaulo} 110252190Srpaulo 111252190Srpaulo 112252190Srpaulostruct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) 113252190Srpaulo{ 114252190Srpaulo struct wpabuf *buf; 115252190Srpaulo 116252190Srpaulo buf = gas_build_initial_req(dialog_token, 4 + size); 117252190Srpaulo if (buf == NULL) 118252190Srpaulo return NULL; 119252190Srpaulo 120252190Srpaulo gas_add_adv_proto_anqp(buf, 0, 0); 121252190Srpaulo 122252190Srpaulo wpabuf_put(buf, 2); /* Query Request Length to be filled */ 123252190Srpaulo 124252190Srpaulo return buf; 125252190Srpaulo} 126252190Srpaulo 127252190Srpaulo 128252190Srpaulostruct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, 129252190Srpaulo u16 comeback_delay, size_t size) 130252190Srpaulo{ 131252190Srpaulo struct wpabuf *buf; 132252190Srpaulo 133252190Srpaulo buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, 134252190Srpaulo 4 + size); 135252190Srpaulo if (buf == NULL) 136252190Srpaulo return NULL; 137252190Srpaulo 138252190Srpaulo gas_add_adv_proto_anqp(buf, 0x7f, 0); 139252190Srpaulo 140252190Srpaulo wpabuf_put(buf, 2); /* Query Response Length to be filled */ 141252190Srpaulo 142252190Srpaulo return buf; 143252190Srpaulo} 144252190Srpaulo 145252190Srpaulo 146252190Srpaulostruct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, 147252190Srpaulo u16 status_code, 148252190Srpaulo u16 comeback_delay, 149252190Srpaulo struct wpabuf *payload) 150252190Srpaulo{ 151252190Srpaulo struct wpabuf *buf; 152252190Srpaulo 153252190Srpaulo buf = gas_anqp_build_initial_resp(dialog_token, status_code, 154252190Srpaulo comeback_delay, 155252190Srpaulo payload ? wpabuf_len(payload) : 0); 156252190Srpaulo if (buf == NULL) 157252190Srpaulo return NULL; 158252190Srpaulo 159252190Srpaulo if (payload) 160252190Srpaulo wpabuf_put_buf(buf, payload); 161252190Srpaulo 162252190Srpaulo gas_anqp_set_len(buf); 163252190Srpaulo 164252190Srpaulo return buf; 165252190Srpaulo} 166252190Srpaulo 167252190Srpaulo 168252190Srpaulostruct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, 169252190Srpaulo u8 frag_id, u8 more, 170252190Srpaulo u16 comeback_delay, size_t size) 171252190Srpaulo{ 172252190Srpaulo struct wpabuf *buf; 173252190Srpaulo 174252190Srpaulo buf = gas_build_comeback_resp(dialog_token, status_code, 175252190Srpaulo frag_id, more, comeback_delay, 4 + size); 176252190Srpaulo if (buf == NULL) 177252190Srpaulo return NULL; 178252190Srpaulo 179252190Srpaulo gas_add_adv_proto_anqp(buf, 0x7f, 0); 180252190Srpaulo 181252190Srpaulo wpabuf_put(buf, 2); /* Query Response Length to be filled */ 182252190Srpaulo 183252190Srpaulo return buf; 184252190Srpaulo} 185252190Srpaulo 186252190Srpaulo 187252190Srpaulostruct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, 188252190Srpaulo u16 status_code, 189252190Srpaulo u8 frag_id, u8 more, 190252190Srpaulo u16 comeback_delay, 191252190Srpaulo struct wpabuf *payload) 192252190Srpaulo{ 193252190Srpaulo struct wpabuf *buf; 194252190Srpaulo 195252190Srpaulo buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, 196252190Srpaulo more, comeback_delay, 197252190Srpaulo payload ? wpabuf_len(payload) : 0); 198252190Srpaulo if (buf == NULL) 199252190Srpaulo return NULL; 200252190Srpaulo 201252190Srpaulo if (payload) 202252190Srpaulo wpabuf_put_buf(buf, payload); 203252190Srpaulo 204252190Srpaulo gas_anqp_set_len(buf); 205252190Srpaulo 206252190Srpaulo return buf; 207252190Srpaulo} 208252190Srpaulo 209252190Srpaulo 210252190Srpaulo/** 211252190Srpaulo * gas_anqp_set_len - Set Query Request/Response Length 212252190Srpaulo * @buf: GAS message 213252190Srpaulo * 214252190Srpaulo * This function is used to update the Query Request/Response Length field once 215252190Srpaulo * the payload has been filled. 216252190Srpaulo */ 217252190Srpaulovoid gas_anqp_set_len(struct wpabuf *buf) 218252190Srpaulo{ 219252190Srpaulo u8 action; 220252190Srpaulo size_t offset; 221252190Srpaulo u8 *len; 222252190Srpaulo 223252190Srpaulo if (buf == NULL || wpabuf_len(buf) < 2) 224252190Srpaulo return; 225252190Srpaulo 226252190Srpaulo action = *(wpabuf_head_u8(buf) + 1); 227252190Srpaulo switch (action) { 228252190Srpaulo case WLAN_PA_GAS_INITIAL_REQ: 229252190Srpaulo offset = 3 + 4; 230252190Srpaulo break; 231252190Srpaulo case WLAN_PA_GAS_INITIAL_RESP: 232252190Srpaulo offset = 7 + 4; 233252190Srpaulo break; 234252190Srpaulo case WLAN_PA_GAS_COMEBACK_RESP: 235252190Srpaulo offset = 8 + 4; 236252190Srpaulo break; 237252190Srpaulo default: 238252190Srpaulo return; 239252190Srpaulo } 240252190Srpaulo 241252190Srpaulo if (wpabuf_len(buf) < offset + 2) 242252190Srpaulo return; 243252190Srpaulo 244252190Srpaulo len = wpabuf_mhead_u8(buf) + offset; 245252190Srpaulo WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); 246252190Srpaulo} 247252190Srpaulo 248252190Srpaulo 249252190Srpaulo/** 250252190Srpaulo * gas_anqp_add_element - Add ANQP element header 251252190Srpaulo * @buf: GAS message 252252190Srpaulo * @info_id: ANQP Info ID 253252190Srpaulo * Returns: Pointer to the Length field for gas_anqp_set_element_len() 254252190Srpaulo */ 255252190Srpaulou8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) 256252190Srpaulo{ 257252190Srpaulo wpabuf_put_le16(buf, info_id); 258252190Srpaulo return wpabuf_put(buf, 2); /* Length to be filled */ 259252190Srpaulo} 260252190Srpaulo 261252190Srpaulo 262252190Srpaulo/** 263252190Srpaulo * gas_anqp_set_element_len - Update ANQP element Length field 264252190Srpaulo * @buf: GAS message 265252190Srpaulo * @len_pos: Length field position from gas_anqp_add_element() 266252190Srpaulo * 267252190Srpaulo * This function is called after the ANQP element payload has been added to the 268252190Srpaulo * buffer. 269252190Srpaulo */ 270252190Srpaulovoid gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) 271252190Srpaulo{ 272252190Srpaulo WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); 273252190Srpaulo} 274