[PATCH v2 3/3] Add address masks to BSSID lists
Stefan Tomanek
stefan.tomanek
Mon Jan 5 12:10:16 PST 2015
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 at wertarbyte.de>
---
src/utils/common.c | 83 ++++++++++++++++++++++++++++++++------
src/utils/common.h | 3 ++
wpa_supplicant/config.c | 22 +++++-----
wpa_supplicant/events.c | 15 ++++++-
wpa_supplicant/wpa_supplicant.conf | 4 +-
5 files changed, 101 insertions(+), 26 deletions(-)
diff --git a/src/utils/common.c b/src/utils/common.c
index 182c6a8..ff6d19d 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -35,6 +35,20 @@ int hex2byte(const char *hex)
return (a << 4) | b;
}
+static const char *hwaddr_parse(const char *txt, u8 *addr) {
+ size_t i;
+ for (i = 0; i < ETH_ALEN; i++) {
+ int a = 0;
+ 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)
@@ -44,22 +58,39 @@ 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;
+/**
+ * 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;
- a = hex2num(*txt++);
- if (a < 0)
- return -1;
- b = hex2num(*txt++);
- if (b < 0)
- return -1;
- *addr++ = (a << 4) | b;
- if (i < 5 && *txt++ != ':')
+ /* check for optional mask */
+ if (! *r || 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;
}
@@ -143,6 +174,34 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len)
return 0;
}
+static char *hwaddr_txt(char *buf, u8 *addr) {
+ size_t i;
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf += sprintf(buf, "%02x", addr[i]);
+ if (i < ETH_ALEN-1)
+ *buf++ = ':';
+ }
+ return buf;
+}
+
+size_t hwaddr_mask_txt(char *buf, u8 *addr, u8 *mask) {
+ size_t i;
+ char *pos = buf;
+ int print_mask = 0;
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (mask[i] != 0xFF) {
+ print_mask = 1;
+ break;
+ }
+ }
+ pos = hwaddr_txt(pos, addr);
+ if (print_mask) {
+ *pos++ = '/';
+ pos = hwaddr_txt(pos, mask);
+ }
+ *pos = '\0';
+ return pos - buf;
+}
/**
* inc_byte_array - Increment arbitrary length byte array by one
diff --git a/src/utils/common.h b/src/utils/common.h
index 7eca409..8ccebfb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -471,6 +471,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);
@@ -482,6 +483,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);
+size_t hwaddr_mask_txt(char *buf, u8 *addr, 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 d712522..427fe75 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -237,10 +237,11 @@ static char * wpa_config_write_int(const struct parse_data *data,
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 abort_on_error, u8 masked)
{
const char *pos;
u8 *buf, *n, addr[ETH_ALEN];
+ u8 mask[ETH_ALEN];
size_t count;
buf = NULL;
@@ -251,7 +252,7 @@ static int wpa_config_parse_addr_list(const struct parse_data *data,
while (*pos == ' ')
pos++;
- if (hwaddr_aton(pos, addr)) {
+ if (hwaddr_masked_aton(pos, addr, mask, masked)) {
if (abort_on_error || count == 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid "
"%s address '%s'.",
@@ -265,14 +266,15 @@ static int wpa_config_parse_addr_list(const struct parse_data *data,
"truncated %s address '%s'",
line, name, pos);
} else {
- n = os_realloc_array(buf, count + 1, ETH_ALEN);
+ n = os_realloc_array(buf, count + 1, 2*ETH_ALEN);
if (n == NULL) {
os_free(buf);
return -1;
}
buf = n;
- os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
+ os_memmove(buf + 2*ETH_ALEN, buf, count * 2*ETH_ALEN);
os_memcpy(buf, addr, ETH_ALEN);
+ os_memcpy(buf + ETH_ALEN, mask, ETH_ALEN);
count++;
wpa_hexdump(MSG_MSGDUMP, name, addr, ETH_ALEN);
}
@@ -305,9 +307,9 @@ static char * wpa_config_write_addr_list(const struct parse_data *data,
end = value + 20 * num;
for (i = num; i > 0; i--) {
- res = os_snprintf(pos, end - pos, MACSTR " ",
- MAC2STR(list +
- (i - 1) * ETH_ALEN));
+ u8 *a = list + (i-1) * 2*ETH_ALEN;
+ u8 *m = list + (i-1) * 2*ETH_ALEN + ETH_ALEN;
+ res = hwaddr_mask_txt(pos, a, m);
if (os_snprintf_error(end - pos, res)) {
os_free(value);
return NULL;
@@ -373,7 +375,7 @@ static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_blacklist,
&ssid->num_bssid_blacklist,
- "bssid_blacklist", 1);
+ "bssid_blacklist", 1, 1);
}
#ifndef NO_CONFIG_WRITE
@@ -394,7 +396,7 @@ static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_whitelist,
&ssid->num_bssid_whitelist,
- "bssid_whitelist", 1);
+ "bssid_whitelist", 1, 1);
}
#ifndef NO_CONFIG_WRITE
@@ -1585,7 +1587,7 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
return wpa_config_parse_addr_list(data, line, value,
&ssid->p2p_client_list,
&ssid->num_p2p_clients,
- "p2p_client_list", 0);
+ "p2p_client_list", 0, 0);
}
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 263b677..5f70be0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -697,11 +697,22 @@ static int bss_is_ess(struct wpa_bss *bss)
IEEE80211_CAP_ESS);
}
+static int match_mac_mask(u8 *addrA, u8 *addrB, u8 *mask) {
+ size_t i;
+ for (i = 0; i < ETH_ALEN; i++) {
+ if ((addrA[i] & mask[i]) != (addrB[i] & mask[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
static int addr_in_list(u8 *addr, u8 *list, size_t num) {
size_t i;
for (i = 0; i < num; i++) {
- u8 *a = list + (i*ETH_ALEN);
- if (os_memcmp(a, addr, ETH_ALEN) == 0) {
+ u8 *a = list + (i*ETH_ALEN*2);
+ u8 *m = list + (i*ETH_ALEN*2) + ETH_ALEN;
+ if (match_mac_mask(a, addr, m)) {
return 1;
}
}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index a80bf21..38bfce0 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1440,11 +1440,11 @@ network={
}
# Example configuration limiting AP selection to a specific set of APs;
-# any other AP will be ignored for this network entry
+# any other AP not matching the masked address will be ignored
network={
ssid="example"
psk="very secret passphrase"
- bssid_whitelist=ca:fe:ba:be:d0:0d de:ad:be:ef:00:00
+ bssid_whitelist=ca:fe:ba:be:00:00/FF:FF:FF:FF:00:00 00:00:F0:0F:D0:0D/00:00:FF:FF:FF:FF
}
# Example config file that will only scan on channel 36.
--
2.1.3
More information about the Hostap
mailing list