xref: /freebsd/contrib/wpa/src/ap/neighbor_db.c (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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 static 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 
51 static struct hostapd_neighbor_entry *
52 hostapd_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 
66 int 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 
104 fail:
105 	hostapd_neighbor_remove(hapd, bssid, ssid);
106 	return -1;
107 }
108 
109 
110 int 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 
127 void 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
141 static 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 
163 void 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