hostapd: Add a database of neighboring APs
authorDavid Spinadel <david.spinadel@intel.com>
Wed, 6 Apr 2016 16:42:07 +0000 (19:42 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 16 Apr 2016 18:05:40 +0000 (21:05 +0300)
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 <BSSID> <ssid=SSID> <nr=data> [lci=<data>] [civic=<data>]

To delete a neighbor use:
REMOVE_NEIGHBOR <BSSID> <SSID>

Signed-off-by: David Spinadel <david.spinadel@intel.com>
hostapd/Android.mk
hostapd/Makefile
hostapd/ctrl_iface.c
hostapd/hostapd_cli.c
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/neighbor_db.c [new file with mode: 0644]
src/ap/neighbor_db.h [new file with mode: 0644]
wpa_supplicant/Android.mk
wpa_supplicant/Makefile

index 67ca129..42f67bc 100644 (file)
@@ -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 =
index fa4af82..35b055c 100644 (file)
@@ -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
 
index 6f22f65..dd115df 100644 (file)
@@ -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;
index bf86d37..ec91f7e 100644 (file)
@@ -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 }
 };
 
index ee80f4f..1ad7510 100644 (file)
@@ -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;
index 75a7c04..a06b727 100644 (file)
@@ -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 (file)
index 0000000..1156aa2
--- /dev/null
@@ -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 (file)
index 0000000..40c42e7
--- /dev/null
@@ -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 */
index f65076c..d1adda5 100644 (file)
@@ -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
index 550d44b..06c26d2 100644 (file)
@@ -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