Add support for using random local MAC address

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>
This commit is contained in:
Jouni Malinen 2014-09-27 19:12:41 +03:00
parent 4d8fb63799
commit c267753ba2
10 changed files with 178 additions and 0 deletions

View 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

View 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;
};

View 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 */

View 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 */

View 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;

View 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) {

View 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)

View 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;
}

View 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

View 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