]>
Commit | Line | Data |
---|---|---|
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 | 18 | struct hostapd_neighbor_entry * |
9b4b2264 DS |
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 && | |
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 |
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 | ||
9b4b2264 DS |
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)); | |
451a27b1 | 101 | nr->stationary = 0; |
9b4b2264 DS |
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, | |
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 | ||
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 | ||
dc679823 | 181 | void 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 | |
195 | static 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 | ||
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; | |
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 | ¢er_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 | } |