1/* 2 * Generic advertisement service (GAS) (IEEE 802.11u) 3 * Copyright (c) 2009, Atheros Communications 4 * Copyright (c) 2011-2012, Qualcomm Atheros 5 * 6 * This software may be distributed under the terms of the BSD license. 7 * See README for more details. 8 */ 9 10#include "includes.h" 11 12#include "common.h" 13#include "ieee802_11_defs.h" 14#include "gas.h" 15 16 17static struct wpabuf * 18gas_build_req(u8 action, u8 dialog_token, size_t size) 19{ 20 struct wpabuf *buf; 21 22 buf = wpabuf_alloc(100 + size); 23 if (buf == NULL) 24 return NULL; 25 26 wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); 27 wpabuf_put_u8(buf, action); 28 wpabuf_put_u8(buf, dialog_token); 29 30 return buf; 31} 32 33 34struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) 35{ 36 return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, 37 size); 38} 39 40 41struct wpabuf * gas_build_comeback_req(u8 dialog_token) 42{ 43 return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); 44} 45 46 47static struct wpabuf * 48gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, 49 u8 more, u16 comeback_delay, size_t size) 50{ 51 struct wpabuf *buf; 52 53 buf = wpabuf_alloc(100 + size); 54 if (buf == NULL) 55 return NULL; 56 57 wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); 58 wpabuf_put_u8(buf, action); 59 wpabuf_put_u8(buf, dialog_token); 60 wpabuf_put_le16(buf, status_code); 61 if (action == WLAN_PA_GAS_COMEBACK_RESP) 62 wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); 63 wpabuf_put_le16(buf, comeback_delay); 64 65 return buf; 66} 67 68 69struct wpabuf * 70gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, 71 size_t size) 72{ 73 return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, 74 status_code, 0, 0, comeback_delay, size); 75} 76 77 78static struct wpabuf * 79gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, 80 u16 comeback_delay, size_t size) 81{ 82 return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, 83 status_code, frag_id, more, comeback_delay, 84 size); 85} 86 87 88/** 89 * gas_add_adv_proto_anqp - Add an Advertisement Protocol element 90 * @buf: Buffer to which the element is added 91 * @query_resp_len_limit: Query Response Length Limit in units of 256 octets 92 * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) 93 * 94 * 95 * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means 96 * that the maximum limit is determined by the maximum allowable number of 97 * fragments in the GAS Query Response Fragment ID. 98 */ 99static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, 100 u8 pame_bi) 101{ 102 /* Advertisement Protocol IE */ 103 wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 104 wpabuf_put_u8(buf, 2); /* Length */ 105 wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | 106 (pame_bi ? 0x80 : 0)); 107 /* Advertisement Protocol */ 108 wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); 109} 110 111 112struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) 113{ 114 struct wpabuf *buf; 115 116 buf = gas_build_initial_req(dialog_token, 4 + size); 117 if (buf == NULL) 118 return NULL; 119 120 gas_add_adv_proto_anqp(buf, 0, 0); 121 122 wpabuf_put(buf, 2); /* Query Request Length to be filled */ 123 124 return buf; 125} 126 127 128struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, 129 u16 comeback_delay, size_t size) 130{ 131 struct wpabuf *buf; 132 133 buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, 134 4 + size); 135 if (buf == NULL) 136 return NULL; 137 138 gas_add_adv_proto_anqp(buf, 0x7f, 0); 139 140 wpabuf_put(buf, 2); /* Query Response Length to be filled */ 141 142 return buf; 143} 144 145 146struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, 147 u16 status_code, 148 u16 comeback_delay, 149 struct wpabuf *payload) 150{ 151 struct wpabuf *buf; 152 153 buf = gas_anqp_build_initial_resp(dialog_token, status_code, 154 comeback_delay, 155 payload ? wpabuf_len(payload) : 0); 156 if (buf == NULL) 157 return NULL; 158 159 if (payload) 160 wpabuf_put_buf(buf, payload); 161 162 gas_anqp_set_len(buf); 163 164 return buf; 165} 166 167 168struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, 169 u8 frag_id, u8 more, 170 u16 comeback_delay, size_t size) 171{ 172 struct wpabuf *buf; 173 174 buf = gas_build_comeback_resp(dialog_token, status_code, 175 frag_id, more, comeback_delay, 4 + size); 176 if (buf == NULL) 177 return NULL; 178 179 gas_add_adv_proto_anqp(buf, 0x7f, 0); 180 181 wpabuf_put(buf, 2); /* Query Response Length to be filled */ 182 183 return buf; 184} 185 186 187struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, 188 u16 status_code, 189 u8 frag_id, u8 more, 190 u16 comeback_delay, 191 struct wpabuf *payload) 192{ 193 struct wpabuf *buf; 194 195 buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, 196 more, comeback_delay, 197 payload ? wpabuf_len(payload) : 0); 198 if (buf == NULL) 199 return NULL; 200 201 if (payload) 202 wpabuf_put_buf(buf, payload); 203 204 gas_anqp_set_len(buf); 205 206 return buf; 207} 208 209 210/** 211 * gas_anqp_set_len - Set Query Request/Response Length 212 * @buf: GAS message 213 * 214 * This function is used to update the Query Request/Response Length field once 215 * the payload has been filled. 216 */ 217void gas_anqp_set_len(struct wpabuf *buf) 218{ 219 u8 action; 220 size_t offset; 221 u8 *len; 222 223 if (buf == NULL || wpabuf_len(buf) < 2) 224 return; 225 226 action = *(wpabuf_head_u8(buf) + 1); 227 switch (action) { 228 case WLAN_PA_GAS_INITIAL_REQ: 229 offset = 3 + 4; 230 break; 231 case WLAN_PA_GAS_INITIAL_RESP: 232 offset = 7 + 4; 233 break; 234 case WLAN_PA_GAS_COMEBACK_RESP: 235 offset = 8 + 4; 236 break; 237 default: 238 return; 239 } 240 241 if (wpabuf_len(buf) < offset + 2) 242 return; 243 244 len = wpabuf_mhead_u8(buf) + offset; 245 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); 246} 247 248 249/** 250 * gas_anqp_add_element - Add ANQP element header 251 * @buf: GAS message 252 * @info_id: ANQP Info ID 253 * Returns: Pointer to the Length field for gas_anqp_set_element_len() 254 */ 255u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) 256{ 257 wpabuf_put_le16(buf, info_id); 258 return wpabuf_put(buf, 2); /* Length to be filled */ 259} 260 261 262/** 263 * gas_anqp_set_element_len - Update ANQP element Length field 264 * @buf: GAS message 265 * @len_pos: Length field position from gas_anqp_add_element() 266 * 267 * This function is called after the ANQP element payload has been added to the 268 * buffer. 269 */ 270void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) 271{ 272 WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); 273} 274