Home | History | Annotate | Download | only in common
      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