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 17 static struct wpabuf * 18 gas_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 34 struct 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 41 struct 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 47 static struct wpabuf * 48 gas_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 69 struct wpabuf * 70 gas_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 78 struct wpabuf * 79 gas_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 */ 99 static 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 112 struct 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 128 struct 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 146 struct 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 168 struct 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 187 struct 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 */ 217 void 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 */ 255 u8 * 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 */ 270 void 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