#include "includes.h"
#include <sys/ioctl.h>
-#include <net/if_arp.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "netlink.h"
+#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
#include "driver.h"
struct netlink_data *netlink;
int ioctl_sock; /* socket for ioctl() use */
char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
int ifindex;
int if_removed;
struct wpa_driver_capa capa;
int monitor_sock;
int monitor_ifidx;
int probe_req_report;
+ int disable_11b_rates;
unsigned int beacon_set:1;
unsigned int pending_remain_on_chan:1;
+ unsigned int added_bridge:1;
+ unsigned int added_if_into_bridge:1;
u64 remain_on_chan_cookie;
static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
void *timeout_ctx);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled);
/* nl80211 code */
}
-static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
- const char *ifname, u8 *addr)
-{
- struct ifreq ifr;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr)) {
- wpa_printf(MSG_ERROR, "%s: ioctl(SIOCGIFHWADDR): %d (%s)",
- ifname, errno, strerror(errno));
- return -1;
- }
-
- if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
- wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
- ifname, ifr.ifr_hwaddr.sa_family);
- return -1;
- }
- os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
-
- return 0;
-}
-
-
-static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
- const char *ifname, const u8 *addr)
-{
- struct ifreq ifr;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
- os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
- ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
-
- if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) {
- wpa_printf(MSG_DEBUG, "%s: ioctl(SIOCSIFHWADDR): %d (%s)",
- ifname, errno, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct wpa_driver_nl80211_data *drv = priv;
}
-static int hostapd_set_iface_flags(struct wpa_driver_nl80211_data *drv,
- const char *ifname, int dev_up)
-{
- struct ifreq ifr;
-
- if (drv->ioctl_sock < 0)
- return -1;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-
- if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
- perror("ioctl[SIOCGIFFLAGS]");
- wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)",
- ifname);
- return -1;
- }
-
- if (dev_up) {
- if (ifr.ifr_flags & IFF_UP)
- return 0;
- ifr.ifr_flags |= IFF_UP;
- } else {
- if (!(ifr.ifr_flags & IFF_UP))
- return 0;
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
- perror("ioctl[SIOCSIFFLAGS]");
- return -1;
- }
-
- return 0;
-}
-
-
/**
* wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
* @priv: driver_nl80211 private data
"use managed mode");
}
- if (hostapd_set_iface_flags(drv, drv->ifname, 1)) {
- wpa_printf(MSG_ERROR, "Could not set interface '%s' "
- "UP", drv->ifname);
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+ wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
+ drv->ifname);
return -1;
}
{
struct wpa_driver_nl80211_data *drv = priv;
+ if (drv->added_if_into_bridge) {
+ if (linux_br_del_if(drv->ioctl_sock, drv->brname, drv->ifname)
+ < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "interface %s from bridge %s: %s",
+ drv->ifname, drv->brname, strerror(errno));
+ }
+ if (drv->added_bridge) {
+ if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ drv->brname, strerror(errno));
+ }
+
nl80211_remove_monitor_interface(drv);
if (drv->nlmode == NL80211_IFTYPE_AP)
wpa_driver_nl80211_free_bss(drv);
#endif /* HOSTAPD */
+ if (drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
netlink_deinit(drv->netlink);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
- (void) hostapd_set_iface_flags(drv, drv->ifname, 0);
+ (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
if (drv->ioctl_sock >= 0)
[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+ [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
};
struct wpa_scan_results *res = arg;
struct wpa_scan_res **tmp;
struct wpa_scan_res *r;
- const u8 *ie;
- size_t ie_len;
+ const u8 *ie, *beacon_ie;
+ size_t ie_len, beacon_ie_len;
+ u8 *pos;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
ie = NULL;
ie_len = 0;
}
+ if (bss[NL80211_BSS_BEACON_IES]) {
+ beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+ beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+ } else {
+ beacon_ie = NULL;
+ beacon_ie_len = 0;
+ }
- r = os_zalloc(sizeof(*r) + ie_len);
+ r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
if (r == NULL)
return NL_SKIP;
if (bss[NL80211_BSS_BSSID])
if (bss[NL80211_BSS_SEEN_MS_AGO])
r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
r->ie_len = ie_len;
- if (ie)
- os_memcpy(r + 1, ie, ie_len);
+ pos = (u8 *) (r + 1);
+ if (ie) {
+ os_memcpy(pos, ie, ie_len);
+ pos += ie_len;
+ }
+ r->beacon_ie_len = beacon_ie_len;
+ if (beacon_ie)
+ os_memcpy(pos, beacon_ie, beacon_ie_len);
if (bss[NL80211_BSS_STATUS]) {
enum nl80211_bss_status status;
}
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
/**
* wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
* @priv: Pointer to private wext data from wpa_driver_nl80211_init()
drv->associated = 0;
os_memset(drv->auth_bssid, 0, ETH_ALEN);
+ /* FIX: IBSS mode */
+ if (drv->nlmode != NL80211_IFTYPE_STATION)
+ wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
if (wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA) < 0)
return -1;
* TODO: if multiple auth_alg options enabled, try them one by one if
* the AP rejects authentication due to unknown auth alg
*/
- if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & AUTH_ALG_LEAP)
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & AUTH_ALG_FT)
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
type = NL80211_AUTHTYPE_FT;
else
goto nla_put_failure;
#endif /* HOSTAPD */
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
- set_ifhwaddr(drv, ifname, addr)) {
+ linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
wds);
}
+ if (ret >= 0 && drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, ret, 1);
+
return ret;
}
if (drv->monitor_ifidx < 0)
return -1;
- if (hostapd_set_iface_flags(drv, buf, 1))
+ if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
goto error;
memset(&ll, 0, sizeof(ll));
NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie);
- if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
type = NL80211_AUTHTYPE_OPEN_SYSTEM;
- else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
type = NL80211_AUTHTYPE_SHARED_KEY;
- else if (params->auth_alg & AUTH_ALG_LEAP)
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
type = NL80211_AUTHTYPE_NETWORK_EAP;
- else if (params->auth_alg & AUTH_ALG_FT)
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
type = NL80211_AUTHTYPE_FT;
else
goto nla_put_failure;
* take the device down, try to set the mode again, and bring the
* device back up.
*/
- if (hostapd_set_iface_flags(drv, drv->ifname, 0) == 0) {
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
/* Try to set the mode again while the interface is down */
ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
- if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
ret = -1;
}
} else if (!ret && nlmode != NL80211_IFTYPE_AP) {
/* Remove additional AP mode functionality */
nl80211_remove_monitor_interface(drv);
+#ifndef HOSTAPD
+ drv->beacon_set = 0;
+#endif /* HOSTAPD */
}
if (ret)
if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
NULL, 1) < 0)
return -1;
- hostapd_set_iface_flags(drv, name, 1);
+ linux_set_iface_flags(drv->ioctl_sock, name, 1);
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
i802_set_sta_vlan(priv, addr, drv->ifname, 0);
return;
}
- if (have_ifidx(drv, lladdr.sll_ifindex)) {
- union wpa_event_data event;
- os_memset(&event, 0, sizeof(event));
- event.eapol_rx.src = lladdr.sll_addr;
- event.eapol_rx.data = buf;
- event.eapol_rx.data_len = len;
- wpa_supplicant_event(drv->ctx, EVENT_EAPOL_RX, &event);
- }
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
}
}
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ const char *brname, const char *ifname)
+{
+ int ifindex;
+ char in_br[IFNAMSIZ];
+
+ os_strlcpy(drv->brname, brname, IFNAMSIZ);
+ ifindex = if_nametoindex(brname);
+ if (ifindex == 0) {
+ /*
+ * Bridge was configured, but the bridge device does
+ * not exist. Try to add it now.
+ */
+ if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+ "bridge interface %s: %s",
+ brname, strerror(errno));
+ return -1;
+ }
+ drv->added_bridge = 1;
+ add_ifidx(drv, if_nametoindex(brname));
+ }
+
+ if (linux_br_get(in_br, ifname) == 0) {
+ if (os_strcmp(in_br, brname) == 0)
+ return 0; /* already in the bridge */
+
+ wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+ "bridge %s", ifname, in_br);
+ if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to "
+ "remove interface %s from bridge "
+ "%s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+ ifname, brname);
+ if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+ "into bridge %s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ drv->added_if_into_bridge = 1;
+
+ return 0;
+}
+
+
static void *i802_init(struct hostapd_data *hapd,
struct wpa_init_params *params)
{
struct wpa_driver_nl80211_data *drv;
size_t i;
+ char brname[IFNAMSIZ];
+ int ifindex, br_ifindex;
+ int br_added = 0;
drv = wpa_driver_nl80211_init(hapd, params->ifname);
if (drv == NULL)
drv->bss.ifindex = drv->ifindex;
+ if (linux_br_get(brname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+ params->ifname, brname);
+ br_ifindex = if_nametoindex(brname);
+ } else {
+ brname[0] = '\0';
+ br_ifindex = 0;
+ }
+
drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
drv->if_indices = drv->default_if_indices;
for (i = 0; i < params->num_bridge; i++) {
- if (params->bridge[i])
- add_ifidx(drv, if_nametoindex(params->bridge[i]));
+ if (params->bridge[i]) {
+ ifindex = if_nametoindex(params->bridge[i]);
+ if (ifindex)
+ add_ifidx(drv, ifindex);
+ if (ifindex == br_ifindex)
+ br_added = 1;
+ }
}
+ if (!br_added && br_ifindex &&
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex);
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex);
- if (hostapd_set_iface_flags(drv, drv->ifname, 0))
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0))
goto failed;
if (params->bssid) {
- if (set_ifhwaddr(drv, drv->ifname, params->bssid))
+ if (linux_set_ifhwaddr(drv->ioctl_sock, drv->ifname,
+ params->bssid))
goto failed;
}
goto failed;
}
- if (hostapd_set_iface_flags(drv, drv->ifname, 1))
+ if (params->num_bridge && params->bridge[0] &&
+ i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+ goto failed;
+
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1))
goto failed;
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
goto failed;
}
- if (get_ifhwaddr(drv, drv->ifname, params->own_addr))
+ if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, params->own_addr))
goto failed;
return drv;
#ifdef HOSTAPD
if (type == WPA_IF_AP_BSS) {
- if (hostapd_set_iface_flags(priv, ifname, 1)) {
- nl80211_remove_iface(priv, ifidx);
+ if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ nl80211_remove_iface(drv, ifidx);
os_free(bss);
return -1;
}
{
struct wpa_driver_nl80211_data *drv = priv;
- if (get_ifhwaddr(drv, drv->ifname, addr) < 0)
+ if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr) < 0)
return -1;
if (addr[0] & 0x02) {
}
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled)
+{
+ struct nl_msg *msg;
+ struct nlattr *bands, *band;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto nla_put_failure;
+
+ /*
+ * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+ * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+ * rates. All 5 GHz rates are left enabled.
+ */
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ if (!band)
+ goto nla_put_failure;
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ }
+
+ return ret;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ drv->disable_11b_rates = disabled;
+ return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ if (drv->nlmode != NL80211_IFTYPE_AP)
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ return wpa_driver_nl80211_set_mode(drv, IEEE80211_MODE_INFRA);
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.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,
+ .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
+ .deinit_ap = wpa_driver_nl80211_deinit_ap,
};