UPSTREAM: hostapd: Add a database of neighbor 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.

[cherry pick from 9b4b226426b169d0e9d0292cd8a0c822df867bd8]
[git remote add hostap git://w1.fi/srv/git/hostap.git]

BUG=b:36055223
TEST=Executed below commands
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>

Change-Id: Id8fe67d204d5d4db89ed392dc72e8c562386625b
Reviewed-on: https://chromium-review.googlesource.com/456538
Commit-Ready: NARAYANRADDI MASTI <nmasti@google.com>
Tested-by: NARAYANRADDI MASTI <nmasti@google.com>
Reviewed-by: Srinivasa duvvuri <sduvvuri@chromium.org>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 5885f2b..a6f1e7a 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -97,6 +97,7 @@
 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 96725ab..4a13121 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -67,6 +67,7 @@
 OBJS += ../src/ap/bss_load.o
 OBJS += ../src/ap/monitor_sta.o
 OBJS += ../src/ap/blacklist.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 ccbcf04..6e63bc6 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -42,6 +42,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"
@@ -2080,6 +2081,123 @@
 	return monitor_sta_set_hysteresis(hapd->mon_sta, atoi(buf));
 }
 
+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,
@@ -2322,6 +2440,12 @@
 	} else if (os_strncmp(buf, "BLACKLIST_ADD ", 14) == 0) {
 		if (hostapd_ctrl_iface_blacklist_add(hapd, buf + 14))
 			reply_len = -1;
+	} 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 46c2f37..a5acce5 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1068,6 +1068,49 @@
 }
 
 
+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[]);
@@ -1126,6 +1169,8 @@
 	{ "disable", hostapd_cli_cmd_disable },
 	{ "erp_flush", hostapd_cli_cmd_erp_flush },
 	{ "log_level", hostapd_cli_cmd_log_level },
+	{ "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 461ccd5..83987a2 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -42,6 +42,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);
@@ -338,6 +339,8 @@
 	wpabuf_free(hapd->mesh_pending_auth);
 	hapd->mesh_pending_auth = NULL;
 #endif /* CONFIG_MESH */
+
+	hostpad_free_neighbor_db(hapd);
 }
 
 
@@ -907,6 +910,7 @@
 		return -1;
 	}
 	hapd->started = 1;
+	dl_list_init(&hapd->nr_db);
 
 	if (!first || first == -1) {
 		if (hostapd_mac_comp_empty(conf->bssid) == 0) {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 456adad..6b83524 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -101,6 +101,16 @@
 	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
@@ -282,6 +292,8 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	mon_sta_t mon_sta;
 	struct hapd_blacklist *blacklist;
+
+	struct dl_list nr_db;
 };
 
 
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
new file mode 100644
index 0000000..1156aa2
--- /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 0000000..40c42e7
--- /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 0d818ed..1233665 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -820,6 +820,7 @@
 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 ad9ead9..5464b18 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -833,6 +833,7 @@
 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