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