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"
14#include "ieee802_11.h"
15#include "neighbor_db.h"
16
17
18struct hostapd_neighbor_entry *
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 &&
27		    (!ssid ||
28		     (ssid->ssid_len == nr->ssid.ssid_len &&
29		      os_memcmp(ssid->ssid, nr->ssid.ssid,
30				ssid->ssid_len) == 0)))
31			return nr;
32	}
33	return NULL;
34}
35
36
37static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
38{
39	wpabuf_free(nr->nr);
40	nr->nr = NULL;
41	wpabuf_free(nr->lci);
42	nr->lci = NULL;
43	wpabuf_free(nr->civic);
44	nr->civic = NULL;
45	os_memset(nr->bssid, 0, sizeof(nr->bssid));
46	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
47	nr->stationary = 0;
48}
49
50
51static struct hostapd_neighbor_entry *
52hostapd_neighbor_add(struct hostapd_data *hapd)
53{
54	struct hostapd_neighbor_entry *nr;
55
56	nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
57	if (!nr)
58		return NULL;
59
60	dl_list_add(&hapd->nr_db, &nr->list);
61
62	return nr;
63}
64
65
66int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
67			 const struct wpa_ssid_value *ssid,
68			 const struct wpabuf *nr, const struct wpabuf *lci,
69			 const struct wpabuf *civic, int stationary)
70{
71	struct hostapd_neighbor_entry *entry;
72
73	entry = hostapd_neighbor_get(hapd, bssid, ssid);
74	if (!entry)
75		entry = hostapd_neighbor_add(hapd);
76	if (!entry)
77		return -1;
78
79	hostapd_neighbor_clear_entry(entry);
80
81	os_memcpy(entry->bssid, bssid, ETH_ALEN);
82	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
83
84	entry->nr = wpabuf_dup(nr);
85	if (!entry->nr)
86		goto fail;
87
88	if (lci && wpabuf_len(lci)) {
89		entry->lci = wpabuf_dup(lci);
90		if (!entry->lci || os_get_time(&entry->lci_date))
91			goto fail;
92	}
93
94	if (civic && wpabuf_len(civic)) {
95		entry->civic = wpabuf_dup(civic);
96		if (!entry->civic)
97			goto fail;
98	}
99
100	entry->stationary = stationary;
101
102	return 0;
103
104fail:
105	hostapd_neighbor_remove(hapd, bssid, ssid);
106	return -1;
107}
108
109
110int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
111			    const struct wpa_ssid_value *ssid)
112{
113	struct hostapd_neighbor_entry *nr;
114
115	nr = hostapd_neighbor_get(hapd, bssid, ssid);
116	if (!nr)
117		return -1;
118
119	hostapd_neighbor_clear_entry(nr);
120	dl_list_del(&nr->list);
121	os_free(nr);
122
123	return 0;
124}
125
126
127void hostapd_free_neighbor_db(struct hostapd_data *hapd)
128{
129	struct hostapd_neighbor_entry *nr, *prev;
130
131	dl_list_for_each_safe(nr, prev, &hapd->nr_db,
132			      struct hostapd_neighbor_entry, list) {
133		hostapd_neighbor_clear_entry(nr);
134		dl_list_del(&nr->list);
135		os_free(nr);
136	}
137}
138
139
140#ifdef NEED_AP_MLME
141static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
142						    int ht, int vht, int he)
143{
144	u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
145
146	if (!ht && !vht && !he)
147		return NR_CHAN_WIDTH_20;
148	if (!hapd->iconf->secondary_channel)
149		return NR_CHAN_WIDTH_20;
150	if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
151		return NR_CHAN_WIDTH_40;
152	if (oper_chwidth == CHANWIDTH_80MHZ)
153		return NR_CHAN_WIDTH_80;
154	if (oper_chwidth == CHANWIDTH_160MHZ)
155		return NR_CHAN_WIDTH_160;
156	if (oper_chwidth == CHANWIDTH_80P80MHZ)
157		return NR_CHAN_WIDTH_80P80;
158	return NR_CHAN_WIDTH_20;
159}
160#endif /* NEED_AP_MLME */
161
162
163void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
164{
165#ifdef NEED_AP_MLME
166	u16 capab = hostapd_own_capab_info(hapd);
167	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
168	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
169	int he = hapd->iconf->ieee80211ax;
170	struct wpa_ssid_value ssid;
171	u8 channel, op_class;
172	u8 center_freq1_idx = 0, center_freq2_idx = 0;
173	enum nr_chan_width width;
174	u32 bssid_info;
175	struct wpabuf *nr;
176
177	if (!(hapd->conf->radio_measurements[0] &
178	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
179		return;
180
181	bssid_info = 3; /* AP is reachable */
182	bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
183	bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
184
185	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
186		bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
187
188	bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
189
190	if (hapd->conf->wmm_enabled) {
191		bssid_info |= NEI_REP_BSSID_INFO_QOS;
192
193		if (hapd->conf->wmm_uapsd &&
194		    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
195			bssid_info |= NEI_REP_BSSID_INFO_APSD;
196	}
197
198	if (ht) {
199		bssid_info |= NEI_REP_BSSID_INFO_HT |
200			NEI_REP_BSSID_INFO_DELAYED_BA;
201
202		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
203		if (vht)
204			bssid_info |= NEI_REP_BSSID_INFO_VHT;
205	}
206
207	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
208
209	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
210					  hapd->iconf->secondary_channel,
211					  hostapd_get_oper_chwidth(hapd->iconf),
212					  &op_class, &channel) ==
213	    NUM_HOSTAPD_MODES)
214		return;
215	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
216	if (vht) {
217		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
218			hapd->iconf);
219		if (width == NR_CHAN_WIDTH_80P80)
220			center_freq2_idx =
221				hostapd_get_oper_centr_freq_seg1_idx(
222					hapd->iconf);
223	} else if (ht) {
224		ieee80211_freq_to_chan(hapd->iface->freq +
225				       10 * hapd->iconf->secondary_channel,
226				       &center_freq1_idx);
227	}
228
229	ssid.ssid_len = hapd->conf->ssid.ssid_len;
230	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
231
232	/*
233	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
234	 * phy type + wide bandwidth channel subelement.
235	 */
236	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
237	if (!nr)
238		return;
239
240	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
241	wpabuf_put_le32(nr, bssid_info);
242	wpabuf_put_u8(nr, op_class);
243	wpabuf_put_u8(nr, channel);
244	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
245
246	/*
247	 * Wide Bandwidth Channel subelement may be needed to allow the
248	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
249	 * Figure 9-301.
250	 */
251	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
252	wpabuf_put_u8(nr, 3);
253	wpabuf_put_u8(nr, width);
254	wpabuf_put_u8(nr, center_freq1_idx);
255	wpabuf_put_u8(nr, center_freq2_idx);
256
257	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
258			     hapd->iconf->civic, hapd->iconf->stationary_ap);
259
260	wpabuf_free(nr);
261#endif /* NEED_AP_MLME */
262}
263