diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 1dd358ad5..5fb936ae6 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1519,6 +1519,36 @@ struct wpa_driver_ops { */ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val); + /** + * alloc_interface_addr - Allocate a virtual interface address + * @priv: Private driver interface data + * @addr: Buffer for returning the address + * Returns: 0 on success, -1 on failure + * + * This command pre-allocates an interface address for a new virtual + * interface. This can be used before creating a virtual interface if + * the interface mode (e.g., AP vs. station) is not yet known, but the + * address of the virtual interface is already needed. This helps with + * drivers that cannot change interface mode without destroying and + * re-creating the interface. + * + * The allocated address can be used in a bss_add() call to request a + * specific bssid. + */ + int (*alloc_interface_addr)(void *priv, u8 *addr); + + /** + * release_interface_addr - Release a virtual interface address + * @priv: Private driver interface data + * @addr: Address to be freed from alloc_interface_addr() + * + * This command is used to release a virtual interface address that was + * allocated with alloc_interface_addr(), but has not yet been used + * with bss_add() to actually create the interface. This allows the + * driver to release the pending allocation for a new interface. + */ + void (*release_interface_addr)(void *priv, const u8 *addr); + /** * probe_req_report - Request Probe Request frames to be indicated * @priv: Private driver interface data diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index d3e56cf7c..0789e082a 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -3246,5 +3246,7 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* set_ap_wps_ie */, NULL /* set_supp_port */, NULL /* set_wds_sta */, + NULL /* alloc_interface_addr */, + NULL /* release_interface_addr */, NULL /* probe_req_report */ }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index b669c87d5..fc3e9743c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -266,7 +266,6 @@ nla_put_failure: } -#ifdef HOSTAPD static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv, const char *ifname, u8 *addr) { @@ -309,7 +308,6 @@ static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv, return 0; } -#endif /* HOSTAPD */ static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) @@ -2661,13 +2659,13 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, #ifdef HOSTAPD /* start listening for EAPOL on this interface */ add_ifidx(drv, ifidx); +#endif /* HOSTAPD */ - if (addr && iftype == NL80211_IFTYPE_AP && + if (addr && iftype != NL80211_IFTYPE_MONITOR && set_ifhwaddr(drv, ifname, addr)) { nl80211_remove_iface(drv, ifidx); return -1; } -#endif /* HOSTAPD */ return ifidx; } @@ -4473,6 +4471,30 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) } +static int wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr) +{ + struct wpa_driver_nl80211_data *drv = priv; + + if (get_ifhwaddr(drv, drv->ifname, addr) < 0) + return -1; + + if (addr[0] & 0x02) { + /* TODO: add support for generating multiple addresses */ + addr[0] ^= 0x80; + } else + addr[0] = 0x02; /* locally administered */ + + return 0; +} + + +static void wpa_driver_nl80211_release_interface_addr(void *priv, + const u8 *addr) +{ + /* TODO: keep list of allocated address and release them here */ +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -4522,4 +4544,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_wds_sta = i802_set_wds_sta, #endif /* HOSTAPD */ .probe_req_report = wpa_driver_nl80211_probe_req_report, + .alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr, + .release_interface_addr = wpa_driver_nl80211_release_interface_addr, }; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 9cdeac158..5596f2060 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -101,6 +101,8 @@ struct wpa_driver_test_data { struct test_driver_bss *bss; int udp_port; + int alloc_iface_idx; + int probe_req_report; }; @@ -2462,6 +2464,24 @@ fail: } +static int wpa_driver_test_alloc_interface_addr(void *priv, u8 *addr) +{ + struct wpa_driver_test_data *drv = priv; + drv->alloc_iface_idx++; + addr[0] = 0x02; /* locally administered */ + sha1_prf(drv->own_addr, ETH_ALEN, "hostapd test addr generation", + (const u8 *) &drv->alloc_iface_idx, + sizeof(drv->alloc_iface_idx), + addr + 1, ETH_ALEN - 1); + return 0; +} + + +static void wpa_driver_test_release_interface_addr(void *priv, const u8 *addr) +{ +} + + static int wpa_driver_test_probe_req_report(void *priv, int report) { struct wpa_driver_test_data *drv = priv; @@ -2514,5 +2534,7 @@ const struct wpa_driver_ops wpa_driver_test_ops = { .init2 = wpa_driver_test_init2, .get_interfaces = wpa_driver_test_get_interfaces, .scan2 = wpa_driver_test_scan, + .alloc_interface_addr = wpa_driver_test_alloc_interface_addr, + .release_interface_addr = wpa_driver_test_release_interface_addr, .probe_req_report = wpa_driver_test_probe_req_report, }; diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 9dbbc8534..4a701f179 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -383,6 +383,22 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s, return 0; } +static inline int wpa_drv_alloc_interface_addr(struct wpa_supplicant *wpa_s, + u8 *addr) +{ + if (wpa_s->driver->alloc_interface_addr) + return wpa_s->driver->alloc_interface_addr(wpa_s->drv_priv, + addr); + return -1; +} + +static inline void wpa_drv_release_interface_addr(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (wpa_s->driver->release_interface_addr) + wpa_s->driver->release_interface_addr(wpa_s->drv_priv, addr); +} + static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s, int report) {