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 (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
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 nr->stationary = 0;
103 }
104
105
106 static struct hostapd_neighbor_entry *
hostapd_neighbor_add(struct hostapd_data * hapd)107 hostapd_neighbor_add(struct hostapd_data *hapd)
108 {
109 struct hostapd_neighbor_entry *nr;
110
111 nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
112 if (!nr)
113 return NULL;
114
115 dl_list_add(&hapd->nr_db, &nr->list);
116
117 return nr;
118 }
119
120
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)121 int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
122 const struct wpa_ssid_value *ssid,
123 const struct wpabuf *nr, const struct wpabuf *lci,
124 const struct wpabuf *civic, int stationary,
125 u8 bss_parameters)
126 {
127 struct hostapd_neighbor_entry *entry;
128
129 entry = hostapd_neighbor_get(hapd, bssid, ssid);
130 if (!entry)
131 entry = hostapd_neighbor_add(hapd);
132 if (!entry)
133 return -1;
134
135 hostapd_neighbor_clear_entry(entry);
136
137 os_memcpy(entry->bssid, bssid, ETH_ALEN);
138 os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
139 entry->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
140
141 entry->nr = wpabuf_dup(nr);
142 if (!entry->nr)
143 goto fail;
144
145 if (lci && wpabuf_len(lci)) {
146 entry->lci = wpabuf_dup(lci);
147 if (!entry->lci || os_get_time(&entry->lci_date))
148 goto fail;
149 }
150
151 if (civic && wpabuf_len(civic)) {
152 entry->civic = wpabuf_dup(civic);
153 if (!entry->civic)
154 goto fail;
155 }
156
157 entry->stationary = stationary;
158 entry->bss_parameters = bss_parameters;
159
160 return 0;
161
162 fail:
163 hostapd_neighbor_remove(hapd, bssid, ssid);
164 return -1;
165 }
166
167
hostapd_neighbor_remove(struct hostapd_data * hapd,const u8 * bssid,const struct wpa_ssid_value * ssid)168 int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
169 const struct wpa_ssid_value *ssid)
170 {
171 struct hostapd_neighbor_entry *nr;
172
173 nr = hostapd_neighbor_get(hapd, bssid, ssid);
174 if (!nr)
175 return -1;
176
177 hostapd_neighbor_clear_entry(nr);
178 dl_list_del(&nr->list);
179 os_free(nr);
180
181 return 0;
182 }
183
184
hostapd_free_neighbor_db(struct hostapd_data * hapd)185 void hostapd_free_neighbor_db(struct hostapd_data *hapd)
186 {
187 struct hostapd_neighbor_entry *nr, *prev;
188
189 dl_list_for_each_safe(nr, prev, &hapd->nr_db,
190 struct hostapd_neighbor_entry, list) {
191 hostapd_neighbor_clear_entry(nr);
192 dl_list_del(&nr->list);
193 os_free(nr);
194 }
195 }
196
197
198 #ifdef NEED_AP_MLME
hostapd_get_nr_chan_width(struct hostapd_data * hapd,int ht,int vht,int he)199 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
200 int ht, int vht, int he)
201 {
202 u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
203
204 if (!ht && !vht && !he)
205 return NR_CHAN_WIDTH_20;
206 if (!hapd->iconf->secondary_channel)
207 return NR_CHAN_WIDTH_20;
208 if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
209 return NR_CHAN_WIDTH_40;
210 if (oper_chwidth == CHANWIDTH_80MHZ)
211 return NR_CHAN_WIDTH_80;
212 if (oper_chwidth == CHANWIDTH_160MHZ)
213 return NR_CHAN_WIDTH_160;
214 if (oper_chwidth == CHANWIDTH_80P80MHZ)
215 return NR_CHAN_WIDTH_80P80;
216 return NR_CHAN_WIDTH_20;
217 }
218 #endif /* NEED_AP_MLME */
219
220
hostapd_neighbor_set_own_report(struct hostapd_data * hapd)221 void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
222 {
223 #ifdef NEED_AP_MLME
224 u16 capab = hostapd_own_capab_info(hapd);
225 int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
226 int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
227 int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
228 struct wpa_ssid_value ssid;
229 u8 channel, op_class;
230 u8 center_freq1_idx = 0, center_freq2_idx = 0;
231 enum nr_chan_width width;
232 u32 bssid_info;
233 struct wpabuf *nr;
234
235 if (!(hapd->conf->radio_measurements[0] &
236 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
237 return;
238
239 bssid_info = 3; /* AP is reachable */
240 bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
241 bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
242
243 if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
244 bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
245
246 bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
247
248 if (hapd->conf->wmm_enabled) {
249 bssid_info |= NEI_REP_BSSID_INFO_QOS;
250
251 if (hapd->conf->wmm_uapsd &&
252 (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
253 bssid_info |= NEI_REP_BSSID_INFO_APSD;
254 }
255
256 if (ht) {
257 bssid_info |= NEI_REP_BSSID_INFO_HT |
258 NEI_REP_BSSID_INFO_DELAYED_BA;
259
260 /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
261 if (vht)
262 bssid_info |= NEI_REP_BSSID_INFO_VHT;
263 if (he)
264 bssid_info |= NEI_REP_BSSID_INFO_HE;
265 }
266
267 /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
268
269 if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
270 hapd->iconf->secondary_channel,
271 hostapd_get_oper_chwidth(hapd->iconf),
272 &op_class, &channel) ==
273 NUM_HOSTAPD_MODES)
274 return;
275 width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
276 if (vht) {
277 center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
278 hapd->iconf);
279 if (width == NR_CHAN_WIDTH_80P80)
280 center_freq2_idx =
281 hostapd_get_oper_centr_freq_seg1_idx(
282 hapd->iconf);
283 } else if (ht) {
284 ieee80211_freq_to_chan(hapd->iface->freq +
285 10 * hapd->iconf->secondary_channel,
286 ¢er_freq1_idx);
287 }
288
289 ssid.ssid_len = hapd->conf->ssid.ssid_len;
290 os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
291
292 /*
293 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
294 * phy type + wide bandwidth channel subelement.
295 */
296 nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
297 if (!nr)
298 return;
299
300 wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
301 wpabuf_put_le32(nr, bssid_info);
302 wpabuf_put_u8(nr, op_class);
303 wpabuf_put_u8(nr, channel);
304 wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
305
306 /*
307 * Wide Bandwidth Channel subelement may be needed to allow the
308 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
309 * Figure 9-301.
310 */
311 wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
312 wpabuf_put_u8(nr, 3);
313 wpabuf_put_u8(nr, width);
314 wpabuf_put_u8(nr, center_freq1_idx);
315 wpabuf_put_u8(nr, center_freq2_idx);
316
317 hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
318 hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
319
320 wpabuf_free(nr);
321 #endif /* NEED_AP_MLME */
322 }
323