xref: /freebsd/contrib/wpa/src/ap/neighbor_db.c (revision efe014e6b177ba1b0d70f7df751f5066d4eafe9b)
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 
18 struct hostapd_neighbor_entry *
19 hostapd_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 
37 int 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 
91 static 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));
101 	nr->stationary = 0;
102 }
103 
104 
105 static struct hostapd_neighbor_entry *
106 hostapd_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 
120 int 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,
123 			 const struct wpabuf *civic, int stationary)
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 
142 	if (lci && wpabuf_len(lci)) {
143 		entry->lci = wpabuf_dup(lci);
144 		if (!entry->lci || os_get_time(&entry->lci_date))
145 			goto fail;
146 	}
147 
148 	if (civic && wpabuf_len(civic)) {
149 		entry->civic = wpabuf_dup(civic);
150 		if (!entry->civic)
151 			goto fail;
152 	}
153 
154 	entry->stationary = stationary;
155 
156 	return 0;
157 
158 fail:
159 	hostapd_neighbor_remove(hapd, bssid, ssid);
160 	return -1;
161 }
162 
163 
164 int 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 
181 void hostapd_free_neighbor_db(struct hostapd_data *hapd)
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 }
192 
193 
194 #ifdef NEED_AP_MLME
195 static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
196 						    int ht, int vht, int he)
197 {
198 	u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
199 
200 	if (!ht && !vht && !he)
201 		return NR_CHAN_WIDTH_20;
202 	if (!hapd->iconf->secondary_channel)
203 		return NR_CHAN_WIDTH_20;
204 	if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
205 		return NR_CHAN_WIDTH_40;
206 	if (oper_chwidth == CHANWIDTH_80MHZ)
207 		return NR_CHAN_WIDTH_80;
208 	if (oper_chwidth == CHANWIDTH_160MHZ)
209 		return NR_CHAN_WIDTH_160;
210 	if (oper_chwidth == CHANWIDTH_80P80MHZ)
211 		return NR_CHAN_WIDTH_80P80;
212 	return NR_CHAN_WIDTH_20;
213 }
214 #endif /* NEED_AP_MLME */
215 
216 
217 void 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;
223 	int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
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 		if (he)
260 			bssid_info |= NEI_REP_BSSID_INFO_HE;
261 	}
262 
263 	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
264 
265 	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
266 					  hapd->iconf->secondary_channel,
267 					  hostapd_get_oper_chwidth(hapd->iconf),
268 					  &op_class, &channel) ==
269 	    NUM_HOSTAPD_MODES)
270 		return;
271 	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
272 	if (vht) {
273 		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
274 			hapd->iconf);
275 		if (width == NR_CHAN_WIDTH_80P80)
276 			center_freq2_idx =
277 				hostapd_get_oper_centr_freq_seg1_idx(
278 					hapd->iconf);
279 	} else if (ht) {
280 		ieee80211_freq_to_chan(hapd->iface->freq +
281 				       10 * hapd->iconf->secondary_channel,
282 				       &center_freq1_idx);
283 	}
284 
285 	ssid.ssid_len = hapd->conf->ssid.ssid_len;
286 	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
287 
288 	/*
289 	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
290 	 * phy type + wide bandwidth channel subelement.
291 	 */
292 	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
293 	if (!nr)
294 		return;
295 
296 	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
297 	wpabuf_put_le32(nr, bssid_info);
298 	wpabuf_put_u8(nr, op_class);
299 	wpabuf_put_u8(nr, channel);
300 	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
301 
302 	/*
303 	 * Wide Bandwidth Channel subelement may be needed to allow the
304 	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
305 	 * Figure 9-301.
306 	 */
307 	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
308 	wpabuf_put_u8(nr, 3);
309 	wpabuf_put_u8(nr, width);
310 	wpabuf_put_u8(nr, center_freq1_idx);
311 	wpabuf_put_u8(nr, center_freq2_idx);
312 
313 	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
314 			     hapd->iconf->civic, hapd->iconf->stationary_ap);
315 
316 	wpabuf_free(nr);
317 #endif /* NEED_AP_MLME */
318 }
319