]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Add support for using random local MAC address
authorJouni Malinen <j@w1.fi>
Sat, 27 Sep 2014 16:12:41 +0000 (19:12 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 27 Sep 2014 17:07:19 +0000 (20:07 +0300)
This adds experimental support for wpa_supplicant to assign random local
MAC addresses for both pre-association cases (scan, GAS/ANQP) and for
connections. MAC address policy for each part can be controlled
separately and the connection part can be set per network block.

This requires support from the driver to allow local MAC address to be
changed if random address policy is enabled. It should also be noted
that number of drivers would not support concurrent operations (e.g.,
P2P and station association) with random addresses in use for one or
both.

This functionality can be controlled with the global configuration
parameters mac_addr and preassoc_mac_addr which set the default MAC
address policies for connections and pre-association operations (scan
and GAS/ANQP while not connected). The global rand_addr_lifetime
parameter can be used to set the lifetime of a random MAC address in
seconds (default: 60 seconds). This is used to avoid unnecessarily
frequent MAC address changes since those are likely to result in driver
clearing most of its state. It should be noted that the random MAC
address does not expire during an ESS connection, i.e., this lifetime is
only for the case where the device is disconnected.

The mac_addr parameter can also be set in the network blocks to define
different behavior per network. For example, the global mac_addr=1 and
preassoc_mac_addr=1 settings and mac_addr=0 in a home network profile
would result in behavior where all scanning is performed using a random
MAC address while connections to new networks (e.g.,
Interworking/Hotspot 2.0) would use random address and connections to
the home network would use the permanent MAC address.

Signed-off-by: Jouni Malinen <j@w1.fi>
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/gas_query.c
wpa_supplicant/scan.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant.conf
wpa_supplicant/wpa_supplicant_i.h

index 8cd4a2f0695c33cdd5dd6693914a664e7cdfcb62..f3a4917105bdf822ac0a3165205d0ea84011b5f9 100644 (file)
@@ -1754,6 +1754,7 @@ static const struct parse_data ssid_fields[] = {
 #ifdef CONFIG_HS20
        { INT(update_identifier) },
 #endif /* CONFIG_HS20 */
+       { INT_RANGE(mac_addr, 0, 1) },
 };
 
 #undef OFFSET
@@ -2211,6 +2212,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 #ifdef CONFIG_IEEE80211W
        ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
 #endif /* CONFIG_IEEE80211W */
+       ssid->mac_addr = -1;
 }
 
 
@@ -3287,6 +3289,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->wmm_ac_params[2] = ac_vi;
        config->wmm_ac_params[3] = ac_vo;
        config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
+       config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 
        if (ctrl_interface)
                config->ctrl_interface = os_strdup(ctrl_interface);
@@ -3909,6 +3912,9 @@ static const struct global_parse_data global_fields[] = {
        { STR(osu_dir), 0 },
        { STR(wowlan_triggers), 0 },
        { INT(p2p_search_delay), 0},
+       { INT(mac_addr), 0 },
+       { INT(rand_addr_lifetime), 0 },
+       { INT(preassoc_mac_addr), 0 },
 };
 
 #undef FUNC
index 52add9da3560271d6d9550f6a206cea5907babfa..75257c5529d31b5718c21811ae757a5fd3b5bc0d 100644 (file)
@@ -27,6 +27,7 @@
 #define DEFAULT_ACCESS_NETWORK_TYPE 15
 #define DEFAULT_SCAN_CUR_FREQ 0
 #define DEFAULT_P2P_SEARCH_DELAY 500
+#define DEFAULT_RAND_ADDR_LIFETIME 60
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1051,6 +1052,31 @@ struct wpa_config {
         * resources.
         */
        unsigned int p2p_search_delay;
+
+       /**
+        * mac_addr - MAC address policy default
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address for each ESS connection
+        *
+        * By default, permanent MAC address is used unless policy is changed by
+        * the per-network mac_addr parameter. Global mac_addr=1 can be used to
+        * change this default behavior.
+        */
+       int mac_addr;
+
+       /**
+        * rand_addr_lifetime - Lifetime of random MAC address in seconds
+        */
+       unsigned int rand_addr_lifetime;
+
+       /**
+        * preassoc_mac_addr - Pre-association MAC address policy
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address
+        */
+       int preassoc_mac_addr;
 };
 
 
