From 79cd993a623e101952b81fa6a29c674cd858504f Mon Sep 17 00:00:00 2001 From: Stefan Tomanek Date: Mon, 5 Jan 2015 21:10:16 +0100 Subject: [PATCH] Add address masks to BSSID lists 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 --- src/utils/common.c | 86 ++++++++++++++++++++++++++---- src/utils/common.h | 3 ++ wpa_supplicant/config.c | 43 ++++++++------- wpa_supplicant/events.c | 17 +++++- wpa_supplicant/p2p_supplicant.c | 40 ++++++++------ wpa_supplicant/wpa_supplicant.conf | 4 +- 6 files changed, 145 insertions(+), 48 deletions(-) diff --git a/src/utils/common.c b/src/utils/common.c index 182c6a8ac..93f17227e 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 7eca4095b..d30ad761e 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); +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 8925705a3..a8106324e 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -238,10 +238,10 @@ 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 *buf, *n, addr[2 * ETH_ALEN]; size_t count; buf = NULL; @@ -252,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, &addr[ETH_ALEN], masked)) { if (abort_on_error || count == 0) { wpa_printf(MSG_ERROR, "Line %d: Invalid %s address '%s'", @@ -266,16 +266,20 @@ static int wpa_config_parse_addr_list(const struct parse_data *data, "Line %d: Ignore likely 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_memcpy(buf, addr, ETH_ALEN); + os_memmove(buf + 2 * ETH_ALEN, buf, + count * 2 * ETH_ALEN); + os_memcpy(buf, addr, 2 * ETH_ALEN); count++; - wpa_hexdump(MSG_MSGDUMP, name, addr, ETH_ALEN); + wpa_printf(MSG_MSGDUMP, + "%s: addr=" MACSTR " mask=" MACSTR, + name, MAC2STR(addr), + MAC2STR(&addr[ETH_ALEN])); } pos = os_strchr(pos, ' '); @@ -300,25 +304,26 @@ static char * wpa_config_write_addr_list(const struct parse_data *data, if (list == NULL || num == 0) return NULL; - value = os_malloc(20 * num); + value = os_malloc(2 * 20 * num); if (value == NULL) return NULL; pos = value; - end = value + 20 * num; + end = value + 2 * 20 * num; for (i = num; i > 0; i--) { - res = os_snprintf(pos, end - pos, MACSTR " ", - MAC2STR(list + (i - 1) * ETH_ALEN)); - if (os_snprintf_error(end - pos, res)) { + 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; } - if (pos > value) - pos[-1] = '\0'; - return value; } #endif /* NO_CONFIG_WRITE */ @@ -375,7 +380,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); } @@ -397,7 +402,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); } @@ -1588,7 +1593,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); } @@ -2101,6 +2106,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/events.c b/wpa_supplicant/events.c index 4dd9bc611..0e78ea23d 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -698,14 +698,27 @@ 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); + const u8 *a = list + i * ETH_ALEN * 2; + const u8 *m = a + ETH_ALEN; - if (os_memcmp(a, addr, ETH_ALEN) == 0) + if (match_mac_mask(a, addr, m)) return 1; } return 0; diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 42e50141a..59f95c355 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -837,7 +837,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; @@ -845,32 +845,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 && @@ -3348,7 +3358,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; } @@ -3368,9 +3378,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)) @@ -7047,7 +7057,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 4b2c3cc0e..cf5230a1b 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=02:55:ae:bc:de:f0 02:88:77:66:55:44 + 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.