mbo_ap.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * hostapd - MBO
  3. * Copyright (c) 2016, Qualcomm Atheros, Inc.
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. */
  8. #include "utils/includes.h"
  9. #include "utils/common.h"
  10. #include "common/ieee802_11_defs.h"
  11. #include "common/ieee802_11_common.h"
  12. #include "hostapd.h"
  13. #include "sta_info.h"
  14. #include "mbo_ap.h"
  15. void mbo_ap_sta_free(struct sta_info *sta)
  16. {
  17. struct mbo_non_pref_chan_info *info, *prev;
  18. info = sta->non_pref_chan;
  19. sta->non_pref_chan = NULL;
  20. while (info) {
  21. prev = info;
  22. info = info->next;
  23. os_free(prev);
  24. }
  25. }
  26. static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
  27. const u8 *buf, size_t len)
  28. {
  29. struct mbo_non_pref_chan_info *info, *tmp;
  30. char channels[200], *pos, *end;
  31. size_t num_chan, i;
  32. int ret;
  33. if (len <= 4)
  34. return; /* Not enough room for any channels */
  35. num_chan = len - 4;
  36. info = os_zalloc(sizeof(*info) + num_chan);
  37. if (!info)
  38. return;
  39. info->op_class = buf[0];
  40. info->pref = buf[len - 3];
  41. info->reason_code = buf[len - 2];
  42. info->reason_detail = buf[len - 1];
  43. info->num_channels = num_chan;
  44. buf++;
  45. os_memcpy(info->channels, buf, num_chan);
  46. if (!sta->non_pref_chan) {
  47. sta->non_pref_chan = info;
  48. } else {
  49. tmp = sta->non_pref_chan;
  50. while (tmp->next)
  51. tmp = tmp->next;
  52. tmp->next = info;
  53. }
  54. pos = channels;
  55. end = pos + sizeof(channels);
  56. *pos = '\0';
  57. for (i = 0; i < num_chan; i++) {
  58. ret = os_snprintf(pos, end - pos, "%s%u",
  59. i == 0 ? "" : " ", buf[i]);
  60. if (os_snprintf_error(end - pos, ret)) {
  61. *pos = '\0';
  62. break;
  63. }
  64. pos += ret;
  65. }
  66. wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
  67. " non-preferred channel list (op class %u, pref %u, reason code %u, reason detail %u, channels %s)",
  68. MAC2STR(sta->addr), info->op_class, info->pref,
  69. info->reason_code, info->reason_detail, channels);
  70. }
  71. void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
  72. struct ieee802_11_elems *elems)
  73. {
  74. const u8 *pos, *attr, *end;
  75. size_t len;
  76. if (!hapd->conf->mbo_enabled || !elems->mbo)
  77. return;
  78. pos = elems->mbo + 4;
  79. len = elems->mbo_len - 4;
  80. wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
  81. attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
  82. if (attr && attr[1] >= 1)
  83. sta->cell_capa = attr[2];
  84. mbo_ap_sta_free(sta);
  85. end = pos + len;
  86. while (end - pos > 1) {
  87. u8 ie_len = pos[1];
  88. if (2 + ie_len > end - pos)
  89. break;
  90. if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
  91. mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
  92. pos += 2 + pos[1];
  93. }
  94. }
  95. int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
  96. {
  97. char *pos = buf, *end = buf + buflen;
  98. int ret;
  99. struct mbo_non_pref_chan_info *info;
  100. u8 i;
  101. unsigned int count = 0;
  102. if (!sta->cell_capa)
  103. return 0;
  104. ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
  105. if (os_snprintf_error(end - pos, ret))
  106. return pos - buf;
  107. pos += ret;
  108. for (info = sta->non_pref_chan; info; info = info->next) {
  109. char *pos2 = pos;
  110. ret = os_snprintf(pos2, end - pos2,
  111. "non_pref_chan[%u]=%u:%u:%u:%u:",
  112. count, info->op_class, info->pref,
  113. info->reason_code, info->reason_detail);
  114. count++;
  115. if (os_snprintf_error(end - pos2, ret))
  116. break;
  117. pos2 += ret;
  118. for (i = 0; i < info->num_channels; i++) {
  119. ret = os_snprintf(pos2, end - pos2, "%u%s",
  120. info->channels[i],
  121. i + 1 < info->num_channels ?
  122. "," : "");
  123. if (os_snprintf_error(end - pos2, ret)) {
  124. pos2 = NULL;
  125. break;
  126. }
  127. pos2 += ret;
  128. }
  129. if (!pos2)
  130. break;
  131. ret = os_snprintf(pos2, end - pos2, "\n");
  132. if (os_snprintf_error(end - pos2, ret))
  133. break;
  134. pos2 += ret;
  135. pos = pos2;
  136. }
  137. return pos - buf;
  138. }
  139. static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
  140. const u8 *buf, size_t len)
  141. {
  142. if (len < 1)
  143. return;
  144. wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
  145. " updated cellular data capability: %u",
  146. MAC2STR(sta->addr), buf[0]);
  147. sta->cell_capa = buf[0];
  148. }
  149. static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
  150. const u8 *buf, size_t len,
  151. int *first_non_pref_chan)
  152. {
  153. switch (type) {
  154. case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
  155. if (*first_non_pref_chan) {
  156. /*
  157. * Need to free the previously stored entries now to
  158. * allow the update to replace all entries.
  159. */
  160. *first_non_pref_chan = 0;
  161. mbo_ap_sta_free(sta);
  162. }
  163. mbo_ap_parse_non_pref_chan(sta, buf, len);
  164. break;
  165. case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
  166. mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
  167. break;
  168. default:
  169. wpa_printf(MSG_DEBUG,
  170. "MBO: Ignore unknown WNM Notification WFA subelement %u",
  171. type);
  172. break;
  173. }
  174. }
  175. void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
  176. const u8 *buf, size_t len)
  177. {
  178. const u8 *pos, *end;
  179. u8 ie_len;
  180. struct sta_info *sta;
  181. int first_non_pref_chan = 1;
  182. if (!hapd->conf->mbo_enabled)
  183. return;
  184. sta = ap_get_sta(hapd, addr);
  185. if (!sta)
  186. return;
  187. pos = buf;
  188. end = buf + len;
  189. while (end - pos > 1) {
  190. ie_len = pos[1];
  191. if (2 + ie_len > end - pos)
  192. break;
  193. if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
  194. ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
  195. mbo_ap_wnm_notif_req_elem(sta, pos[5],
  196. pos + 6, ie_len - 4,
  197. &first_non_pref_chan);
  198. else
  199. wpa_printf(MSG_DEBUG,
  200. "MBO: Ignore unknown WNM Notification element %u (len=%u)",
  201. pos[0], pos[1]);
  202. pos += 2 + pos[1];
  203. }
  204. }