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
19struct hostapd_neighbor_entry *
20hostapd_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
38int 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
92static 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
106static struct hostapd_neighbor_entry *
107hostapd_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
121int 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
162fail:
163	hostapd_neighbor_remove(hapd, bssid, ssid);
164	return -1;
165}
166
167
168int 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
185void 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
199static 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
221void 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				       &center_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