[OpenWrt-Devel] hostapd: backport BSSID black/whitelists
diff mbox

Message ID 20150121135928.GL13405@zirkel.wertarbyte.de
State Accepted
Delegated to: Felix Fietkau
Headers show

Commit Message

Stefan Tomanek Jan. 21, 2015, 1:59 p.m. UTC
This change adds the configuration options "bssid_whitelist" and
"bssid_blacklist" used to limit the AP selection of a network to a
specified (finite) set or discard certain APs.

This can be useful for environments where multiple networks operate
using the same SSID and roaming between those is not desired. It is also
useful to ignore a faulty or otherwise unwanted AP.

In many applications it is useful not just to enumerate a group of well
known access points, but to use a address/mask notation to match an
entire set of addresses (ca:ff:ee:00:00:00/ff:ff:ff:00:00:00).

This is especially useful if an OpenWrt device with two radios is used to
retransmit the same network (one in AP mode for other clients, one as STA for
the uplink); the following configuration prevents the device from associating
with itself, given that the own AP to be avoided is using the bssid
'C0:FF:EE:D0:0D:42':

config wifi-iface
	option device 'radio2'
	option network 'uplink'
	option mode 'sta'
	option ssid 'MyNetwork'
	option encryption 'none'
	list bssid_blacklist 'C0:FF:EE:D0:0D:42/00:FF:FF:FF:FF:FF'

This change consists of the following cherry-picked upstream commits:

b3d6a0a8259002448a29f14855d58fe0a624ab76
b83e455451a875ba233b3b8ac29aff8b62f064f2
79cd993a623e101952b81fa6a29c674cd858504f
(squashed to implement bssid_{white,black}lists)

0047306bc9ab7d46e8cc22ff9a3e876c47626473
(Add os_snprintf_error() helper)

Signed-off-by: Stefan Tomanek <stefan.tomanek+openwrt@wertarbyte.de>
---
 package/network/services/hostapd/files/netifd.sh   |    9 +
 .../patches/700-Add-os_snprintf_error-helper.patch |   29 +
 ...work-specific-BSSID-black-and-white-lists.patch |  649 ++++++++++++++++++++
 3 files changed, 687 insertions(+), 0 deletions(-)
 create mode 100644 package/network/services/hostapd/patches/700-Add-os_snprintf_error-helper.patch
 create mode 100644 package/network/services/hostapd/patches/710-Add-network-specific-BSSID-black-and-white-lists.patch

Comments

Stefan Tomanek Jan. 28, 2015, 4:56 p.m. UTC | #1
Dies schrieb Stefan Tomanek (stefan.tomanek+openwrt@wertarbyte.de):

> This change adds the configuration options "bssid_whitelist" and
> "bssid_blacklist" used to limit the AP selection of a network to a
> specified (finite) set or discard certain APs.

Any feedback regarding this backported feature?
John Crispin Jan. 28, 2015, 5:19 p.m. UTC | #2
On 28/01/2015 17:56, Stefan Tomanek wrote:
> Dies schrieb Stefan Tomanek
> (stefan.tomanek+openwrt@wertarbyte.de):
> 
>> This change adds the configuration options "bssid_whitelist" and 
>> "bssid_blacklist" used to limit the AP selection of a network to
>> a specified (finite) set or discard certain APs.
> 
> Any feedback regarding this backported feature?

this patch is fine, the log file one scares me though as there is no
log rotate logic.

> _______________________________________________ openwrt-devel
> mailing list openwrt-devel@lists.openwrt.org 
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>
Stefan Tomanek Jan. 28, 2015, 5:49 p.m. UTC | #3
Dies schrieb John Crispin (blogic@openwrt.org):

> > Any feedback regarding this backported feature?
> 
> this patch is fine, the log file one scares me though as there is no
> log rotate logic.

What kind of log rotate logic do you mean? That can be implemented externally
if needed: just move the old logfile away and send SIGHUP to the hostapd
process, to make it reopen (and recreate) the file.
Stefan Tomanek Feb. 11, 2015, 2:42 p.m. UTC | #4
Dies schrieb Stefan Tomanek (stefan.tomanek+openwrt@wertarbyte.de):

