1 /*
2  * hostapd / Neighboring APs DB
3  * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
4  * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  */
9 
10 #include "utils/includes.h"
11 
12 #include "utils/common.h"
13 #include "utils/crc32.h"
14 #include "hostapd.h"
15 #include "ieee802_11.h"
16 #include "neighbor_db.h"
17 
18 
19 struct hostapd_neighbor_entry *
hostapd_neighbor_get(struct hostapd_data * hapd,const u8 * bssid,const struct wpa_ssid_value * ssid)20 hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
21                          const struct wpa_ssid_value *ssid)
22 {
23           struct hostapd_neighbor_entry *nr;
24 
25           dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
26                                list) {
27                     if (ether_addr_equal(bssid, nr->bssid) &&
28                         (!ssid ||
29                          (ssid->ssid_len == nr->ssid.ssid_len &&
30                           os_memcmp(ssid->ssid, nr->ssid.ssid,
31                                         ssid->ssid_len) == 0)))
32                               return nr;
33           }
34           return NULL;
35 }
36 
37 
hostapd_neighbor_show(struct hostapd_data * hapd,char * buf,size_t buflen)38 int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
39 {
40           struct hostapd_neighbor_entry *nr;
41           char *pos, *end;
42 
43           pos = buf;
44           end = buf + buflen;
45 
46           dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
47                                list) {
48                     int ret;
49                     char nrie[2 * 255 + 1];
50                     char lci[2 * 255 + 1];
51                     char civic[2 * 255 + 1];
52                     char ssid[SSID_MAX_LEN * 2 + 1];
53 
54                     ssid[0] = '\0';
55                     wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
56                                          nr->ssid.ssid_len);
57 
58                     nrie[0] = '\0';
59                     if (nr->nr)
60                               wpa_snprintf_hex(nrie, sizeof(nrie),
61                                                    wpabuf_head(nr->nr),
62                                                    wpabuf_len(nr->nr));
63 
64                     lci[0] = '\0';
65                     if (nr->lci)
66                               wpa_snprintf_hex(lci, sizeof(lci),
67                                                    wpabuf_head(nr->lci),
68                                                    wpabuf_len(nr->lci));
69 
70                     civic[0] = '\0';
71                     if (nr->civic)
72                               wpa_snprintf_hex(civic, sizeof(civic),
73                                                    wpabuf_head(nr->civic),
74                                                    wpabuf_len(nr->civic));
75 
76                     ret = os_snprintf(pos, end - pos, MACSTR
77                                           " ssid=%s%s%s%s%s%s%s%s\n",
78                                           MAC2STR(nr->bssid), ssid,
79                                           nr->nr ? " nr=" : "", nrie,
80                                           nr->lci ? " lci=" : "", lci,
81                                           nr->civic ? " civic=" : "", civic,
82                                           nr->stationary ? " stat" : "");
83                     if (os_snprintf_error(end - pos, ret))
84                               break;
85                     pos += ret;
86           }
87 
88           return pos - buf;
89 }
90 
91 
hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry * nr)92 static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
93 {
94           wpabuf_free(nr->nr);
95           nr->nr = NULL;
96           wpabuf_free(nr->lci);
97           nr->lci = NULL;
98           wpabuf_free(nr->civic);
99           nr->civic = NULL;
100           os_memset(nr->bssid, 0, sizeof(nr->bssid));
101           os_memset(&nr->ssid, 0, sizeof(nr->ssid));
102           os_memset(&nr->lci_date, 0, sizeof(nr->lci_date));
103           nr->stationary = 0;
104           nr->short_ssid = 0;
105           nr->bss_parameters = 0;
106 }
107 
108 
109 static struct hostapd_neighbor_entry *
hostapd_neighbor_add(struct hostapd_data * hapd)110 hostapd_neighbor_add(struct hostapd_data *hapd)
111 {
112           struct hostapd_neighbor_entry *nr;
113 
114           nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
115           if (!nr)
116                     return NULL;
117 
118           dl_list_add(&hapd->nr_db, &nr->list);
119 
120           return nr;
121 }
122 
123 
hostapd_neighbor_set(struct hostapd_data * hapd,const u8 * bssid,const struct wpa_ssid_value * ssid,const struct wpabuf * nr,const struct wpabuf * lci,const struct wpabuf * civic,int stationary,u8 bss_parameters)124 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
125                                const struct wpa_ssid_value *ssid,
126                                const struct wpabuf *nr, const struct wpabuf *lci,
127                                const struct wpabuf *civic, int stationary,
128                                u8 bss_parameters)
129 {
130           struct hostapd_neighbor_entry *entry;
131 
132           entry = hostapd_neighbor_get(hapd, bssid, ssid);
133           if (!entry)
134                     entry = hostapd_neighbor_add(hapd);
135           if (!entry)
136                     return -1;
137 
138           hostapd_neighbor_clear_entry(entry);
139 
140           os_memcpy(entry->bssid, bssid, ETH_ALEN);
141           os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
142           entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
143 
144           entry->nr = wpabuf_dup(nr);
145           if (!entry->nr)
146                     goto fail;
147 
148           if (lci && wpabuf_len(lci)) {
149                     entry->lci = wpabuf_dup(lci);
150                     if (!entry->lci || os_get_time(&entry->lci_date))
151                               goto fail;
152           }
153 
154           if (civic && wpabuf_len(civic)) {
155                     entry->civic = wpabuf_dup(civic);
156                     if (!entry->civic)
157                               goto fail;
158           }
159 
160           entry->stationary = stationary;
161           entry->bss_parameters = bss_parameters;
162 
163           return 0;
164 
165 fail:
166           hostapd_neighbor_remove(hapd, bssid, ssid);
167           return -1;
168 }
169 
170 
hostapd_neighbor_free(struct hostapd_neighbor_entry * nr)171 static void hostapd_neighbor_free(struct hostapd_neighbor_entry *nr)
172 {
173           hostapd_neighbor_clear_entry(nr);
174           dl_list_del(&nr->list);
175           os_free(nr);
176 }
177 
178 
hostapd_neighbor_remove(struct hostapd_data * hapd,const u8 * bssid,const struct wpa_ssid_value * ssid)179 int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
180                                   const struct wpa_ssid_value *ssid)
181 {
182           struct hostapd_neighbor_entry *nr;
183 
184           nr = hostapd_neighbor_get(hapd, bssid, ssid);
185           if (!nr)
186                     return -1;
187 
188           hostapd_neighbor_free(nr);
189 
190           return 0;
191 }
192 
193 
hostapd_free_neighbor_db(struct hostapd_data * hapd)194 void hostapd_free_neighbor_db(struct hostapd_data *hapd)
195 {
196           struct hostapd_neighbor_entry *nr, *prev;
197 
198           dl_list_for_each_safe(nr, prev, &hapd->nr_db,
199                                     struct hostapd_neighbor_entry, list) {
200                     hostapd_neighbor_free(nr);
201           }
202 }
203 
204 
205 #ifdef NEED_AP_MLME
hostapd_get_nr_chan_width(struct hostapd_data * hapd,int ht,int vht,int he)206 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
207                                                                 int ht, int vht, int he)
208 {
209           enum oper_chan_width oper_chwidth;
210 
211           oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
212 
213           if (!ht && !vht && !he)
214                     return NR_CHAN_WIDTH_20;
215           if (!hapd->iconf->secondary_channel)
216                     return NR_CHAN_WIDTH_20;
217           if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
218                     return NR_CHAN_WIDTH_40;
219           if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
220                     return NR_CHAN_WIDTH_80;
221           if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
222                     return NR_CHAN_WIDTH_160;
223           if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
224                     return NR_CHAN_WIDTH_80P80;
225           return NR_CHAN_WIDTH_20;
226 }
227 #endif /* NEED_AP_MLME */
228 
229 
hostapd_neighbor_set_own_report(struct hostapd_data * hapd)230 void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
231 {
232 #ifdef NEED_AP_MLME
233           u16 capab = hostapd_own_capab_info(hapd);
234           int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
235           int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
236           int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
237           bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
238           struct wpa_ssid_value ssid;
239           u8 channel, op_class;
240           u8 center_freq1_idx = 0, center_freq2_idx = 0;
241           enum nr_chan_width width;
242           u32 bssid_info;
243           struct wpabuf *nr;
244 
245           if (!(hapd->conf->radio_measurements[0] &
246                 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
247                     return;
248 
249           bssid_info = 3; /* AP is reachable */
250           bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
251           bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
252 
253           if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
254                     bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
255 
256           bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
257 
258           if (hapd->conf->wmm_enabled) {
259                     bssid_info |= NEI_REP_BSSID_INFO_QOS;
260 
261                     if (hapd->conf->wmm_uapsd &&
262                         (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
263                               bssid_info |= NEI_REP_BSSID_INFO_APSD;
264           }
265 
266           if (ht) {
267                     bssid_info |= NEI_REP_BSSID_INFO_HT |
268                               NEI_REP_BSSID_INFO_DELAYED_BA;
269 
270                     /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
271                     if (vht)
272                               bssid_info |= NEI_REP_BSSID_INFO_VHT;
273           }
274 
275           if (he)
276                     bssid_info |= NEI_REP_BSSID_INFO_HE;
277           if (eht)
278                     bssid_info |= NEI_REP_BSSID_INFO_EHT;
279           /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
280 
281           if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
282                                                     hapd->iconf->secondary_channel,
283                                                     hostapd_get_oper_chwidth(hapd->iconf),
284                                                     &op_class, &channel) ==
285               NUM_HOSTAPD_MODES)
286                     return;
287           width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
288           if (vht) {
289                     center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
290                               hapd->iconf);
291                     if (width == NR_CHAN_WIDTH_80P80)
292                               center_freq2_idx =
293                                         hostapd_get_oper_centr_freq_seg1_idx(
294                                                   hapd->iconf);
295           } else if (ht) {
296                     ieee80211_freq_to_chan(hapd->iface->freq +
297                                                10 * hapd->iconf->secondary_channel,
298                                                &center_freq1_idx);
299           }
300 
301           ssid.ssid_len = hapd->conf->ssid.ssid_len;
302           os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
303 
304           /*
305            * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
306            * phy type + wide bandwidth channel subelement.
307            */
308           nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
309           if (!nr)
310                     return;
311 
312           wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
313           wpabuf_put_le32(nr, bssid_info);
314           wpabuf_put_u8(nr, op_class);
315           wpabuf_put_u8(nr, channel);
316           wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
317 
318           /*
319            * Wide Bandwidth Channel subelement may be needed to allow the
320            * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
321            * Figure 9-301.
322            */
323           wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
324           wpabuf_put_u8(nr, 3);
325           wpabuf_put_u8(nr, width);
326           wpabuf_put_u8(nr, center_freq1_idx);
327           wpabuf_put_u8(nr, center_freq2_idx);
328 
329           hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
330                                    hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
331 
332           wpabuf_free(nr);
333 #endif /* NEED_AP_MLME */
334 }
335 
336 
337 static struct hostapd_neighbor_entry *
hostapd_neighbor_get_diff_short_ssid(struct hostapd_data * hapd,const u8 * bssid)338 hostapd_neighbor_get_diff_short_ssid(struct hostapd_data *hapd, const u8 *bssid)
339 {
340           struct hostapd_neighbor_entry *nr;
341 
342           dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
343                                list) {
344                     if (ether_addr_equal(bssid, nr->bssid) &&
345                         nr->short_ssid != hapd->conf->ssid.short_ssid)
346                               return nr;
347           }
348           return NULL;
349 }
350 
351 
hostapd_neighbor_sync_own_report(struct hostapd_data * hapd)352 int hostapd_neighbor_sync_own_report(struct hostapd_data *hapd)
353 {
354           struct hostapd_neighbor_entry *nr;
355 
356           nr = hostapd_neighbor_get_diff_short_ssid(hapd, hapd->own_addr);
357           if (!nr)
358                     return -1;
359 
360           /* Clear old entry due to SSID change */
361           hostapd_neighbor_free(nr);
362 
363           hostapd_neighbor_set_own_report(hapd);
364 
365           return 0;
366 }
367