}
+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")
*/
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")
}
+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
#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);
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);
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;
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'",
"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, ' ');
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 */
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_blacklist,
&ssid->num_bssid_blacklist,
- "bssid_blacklist", 1);
+ "bssid_blacklist", 1, 1);
}
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_whitelist,
&ssid->num_bssid_whitelist,
- "bssid_whitelist", 1);
+ "bssid_whitelist", 1, 1);
}
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);
}
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 */
}
+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;
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;
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 &&
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;
}
"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))
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 */
}
# 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.