From 9b4b226426b169d0e9d0292cd8a0c822df867bd8 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 6 Apr 2016 19:42:07 +0300 Subject: [PATCH] hostapd: Add a database of neighboring APs Add a configurable neighbor database that includes the content of Nighbor Report element, LCI and Location Civic subelements and SSID. All parameters for a neighbor must be updated at once; Neighbor Report element and SSID are mandatory, LCI and civic are optional. The age of LCI is set to the time of neighbor update. The control interface API is: SET_NEIGHBOR [lci=] [civic=] To delete a neighbor use: REMOVE_NEIGHBOR Signed-off-by: David Spinadel --- hostapd/Android.mk | 1 + hostapd/Makefile | 1 + hostapd/ctrl_iface.c | 124 ++++++++++++++++++++++++++++++++++++ hostapd/hostapd_cli.c | 45 +++++++++++++ src/ap/hostapd.c | 4 ++ src/ap/hostapd.h | 12 ++++ src/ap/neighbor_db.c | 131 ++++++++++++++++++++++++++++++++++++++ src/ap/neighbor_db.h | 21 ++++++ wpa_supplicant/Android.mk | 1 + wpa_supplicant/Makefile | 1 + 10 files changed, 341 insertions(+) create mode 100644 src/ap/neighbor_db.c create mode 100644 src/ap/neighbor_db.h diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 67ca1295b..42f67bc1a 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -96,6 +96,7 @@ OBJS += src/ap/pmksa_cache_auth.c OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/beacon.c OBJS += src/ap/bss_load.c +OBJS += src/ap/neighbor_db.c OBJS_d = OBJS_p = LIBS = diff --git a/hostapd/Makefile b/hostapd/Makefile index fa4af82a5..35b055c66 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -84,6 +84,7 @@ OBJS += ../src/ap/pmksa_cache_auth.o OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/beacon.o OBJS += ../src/ap/bss_load.o +OBJS += ../src/ap/neighbor_db.o OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 6f22f6598..dd115df1d 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -47,6 +47,7 @@ #include "ap/wnm_ap.h" #include "ap/wpa_auth.h" #include "ap/beacon.h" +#include "ap/neighbor_db.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -2071,6 +2072,123 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + struct wpabuf *nr, *lci = NULL, *civic = NULL; + char *tmp; + int ret; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); + return -1; + } + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad or missing SSID"); + return -1; + } + buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); + if (!buf) + return -1; + + tmp = os_strstr(buf, "nr="); + if (!tmp) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); + return -1; + } + + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + + nr = wpabuf_parse_bin(tmp + 3); + if (!nr) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); + return -1; + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "lci="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + lci = wpabuf_parse_bin(tmp + 4); + if (!lci) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad LCI subelement"); + wpabuf_free(nr); + return -1; + } + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "civic="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + civic = wpabuf_parse_bin(tmp + 6); + if (!civic) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad civic subelement"); + wpabuf_free(nr); + wpabuf_free(lci); + return -1; + } + } + +set: + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic); + + wpabuf_free(nr); + wpabuf_free(lci); + wpabuf_free(civic); + + return ret; +} + + +static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd, + char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + char *tmp; + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID"); + return -1; + } + + return hostapd_neighbor_remove(hapd, bssid, &ssid); +} + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, @@ -2315,6 +2433,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { + if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { + if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index bf86d376e..ec91f7ec8 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1143,6 +1143,49 @@ static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc < 3 || argc > 5) { + printf("Invalid set_neighbor command: needs 3-5 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s", + argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", + argc == 5 ? argv[4] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[400]; + int res; + + if (argc != 2) { + printf("Invalid remove_neighbor command: needs 2 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REMOVE_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1204,6 +1247,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "log_level", hostapd_cli_cmd_log_level }, { "pmksa", hostapd_cli_cmd_pmksa }, { "pmksa_flush", hostapd_cli_cmd_pmksa_flush }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor }, + { "remove_neighbor", hostapd_cli_cmd_remove_neighbor }, { NULL, NULL } }; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index ee80f4f77..1ad751089 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -43,6 +43,7 @@ #include "x_snoop.h" #include "dhcp_snoop.h" #include "ndisc_snoop.h" +#include "neighbor_db.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -335,6 +336,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = NULL; #endif /* CONFIG_MESH */ + + hostpad_free_neighbor_db(hapd); } @@ -906,6 +909,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } hapd->started = 1; + dl_list_init(&hapd->nr_db); if (!first || first == -1) { u8 *addr = hapd->own_addr; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 75a7c0499..a06b72710 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -99,6 +99,16 @@ struct wps_stat { u8 peer_addr[ETH_ALEN]; }; +struct hostapd_neighbor_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct wpa_ssid_value ssid; + struct wpabuf *nr; + struct wpabuf *lci; + struct wpabuf *civic; + /* LCI update time */ + struct os_time lci_date; +}; /** * struct hostapd_data - hostapd per-BSS data structure @@ -286,6 +296,8 @@ struct hostapd_data { #ifdef CONFIG_MBO unsigned int mbo_assoc_disallow; #endif /* CONFIG_MBO */ + + struct dl_list nr_db; }; diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c new file mode 100644 index 000000000..1156aa2ad --- /dev/null +++ b/src/ap/neighbor_db.c @@ -0,0 +1,131 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "neighbor_db.h" + + +static struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 && + ssid->ssid_len == nr->ssid.ssid_len && + os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) == 0) + return nr; + } + return NULL; +} + + +static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) +{ + wpabuf_free(nr->nr); + nr->nr = NULL; + wpabuf_free(nr->lci); + nr->lci = NULL; + wpabuf_free(nr->civic); + nr->civic = NULL; + os_memset(nr->bssid, 0, sizeof(nr->bssid)); + os_memset(&nr->ssid, 0, sizeof(nr->ssid)); +} + + +static struct hostapd_neighbor_entry * +hostapd_neighbor_add(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr; + + nr = os_zalloc(sizeof(struct hostapd_neighbor_entry)); + if (!nr) + return NULL; + + dl_list_add(&hapd->nr_db, &nr->list); + + return nr; +} + + +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic) +{ + struct hostapd_neighbor_entry *entry; + + entry = hostapd_neighbor_get(hapd, bssid, ssid); + if (!entry) + entry = hostapd_neighbor_add(hapd); + if (!entry) + return -1; + + hostapd_neighbor_clear_entry(entry); + + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid)); + + entry->nr = wpabuf_dup(nr); + if (!entry->nr) + goto fail; + + if (lci) { + entry->lci = wpabuf_dup(lci); + if (!entry->lci || os_get_time(&entry->lci_date)) + goto fail; + } + + if (civic) { + entry->civic = wpabuf_dup(civic); + if (!entry->civic) + goto fail; + } + + return 0; + +fail: + hostapd_neighbor_remove(hapd, bssid, ssid); + return -1; +} + + +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, bssid, ssid); + if (!nr) + return -1; + + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + + return 0; +} + + +void hostpad_free_neighbor_db(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr, *prev; + + dl_list_for_each_safe(nr, prev, &hapd->nr_db, + struct hostapd_neighbor_entry, list) { + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + } +} diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h new file mode 100644 index 000000000..40c42e725 --- /dev/null +++ b/src/ap/neighbor_db.h @@ -0,0 +1,21 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NEIGHBOR_DB_H +#define NEIGHBOR_DB_H + +int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid, + const struct wpabuf *nr, const struct wpabuf *lci, + const struct wpabuf *civic); +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +void hostpad_free_neighbor_db(struct hostapd_data *hapd); + +#endif /* NEIGHBOR_DB_H */ diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index f65076c77..d1adda556 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -797,6 +797,7 @@ OBJS += src/ap/ap_drv_ops.c OBJS += src/ap/beacon.c OBJS += src/ap/bss_load.c OBJS += src/ap/eap_user_db.c +OBJS += src/ap/neighbor_db.c ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c ifdef CONFIG_IEEE80211AC diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 550d44b2e..06c26d24e 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -844,6 +844,7 @@ OBJS += ../src/ap/ap_drv_ops.o OBJS += ../src/ap/beacon.o OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o +OBJS += ../src/ap/neighbor_db.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o ifdef CONFIG_IEEE80211AC