index 73ad57a502584798d060ba42ac9f384f2dec035a..5c8f04509f9ef3ce9cbffe158b4778a076c72332 100644 (file)
@@ -742,6 +742,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 #ifdef CONFIG_HS20
        INT(update_identifier);
 #endif /* CONFIG_HS20 */
+       write_int(f, "mac_addr", ssid->mac_addr, -1);
 
 #undef STR
 #undef INT
@@ -1179,6 +1180,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
        if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
                fprintf(f, "p2p_search_delay=%u\n",
                        config->p2p_search_delay);
+
+       if (config->mac_addr)
+               fprintf(f, "mac_addr=%d\n", config->mac_addr);
+
+       if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+               fprintf(f, "rand_addr_lifetime=%u\n",
+                       config->rand_addr_lifetime);
+
+       if (config->preassoc_mac_addr)
+               fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
index 26b91bd18024e070d7c18a88673f97a3c4e2fe43..b5dbf6ee38c8bc2e0310368ac1cf2907d0c20d78 100644 (file)
@@ -653,6 +653,18 @@ struct wpa_ssid {
 #endif /* CONFIG_HS20 */
 
        unsigned int wps_run;
+
+       /**
+        * mac_addr - MAC address policy
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address for each ESS connection
+        *
+        * Internally, special value -1 is used to indicate that the parameter
+        * was not specified in the configuration (i.e., default behavior is
+        * followed).
+        */
+       int mac_addr;
 };
 
 #endif /* CONFIG_SSID_H */
index c2b75f31cffea974bcb7fcbe858339ebc3bc2f2d..510b802ca9cbed3f9f28b7ca5b7920426d5f6e5c 100644 (file)
@@ -2480,6 +2480,8 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                        struct wpa_ssid *remove_ssid = ssid;
                        id = ssid->id;
                        ssid = ssid->next;
+                       if (wpa_s->last_ssid == remove_ssid)
+                               wpa_s->last_ssid = NULL;
                        wpas_notify_network_removed(wpa_s, remove_ssid);
                        wpa_config_remove_network(wpa_s->conf, id);
                }
@@ -2498,6 +2500,9 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                return -1;
        }
 
+       if (wpa_s->last_ssid == ssid)
+               wpa_s->last_ssid = NULL;
+
        if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
 #ifdef CONFIG_SME
                wpa_s->sme.prev_bssid_set = 0;
index 39862681c86ed6b3ec8af000d39127519ff0bcb7..3a89674fa87200b94c47d6187c98ab1e8b02c0d0 100644 (file)
@@ -597,6 +597,7 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
 {
        struct gas_query_pending *query = work->ctx;
        struct gas_query *gas = query->gas;
+       struct wpa_supplicant *wpa_s = gas->wpa_s;
 
        if (deinit) {
                if (work->started) {
@@ -609,6 +610,14 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
                return;
        }
 
+       if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to assign random MAC address for GAS");
+               gas_query_free(query, 1);
+               radio_work_done(work);
+               return;
+       }
+
        gas->work = work;
 
        if (gas_query_tx(gas, query, query->req) < 0) {
index ec808772c5dc8236ba2607dbd60f2b359989a50d..debceb91361b46785519014c54dd5eb6a041e47f 100644 (file)
@@ -158,6 +158,13 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
                return;
        }
 
+       if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to assign random MAC address for a scan");
+               radio_work_done(work);
+               return;
+       }
+
        wpa_supplicant_notify_scanning(wpa_s, 1);
 
        if (wpa_s->clear_driver_scan_cache)