> > > Any feedback regarding this backported feature?
> > 
> > this patch is fine, the log file one scares me though as there is no
> > log rotate logic.
> 
> What kind of log rotate logic do you mean? That can be implemented externally
> if needed: just move the old logfile away and send SIGHUP to the hostapd
> process, to make it reopen (and recreate) the file.

Any more feedback regarding both patches?
John Crispin Feb. 11, 2015, 2:44 p.m. UTC | #5
On 11/02/2015 15:42, Stefan Tomanek wrote:
> Dies schrieb Stefan Tomanek (stefan.tomanek+openwrt@wertarbyte.de):
> 
>>>> Any feedback regarding this backported feature?
>>>
>>> this patch is fine, the log file one scares me though as there is no
>>> log rotate logic.
>>
>> What kind of log rotate logic do you mean? That can be implemented externally
>> if needed: just move the old logfile away and send SIGHUP to the hostapd
>> process, to make it reopen (and recreate) the file.
> 
> Any more feedback regarding both patches?

still on my todo, sorry for the delay it'll take a few more days till i
can go over all the hostapd patches in the queue.




> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>
Stefan Tomanek Feb. 11, 2015, 5:42 p.m. UTC | #6
Dies schrieb John Crispin (blogic@openwrt.org):

> > Any more feedback regarding both patches?
> 
> still on my todo, sorry for the delay it'll take a few more days till i
> can go over all the hostapd patches in the queue.

Nevermind, I just wanted to make sure that it didn't slide off the table :-)
John Crispin Feb. 13, 2015, 10:55 a.m. UTC | #7
this one is ok, just pulled it into my tree

On 11/02/2015 18:42, Stefan Tomanek wrote:
> Dies schrieb John Crispin (blogic@openwrt.org):
> 
>>> Any more feedback regarding both patches?
>>
>> still on my todo, sorry for the delay it'll take a few more days till i
>> can go over all the hostapd patches in the queue.
> 
> Nevermind, I just wanted to make sure that it didn't slide off the table :-)
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>

Patch
diff mbox

