X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=hostapd%2Fdriver_nl80211.c;h=877245de96ebfb85df93b05229a6845ba0f90792;hb=c2220ec0b7f47841fbec71d9f4626e2705a03a89;hp=9357ff2b249d85419fa322e9221730ae331d8f12;hpb=10b83bd712c8f6c049f3cf5098d525294ca848f5;p=wpasupplicant diff --git a/hostapd/driver_nl80211.c b/hostapd/driver_nl80211.c index 9357ff2..877245d 100644 --- a/hostapd/driver_nl80211.c +++ b/hostapd/driver_nl80211.c @@ -23,23 +23,31 @@ #include #include #include -#include +#include "nl80211_copy.h" #include -#include -#include /* The L2 protocols */ +#include #include "wireless_copy.h" +#include #include #include "hostapd.h" +#include "config.h" #include "driver.h" -#include "ieee802_1x.h" #include "eloop.h" -#include "ieee802_11.h" -#include "sta_info.h" #include "hw_features.h" #include "mlme.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle_alloc_cb nl_socket_alloc_cb +#define nl_handle_destroy nl_socket_free +#endif /* CONFIG_LIBNL20 */ + +static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; enum ieee80211_msg_type { ieee80211_msg_normal = 0, @@ -47,11 +55,16 @@ enum ieee80211_msg_type { ieee80211_msg_tx_callback_fail = 2, }; +struct i802_bss { + struct i802_bss *next; + char iface[IFNAMSIZ + 1]; + unsigned int beacon_set:1; +}; + struct i802_driver_data { struct hostapd_data *hapd; char iface[IFNAMSIZ + 1]; - int bridge; int ioctl_sock; /* socket for ioctl() use */ int wext_sock; /* socket for wireless events */ int eapol_sock; /* socket for EAPOL frames */ @@ -67,17 +80,42 @@ struct i802_driver_data { struct nl_cache *nl_cache; struct nl_cb *nl_cb; struct genl_family *nl80211; - int dtim_period, beacon_int; - unsigned int beacon_set:1; - unsigned int ieee802_1x_active:1; + int beacon_int; + struct i802_bss bss; + unsigned int ht_40mhz_scan:1; + + int last_freq; + int last_freq_ht; + struct hostapd_neighbor_bss *neighbors; + size_t num_neighbors; }; +static int i802_sta_deauth(void *priv, const u8 *addr, int reason); +static int i802_sta_disassoc(void *priv, const u8 *addr, int reason); + + +static struct i802_bss * get_bss(struct i802_driver_data *drv, + const char *iface) +{ + struct i802_bss *bss = &drv->bss; + while (bss) { + if (os_strncmp(iface, bss->iface, IFNAMSIZ) == 0) + return bss; + bss = bss->next; + } + wpa_printf(MSG_DEBUG, "nl80211: get_bss(%s) failed", iface); + return NULL; +} + + static void add_ifidx(struct i802_driver_data *drv, int ifidx) { int i; int *old; + wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", + ifidx); for (i = 0; i < drv->num_if_indices; i++) { if (drv->if_indices[i] == 0) { drv->if_indices[i] = ifidx; @@ -124,9 +162,6 @@ static int have_ifidx(struct i802_driver_data *drv, int ifidx) { int i; - if (ifidx == drv->bridge) - return 1; - for (i = 0; i < drv->num_if_indices; i++) if (drv->if_indices[i] == ifidx) return 1; @@ -225,7 +260,7 @@ static int hostapd_set_iface_flags(struct i802_driver_data *drv, static int nl_set_encr(int ifindex, struct i802_driver_data *drv, - const char *alg, const u8 *addr, int idx, const u8 *key, + wpa_alg alg, const u8 *addr, int idx, const u8 *key, size_t key_len, int txkey) { struct nl_msg *msg; @@ -235,29 +270,34 @@ static int nl_set_encr(int ifindex, struct i802_driver_data *drv, if (!msg) return -ENOMEM; - if (strcmp(alg, "none") == 0) { + if (alg == WPA_ALG_NONE) { genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_DEL_KEY, 0); } else { genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_NEW_KEY, 0); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); - if (strcmp(alg, "WEP") == 0) { + switch (alg) { + case WPA_ALG_WEP: if (key_len == 5) NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC01); else NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC05); - } else if (strcmp(alg, "TKIP") == 0) + break; + case WPA_ALG_TKIP: NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); - else if (strcmp(alg, "CCMP") == 0) + break; + case WPA_ALG_CCMP: NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); - else if (strcmp(alg, "IGTK") == 0) + break; + case WPA_ALG_IGTK: NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); - else { + break; + default: wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm '%s'", __func__, alg); + "algorithm %d", __func__, alg); nlmsg_free(msg); return -1; } @@ -287,14 +327,10 @@ static int nl_set_encr(int ifindex, struct i802_driver_data *drv, 0, NL80211_CMD_SET_KEY, 0); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); -#ifdef NL80211_MFP_PENDING - if (strcmp(alg, "IGTK") == 0) + if (alg == WPA_ALG_IGTK) NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); else NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); -#else /* NL80211_MFP_PENDING */ - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); -#endif /* NL80211_MFP_PENDING */ ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret == -ENOENT) @@ -305,23 +341,18 @@ static int nl_set_encr(int ifindex, struct i802_driver_data *drv, } -static int i802_set_encryption(const char *iface, void *priv, const char *alg, - const u8 *addr, int idx, const u8 *key, - size_t key_len, int txkey) +static int i802_set_key(const char *iface, void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) { struct i802_driver_data *drv = priv; int ret; - ret = nl_set_encr(if_nametoindex(iface), drv, alg, addr, idx, key, - key_len, txkey); + ret = nl_set_encr(if_nametoindex(iface), drv, alg, addr, key_idx, key, + key_len, set_tx); if (ret < 0) return ret; - if (strcmp(alg, "IGTK") == 0) { - ret = nl_set_encr(drv->monitor_ifidx, drv, alg, addr, idx, key, - key_len, txkey); - } - return ret; } @@ -384,7 +415,6 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, int mode) { -#ifdef NL80211_ATTR_BSS_BASIC_RATES struct i802_driver_data *drv = priv; struct nl_msg *msg; u8 rates[NL80211_MAX_SUPP_RATES]; @@ -409,9 +439,6 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: return -ENOBUFS; -#else /* NL80211_ATTR_BSS_BASIC_RATES */ - return -1; -#endif /* NL80211_ATTR_BSS_BASIC_RATES */ } @@ -457,26 +484,70 @@ static int i802_send_frame(void *priv, const void *data, size_t len, static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, int flags) { - return i802_send_frame(priv, data, len, 1, flags); + struct ieee80211_mgmt *mgmt; + int do_not_encrypt = 0; + u16 fc; + + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { + /* + * Only one of the authentication frame types is encrypted. + * In order for static WEP encryption to work properly (i.e., + * to not encrypt the frame), we need to tell mac80211 about + * the frames that must not be encrypted. + */ + u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_alg == WLAN_AUTH_OPEN || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3)) + do_not_encrypt = 1; + } + + return i802_send_frame(priv, data, len, !do_not_encrypt, flags); } /* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq(void *priv, int mode, int freq) +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) { struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - iwr.u.freq.m = freq; - iwr.u.freq.e = 6; + struct nl_msg *msg; - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); + msg = nlmsg_alloc(); + if (!msg) return -1; + + drv->last_freq = freq->freq; + drv->last_freq_ht = freq->ht_enabled; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } } - return 0; + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; } @@ -486,7 +557,7 @@ static int i802_set_rts(void *priv, int rts) struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.rts.value = rts; iwr.u.rts.fixed = 1; @@ -499,32 +570,13 @@ static int i802_set_rts(void *priv, int rts) } -static int i802_get_rts(void *priv, int *rts) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { - perror("ioctl[SIOCGIWRTS]"); - return -1; - } - - *rts = iwr.u.rts.value; - - return 0; -} - - static int i802_set_frag(void *priv, int frag) { struct i802_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.frag.value = frag; iwr.u.frag.fixed = 1; @@ -537,43 +589,24 @@ static int i802_set_frag(void *priv, int frag) } -static int i802_get_frag(void *priv, int *frag) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWFRAG, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG]"); - return -1; - } - - *frag = iwr.u.frag.value; - - return 0; -} - - static int i802_set_retry(void *priv, int short_retry, int long_retry) { struct i802_driver_data *drv = priv; struct iwreq iwr; memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.retry.value = short_retry; iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; - if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { perror("ioctl[SIOCSIWRETRY(short)]"); return -1; } iwr.u.retry.value = long_retry; iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; - if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { perror("ioctl[SIOCSIWRETRY(long)]"); return -1; } @@ -582,32 +615,6 @@ static int i802_set_retry(void *priv, int short_retry, int long_retry) } -static int i802_get_retry(void *priv, int *short_retry, int *long_retry) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; - if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG(short)]"); - return -1; - } - *short_retry = iwr.u.retry.value; - - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; - if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG(long)]"); - return -1; - } - *long_retry = iwr.u.retry.value; - - return 0; -} - - static int i802_flush(void *priv) { struct i802_driver_data *drv = priv; @@ -642,6 +649,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -670,7 +679,13 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_RX_BYTES]) data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); if (stats[NL80211_STA_INFO_TX_BYTES]) - data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_PACKETS]) + data->rx_packets = + nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_PACKETS]) + data->tx_packets = + nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); return NL_SKIP; } @@ -681,6 +696,7 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, struct i802_driver_data *drv = priv; struct nl_msg *msg; + os_memset(data, 0, sizeof(*data)); msg = nlmsg_alloc(); if (!msg) return -ENOMEM; @@ -765,8 +781,8 @@ static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, } -static int i802_sta_add2(const char *ifname, void *priv, - struct hostapd_sta_add_params *params) +static int i802_sta_add(const char *ifname, void *priv, + struct hostapd_sta_add_params *params) { struct i802_driver_data *drv = priv; struct nl_msg *msg; @@ -789,16 +805,17 @@ static int i802_sta_add2(const char *ifname, void *priv, params->listen_interval); #ifdef CONFIG_IEEE80211N -#ifdef NL80211_ATTR_HT_CAPABILITY if (params->ht_capabilities) { NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, params->ht_capabilities->length, ¶ms->ht_capabilities->data); } -#endif /* NL80211_ATTR_HT_CAPABILITY */ #endif /* CONFIG_IEEE80211N */ ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " + "result: %d (%s)", ret, strerror(-ret)); if (ret == -EEXIST) ret = 0; nla_put_failure: @@ -855,19 +872,17 @@ static int i802_sta_set_flags(void *priv, const u8 *addr, if_nametoindex(drv->iface)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - if (total_flags & WLAN_STA_AUTHORIZED || !drv->ieee802_1x_active) + if (total_flags & WLAN_STA_AUTHORIZED) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); - if (total_flags & WLAN_STA_WME) + if (total_flags & WLAN_STA_WMM) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); if (total_flags & WLAN_STA_SHORT_PREAMBLE) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); -#ifdef NL80211_MFP_PENDING if (total_flags & WLAN_STA_MFP) NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); -#endif /* NL80211_MFP_PENDING */ if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) goto nla_put_failure; @@ -881,16 +896,9 @@ static int i802_sta_set_flags(void *priv, const u8 *addr, } -static int i802_set_regulatory_domain(void *priv, unsigned int rd) -{ - return -1; -} - - static int i802_set_tx_queue_params(void *priv, int queue, int aifs, int cw_min, int cw_max, int burst_time) { -#ifdef NL80211_ATTR_WIPHY_TXQ_PARAMS struct i802_driver_data *drv = priv; struct nl_msg *msg; struct nlattr *txq, *params; @@ -929,9 +937,6 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, return 0; nla_put_failure: return -1; -#else /* NL80211_ATTR_WIPHY_TXQ_PARAMS */ - return -1; -#endif /* NL80211_ATTR_WIPHY_TXQ_PARAMS */ } @@ -974,8 +979,7 @@ static int nl80211_create_iface(struct i802_driver_data *drv, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_NEW_INTERFACE, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->hapd->conf->iface)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); @@ -1043,47 +1047,72 @@ static int nl80211_create_iface(struct i802_driver_data *drv, static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid) { + struct i802_driver_data *drv = priv; int ifidx; + struct i802_bss *bss; - /* - * The kernel supports that when the low-level driver does, - * but we currently don't because we need per-BSS data that - * currently we can't handle easily. - */ - return -1; + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; + os_strlcpy(bss->iface, ifname, IFNAMSIZ); ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid); - if (ifidx < 0) + if (ifidx < 0) { + os_free(bss); return -1; + } if (hostapd_set_iface_flags(priv, ifname, 1)) { nl80211_remove_iface(priv, ifidx); + os_free(bss); return -1; } + bss->next = drv->bss.next; + drv->bss.next = bss; return 0; } static int i802_bss_remove(void *priv, const char *ifname) { + struct i802_driver_data *drv = priv; + struct i802_bss *bss, *prev; nl80211_remove_iface(priv, if_nametoindex(ifname)); + prev = &drv->bss; + bss = drv->bss.next; + while (bss) { + if (os_strncmp(ifname, bss->iface, IFNAMSIZ) == 0) { + prev->next = bss->next; + os_free(bss); + break; + } + prev = bss; + bss = bss->next; + } return 0; } static int i802_set_beacon(const char *iface, void *priv, - u8 *head, size_t head_len, - u8 *tail, size_t tail_len) + const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, int dtim_period) { struct i802_driver_data *drv = priv; struct nl_msg *msg; u8 cmd = NL80211_CMD_NEW_BEACON; int ret; + struct i802_bss *bss; + + bss = get_bss(drv, iface); + if (bss == NULL) + return -ENOENT; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - if (drv->beacon_set) + wpa_printf(MSG_DEBUG, "nl80211: Set beacon (iface=%s beacon_set=%d)", + iface, bss->beacon_set); + if (bss->beacon_set) cmd = NL80211_CMD_SET_BEACON; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, @@ -1092,14 +1121,11 @@ static int i802_set_beacon(const char *iface, void *priv, NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int); - - if (!drv->dtim_period) - drv->dtim_period = 2; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (!ret) - drv->beacon_set = 1; + bss->beacon_set = 1; return ret; nla_put_failure: return -ENOBUFS; @@ -1124,42 +1150,6 @@ static int i802_del_beacon(struct i802_driver_data *drv) } -static int i802_set_ieee8021x(const char *ifname, void *priv, int enabled) -{ - struct i802_driver_data *drv = priv; - - /* - * FIXME: This needs to be per interface (BSS) - */ - drv->ieee802_1x_active = enabled; - return 0; -} - - -static int i802_set_privacy(const char *ifname, void *priv, int enabled) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - - os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); - iwr.u.param.flags = IW_AUTH_PRIVACY_INVOKED; - iwr.u.param.value = enabled; - - ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr); - - /* ignore errors, the kernel/driver might not care */ - return 0; -} - - -static int i802_set_internal_bridge(void *priv, int value) -{ - return -1; -} - - static int i802_set_beacon_int(void *priv, int value) { struct i802_driver_data *drv = priv; @@ -1167,13 +1157,15 @@ static int i802_set_beacon_int(void *priv, int value) drv->beacon_int = value; - if (!drv->beacon_set) + if (!drv->bss.beacon_set) return 0; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; + wpa_printf(MSG_DEBUG, "nl80211: Set beacon interval %d " + "(beacon_set=%d)", value, drv->bss.beacon_set); genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_SET_BEACON, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); @@ -1186,31 +1178,8 @@ static int i802_set_beacon_int(void *priv, int value) } -static int i802_set_dtim_period(const char *iface, void *priv, int value) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_BEACON, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); - - drv->dtim_period = value; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - static int i802_set_bss(void *priv, int cts, int preamble, int slot) { -#ifdef NL80211_CMD_SET_BSS struct i802_driver_data *drv = priv; struct nl_msg *msg; @@ -1234,9 +1203,6 @@ static int i802_set_bss(void *priv, int cts, int preamble, int slot) return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: return -ENOBUFS; -#else /* NL80211_CMD_SET_BSS */ - return -1; -#endif /* NL80211_CMD_SET_BSS */ } @@ -1316,6 +1282,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, }; struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; @@ -1352,6 +1319,11 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL); + if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { + mode->ht_capab = nla_get_u16( + tb_band[NL80211_BAND_ATTR_HT_CAPA]); + } + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); @@ -1405,6 +1377,12 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) mode->channels[idx].flag |= HOSTAPD_CHAN_RADAR; + + if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && + !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].max_tx_power = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; + idx++; } @@ -1444,6 +1422,80 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; } +static struct hostapd_hw_modes *i802_add_11b(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(struct hostapd_rate_data)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i].rate > 110 || + mode11g->rates[i].flags & + (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM)) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); + + return modes; +} + static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) @@ -1468,7 +1520,7 @@ static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) - return result.modes; + return i802_add_11b(result.modes, num_modes); nla_put_failure: return NULL; } @@ -1499,23 +1551,27 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, } -static void handle_unknown_sta(struct hostapd_data *hapd, u8 *ta) +static int i802_set_country(void *priv, const char *country) { - struct sta_info *sta; + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + char alpha2[3]; - sta = ap_get_sta(hapd, ta); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { - printf("Data/PS-poll frame from not associated STA " - MACSTR "\n", MAC2STR(ta)); - if (sta && (sta->flags & WLAN_STA_AUTH)) - hostapd_sta_disassoc( - hapd, ta, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - else - hostapd_sta_deauth( - hapd, ta, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - } + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + alpha2[0] = country[0]; + alpha2[1] = country[1]; + alpha2[2] = '\0'; + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; } @@ -1524,7 +1580,6 @@ static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, { struct ieee80211_hdr *hdr; u16 fc, type, stype; - struct sta_info *sta; hdr = (struct ieee80211_hdr *) buf; fc = le_to_host16(hdr->frame_control); @@ -1536,25 +1591,14 @@ static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, case WLAN_FC_TYPE_MGMT: wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", ok ? "ACK" : "fail"); - ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + hostapd_mgmt_tx_cb(hapd, buf, len, stype, ok); break; case WLAN_FC_TYPE_CTRL: wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", ok ? "ACK" : "fail"); break; case WLAN_FC_TYPE_DATA: - wpa_printf(MSG_DEBUG, "DATA (TX callback) %s", - ok ? "ACK" : "fail"); - sta = ap_get_sta(hapd, hdr->addr1); - if (sta && sta->flags & WLAN_STA_PENDING_POLL) { - wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " - "activity poll", MAC2STR(sta->addr), - ok ? "ACKed" : "did not ACK"); - if (ok) - sta->flags &= ~WLAN_STA_PENDING_POLL; - } - if (sta) - ieee802_1x_tx_status(hapd, sta, buf, len, ok); + hostapd_tx_status(hapd, hdr->addr1, buf, len, ok); break; default: printf("unknown TX callback frame type %d\n", type); @@ -1563,7 +1607,8 @@ static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, } -static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, +static void handle_frame(struct i802_driver_data *drv, + struct hostapd_iface *iface, u8 *buf, size_t len, struct hostapd_frame_info *hfi, enum ieee80211_msg_type msg_type) { @@ -1596,6 +1641,9 @@ static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, case WLAN_FC_TODS: bssid = hdr->addr1; break; + case WLAN_FC_FROMDS: + bssid = hdr->addr2; + break; default: /* discard */ return; @@ -1661,19 +1709,18 @@ static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, wpa_printf(MSG_MSGDUMP, "MGMT"); if (broadcast_bssid) { for (i = 0; i < iface->num_bss; i++) - ieee802_11_mgmt(iface->bss[i], buf, data_len, + hostapd_mgmt_rx(iface->bss[i], buf, data_len, stype, hfi); } else - ieee802_11_mgmt(hapd, buf, data_len, stype, hfi); + hostapd_mgmt_rx(hapd, buf, data_len, stype, hfi); break; case WLAN_FC_TYPE_CTRL: /* can only get here with PS-Poll frames */ wpa_printf(MSG_DEBUG, "CTRL"); - handle_unknown_sta(hapd, hdr->addr2); + hostapd_rx_from_unknown_sta(drv->hapd, hdr->addr2); break; case WLAN_FC_TYPE_DATA: - wpa_printf(MSG_DEBUG, "DATA"); - handle_unknown_sta(hapd, hdr->addr2); + hostapd_rx_from_unknown_sta(drv->hapd, hdr->addr2); break; } } @@ -1682,7 +1729,6 @@ static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) { struct i802_driver_data *drv = eloop_ctx; - struct hostapd_data *hapd = drv->hapd; struct sockaddr_ll lladdr; unsigned char buf[3000]; int len; @@ -1695,8 +1741,13 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (have_ifidx(drv, lladdr.sll_ifindex)) - ieee802_1x_receive(hapd, lladdr.sll_addr, buf, len); + if (have_ifidx(drv, lladdr.sll_ifindex)) { + struct hostapd_data *hapd; + hapd = hostapd_sta_get_bss(drv->hapd, lladdr.sll_addr); + if (!hapd) + return; + hostapd_eapol_receive(hapd, lladdr.sll_addr, buf, len); + } } @@ -1772,11 +1823,155 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) else msg_type = ieee80211_msg_tx_callback_ack; - handle_frame(hapd->iface, buf + iter.max_length, + handle_frame(drv, hapd->iface, buf + iter.max_length, len - iter.max_length, &hfi, msg_type); } +/* + * we post-process the filter code later and rewrite + * this to the offset to the last instruction + */ +#define PASS 0xFF +#define FAIL 0xFE + +static struct sock_filter msock_filter_insns[] = { + /* + * do a little-endian load of the radiotap length field + */ + /* load lower byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + /* put it into X (== index register) */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + /* load upper byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), + /* left-shift it by 8 */ + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), + /* or with X */ + BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), + /* put result into X */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + + /* + * Allow management frames through, this also gives us those + * management frames that we sent ourselves with status + */ + /* load the lower byte of the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off frame type and version */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), + /* accept frame if it's both 0, fall through otherwise */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + + /* + * TODO: add a bit to radiotap RX flags that indicates + * that the sending station is not associated, then + * add a filter here that filters on our DA and that flag + * to allow us to deauth frames to that bad station. + * + * Not a regression -- we didn't do it before either. + */ + +#if 0 + /* + * drop non-data frames, WDS frames + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), + /* drop non-data frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* drop WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), +#endif + + /* + * add header length to index + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), + /* right shift it by 6 to give 0 or 2 */ + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), + /* add data frame header length */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), + /* add index, was start of 802.11 header */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + /* move to index, now start of LL header */ + BPF_STMT(BPF_MISC | BPF_TAX, 0), + + /* + * Accept empty data frames, we use those for + * polling activity. + */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + + /* + * Accept EAPOL frames + */ + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), + + /* keep these last two statements or change the code below */ + /* return 0 == "DROP" */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return ~0 == "keep all" */ + BPF_STMT(BPF_RET | BPF_K, ~0), +}; + +static struct sock_fprog msock_filter = { + .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), + .filter = msock_filter_insns, +}; + + +static int add_monitor_filter(int s) +{ + int idx; + + /* rewrite all PASS/FAIL jump offsets */ + for (idx = 0; idx < msock_filter.len; idx++) { + struct sock_filter *insn = &msock_filter_insns[idx]; + + if (BPF_CLASS(insn->code) == BPF_JMP) { + if (insn->code == (BPF_JMP|BPF_JA)) { + if (insn->k == PASS) + insn->k = msock_filter.len - idx - 2; + else if (insn->k == FAIL) + insn->k = msock_filter.len - idx - 3; + } + + if (insn->jt == PASS) + insn->jt = msock_filter.len - idx - 2; + else if (insn->jt == FAIL) + insn->jt = msock_filter.len - idx - 3; + + if (insn->jf == PASS) + insn->jf = msock_filter.len - idx - 2; + else if (insn->jf == FAIL) + insn->jf = msock_filter.len - idx - 3; + } + } + + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &msock_filter, sizeof(msock_filter))) { + perror("SO_ATTACH_FILTER"); + return -1; + } + + return 0; +} + + static int nl80211_create_monitor_interface(struct i802_driver_data *drv) { char buf[IFNAMSIZ]; @@ -1805,6 +2000,12 @@ static int nl80211_create_monitor_interface(struct i802_driver_data *drv) goto error; } + if (add_monitor_filter(drv->monitor_sock)) { + wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " + "interface; do filtering in user space"); + /* This works, but will cost in performance. */ + } + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { perror("monitor socket bind"); @@ -1832,8 +2033,8 @@ static int nl80211_create_monitor_interface(struct i802_driver_data *drv) } -static int nl80211_set_master_mode(struct i802_driver_data *drv, - const char *ifname) +static int nl80211_set_mode(struct i802_driver_data *drv, const char *ifname, + int mode) { struct nl_msg *msg; int ret = -ENOBUFS; @@ -1846,7 +2047,7 @@ static int nl80211_set_master_mode(struct i802_driver_data *drv, 0, NL80211_CMD_SET_INTERFACE, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(ifname)); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (!ret) @@ -1858,12 +2059,382 @@ static int nl80211_set_master_mode(struct i802_driver_data *drv, } +#ifdef CONFIG_IEEE80211N +static void i802_add_neighbor(struct i802_driver_data *drv, u8 *bssid, + int freq, u8 *ie, size_t ie_len) +{ + struct ieee802_11_elems elems; + int ht, pri_chan = 0, sec_chan = 0; + struct ieee80211_ht_operation *oper; + struct hostapd_neighbor_bss *nnei; + + ieee802_11_parse_elems(ie, ie_len, &elems, 0); + ht = elems.ht_capabilities || elems.ht_operation; + if (elems.ht_operation && elems.ht_operation_len >= sizeof(*oper)) { + oper = (struct ieee80211_ht_operation *) elems.ht_operation; + pri_chan = oper->control_chan; + if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { + if (oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = pri_chan + 4; + else if (oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = pri_chan - 4; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Neighboring BSS - bssid=" MACSTR + " freq=%d MHz HT=%d pri_chan=%d sec_chan=%d", + MAC2STR(bssid), freq, ht, pri_chan, sec_chan); + + nnei = os_realloc(drv->neighbors, (drv->num_neighbors + 1) * + sizeof(struct hostapd_neighbor_bss)); + if (nnei == NULL) + return; + drv->neighbors = nnei; + nnei = &nnei[drv->num_neighbors]; + os_memcpy(nnei->bssid, bssid, ETH_ALEN); + nnei->freq = freq; + nnei->ht = !!ht; + nnei->pri_chan = pri_chan; + nnei->sec_chan = sec_chan; + drv->num_neighbors++; +} + + +static int i802_get_scan_freq(struct iw_event *iwe, int *freq) +{ + int divi = 1000000, i; + + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report frequency, but a channel. + * Try to map this to frequency by assuming they are using + * IEEE 802.11b/g. But don't overwrite a previously parsed + * frequency if the driver sends both frequency and channel, + * since the driver may be sending an A-band channel that we + * don't handle here. + */ + + if (*freq) + return 0; + + if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { + *freq = 2407 + 5 * iwe->u.freq.m; + return 0; + } else if (iwe->u.freq.m == 14) { + *freq = 2484; + return 0; + } + } + + if (iwe->u.freq.e > 6) { + wpa_printf(MSG_DEBUG, "Invalid freq in scan results: " + "m=%d e=%d", iwe->u.freq.m, iwe->u.freq.e); + return -1; + } + + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + *freq = iwe->u.freq.m / divi; + return 0; +} + + +static int i802_parse_scan(struct i802_driver_data *drv, u8 *res_buf, + size_t len) +{ + size_t ap_num = 0; + int first; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom; + u8 bssid[ETH_ALEN]; + int freq = 0; + u8 *ie = NULL; + size_t ie_len = 0; + + ap_num = 0; + first = 1; + + pos = (char *) res_buf; + end = (char *) res_buf + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (iwe->cmd == IWEVGENIE) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + i802_add_neighbor(drv, bssid, freq, ie, + ie_len); + first = 0; + os_memcpy(bssid, iwe->u.ap_addr.sa_data, ETH_ALEN); + freq = 0; + ie = NULL; + ie_len = 0; + break; + case SIOCGIWFREQ: + i802_get_scan_freq(iwe, &freq); + break; + case IWEVGENIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_ERROR, "IWEVGENIE overflow"); + return -1; + } + ie = (u8 *) custom; + ie_len = iwe->u.data.length; + break; + } + + pos += iwe->len; + } + + if (!first) + i802_add_neighbor(drv, bssid, freq, ie, ie_len); + + return 0; +} + + +static int i802_get_ht_scan_res(struct i802_driver_data *drv) +{ + struct iwreq iwr; + u8 *res_buf; + size_t res_buf_len; + int res; + + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 65535) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + if (res_buf_len > 65535) + res_buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return -1; + } + } + + if (iwr.u.data.length > res_buf_len) { + os_free(res_buf); + return -1; + } + + res = i802_parse_scan(drv, res_buf, iwr.u.data.length); + os_free(res_buf); + + return res; +} + + +static int i802_is_event_wireless_scan_complete(char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->cmd == SIOCGIWSCAN) + return 1; + + pos += iwe->len; + } + + return 0; +} + + +static int i802_is_rtm_scan_complete(int ifindex, struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < (int) sizeof(*ifi)) + return 0; + + ifi = NLMSG_DATA(h); + + if (ifindex != ifi->ifi_index) + return 0; /* event for foreign ifindex */ + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS && + i802_is_event_wireless_scan_complete( + ((char *) attr) + rta_len, + attr->rta_len - rta_len)) + return 1; + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int i802_is_scan_complete(int s, int ifindex) +{ + char buf[1024]; + int left; + struct nlmsghdr *h; + + left = recv(s, buf, sizeof(buf), MSG_DONTWAIT); + if (left < 0) { + perror("recv(netlink)"); + return 0; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + if (i802_is_rtm_scan_complete(ifindex, h, plen)) + return 1; + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + return 0; +} + + +static int i802_ht_scan(struct i802_driver_data *drv) +{ + struct iwreq iwr; + int s, res, ifindex; + struct sockaddr_nl local; + time_t now, end; + fd_set rfds; + struct timeval tv; + + wpa_printf(MSG_DEBUG, "nl80211: Scanning overlapping BSSes before " + "starting HT 20/40 MHz BSS"); + + /* Request a new scan */ + /* TODO: would be enough to scan the selected band */ + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + return -1; + } + + ifindex = if_nametoindex(drv->iface); + + /* Wait for scan completion event or timeout */ + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + time(&end); + end += 30; /* Wait at most 30 seconds for scan results */ + for (;;) { + time(&now); + tv.tv_sec = end > now ? end - now : 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(s, &rfds); + res = select(s + 1, &rfds, NULL, NULL, &tv); + if (res < 0) { + perror("select"); + /* Assume results are ready after 10 seconds wait */ + os_sleep(10, 0); + break; + } else if (res) { + if (i802_is_scan_complete(s, ifindex)) { + wpa_printf(MSG_DEBUG, "nl80211: Scan " + "completed"); + break; + } + } else { + wpa_printf(MSG_DEBUG, "nl80211: Scan timeout"); + /* Assume results are ready to be read now */ + break; + } + } + + close(s); + + return i802_get_ht_scan_res(drv); +} +#endif /* CONFIG_IEEE80211N */ + + static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) { struct ifreq ifr; - struct sockaddr_ll addr; - - drv->ioctl_sock = -1; drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { @@ -1908,11 +2479,18 @@ static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) return -1; } +#ifdef CONFIG_LIBNL20 + if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { + printf("Failed to allocate generic netlink cache.\n"); + return -1; + } +#else /* CONFIG_LIBNL20 */ drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); if (!drv->nl_cache) { printf("Failed to allocate generic netlink cache.\n"); return -1; } +#endif /* CONFIG_LIBNL20 */ drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); if (!drv->nl80211) { @@ -1920,26 +2498,33 @@ static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) return -1; } +#ifdef CONFIG_IEEE80211N + if (drv->ht_40mhz_scan) { + if (nl80211_set_mode(drv, drv->iface, NL80211_IFTYPE_STATION) + || hostapd_set_iface_flags(drv, drv->iface, 1) || + i802_ht_scan(drv) || + hostapd_set_iface_flags(drv, drv->iface, 0)) { + wpa_printf(MSG_ERROR, "Failed to scan channels for " + "HT 40 MHz operations"); + return -1; + } + } +#endif /* CONFIG_IEEE80211N */ + /* Initialise a monitor interface */ if (nl80211_create_monitor_interface(drv)) return -1; - if (nl80211_set_master_mode(drv, drv->iface)) - return -1; + if (nl80211_set_mode(drv, drv->iface, NL80211_IFTYPE_AP)) + goto fail1; if (hostapd_set_iface_flags(drv, drv->iface, 1)) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", - addr.sll_ifindex); + goto fail1; drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); if (drv->eapol_sock < 0) { perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); - return -1; + goto fail1; } if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) @@ -1952,17 +2537,21 @@ static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { perror("ioctl(SIOCGIFHWADDR)"); - return -1; + goto fail1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { printf("Invalid HW-addr family 0x%04x\n", ifr.ifr_hwaddr.sa_family); - return -1; + goto fail1; } memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); return 0; + +fail1: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; } @@ -2006,7 +2595,7 @@ hostapd_wireless_event_wireless_custom(struct i802_driver_data *drv, } pos += 5; if (hwaddr_aton(pos, addr) == 0) { - ieee80211_michael_mic_failure(drv->hapd, addr, 1); + hostapd_michael_mic_failure(drv->hapd, addr); } else { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " @@ -2071,7 +2660,7 @@ static void hostapd_wireless_event_rtm_newlink(struct i802_driver_data *drv, struct nlmsghdr *h, int len) { struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; + int attrlen, _nlmsg_len, rta_len; struct rtattr *attr; if (len < (int) sizeof(*ifi)) @@ -2082,13 +2671,13 @@ static void hostapd_wireless_event_rtm_newlink(struct i802_driver_data *drv, /* TODO: use ifi->ifi_index to filter out wireless events from other * interfaces */ - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - attrlen = h->nlmsg_len - nlmsg_len; + attrlen = h->nlmsg_len - _nlmsg_len; if (attrlen < 0) return; - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { @@ -2196,9 +2785,8 @@ static int hostap_get_we_version(struct i802_driver_data *drv) } -static int i802_wireless_event_init(void *priv) +static int i802_wireless_event_init(struct i802_driver_data *drv) { - struct i802_driver_data *drv = priv; int s; struct sockaddr_nl local; @@ -2229,9 +2817,8 @@ static int i802_wireless_event_init(void *priv) } -static void i802_wireless_event_deinit(void *priv) +static void i802_wireless_event_deinit(struct i802_driver_data *drv) { - struct i802_driver_data *drv = priv; if (drv->wext_sock < 0) return; eloop_unregister_read_sock(drv->wext_sock); @@ -2273,9 +2860,19 @@ static int i802_sta_disassoc(void *priv, const u8 *addr, int reason) } +static const struct hostapd_neighbor_bss * +i802_get_neighbor_bss(void *priv, size_t *num) +{ + struct i802_driver_data *drv = priv; + *num = drv->num_neighbors; + return drv->neighbors; +} + + static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) { struct i802_driver_data *drv; + size_t i; drv = os_zalloc(sizeof(struct i802_driver_data)); if (drv == NULL) { @@ -2285,14 +2882,23 @@ static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) drv->hapd = hapd; memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + memcpy(drv->bss.iface, hapd->conf->iface, sizeof(drv->iface)); drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; - drv->bridge = if_nametoindex(hapd->conf->bridge); + for (i = 0; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + if (bss->conf->bridge) + add_ifidx(drv, if_nametoindex(bss->conf->bridge)); + } + drv->ht_40mhz_scan = hapd->iconf->secondary_channel != 0; if (i802_init_sockets(drv, bssid)) goto failed; + if (i802_wireless_event_init(drv)) + goto failed; + return drv; failed: @@ -2310,6 +2916,17 @@ static void *i802_init(struct hostapd_data *hapd) static void i802_deinit(void *priv) { struct i802_driver_data *drv = priv; + struct i802_bss *bss, *prev; + + i802_wireless_event_deinit(drv); + + if (drv->last_freq_ht) { + /* Clear HT flags from the driver */ + struct hostapd_freq_params freq; + os_memset(&freq, 0, sizeof(freq)); + freq.freq = drv->last_freq; + i802_set_freq(priv, &freq); + } i802_del_beacon(drv); @@ -2337,20 +2954,25 @@ static void i802_deinit(void *priv) if (drv->if_indices != drv->default_if_indices) free(drv->if_indices); + os_free(drv->neighbors); + + bss = drv->bss.next; + while (bss) { + prev = bss; + bss = bss->next; + os_free(bss); + } + free(drv); } -const struct wpa_driver_ops wpa_driver_nl80211_ops = { +const struct hapd_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .init = i802_init, .init_bssid = i802_init_bssid, .deinit = i802_deinit, - .wireless_event_init = i802_wireless_event_init, - .wireless_event_deinit = i802_wireless_event_deinit, - .set_ieee8021x = i802_set_ieee8021x, - .set_privacy = i802_set_privacy, - .set_encryption = i802_set_encryption, + .set_key = i802_set_key, .get_seqnum = i802_get_seqnum, .flush = i802_flush, .read_sta_data = i802_read_sta_data, @@ -2360,22 +2982,16 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sta_disassoc = i802_sta_disassoc, .sta_remove = i802_sta_remove, .send_mgmt_frame = i802_send_mgmt_frame, - .sta_add2 = i802_sta_add2, + .sta_add = i802_sta_add, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, .set_freq = i802_set_freq, .set_rts = i802_set_rts, - .get_rts = i802_get_rts, .set_frag = i802_set_frag, - .get_frag = i802_get_frag, .set_retry = i802_set_retry, - .get_retry = i802_get_retry, .set_rate_sets = i802_set_rate_sets, - .set_regulatory_domain = i802_set_regulatory_domain, .set_beacon = i802_set_beacon, - .set_internal_bridge = i802_set_internal_bridge, .set_beacon_int = i802_set_beacon_int, - .set_dtim_period = i802_set_dtim_period, .set_cts_protect = i802_set_cts_protect, .set_preamble = i802_set_preamble, .set_short_slot_time = i802_set_short_slot_time, @@ -2387,4 +3003,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .if_remove = i802_if_remove, .get_hw_feature_data = i802_get_hw_feature_data, .set_sta_vlan = i802_set_sta_vlan, + .set_country = i802_set_country, + .get_neighbor_bss = i802_get_neighbor_bss, };