nl80211: Add command for changing local MAC address
authorJouni Malinen <j@w1.fi>
Sat, 27 Sep 2014 16:11:24 +0000 (19:11 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 27 Sep 2014 16:11:24 +0000 (19:11 +0300)
This can be used to allow wpa_supplicant to control local MAC address
for connections.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/drivers/driver.h
src/drivers/driver_nl80211.c
wpa_supplicant/driver_i.h

index ff052ba..501314b 100644 (file)
@@ -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);
 
index e678827..69f285c 100644 (file)
@@ -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,
 };
index 49653c2..cba32a9 100644 (file)
@@ -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