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