gas.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. #include "includes.h"
  16. #include "common.h"
  17. #include "ieee802_11_defs.h"
  18. #include "gas.h"
  19. static struct wpabuf *
  20. gas_build_req(u8 action, u8 dialog_token, size_t size)
  21. {
  22. struct wpabuf *buf;
  23. buf = wpabuf_alloc(100 + size);
  24. if (buf == NULL)
  25. return NULL;
  26. wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
  27. wpabuf_put_u8(buf, action);
  28. wpabuf_put_u8(buf, dialog_token);
  29. return buf;
  30. }
  31. static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
  32. {
  33. return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
  34. size);
  35. }
  36. struct wpabuf * gas_build_comeback_req(u8 dialog_token)
  37. {
  38. return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
  39. }
  40. static struct wpabuf *
  41. gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
  42. u8 more, u16 comeback_delay, size_t size)
  43. {
  44. struct wpabuf *buf;
  45. buf = wpabuf_alloc(100 + size);
  46. if (buf == NULL)
  47. return NULL;
  48. wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
  49. wpabuf_put_u8(buf, action);
  50. wpabuf_put_u8(buf, dialog_token);
  51. wpabuf_put_le16(buf, status_code);
  52. if (action == WLAN_PA_GAS_COMEBACK_RESP)
  53. wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
  54. wpabuf_put_le16(buf, comeback_delay);
  55. return buf;
  56. }
  57. static struct wpabuf *
  58. gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
  59. size_t size)
  60. {
  61. return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
  62. status_code, 0, 0, comeback_delay, size);
  63. }
  64. static struct wpabuf *
  65. gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
  66. u16 comeback_delay, size_t size)
  67. {
  68. return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
  69. status_code, frag_id, more, comeback_delay,
  70. size);
  71. }
  72. /**
  73. * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
  74. * @buf: Buffer to which the element is added
  75. * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
  76. * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
  77. *
  78. *
  79. * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
  80. * that the maximum limit is determined by the maximum allowable number of
  81. * fragments in the GAS Query Response Fragment ID.
  82. */
  83. static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
  84. u8 pame_bi)
  85. {
  86. /* Advertisement Protocol IE */
  87. wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
  88. wpabuf_put_u8(buf, 2); /* Length */
  89. wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
  90. (pame_bi ? 0x80 : 0));
  91. /* Advertisement Protocol */
  92. wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
  93. }
  94. struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
  95. {
  96. struct wpabuf *buf;
  97. buf = gas_build_initial_req(dialog_token, 4 + size);
  98. if (buf == NULL)
  99. return NULL;
  100. gas_add_adv_proto_anqp(buf, 0, 0);
  101. wpabuf_put(buf, 2); /* Query Request Length to be filled */
  102. return buf;
  103. }
  104. struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
  105. u16 comeback_delay, size_t size)
  106. {
  107. struct wpabuf *buf;
  108. buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
  109. 4 + size);
  110. if (buf == NULL)
  111. return NULL;
  112. gas_add_adv_proto_anqp(buf, 0x7f, 0);
  113. wpabuf_put(buf, 2); /* Query Response Length to be filled */
  114. return buf;
  115. }
  116. struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
  117. u16 status_code,
  118. u16 comeback_delay,
  119. struct wpabuf *payload)
  120. {
  121. struct wpabuf *buf;
  122. buf = gas_anqp_build_initial_resp(dialog_token, status_code,
  123. comeback_delay,
  124. payload ? wpabuf_len(payload) : 0);
  125. if (buf == NULL)
  126. return NULL;
  127. if (payload)
  128. wpabuf_put_buf(buf, payload);
  129. gas_anqp_set_len(buf);
  130. return buf;
  131. }
  132. struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
  133. u8 frag_id, u8 more,
  134. u16 comeback_delay, size_t size)
  135. {
  136. struct wpabuf *buf;
  137. buf = gas_build_comeback_resp(dialog_token, status_code,
  138. frag_id, more, comeback_delay, 4 + size);
  139. if (buf == NULL)
  140. return NULL;
  141. gas_add_adv_proto_anqp(buf, 0x7f, 0);
  142. wpabuf_put(buf, 2); /* Query Response Length to be filled */
  143. return buf;
  144. }
  145. struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
  146. u16 status_code,
  147. u8 frag_id, u8 more,
  148. u16 comeback_delay,
  149. struct wpabuf *payload)
  150. {
  151. struct wpabuf *buf;
  152. buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
  153. more, comeback_delay,
  154. payload ? wpabuf_len(payload) : 0);
  155. if (buf == NULL)
  156. return NULL;
  157. if (payload)
  158. wpabuf_put_buf(buf, payload);
  159. gas_anqp_set_len(buf);
  160. return buf;
  161. }
  162. /**
  163. * gas_anqp_set_len - Set Query Request/Response Length
  164. * @buf: GAS message
  165. *
  166. * This function is used to update the Query Request/Response Length field once
  167. * the payload has been filled.
  168. */
  169. void gas_anqp_set_len(struct wpabuf *buf)
  170. {
  171. u8 action;
  172. size_t offset;
  173. u8 *len;
  174. if (buf == NULL || wpabuf_len(buf) < 2)
  175. return;
  176. action = *(wpabuf_head_u8(buf) + 1);
  177. switch (action) {
  178. case WLAN_PA_GAS_INITIAL_REQ:
  179. offset = 3 + 4;
  180. break;
  181. case WLAN_PA_GAS_INITIAL_RESP:
  182. offset = 7 + 4;
  183. break;
  184. case WLAN_PA_GAS_COMEBACK_RESP:
  185. offset = 8 + 4;
  186. break;
  187. default:
  188. return;
  189. }
  190. if (wpabuf_len(buf) < offset + 2)
  191. return;
  192. len = wpabuf_mhead_u8(buf) + offset;
  193. WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
  194. }
  195. /**
  196. * gas_anqp_add_element - Add ANQP element header
  197. * @buf: GAS message
  198. * @info_id: ANQP Info ID
  199. * Returns: Pointer to the Length field for gas_anqp_set_element_len()
  200. */
  201. u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
  202. {
  203. wpabuf_put_le16(buf, info_id);
  204. return wpabuf_put(buf, 2); /* Length to be filled */
  205. }
  206. /**
  207. * gas_anqp_set_element_len - Update ANQP element Length field
  208. * @buf: GAS message
  209. * @len_pos: Length field position from gas_anqp_add_element()
  210. *
  211. * This function is called after the ANQP element payload has been added to the
  212. * buffer.
  213. */
  214. void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
  215. {
  216. WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
  217. }