From fee354c74d5ac1daa3ddbb9d18ac457c9a667362 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 27 Sep 2014 19:11:24 +0300 Subject: [PATCH] nl80211: Add command for changing local MAC address This can be used to allow wpa_supplicant to control local MAC address for connections. Signed-off-by: Jouni Malinen --- src/drivers/driver.h | 8 +++++ src/drivers/driver_nl80211.c | 57 ++++++++++++++++++++++++++++++++++++ wpa_supplicant/driver_i.h | 8 +++++ 3 files changed, 73 insertions(+) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index ff052bacc..501314bbd 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2820,6 +2820,14 @@ struct wpa_driver_ops { */ int (*roaming)(void *priv, int allowed, const u8 *bssid); + /** + * set_mac_addr - Set MAC address + * @priv: Private driver interface data + * @addr: MAC address to use or %NULL for setting back to permanent + * Returns: 0 on success, -1 on failure + */ + int (*set_mac_addr)(void *priv, const u8 *addr); + #ifdef CONFIG_MACSEC int (*macsec_init)(void *priv, struct macsec_init_params *params); diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index e6788272f..69f285cd7 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -253,6 +253,7 @@ struct wpa_driver_nl80211_data { struct dl_list list; struct dl_list wiphy_list; char phyname[32]; + u8 perm_addr[ETH_ALEN]; void *ctx; int ifindex; int if_removed; @@ -311,6 +312,7 @@ struct wpa_driver_nl80211_data { unsigned int dfs_vendor_cmd_avail:1; unsigned int have_low_prio_scan:1; unsigned int force_connect_cmd:1; + unsigned int addr_changed:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -4874,6 +4876,7 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr)) return -1; + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, @@ -4962,6 +4965,16 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) if (!drv->start_iface_up) (void) i802_set_iface_flags(bss, 0); + + if (drv->addr_changed) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + drv->perm_addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore permanent MAC address"); + } + } + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { if (!drv->hostapd || !drv->start_mode_ap) wpa_driver_nl80211_set_mode(bss, @@ -10057,6 +10070,7 @@ static void *i802_init(struct hostapd_data *hapd, if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, params->own_addr)) goto failed; + os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN); memcpy(bss->addr, params->own_addr, ETH_ALEN); @@ -12012,6 +12026,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) res = os_snprintf(pos, end - pos, "phyname=%s\n" + "perm_addr=" MACSTR "\n" "drv_ifindex=%d\n" "operstate=%d\n" "scan_state=%s\n" @@ -12028,6 +12043,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "eapol_tx_sock=%d\n" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", drv->phyname, + MAC2STR(drv->perm_addr), drv->ifindex, drv->operstate, scan_state_str(drv->scan_state), @@ -12454,6 +12470,46 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) } +static int nl80211_set_mac_addr(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int new_addr = addr != NULL; + + if (!addr) + addr = drv->perm_addr; + + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0) + return -1; + + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: failed to set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, + 1) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after failed set_mac_addr"); + } + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + drv->addr_changed = new_addr; + os_memcpy(bss->addr, addr, ETH_ALEN); + + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after set_mac_addr"); + } + + return 0; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -12546,4 +12602,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_qos_map = nl80211_set_qos_map, .set_wowlan = nl80211_set_wowlan, .roaming = nl80211_roaming, + .set_mac_addr = nl80211_set_mac_addr, }; diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 49653c2d4..cba32a971 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -640,6 +640,14 @@ static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed, return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid); } +static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->set_mac_addr) + return -1; + return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr); +} + #ifdef CONFIG_MACSEC