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 <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>
This commit is contained in:
David Spinadel 2016-04-06 19:42:07 +03:00 committed by Jouni Malinen
parent bf767ac136
commit 9b4b226426
10 changed files with 341 additions and 0 deletions

View file

@ -96,6 +96,7 @@ OBJS += src/ap/pmksa_cache_auth.c
OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/ieee802_11_shared.c
OBJS += src/ap/beacon.c OBJS += src/ap/beacon.c
OBJS += src/ap/bss_load.c OBJS += src/ap/bss_load.c
OBJS += src/ap/neighbor_db.c
OBJS_d = OBJS_d =
OBJS_p = OBJS_p =
LIBS = LIBS =

View file

@ -84,6 +84,7 @@ OBJS += ../src/ap/pmksa_cache_auth.o
OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/ieee802_11_shared.o
OBJS += ../src/ap/beacon.o OBJS += ../src/ap/beacon.o
OBJS += ../src/ap/bss_load.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 OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o

View file

@ -47,6 +47,7 @@
#include "ap/wnm_ap.h" #include "ap/wnm_ap.h"
#include "ap/wpa_auth.h" #include "ap/wpa_auth.h"
#include "ap/beacon.h" #include "ap/beacon.h"
#include "ap/neighbor_db.h"
#include "wps/wps_defs.h" #include "wps/wps_defs.h"
#include "wps/wps.h" #include "wps/wps.h"
#include "fst/fst_ctrl_iface.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 */ #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, static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply, char *buf, char *reply,
int reply_size, int reply_size,
@ -2315,6 +2433,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_size); reply_size);
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
hostapd_ctrl_iface_pmksa_flush(hapd); 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 { } else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16); os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16; reply_len = 16;

View 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 { struct hostapd_cli_cmd {
const char *cmd; const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); 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 }, { "log_level", hostapd_cli_cmd_log_level },
{ "pmksa", hostapd_cli_cmd_pmksa }, { "pmksa", hostapd_cli_cmd_pmksa },
{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush }, { "pmksa_flush", hostapd_cli_cmd_pmksa_flush },
{ "set_neighbor", hostapd_cli_cmd_set_neighbor },
{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor },
{ NULL, NULL } { NULL, NULL }
}; };

View file

@ -43,6 +43,7 @@
#include "x_snoop.h" #include "x_snoop.h"
#include "dhcp_snoop.h" #include "dhcp_snoop.h"
#include "ndisc_snoop.h" #include "ndisc_snoop.h"
#include "neighbor_db.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); 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); wpabuf_free(hapd->mesh_pending_auth);
hapd->mesh_pending_auth = NULL; hapd->mesh_pending_auth = NULL;
#endif /* CONFIG_MESH */ #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; return -1;
} }
hapd->started = 1; hapd->started = 1;
dl_list_init(&hapd->nr_db);
if (!first || first == -1) { if (!first || first == -1) {
u8 *addr = hapd->own_addr; u8 *addr = hapd->own_addr;

View file

@ -99,6 +99,16 @@ struct wps_stat {
u8 peer_addr[ETH_ALEN]; 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 * struct hostapd_data - hostapd per-BSS data structure
@ -286,6 +296,8 @@ struct hostapd_data {
#ifdef CONFIG_MBO #ifdef CONFIG_MBO
unsigned int mbo_assoc_disallow; unsigned int mbo_assoc_disallow;
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
struct dl_list nr_db;
}; };

131
src/ap/neighbor_db.c Normal file
View file

@ -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);
}
}

21
src/ap/neighbor_db.h Normal file
View file

@ -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 */

View file

@ -797,6 +797,7 @@ OBJS += src/ap/ap_drv_ops.c
OBJS += src/ap/beacon.c OBJS += src/ap/beacon.c
OBJS += src/ap/bss_load.c OBJS += src/ap/bss_load.c
OBJS += src/ap/eap_user_db.c OBJS += src/ap/eap_user_db.c
OBJS += src/ap/neighbor_db.c
ifdef CONFIG_IEEE80211N ifdef CONFIG_IEEE80211N
OBJS += src/ap/ieee802_11_ht.c OBJS += src/ap/ieee802_11_ht.c
ifdef CONFIG_IEEE80211AC ifdef CONFIG_IEEE80211AC

View file

@ -844,6 +844,7 @@ OBJS += ../src/ap/ap_drv_ops.o
OBJS += ../src/ap/beacon.o OBJS += ../src/ap/beacon.o
OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/bss_load.o
OBJS += ../src/ap/eap_user_db.o OBJS += ../src/ap/eap_user_db.o
OBJS += ../src/ap/neighbor_db.o
ifdef CONFIG_IEEE80211N ifdef CONFIG_IEEE80211N
OBJS += ../src/ap/ieee802_11_ht.o OBJS += ../src/ap/ieee802_11_ht.o
ifdef CONFIG_IEEE80211AC ifdef CONFIG_IEEE80211AC