diff --git a/package/network/services/hostapd/files/netifd.sh b/package/network/services/hostapd/files/netifd.sh
index d625709..ed2a631 100644
--- a/package/network/services/hostapd/files/netifd.sh
+++ b/package/network/services/hostapd/files/netifd.sh
@@ -153,6 +153,9 @@  hostapd_common_add_bss_config() {
 	config_add_string macfilter 'macfile:file'
 	config_add_array 'maclist:list(macaddr)'
 
+	config_add_array bssid_blacklist
+	config_add_array bssid_whitelist
+
 	config_add_int mcast_rate
 	config_add_array basic_rate
 	config_add_array supported_rates
@@ -580,6 +583,12 @@  wpa_supplicant_add_network() {
 	[ -n "$bssid" ] && append network_data "bssid=$bssid" "$N$T"
 	[ -n "$beacon_int" ] && append network_data "beacon_int=$beacon_int" "$N$T"
 
+	local bssid_blacklist bssid_whitelist
+	json_get_values bssid_blacklist bssid_blacklist
+	json_get_values bssid_whitelist bssid_whitelist
+
+	[ -n "$bssid_blacklist" ] && append network_data "bssid_blacklist=$bssid_blacklist" "$N$T"
+	[ -n "$bssid_whitelist" ] && append network_data "bssid_whitelist=$bssid_whitelist" "$N$T"
 
 	[ -n "$basic_rate" ] && {
 		local br rate_list=
diff --git a/package/network/services/hostapd/patches/700-Add-os_snprintf_error-helper.patch b/package/network/services/hostapd/patches/700-Add-os_snprintf_error-helper.patch
new file mode 100644
index 0000000..51ad1c4
--- /dev/null
+++ b/package/network/services/hostapd/patches/700-Add-os_snprintf_error-helper.patch
@@ -0,0 +1,29 @@ 
+Add os_snprintf_error() helper
+
+This can be used to check os_snprintf() return value more consistently.
+
+Signed-off-by: Jouni Malinen <j@w1.fi>
+---
+ src/utils/os.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/utils/os.h b/src/utils/os.h
+index b9247d8..77250d6 100644
+--- a/src/utils/os.h
++++ b/src/utils/os.h
+@@ -549,6 +549,12 @@ char * os_strdup(const char *s);
+ #endif /* OS_NO_C_LIB_DEFINES */
+ 
+ 
++static inline int os_snprintf_error(size_t size, int res)
++{
++	return res < 0 || (unsigned int) res >= size;
++}
++
++
+ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+ {
+ 	if (size && nmemb > (~(size_t) 0) / size)
+-- 
+2.1.3
+
diff --git a/package/network/services/hostapd/patches/710-Add-network-specific-BSSID-black-and-white-lists.patch b/package/network/services/hostapd/patches/710-Add-network-specific-BSSID-black-and-white-lists.patch
new file mode 100644
index 0000000..5b42158
--- /dev/null
+++ b/package/network/services/hostapd/patches/710-Add-network-specific-BSSID-black-and-white-lists.patch
@@ -0,0 +1,649 @@ 
+Add network specific BSSID black and white lists
+
+This change adds the configuration options "bssid_whitelist" and
+"bssid_blacklist" used to limit the AP selection of a network to a
+specified (finite) set or discard certain APs.
+
+This can be useful for environments where multiple networks operate
+using the same SSID and roaming between those is not desired. It is also
+useful to ignore a faulty or otherwise unwanted AP.
+
+In many applications it is useful not just to enumerate a group of well
+known access points, but to use a address/mask notation to match an
+entire set of addresses (ca:ff:ee:00:00:00/ff:ff:ff:00:00:00).
+
+This change expands the data structures used by MAC lists to include a
+mask indicating the significant (non-masked) portions of an address and
+extends the list parser to recognize mask suffixes.
+
+Signed-off-by: Stefan Tomanek <stefan.tomanek@wertarbyte.de>
+---
+ src/utils/common.c                 |  86 ++++++++++++--
+ src/utils/common.h                 |   3 +
+ wpa_supplicant/config.c            | 223 ++++++++++++++++++++++++-------------
+ wpa_supplicant/config_ssid.h       |  12 ++
+ wpa_supplicant/events.c            |  45 ++++++++
+ wpa_supplicant/p2p_supplicant.c    |  40 ++++---
+ wpa_supplicant/wpa_supplicant.conf |  15 +++
+ 7 files changed, 323 insertions(+), 101 deletions(-)
+
+diff --git a/src/utils/common.c b/src/utils/common.c
+index 9902004..dd6e4aa 100644
+--- a/src/utils/common.c
++++ b/src/utils/common.c
+@@ -36,6 +36,25 @@ int hex2byte(const char *hex)
+ }
+ 
+ 
++static const char * hwaddr_parse(const char *txt, u8 *addr)
++{
++	size_t i;
++
++	for (i = 0; i < ETH_ALEN; i++) {
++		int a;
++
++		a = hex2byte(txt);
++		if (a < 0)
++			return NULL;
++		txt += 2;
++		addr[i] = a;
++		if (i < ETH_ALEN - 1 && *txt++ != ':')
++			return NULL;
++	}
++	return txt;
++}
++
++
+ /**
+  * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
+  * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+@@ -44,25 +63,46 @@ int hex2byte(const char *hex)
+  */
+ int hwaddr_aton(const char *txt, u8 *addr)
+ {
+-	int i;
++	return hwaddr_parse(txt, addr) ? 0 : -1;
++}
+ 
+-	for (i = 0; i < 6; i++) {
+-		int a, b;
+ 
+-		a = hex2num(*txt++);
+-		if (a < 0)
+-			return -1;
+-		b = hex2num(*txt++);
+-		if (b < 0)
+-			return -1;
+-		*addr++ = (a << 4) | b;
+-		if (i < 5 && *txt++ != ':')
++/**
++ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
++ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
++ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
++ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
++ * @maskable: Flag to indicate whether a mask is allowed
++ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
++ */
++int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
++{
++	const char *r;
++
++	/* parse address part */
++	r = hwaddr_parse(txt, addr);
++	if (!r)
++		return -1;
++
++	/* check for optional mask */
++	if (*r == '\0' || isspace(*r)) {
++		/* no mask specified, assume default */
++		os_memset(mask, 0xff, ETH_ALEN);
++	} else if (maskable && *r == '/') {
++		/* mask specified and allowed */
++		r = hwaddr_parse(r + 1, mask);
++		/* parser error? */
++		if (!r)
+ 			return -1;
++	} else {
++		/* mask specified but not allowed or trailing garbage */
++		return -1;
+ 	}
+ 
+ 	return 0;
+ }
+ 
++
+ /**
+  * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+  * @txt: MAC address as a string (e.g., "001122334455")
+@@ -144,6 +184,30 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len)
+ }
+ 
+ 
++int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
++{
++	size_t i;
++	int print_mask = 0;
++	int res;
++
++	for (i = 0; i < ETH_ALEN; i++) {
++		if (mask[i] != 0xff) {
++			print_mask = 1;
++			break;
++		}
++	}
++
++	if (print_mask)
++		res = os_snprintf(buf, len, MACSTR "/" MACSTR,
++				  MAC2STR(addr), MAC2STR(mask));
++	else
++		res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
++	if (os_snprintf_error(len, res))
++		return -1;
++	return res;
++}
++
++
+ /**
+  * inc_byte_array - Increment arbitrary length byte array by one
+  * @counter: Pointer to byte array
+diff --git a/src/utils/common.h b/src/utils/common.h
+index 14d9ad1..1127074 100644
+--- a/src/utils/common.h
++++ b/src/utils/common.h
+@@ -468,6 +468,7 @@ typedef u64 __bitwise le64;
+ #endif /* __must_check */
+ 
+ int hwaddr_aton(const char *txt, u8 *addr);
++int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
+ int hwaddr_compact_aton(const char *txt, u8 *addr);
+ int hwaddr_aton2(const char *txt, u8 *addr);
+ int hex2byte(const char *hex);
+@@ -478,6 +479,8 @@ int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+ int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+ 			       size_t len);
+ 
++int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
++
+ #ifdef CONFIG_NATIVE_WINDOWS
+ void wpa_unicode2ascii_inplace(TCHAR *str);
+ TCHAR * wpa_strdup_tchar(const char *str);
+diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
+index 8d1e1e0..7f742cb 100644
+--- a/wpa_supplicant/config.c
++++ b/wpa_supplicant/config.c
+@@ -235,6 +235,99 @@ static char * wpa_config_write_int(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+ 
+ 
++static int wpa_config_parse_addr_list(const struct parse_data *data,
++				      int line, const char *value,
++				      u8 **list, size_t *num, char *name,
++				      u8 abort_on_error, u8 masked)
++{
++	const char *pos;
++	u8 *buf, *n, addr[2 * ETH_ALEN];
++	size_t count;
++
++	buf = NULL;
++	count = 0;
++
++	pos = value;
++	while (pos && *pos) {
++		while (*pos == ' ')
++			pos++;
++
++		if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
++			if (abort_on_error || count == 0) {
++				wpa_printf(MSG_ERROR,
++					   "Line %d: Invalid %s address '%s'",
++					   line, name, value);
++				os_free(buf);
++				return -1;
++			}
++			/* continue anyway since this could have been from a
++			 * truncated configuration file line */
++			wpa_printf(MSG_INFO,
++				   "Line %d: Ignore likely truncated %s address '%s'",
++				   line, name, pos);
++		} else {
++			n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
++			if (n == NULL) {
++				os_free(buf);
++				return -1;
++			}
++			buf = n;
++			os_memmove(buf + 2 * ETH_ALEN, buf,
++				   count * 2 * ETH_ALEN);
++			os_memcpy(buf, addr, 2 * ETH_ALEN);
++			count++;
++			wpa_printf(MSG_MSGDUMP,
++				   "%s: addr=" MACSTR " mask=" MACSTR,
++				   name, MAC2STR(addr),
++				   MAC2STR(&addr[ETH_ALEN]));
++		}
++
++		pos = os_strchr(pos, ' ');
++	}
++
++	os_free(*list);
++	*list = buf;
++	*num = count;
++
++	return 0;
++}
++
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_addr_list(const struct parse_data *data,
++					 const u8 *list, size_t num, char *name)
++{
++	char *value, *end, *pos;
++	int res;
++	size_t i;
++
++	if (list == NULL || num == 0)
++		return NULL;
++
++	value = os_malloc(2 * 20 * num);
++	if (value == NULL)
++		return NULL;
++	pos = value;
++	end = value + 2 * 20 * num;
++
++	for (i = num; i > 0; i--) {
++		const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
++		const u8 *m = a + ETH_ALEN;
++
++		if (i < num)
++			*pos++ = ' ';
++		res = hwaddr_mask_txt(pos, end - pos, a, m);
++		if (res < 0) {
++			os_free(value);
++			return NULL;
++		}
++		pos += res;
++	}
++
++	return value;
++}
++#endif /* NO_CONFIG_WRITE */
++
+ static int wpa_config_parse_bssid(const struct parse_data *data,
+ 				  struct wpa_ssid *ssid, int line,
+ 				  const char *value)
+@@ -280,6 +373,50 @@ static char * wpa_config_write_bssid(const struct parse_data *data,
+ #endif /* NO_CONFIG_WRITE */
+ 
+ 
++static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
++					    struct wpa_ssid *ssid, int line,
++					    const char *value)
++{
++	return wpa_config_parse_addr_list(data, line, value,
++					  &ssid->bssid_blacklist,
++					  &ssid->num_bssid_blacklist,
++					  "bssid_blacklist", 1, 1);
++}
++
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
++					       struct wpa_ssid *ssid)
++{
++	return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
++					  ssid->num_bssid_blacklist,
++					  "bssid_blacklist");
++}
++#endif /* NO_CONFIG_WRITE */
++
++
++static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
++					    struct wpa_ssid *ssid, int line,
++					    const char *value)
++{
++	return wpa_config_parse_addr_list(data, line, value,
++					  &ssid->bssid_whitelist,
++					  &ssid->num_bssid_whitelist,
++					  "bssid_whitelist", 1, 1);
++}
++
++
++#ifndef NO_CONFIG_WRITE
++static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
++					       struct wpa_ssid *ssid)
++{
++	return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
++					  ssid->num_bssid_whitelist,
++					  "bssid_whitelist");
++}
++#endif /* NO_CONFIG_WRITE */
++
++
+ static int wpa_config_parse_psk(const struct parse_data *data,
+ 				struct wpa_ssid *ssid, int line,
+ 				const char *value)
+@@ -1435,53 +1572,10 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+ 					    struct wpa_ssid *ssid, int line,
+ 					    const char *value)
+ {
+-	const char *pos;
+-	u8 *buf, *n, addr[ETH_ALEN];
+-	size_t count;
+-
+-	buf = NULL;
+-	count = 0;
+-
+-	pos = value;
+-	while (pos && *pos) {
+-		while (*pos == ' ')
+-			pos++;
+-
+-		if (hwaddr_aton(pos, addr)) {
+-			if (count == 0) {
+-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+-					   "p2p_client_list address '%s'.",
+-					   line, value);
+-				os_free(buf);
+-				return -1;
+-			}
+-			/* continue anyway since this could have been from a
+-			 * truncated configuration file line */
+-			wpa_printf(MSG_INFO, "Line %d: Ignore likely "
+-				   "truncated p2p_client_list address '%s'",
+-				   line, pos);
+-		} else {
+-			n = os_realloc_array(buf, count + 1, ETH_ALEN);
+-			if (n == NULL) {
+-				os_free(buf);
+-				return -1;
+-			}
+-			buf = n;
+-			os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
+-			os_memcpy(buf, addr, ETH_ALEN);
+-			count++;
+-			wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
+-				    addr, ETH_ALEN);
+-		}
+-
+-		pos = os_strchr(pos, ' ');
+-	}
+-
+-	os_free(ssid->p2p_client_list);
+-	ssid->p2p_client_list = buf;
+-	ssid->num_p2p_clients = count;
+-
+-	return 0;
++	return wpa_config_parse_addr_list(data, line, value,
++					  &ssid->p2p_client_list,
++					  &ssid->num_p2p_clients,
++					  "p2p_client_list", 0, 0);
+ }
+ 
+ 
+@@ -1489,34 +1583,9 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+ static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+ 					       struct wpa_ssid *ssid)
+ {
+-	char *value, *end, *pos;
+-	int res;
+-	size_t i;
+-
+-	if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
+-		return NULL;
+-
+-	value = os_malloc(20 * ssid->num_p2p_clients);
+-	if (value == NULL)
+-		return NULL;
+-	pos = value;
+-	end = value + 20 * ssid->num_p2p_clients;
+-
+-	for (i = ssid->num_p2p_clients; i > 0; i--) {
+-		res = os_snprintf(pos, end - pos, MACSTR " ",
+-				  MAC2STR(ssid->p2p_client_list +
+-					  (i - 1) * ETH_ALEN));
+-		if (res < 0 || res >= end - pos) {
+-			os_free(value);
+-			return NULL;
+-		}
+-		pos += res;
+-	}
+-
+-	if (pos > value)
+-		pos[-1] = '\0';
+-
+-	return value;
++	return wpa_config_write_addr_list(data, ssid->p2p_client_list,
++					  ssid->num_p2p_clients,
++					  "p2p_client_list");
+ }
+ #endif /* NO_CONFIG_WRITE */
+ 
+@@ -1667,6 +1736,8 @@ static const struct parse_data ssid_fields[] = {
+ 	{ STR_RANGE(ssid, 0, MAX_SSID_LEN) },
+ 	{ INT_RANGE(scan_ssid, 0, 1) },
+ 	{ FUNC(bssid) },
++	{ FUNC(bssid_blacklist) },
++	{ FUNC(bssid_whitelist) },
+ 	{ FUNC_KEY(psk) },
+ 	{ FUNC(proto) },
+ 	{ FUNC(key_mgmt) },
+@@ -1971,6 +2042,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
+ 	os_free(ssid->freq_list);
+ 	os_free(ssid->bgscan);
+ 	os_free(ssid->p2p_client_list);
++	os_free(ssid->bssid_blacklist);
++	os_free(ssid->bssid_whitelist);
+ #ifdef CONFIG_HT_OVERRIDES
+ 	os_free(ssid->ht_mcs);
+ #endif /* CONFIG_HT_OVERRIDES */
+diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
+index a4910d0..7443207 100644
+--- a/wpa_supplicant/config_ssid.h
++++ b/wpa_supplicant/config_ssid.h
+@@ -128,6 +128,18 @@ struct wpa_ssid {
+ 	u8 bssid[ETH_ALEN];
+ 
+ 	/**
++	 * bssid_blacklist - List of inacceptable BSSIDs
++	 */
++	u8 *bssid_blacklist;
++	size_t num_bssid_blacklist;
++
++	/**
++	 * bssid_blacklist - List of acceptable BSSIDs
++	 */
++	u8 *bssid_whitelist;
++	size_t num_bssid_whitelist;
++
++	/**
+ 	 * bssid_set - Whether BSSID is configured for this network
+ 	 */
+ 	int bssid_set;
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 6761c1a..855653c 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -703,6 +703,33 @@ static int bss_is_ess(struct wpa_bss *bss)
+ }
+ 
+ 
++static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
++{
++	size_t i;
++
++	for (i = 0; i < ETH_ALEN; i++) {
++		if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
++			return 0;
++	}
++	return 1;
++}
++
++
++static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
++{
++	size_t i;
++
++	for (i = 0; i < num; i++) {
++		const u8 *a = list + i * ETH_ALEN * 2;
++		const u8 *m = a + ETH_ALEN;
++
++		if (match_mac_mask(a, addr, m))
++			return 1;
++	}
++	return 0;
++}
++
++
+ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+ 					    int i, struct wpa_bss *bss,
+ 					    struct wpa_ssid *group,
+@@ -827,6 +854,24 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+ 			continue;
+ 		}
+ 
++		/* check blacklist */
++		if (ssid->num_bssid_blacklist &&
++		    addr_in_list(bss->bssid, ssid->bssid_blacklist,
++				 ssid->num_bssid_blacklist)) {
++			wpa_dbg(wpa_s, MSG_DEBUG,
++				"   skip - BSSID blacklisted");
++			continue;
++		}
++
++		/* if there is a whitelist, only accept those APs */
++		if (ssid->num_bssid_whitelist &&
++		    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
++				  ssid->num_bssid_whitelist)) {
++			wpa_dbg(wpa_s, MSG_DEBUG,
++				"   skip - BSSID not in whitelist");
++			continue;
++		}
++
+ 		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+ 			continue;
+ 
+diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
+index 8cd43df..60dcb5d 100644
+--- a/wpa_supplicant/p2p_supplicant.c
++++ b/wpa_supplicant/p2p_supplicant.c
+@@ -831,7 +831,7 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
+-		if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
++		if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
+ 			      ETH_ALEN) != 0)
+ 			continue;
+ 
+@@ -839,32 +839,42 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+ 			return; /* already the most recent entry */
+ 
+ 		/* move the entry to mark it most recent */
+-		os_memmove(s->p2p_client_list + i * ETH_ALEN,
+-			   s->p2p_client_list + (i + 1) * ETH_ALEN,
+-			   (s->num_p2p_clients - i - 1) * ETH_ALEN);
++		os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
++			   s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
++			   (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+ 		os_memcpy(s->p2p_client_list +
+-			  (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN);
++			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
++			  ETH_ALEN);
++		os_memset(s->p2p_client_list +
++			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
++			  0xff, ETH_ALEN);
+ 		found = 1;
+ 		break;
+ 	}
+ 
+ 	if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
+ 		n = os_realloc_array(s->p2p_client_list,
+-				     s->num_p2p_clients + 1, ETH_ALEN);
++				     s->num_p2p_clients + 1, 2 * ETH_ALEN);
+ 		if (n == NULL)
+ 			return;
+-		os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
++		os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
++			  ETH_ALEN);
++		os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
++			  0xff, ETH_ALEN);
+ 		s->p2p_client_list = n;
+ 		s->num_p2p_clients++;
+ 	} else if (!found && s->p2p_client_list) {
+ 		/* Not enough room for an additional entry - drop the oldest
+ 		 * entry */
+ 		os_memmove(s->p2p_client_list,
+-			   s->p2p_client_list + ETH_ALEN,
+-			   (s->num_p2p_clients - 1) * ETH_ALEN);
++			   s->p2p_client_list + 2 * ETH_ALEN,
++			   (s->num_p2p_clients - 1) * 2 * ETH_ALEN);
+ 		os_memcpy(s->p2p_client_list +
+-			  (s->num_p2p_clients - 1) * ETH_ALEN,
++			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN,
+ 			  addr, ETH_ALEN);
++		os_memset(s->p2p_client_list +
++			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
++			  0xff, ETH_ALEN);
+ 	}
+ 
+ 	if (wpa_s->parent->conf->update_config &&
+@@ -3276,7 +3286,7 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
+ 		return;
+ 
+ 	for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
+-		if (os_memcmp(ssid->p2p_client_list + i * ETH_ALEN, peer,
++		if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
+ 			      ETH_ALEN) == 0)
+ 			break;
+ 	}
+@@ -3296,9 +3306,9 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
+ 		   "group %d client list%s",
+ 		   MAC2STR(peer), ssid->id,
+ 		   inv ? " due to invitation result" : "");
+-	os_memmove(ssid->p2p_client_list + i * ETH_ALEN,
+-		   ssid->p2p_client_list + (i + 1) * ETH_ALEN,
+-		   (ssid->num_p2p_clients - i - 1) * ETH_ALEN);
++	os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
++		   ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
++		   (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+ 	ssid->num_p2p_clients--;
+ 	if (wpa_s->parent->conf->update_config &&
+ 	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+@@ -6925,7 +6935,7 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ 		if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+ 			continue;
+ 		for (i = 0; i < s->num_p2p_clients; i++) {
+-			if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
++			if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+ 				      addr, ETH_ALEN) == 0)
+ 				return s; /* peer is P2P client in persistent
+ 					   * group */
+diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
+index 0e8a28a..4bc08db 100644
+--- a/wpa_supplicant/wpa_supplicant.conf
++++ b/wpa_supplicant/wpa_supplicant.conf
+@@ -1408,6 +1408,21 @@ network={
+ 	key_mgmt=NONE
+ }
+ 
++# Example configuration blacklisting two APs - these will be ignored
++# for this network.
++network={
++	ssid="example"
++	psk="very secret passphrase"
++	bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
++}
++
++# Example configuration limiting AP selection to a specific set of APs;
++# any other AP not matching the masked address will be ignored.
++network={
++	ssid="example"
++	psk="very secret passphrase"
++	bssid_whitelist=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff
++}
+ 
+ # Example config file that will only scan on channel 36.
+ freq_list=5180
+-- 
+2.1.3
+