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