]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/ap/neighbor_db.c
hostapd: Support showing neighbor list through hostapd_cli
[thirdparty/hostap.git] / src / ap / neighbor_db.c
CommitLineData
9b4b2264
DS
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 "hostapd.h"
0998d9bd 14#include "ieee802_11.h"
9b4b2264
DS
15#include "neighbor_db.h"
16
17
220754c5 18struct hostapd_neighbor_entry *
9b4b2264
DS
19hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
20 const struct wpa_ssid_value *ssid)
21{
22 struct hostapd_neighbor_entry *nr;
23
24 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
25 list) {
26 if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
220754c5
DS
27 (!ssid ||
28 (ssid->ssid_len == nr->ssid.ssid_len &&
29 os_memcmp(ssid->ssid, nr->ssid.ssid,
30 ssid->ssid_len) == 0)))
9b4b2264
DS
31 return nr;
32 }
33 return NULL;
34}
35
36
ee48f48b
BG
37int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
38{
39 struct hostapd_neighbor_entry *nr;
40 char *pos, *end;
41
42 pos = buf;
43 end = buf + buflen;
44
45 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
46 list) {
47 int ret;
48 char nrie[2 * 255 + 1];
49 char lci[2 * 255 + 1];
50 char civic[2 * 255 + 1];
51 char ssid[SSID_MAX_LEN * 2 + 1];
52
53 ssid[0] = '\0';
54 wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
55 nr->ssid.ssid_len);
56
57 nrie[0] = '\0';
58 if (nr->nr)
59 wpa_snprintf_hex(nrie, sizeof(nrie),
60 wpabuf_head(nr->nr),
61 wpabuf_len(nr->nr));
62
63 lci[0] = '\0';
64 if (nr->lci)
65 wpa_snprintf_hex(lci, sizeof(lci),
66 wpabuf_head(nr->lci),
67 wpabuf_len(nr->lci));
68
69 civic[0] = '\0';
70 if (nr->civic)
71 wpa_snprintf_hex(civic, sizeof(civic),
72 wpabuf_head(nr->civic),
73 wpabuf_len(nr->civic));
74
75 ret = os_snprintf(pos, end - pos, MACSTR
76 " ssid=%s%s%s%s%s%s%s%s\n",
77 MAC2STR(nr->bssid), ssid,
78 nr->nr ? " nr=" : "", nrie,
79 nr->lci ? " lci=" : "", lci,
80 nr->civic ? " civic=" : "", civic,
81 nr->stationary ? " stat" : "");
82 if (os_snprintf_error(end - pos, ret))
83 break;
84 pos += ret;
85 }
86
87 return pos - buf;
88}
89
90
9b4b2264
DS
91static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
92{
93 wpabuf_free(nr->nr);
94 nr->nr = NULL;
95 wpabuf_free(nr->lci);
96 nr->lci = NULL;
97 wpabuf_free(nr->civic);
98 nr->civic = NULL;
99 os_memset(nr->bssid, 0, sizeof(nr->bssid));
100 os_memset(&nr->ssid, 0, sizeof(nr->ssid));
451a27b1 101 nr->stationary = 0;
9b4b2264
DS
102}
103
104
105static struct hostapd_neighbor_entry *
106hostapd_neighbor_add(struct hostapd_data *hapd)
107{
108 struct hostapd_neighbor_entry *nr;
109
110 nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
111 if (!nr)
112 return NULL;
113
114 dl_list_add(&hapd->nr_db, &nr->list);
115
116 return nr;
117}
118
119
120int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
121 const struct wpa_ssid_value *ssid,
122 const struct wpabuf *nr, const struct wpabuf *lci,
451a27b1 123 const struct wpabuf *civic, int stationary)
9b4b2264
DS
124{
125 struct hostapd_neighbor_entry *entry;
126
127 entry = hostapd_neighbor_get(hapd, bssid, ssid);
128 if (!entry)
129 entry = hostapd_neighbor_add(hapd);
130 if (!entry)
131 return -1;
132
133 hostapd_neighbor_clear_entry(entry);
134
135 os_memcpy(entry->bssid, bssid, ETH_ALEN);
136 os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
137
138 entry->nr = wpabuf_dup(nr);
139 if (!entry->nr)
140 goto fail;
141
f5ec3469 142 if (lci && wpabuf_len(lci)) {
9b4b2264
DS
143 entry->lci = wpabuf_dup(lci);
144 if (!entry->lci || os_get_time(&entry->lci_date))
145 goto fail;
146 }
147
f5ec3469 148 if (civic && wpabuf_len(civic)) {
9b4b2264
DS
149 entry->civic = wpabuf_dup(civic);
150 if (!entry->civic)
151 goto fail;
152 }
153
451a27b1
DS
154 entry->stationary = stationary;
155
9b4b2264
DS
156 return 0;
157
158fail:
159 hostapd_neighbor_remove(hapd, bssid, ssid);
160 return -1;
161}
162
163
164int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
165 const struct wpa_ssid_value *ssid)
166{
167 struct hostapd_neighbor_entry *nr;
168
169 nr = hostapd_neighbor_get(hapd, bssid, ssid);
170 if (!nr)
171 return -1;
172
173 hostapd_neighbor_clear_entry(nr);
174 dl_list_del(&nr->list);
175 os_free(nr);
176
177 return 0;
178}
179
180
dc679823 181void hostapd_free_neighbor_db(struct hostapd_data *hapd)
9b4b2264
DS
182{
183 struct hostapd_neighbor_entry *nr, *prev;
184
185 dl_list_for_each_safe(nr, prev, &hapd->nr_db,
186 struct hostapd_neighbor_entry, list) {
187 hostapd_neighbor_clear_entry(nr);
188 dl_list_del(&nr->list);
189 os_free(nr);
190 }
191}
0998d9bd
MT
192
193
194#ifdef NEED_AP_MLME
195static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
de21d1d6 196 int ht, int vht, int he)
0998d9bd 197{
c6b7ac07
JC
198 u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
199
de21d1d6 200 if (!ht && !vht && !he)
0998d9bd
MT
201 return NR_CHAN_WIDTH_20;
202 if (!hapd->iconf->secondary_channel)
203 return NR_CHAN_WIDTH_20;
de21d1d6 204 if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
0998d9bd 205 return NR_CHAN_WIDTH_40;
c6b7ac07 206 if (oper_chwidth == CHANWIDTH_80MHZ)
0998d9bd 207 return NR_CHAN_WIDTH_80;
c6b7ac07 208 if (oper_chwidth == CHANWIDTH_160MHZ)
0998d9bd 209 return NR_CHAN_WIDTH_160;
c6b7ac07 210 if (oper_chwidth == CHANWIDTH_80P80MHZ)
0998d9bd
MT
211 return NR_CHAN_WIDTH_80P80;
212 return NR_CHAN_WIDTH_20;
213}
214#endif /* NEED_AP_MLME */
215
216
217void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
218{
219#ifdef NEED_AP_MLME
220 u16 capab = hostapd_own_capab_info(hapd);
221 int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
222 int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
de21d1d6 223 int he = hapd->iconf->ieee80211ax;
0998d9bd
MT
224 struct wpa_ssid_value ssid;
225 u8 channel, op_class;
226 u8 center_freq1_idx = 0, center_freq2_idx = 0;
227 enum nr_chan_width width;
228 u32 bssid_info;
229 struct wpabuf *nr;
230
231 if (!(hapd->conf->radio_measurements[0] &
232 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
233 return;
234
235 bssid_info = 3; /* AP is reachable */
236 bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
237 bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
238
239 if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
240 bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
241
242 bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
243
244 if (hapd->conf->wmm_enabled) {
245 bssid_info |= NEI_REP_BSSID_INFO_QOS;
246
247 if (hapd->conf->wmm_uapsd &&
248 (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
249 bssid_info |= NEI_REP_BSSID_INFO_APSD;
250 }
251
252 if (ht) {
253 bssid_info |= NEI_REP_BSSID_INFO_HT |
254 NEI_REP_BSSID_INFO_DELAYED_BA;
255
256 /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
257 if (vht)
258 bssid_info |= NEI_REP_BSSID_INFO_VHT;
259 }
260
261 /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
262
263 if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
264 hapd->iconf->secondary_channel,
c6b7ac07 265 hostapd_get_oper_chwidth(hapd->iconf),
0998d9bd
MT
266 &op_class, &channel) ==
267 NUM_HOSTAPD_MODES)
268 return;
de21d1d6 269 width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
0998d9bd 270 if (vht) {
c6b7ac07
JC
271 center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
272 hapd->iconf);
0998d9bd
MT
273 if (width == NR_CHAN_WIDTH_80P80)
274 center_freq2_idx =
c6b7ac07
JC
275 hostapd_get_oper_centr_freq_seg1_idx(
276 hapd->iconf);
0998d9bd
MT
277 } else if (ht) {
278 ieee80211_freq_to_chan(hapd->iface->freq +
279 10 * hapd->iconf->secondary_channel,
280 &center_freq1_idx);
281 }
282
283 ssid.ssid_len = hapd->conf->ssid.ssid_len;
284 os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
285
286 /*
287 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
288 * phy type + wide bandwidth channel subelement.
289 */
290 nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
291 if (!nr)
292 return;
293
294 wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
295 wpabuf_put_le32(nr, bssid_info);
296 wpabuf_put_u8(nr, op_class);
297 wpabuf_put_u8(nr, channel);
298 wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
299
300 /*
301 * Wide Bandwidth Channel subelement may be needed to allow the
302 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
303 * Figure 9-301.
304 */
305 wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
306 wpabuf_put_u8(nr, 3);
307 wpabuf_put_u8(nr, width);
308 wpabuf_put_u8(nr, center_freq1_idx);
309 wpabuf_put_u8(nr, center_freq2_idx);
310
311 hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
312 hapd->iconf->civic, hapd->iconf->stationary_ap);
313
314 wpabuf_free(nr);
315#endif /* NEED_AP_MLME */
316}