interworking.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Interworking (IEEE 802.11u)
  3. * Copyright (c) 2011, Qualcomm Atheros
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include "common.h"
  16. #include "common/ieee802_11_defs.h"
  17. #include "common/gas.h"
  18. #include "drivers/driver.h"
  19. #include "wpa_supplicant_i.h"
  20. #include "bss.h"
  21. #include "gas_query.h"
  22. #include "interworking.h"
  23. static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
  24. static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
  25. struct wpabuf *extra)
  26. {
  27. struct wpabuf *buf;
  28. size_t i;
  29. u8 *len_pos;
  30. buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
  31. (extra ? wpabuf_len(extra) : 0));
  32. if (buf == NULL)
  33. return NULL;
  34. len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
  35. for (i = 0; i < num_ids; i++)
  36. wpabuf_put_le16(buf, info_ids[i]);
  37. gas_anqp_set_element_len(buf, len_pos);
  38. if (extra)
  39. wpabuf_put_buf(buf, extra);
  40. gas_anqp_set_len(buf);
  41. return buf;
  42. }
  43. static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
  44. u8 dialog_token,
  45. enum gas_query_result result,
  46. const struct wpabuf *adv_proto,
  47. const struct wpabuf *resp,
  48. u16 status_code)
  49. {
  50. struct wpa_supplicant *wpa_s = ctx;
  51. anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
  52. status_code);
  53. interworking_next_anqp_fetch(wpa_s);
  54. }
  55. static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
  56. struct wpa_bss *bss)
  57. {
  58. struct wpabuf *buf;
  59. int ret = 0;
  60. int res;
  61. u16 info_ids[] = {
  62. ANQP_CAPABILITY_LIST,
  63. ANQP_VENUE_NAME,
  64. ANQP_NETWORK_AUTH_TYPE,
  65. ANQP_ROAMING_CONSORTIUM,
  66. ANQP_IP_ADDR_TYPE_AVAILABILITY,
  67. ANQP_NAI_REALM,
  68. ANQP_3GPP_CELLULAR_NETWORK,
  69. ANQP_DOMAIN_NAME
  70. };
  71. struct wpabuf *extra = NULL;
  72. wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
  73. MAC2STR(bss->bssid));
  74. buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
  75. extra);
  76. wpabuf_free(extra);
  77. if (buf == NULL)
  78. return -1;
  79. res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
  80. interworking_anqp_resp_cb, wpa_s);
  81. if (res < 0) {
  82. wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
  83. ret = -1;
  84. } else
  85. wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
  86. "%u", res);
  87. wpabuf_free(buf);
  88. return ret;
  89. }
  90. static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
  91. {
  92. struct wpa_bss *bss;
  93. int found = 0;
  94. const u8 *ie;
  95. if (!wpa_s->fetch_anqp_in_progress)
  96. return;
  97. dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
  98. if (!(bss->caps & IEEE80211_CAP_ESS))
  99. continue;
  100. ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
  101. if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
  102. continue; /* AP does not support Interworking */
  103. if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
  104. found++;
  105. bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
  106. wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
  107. MACSTR, MAC2STR(bss->bssid));
  108. interworking_anqp_send_req(wpa_s, bss);
  109. break;
  110. }
  111. }
  112. if (found == 0) {
  113. wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
  114. wpa_s->fetch_anqp_in_progress = 0;
  115. }
  116. }
  117. int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
  118. {
  119. struct wpa_bss *bss;
  120. if (wpa_s->fetch_anqp_in_progress)
  121. return 0;
  122. dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
  123. bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
  124. wpa_s->fetch_anqp_in_progress = 1;
  125. interworking_next_anqp_fetch(wpa_s);
  126. return 0;
  127. }
  128. void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
  129. {
  130. if (!wpa_s->fetch_anqp_in_progress)
  131. return;
  132. wpa_s->fetch_anqp_in_progress = 0;
  133. }
  134. int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
  135. u16 info_ids[], size_t num_ids)
  136. {
  137. struct wpabuf *buf;
  138. int ret = 0;
  139. int freq;
  140. struct wpa_bss *bss;
  141. int res;
  142. freq = wpa_s->assoc_freq;
  143. bss = wpa_bss_get_bssid(wpa_s, dst);
  144. if (bss)
  145. freq = bss->freq;
  146. if (freq <= 0)
  147. return -1;
  148. wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
  149. MAC2STR(dst), (unsigned int) num_ids);
  150. buf = anqp_build_req(info_ids, num_ids, NULL);
  151. if (buf == NULL)
  152. return -1;
  153. res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
  154. if (res < 0) {
  155. wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
  156. ret = -1;
  157. } else
  158. wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
  159. "%u", res);
  160. wpabuf_free(buf);
  161. return ret;
  162. }
  163. static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
  164. const u8 *sa, u16 info_id,
  165. const u8 *data, size_t slen)
  166. {
  167. const u8 *pos = data;
  168. struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
  169. switch (info_id) {
  170. case ANQP_CAPABILITY_LIST:
  171. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  172. " ANQP Capability list", MAC2STR(sa));
  173. break;
  174. case ANQP_VENUE_NAME:
  175. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  176. " Venue Name", MAC2STR(sa));
  177. wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
  178. if (bss) {
  179. wpabuf_free(bss->anqp_venue_name);
  180. bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
  181. }
  182. break;
  183. case ANQP_NETWORK_AUTH_TYPE:
  184. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  185. " Network Authentication Type information",
  186. MAC2STR(sa));
  187. wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
  188. "Type", pos, slen);
  189. if (bss) {
  190. wpabuf_free(bss->anqp_network_auth_type);
  191. bss->anqp_network_auth_type =
  192. wpabuf_alloc_copy(pos, slen);
  193. }
  194. break;
  195. case ANQP_ROAMING_CONSORTIUM:
  196. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  197. " Roaming Consortium list", MAC2STR(sa));
  198. wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
  199. pos, slen);
  200. if (bss) {
  201. wpabuf_free(bss->anqp_roaming_consortium);
  202. bss->anqp_roaming_consortium =
  203. wpabuf_alloc_copy(pos, slen);
  204. }
  205. break;
  206. case ANQP_IP_ADDR_TYPE_AVAILABILITY:
  207. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  208. " IP Address Type Availability information",
  209. MAC2STR(sa));
  210. wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
  211. pos, slen);
  212. if (bss) {
  213. wpabuf_free(bss->anqp_ip_addr_type_availability);
  214. bss->anqp_ip_addr_type_availability =
  215. wpabuf_alloc_copy(pos, slen);
  216. }
  217. break;
  218. case ANQP_NAI_REALM:
  219. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  220. " NAI Realm list", MAC2STR(sa));
  221. wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
  222. if (bss) {
  223. wpabuf_free(bss->anqp_nai_realm);
  224. bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
  225. }
  226. break;
  227. case ANQP_3GPP_CELLULAR_NETWORK:
  228. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  229. " 3GPP Cellular Network information", MAC2STR(sa));
  230. wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
  231. pos, slen);
  232. if (bss) {
  233. wpabuf_free(bss->anqp_3gpp);
  234. bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
  235. }
  236. break;
  237. case ANQP_DOMAIN_NAME:
  238. wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
  239. " Domain Name list", MAC2STR(sa));
  240. wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
  241. if (bss) {
  242. wpabuf_free(bss->anqp_domain_name);
  243. bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
  244. }
  245. break;
  246. case ANQP_VENDOR_SPECIFIC:
  247. if (slen < 3)
  248. return;
  249. switch (WPA_GET_BE24(pos)) {
  250. default:
  251. wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
  252. "vendor-specific ANQP OUI %06x",
  253. WPA_GET_BE24(pos));
  254. return;
  255. }
  256. break;
  257. default:
  258. wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
  259. "%u", info_id);
  260. break;
  261. }
  262. }
  263. void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
  264. enum gas_query_result result,
  265. const struct wpabuf *adv_proto,
  266. const struct wpabuf *resp, u16 status_code)
  267. {
  268. struct wpa_supplicant *wpa_s = ctx;
  269. const u8 *pos;
  270. const u8 *end;
  271. u16 info_id;
  272. u16 slen;
  273. if (result != GAS_QUERY_SUCCESS)
  274. return;
  275. pos = wpabuf_head(adv_proto);
  276. if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
  277. pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
  278. wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
  279. "Protocol in response");
  280. return;
  281. }
  282. pos = wpabuf_head(resp);
  283. end = pos + wpabuf_len(resp);
  284. while (pos < end) {
  285. if (pos + 4 > end) {
  286. wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
  287. break;
  288. }
  289. info_id = WPA_GET_LE16(pos);
  290. pos += 2;
  291. slen = WPA_GET_LE16(pos);
  292. pos += 2;
  293. if (pos + slen > end) {
  294. wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
  295. "for Info ID %u", info_id);
  296. break;
  297. }
  298. interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
  299. slen);
  300. pos += slen;
  301. }
  302. }