]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Add address masks to BSSID lists
authorStefan Tomanek <stefan.tomanek@wertarbyte.de>
Mon, 5 Jan 2015 20:10:16 +0000 (21:10 +0100)
committerJouni Malinen <j@w1.fi>
Sat, 10 Jan 2015 15:35:53 +0000 (17:35 +0200)
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
src/utils/common.h
wpa_supplicant/config.c
wpa_supplicant/events.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_supplicant.conf

index 182c6a8ac6c820c0effb0a7634e0652aa16dccb5..93f17227e548ad2b600814935d08b74d71daf18e 100644 (file)
@@ -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
index 7eca4095bc5a80261628264d95f84052ba8b7ed7..d30ad761e3fe15c0aff3ec45f0b30b98479b6ec9 100644 (file)
@@ -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);
index 8925705a3c91075d5ec10b5075bf745b82b4e216..a8106324ed92dfbe51eb254f38f286b084a83733 100644 (file)
@@ -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 */
index 4dd9bc6112fa35087a4915a6de0710581dbb33f4..0e78ea23d10e6262fedd227430a007a117a2f057 100644 (file)
@@ -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;
index 42e50141af33d8ea9d6f6b542445508f956b6425..59f95c355acf54fb1c298edd31cacb906e1afcbd 100644 (file)
@@ -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 */
index 4b2c3cc0e5bf3978c0724eb2da87ff80d2abfbe9..cf5230a1b12d9b8e349c505726609120a746f603 100644 (file)
@@ -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.