index 9e3fe8462e374edc38f8dee2e94d8e6666ad2ef7..92af1124fe4da22814adecd9624f4c5d53cf2075 100644 (file)
@@ -1380,6 +1380,55 @@ void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
 }
 
 
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s)
+{
+       struct os_reltime now;
+       u8 addr[ETH_ALEN];
+
+       os_get_reltime(&now);
+       if (wpa_s->last_mac_addr_change.sec != 0 &&
+           !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
+                               wpa_s->conf->rand_addr_lifetime)) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Previously selected random MAC address has not yet expired");
+               return 0;
+       }
+
+       if (random_mac_addr(addr) < 0)
+               return -1;
+
+       if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to set random MAC address");
+               return -1;
+       }
+
+       os_get_reltime(&wpa_s->last_mac_addr_change);
+       wpa_s->mac_addr_changed = 1;
+
+       if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Could not update MAC address information");
+               return -1;
+       }
+
+       wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
+               MAC2STR(addr));
+
+       return 0;
+}
+
+
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
+           !wpa_s->conf->preassoc_mac_addr)
+               return 0;
+
+       return wpas_update_random_addr(wpa_s);
+}
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
 
 /**
@@ -1395,6 +1444,29 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 {
        struct wpa_connect_work *cwork;
 
+       if (wpa_s->last_ssid == ssid) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+       } else if (ssid->mac_addr == 1 ||
+                  (ssid->mac_addr == -1 && wpa_s->conf->mac_addr == 1)) {
+               if (wpas_update_random_addr(wpa_s) < 0)
+                       return;
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+       } else if (wpa_s->mac_addr_changed) {
+               if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not restore permanent MAC address");
+                       return;
+               }
+               wpa_s->mac_addr_changed = 0;
+               if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not update MAC address information");
+                       return;
+               }
+               wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
+       }
+       wpa_s->last_ssid = ssid;
+
 #ifdef CONFIG_IBSS_RSN
        ibss_rsn_deinit(wpa_s->ibss_rsn);
        wpa_s->ibss_rsn = NULL;
@@ -2662,6 +2734,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
                return -1;
        }
 
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
        return 0;
 }
 
index 2a0dc204fad30958c0a955ca8f4e9c9da6a502fe..f2eaaa8917f2ba3612c4a82347f04024b9833a71 100644 (file)
@@ -332,6 +332,23 @@ fast_reauth=1
 # 1:  Scan current operating frequency if another VIF on the same radio
 #     is already associated.
 
+# MAC address policy default
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+#
+# By default, permanent MAC address is used unless policy is changed by
+# the per-network mac_addr parameter. Global mac_addr=1 can be used to
+# change this default behavior.
+#mac_addr=0
+
+# Lifetime of random MAC address in seconds (default: 60)
+#rand_addr_lifetime=60
+
+# MAC address policy for pre-association operations (scanning, ANQP)
+# 0 = use permanent MAC address
+# 1 = use random MAC address
+#preassoc_mac_addr=0
+
 # Interworking (IEEE 802.11u)
 
 # Enable Interworking
@@ -962,6 +979,11 @@ fast_reauth=1
 # Beacon interval (default: 100 TU)
 #beacon_int=100
 
+# MAC address policy
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+#mac_addr=0
+
 # disable_ht: Whether HT (802.11n) should be disabled.
 # 0 = HT enabled (if AP supports it)
 # 1 = HT disabled
index be779d884d594b0eb2aaef34b7ed70020d322676..2b6ef79ad1c56ba7a7d0ad68bf7fea5d5e7c8629 100644 (file)
@@ -421,6 +421,7 @@ struct wpa_supplicant {
        int disconnected; /* all connections disabled; i.e., do no reassociate
                           * before this has been cleared */
        struct wpa_ssid *current_ssid;
+       struct wpa_ssid *last_ssid;
        struct wpa_bss *current_bss;
        int ap_ies_from_associnfo;
        unsigned int assoc_freq;
@@ -609,6 +610,9 @@ struct wpa_supplicant {
        unsigned int last_eapol_matches_bssid:1;
        unsigned int eap_expected_failure:1;
        unsigned int reattach:1; /* reassociation to the same BSS requested */
+       unsigned int mac_addr_changed:1;
+
+       struct os_reltime last_mac_addr_change;
 
        struct ibss_rsn *ibss_rsn;
 
@@ -958,6 +962,8 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
                    size_t ssid_len);
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s);
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response