X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=hostapd%2Fdriver_nl80211.c;h=877245de96ebfb85df93b05229a6845ba0f90792;hb=c2220ec0b7f47841fbec71d9f4626e2705a03a89;hp=1bc46b49127ed595117af14ba1b1abf6f6a5e0ea;hpb=913cf1caece196f217bef0908d05d25920498702;p=wpasupplicant diff --git a/hostapd/driver_nl80211.c b/hostapd/driver_nl80211.c index 1bc46b4..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 +#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,6 +55,12 @@ 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; @@ -64,18 +78,44 @@ struct i802_driver_data { int we_version; struct nl_handle *nl_handle; struct nl_cache *nl_cache; + struct nl_cb *nl_cb; struct genl_family *nl80211; - int dtim_period; - 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; @@ -130,15 +170,62 @@ static int have_ifidx(struct i802_driver_data *drv, int ifidx) } -/* helper for netlink get routines */ -static int ack_wait_handler(struct nl_msg *msg, void *arg) +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) { - int *finished = arg; - - *finished = 1; + int *err = arg; + *err = 0; return NL_STOP; } +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct i802_driver_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} static int hostapd_set_iface_flags(struct i802_driver_data *drv, const char *ifname, int dev_up) @@ -172,88 +259,100 @@ static int hostapd_set_iface_flags(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 nl_set_encr(int ifindex, struct i802_driver_data *drv, + wpa_alg alg, const u8 *addr, int idx, const u8 *key, + size_t key_len, int txkey) { - struct i802_driver_data *drv = priv; struct nl_msg *msg; - int ret = -1; - int err = 0; + int ret; msg = nlmsg_alloc(); if (!msg) - goto out; + 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 - goto out; + break; + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + break; + default: + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm %d", __func__, alg); + nlmsg_free(msg); + return -1; + } } if (addr) NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - (err = nl_wait_for_ack(drv->nl_handle)) < 0) { - if (err != -ENOENT) { - err = 0; - goto out; - } - } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; /* - * If we need to set the default TX key we do that below, - * otherwise we're done here. + * If we failed or don't need to set the default TX key (below), + * we're done here. */ - if (!txkey || addr) { - ret = 0; - goto out; - } - - nlmsg_free(msg); + if (ret || !txkey || addr) + return ret; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_SET_KEY, 0); NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (alg == WPA_ALG_IGTK) + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); + else + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - (err = nl_wait_for_ack(drv->nl_handle)) < 0) { - if (err != -ENOENT) { - err = 0; - goto out; - } - } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} - ret = 0; - out: - nla_put_failure: - nlmsg_free(msg); +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, key_idx, key, + key_len, set_tx); + if (ret < 0) + return ret; + return ret; } @@ -268,7 +367,7 @@ static inline int min_int(int a, int b) static int get_key_handler(struct nl_msg *msg, void *arg) { - struct nlattr *tb[NL80211_ATTR_MAX]; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -292,14 +391,10 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, { struct i802_driver_data *drv = priv; struct nl_msg *msg; - struct nl_cb *cb = NULL; - int ret = -1; - int err = 0; - int finished = 0; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_GET_KEY, 0); @@ -309,74 +404,52 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); - cb = nl_cb_alloc(NL_CB_CUSTOM); - if (!cb) - goto out; - memset(seq, 0, 6); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0) - goto out; - - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_key_handler, seq); - nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); - - err = nl_recvmsgs(drv->nl_handle, cb); - - if (!finished) - err = nl_wait_for_ack(drv->nl_handle); - - if (err < 0) - goto out; - - ret = 0; - - out: - nl_cb_put(cb); + return send_and_recv_msgs(drv, msg, get_key_handler, seq); nla_put_failure: - nlmsg_free(msg); - return ret; + return -ENOBUFS; } static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, int mode) { - return -1; -} + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; -static int i802_set_ssid(const char *ifname, void *priv, const u8 *buf, - int len) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); - iwr.u.essid.flags = 1; /* SSID active */ - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; - - if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); - return -1; - } + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; - return 0; + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; } -static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, - int flags) +static int i802_send_frame(void *priv, const void *data, size_t len, + int encrypt, int flags) { - struct ieee80211_hdr *hdr = (void*) data; __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ 0x0e, 0x00, /* radiotap length */ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ - 0x0c, /* F_WEP | F_FRAG (encrypt/fragment if required) */ + IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ 0x00, /* padding */ 0x00, 0x00, /* RX and TX flags to indicate that */ 0x00, 0x00, /* this is the injected frame directly */ @@ -402,69 +475,97 @@ static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, .msg_flags = 0, }; - /* - * ugh, guess what, the generic code sets one of the version - * bits to request tx callback - */ - hdr->frame_control &= ~host_to_le16(BIT(1)); + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + return sendmsg(drv->monitor_sock, &msg, flags); } - -/* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq(void *priv, int mode, int freq) +static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, + int flags) { - struct i802_driver_data *drv = priv; - struct iwreq iwr; + struct ieee80211_mgmt *mgmt; + int do_not_encrypt = 0; + u16 fc; - 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; + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); - return -1; + 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 0; + return i802_send_frame(priv, data, len, !do_not_encrypt, flags); } - -static int i802_set_rts(void *priv, int rts) +/* Set kernel driver on given frequency (MHz) */ +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.rts.value = rts; - iwr.u.rts.fixed = 1; + struct nl_msg *msg; - if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { - perror("ioctl[SIOCSIWRTS]"); + 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; } -static int i802_get_rts(void *priv, int *rts) +static int i802_set_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); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.rts.value = rts; + iwr.u.rts.fixed = 1; - if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { - perror("ioctl[SIOCGIWRTS]"); + if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { + perror("ioctl[SIOCSIWRTS]"); return -1; } - *rts = iwr.u.rts.value; - return 0; } @@ -475,7 +576,7 @@ static int i802_set_frag(void *priv, int frag) 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; @@ -488,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; } @@ -533,44 +615,17 @@ 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; struct nl_msg *msg; - int ret = -1; msg = nlmsg_alloc(); if (!msg) - goto out; + return -1; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_STATION, 0); + 0, NL80211_CMD_DEL_STATION, 0); /* * XXX: FIX! this needs to flush all VLANs too @@ -578,18 +633,9 @@ static int i802_flush(void *priv) NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - ret = 0; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) { - ret = -1; - } - + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(msg); - - out: - return ret; + return -ENOBUFS; } @@ -603,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), @@ -631,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; } @@ -641,14 +695,11 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, { struct i802_driver_data *drv = priv; struct nl_msg *msg; - struct nl_cb *cb = NULL; - int ret = -1; - int err = 0; - int finished = 0; + os_memset(data, 0, sizeof(*data)); msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_GET_STATION, 0); @@ -656,32 +707,9 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - cb = nl_cb_alloc(NL_CB_CUSTOM); - if (!cb) - goto out; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0) - goto out; - - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_sta_handler, data); - nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); - - err = nl_recvmsgs(drv->nl_handle, cb); - - if (!finished) - err = nl_wait_for_ack(drv->nl_handle); - - if (err < 0) - goto out; - - ret = 0; - - out: - nl_cb_put(cb); + return send_and_recv_msgs(drv, msg, get_sta_handler, data); nla_put_failure: - nlmsg_free(msg); - return ret; - + return -ENOBUFS; } @@ -740,7 +768,7 @@ static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, pos += 2; memcpy(pos, data, data_len); - res = i802_send_mgmt_frame(drv, (u8 *) hdr, len, 0); + res = i802_send_frame(drv, (u8 *) hdr, len, encrypt, 0); free(hdr); if (res < 0) { @@ -753,42 +781,44 @@ static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, } -static int i802_sta_add(const char *ifname, void *priv, const u8 *addr, - u16 aid, u16 capability, u8 *supp_rates, - size_t supp_rates_len, int flags, u16 listen_interval) +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; - int ret = -1; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_NEW_STATION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, aid); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, - supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, listen_interval); - - ret = nl_send_auto_complete(drv->nl_handle, msg); - if (ret < 0) - goto nla_put_failure; - - ret = nl_wait_for_ack(drv->nl_handle); - /* ignore EEXIST, this happens if a STA associates while associated */ - if (ret == -EEXIST || ret >= 0) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, + params->supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + +#ifdef CONFIG_IEEE80211N + if (params->ht_capabilities) { + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, + params->ht_capabilities->length, + ¶ms->ht_capabilities->data); + } +#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: - nlmsg_free(msg); - - out: return ret; } @@ -797,11 +827,11 @@ static int i802_sta_remove(void *priv, const u8 *addr) { struct i802_driver_data *drv = priv; struct nl_msg *msg; - int ret = -1; + int ret; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_DEL_STATION, 0); @@ -810,18 +840,12 @@ static int i802_sta_remove(void *priv, const u8 *addr) if_nametoindex(drv->iface)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - ret = 0; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) { - ret = -1; - } - - nla_put_failure: - nlmsg_free(msg); - - out: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + return 0; return ret; + nla_put_failure: + return -ENOBUFS; } @@ -830,15 +854,16 @@ static int i802_sta_set_flags(void *priv, const u8 *addr, { struct i802_driver_data *drv = priv; struct nl_msg *msg, *flags = NULL; - int ret = -1; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; flags = nlmsg_alloc(); - if (!flags) - goto free_msg; + if (!flags) { + nlmsg_free(msg); + return -ENOMEM; + } genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_SET_STATION, 0); @@ -847,53 +872,70 @@ 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); + if (total_flags & WLAN_STA_MFP) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) goto nla_put_failure; - ret = 0; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) { - ret = -1; - } + nlmsg_free(flags); + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(flags); + nlmsg_free(flags); + return -ENOBUFS; +} - free_msg: - nlmsg_free(msg); - out: - return ret; -} +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + struct nlattr *txq, *params; + msg = nlmsg_alloc(); + if (!msg) + return -1; -static int i802_set_channel_flag(void *priv, int mode, int chan, int flag, - unsigned char power_level, - unsigned char antenna_max) -{ - return -1; -} + 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)); -static int i802_set_regulatory_domain(void *priv, unsigned int rd) -{ - return -1; -} + txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); + if (!txq) + goto nla_put_failure; + /* We are only sending parameters for a single TXQ at a time */ + params = nla_nest_start(msg, 1); + if (!params) + goto nla_put_failure; -static int i802_set_tx_queue_params(void *priv, int queue, int aifs, - int cw_min, int cw_max, int burst_time) -{ + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + /* Burst time is configured in units of 0.1 msec and TXOP parameter in + * 32 usec, so need to convert the value here. */ + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); + + nla_nest_end(msg, params); + + nla_nest_end(msg, txq); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: return -1; } @@ -912,11 +954,11 @@ static void nl80211_remove_iface(struct i802_driver_data *drv, int ifidx) genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_DEL_INTERFACE, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) - nla_put_failure: - printf("Failed to remove interface.\n"); - nlmsg_free(msg); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + nla_put_failure: + printf("Failed to remove interface.\n"); } @@ -929,6 +971,7 @@ static int nl80211_create_iface(struct i802_driver_data *drv, int ifidx; struct ifreq ifreq; struct iwreq iwr; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) @@ -936,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); @@ -958,16 +1000,13 @@ static int nl80211_create_iface(struct i802_driver_data *drv, goto nla_put_failure; } - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) { + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { nla_put_failure: printf("Failed to create interface %s.\n", ifname); - nlmsg_free(msg); - return -1; + return ret; } - nlmsg_free(msg); - ifidx = if_nametoindex(ifname); if (ifidx <= 0) @@ -1008,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 = -1; + int ret; + struct i802_bss *bss; + + bss = get_bss(drv, iface); + if (bss == NULL) + return -ENOENT; msg = nlmsg_alloc(); if (!msg) - goto out; + 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, @@ -1056,86 +1120,33 @@ static int i802_set_beacon(const char *iface, void *priv, NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); 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, 1000); - - if (!drv->dtim_period) - drv->dtim_period = 2; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) - goto out; + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); - ret = 0; - - drv->beacon_set = 1; - - out: - nla_put_failure: - nlmsg_free(msg); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + bss->beacon_set = 1; return ret; + nla_put_failure: + return -ENOBUFS; } static int i802_del_beacon(struct i802_driver_data *drv) { struct nl_msg *msg; - int ret = -1; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_DEL_BEACON, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) - goto out; - - ret = 0; - - out: + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -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; + return -ENOBUFS; } @@ -1143,76 +1154,73 @@ static int i802_set_beacon_int(void *priv, int value) { struct i802_driver_data *drv = priv; struct nl_msg *msg; - int ret = -1; + + drv->beacon_int = value; + + if (!drv->bss.beacon_set) + return 0; msg = nlmsg_alloc(); if (!msg) - goto out; + 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)); NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) - goto out; - - ret = 0; - - out: + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(msg); - return ret; + return -ENOBUFS; } -static int i802_set_dtim_period(const char *iface, void *priv, int value) +static int i802_set_bss(void *priv, int cts, int preamble, int slot) { struct i802_driver_data *drv = priv; struct nl_msg *msg; - int ret = -1; msg = nlmsg_alloc(); if (!msg) - goto out; + 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)); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); - drv->dtim_period = value; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) - goto out; - - ret = 0; + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - out: + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(msg); - return ret; + return -ENOBUFS; } static int i802_set_cts_protect(void *priv, int value) { - return -1; + return i802_set_bss(priv, value, -1, -1); } static int i802_set_preamble(void *priv, int value) { - return -1; + return i802_set_bss(priv, -1, value, -1); } static int i802_set_short_slot_time(void *priv, int value) { - return -1; + return i802_set_bss(priv, -1, -1, value); } @@ -1257,7 +1265,6 @@ static int i802_if_remove(void *priv, enum hostapd_driver_if_type type, struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; - int error; }; static int phy_info_handler(struct nl_msg *msg, void *arg) @@ -1275,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]; @@ -1311,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); @@ -1332,9 +1345,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) continue; mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - mode->channels[idx].flag |= HOSTAPD_CHAN_W_SCAN | - HOSTAPD_CHAN_W_ACTIVE_SCAN | - HOSTAPD_CHAN_W_IBSS; + mode->channels[idx].flag = 0; if (!mode_is_set) { /* crude heuristic */ @@ -1355,11 +1366,23 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_SCAN; + mode->channels[idx].flag |= + HOSTAPD_CHAN_DISABLED; if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) - mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_ACTIVE_SCAN; + mode->channels[idx].flag |= + HOSTAPD_CHAN_PASSIVE_SCAN; if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) - mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_IBSS; + mode->channels[idx].flag |= + HOSTAPD_CHAN_NO_IBSS; + 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++; } @@ -1396,24 +1419,92 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) } } - phy_info->error = 0; - 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) { struct i802_driver_data *drv = priv; struct nl_msg *msg; - int err = -1; - struct nl_cb *cb = NULL; - int finished; struct phy_info_arg result = { .num_modes = num_modes, .modes = NULL, - .error = 1, }; *num_modes = 0; @@ -1428,33 +1519,10 @@ static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - cb = nl_cb_alloc(NL_CB_CUSTOM); - if (!cb) - goto out; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0) - goto out; - - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, phy_info_handler, &result); - nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); - - err = nl_recvmsgs(drv->nl_handle, cb); - - if (!finished) - err = nl_wait_for_ack(drv->nl_handle); - - if (err < 0 || result.error) { - hostapd_free_hw_features(result.modes, *num_modes); - result.modes = NULL; - } - - out: - nl_cb_put(cb); + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return i802_add_11b(result.modes, num_modes); nla_put_failure: - if (err) - fprintf(stderr, "failed to get information: %d\n", err); - nlmsg_free(msg); - return result.modes; + return NULL; } @@ -1463,11 +1531,10 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, { struct i802_driver_data *drv = priv; struct nl_msg *msg; - int ret = -1; msg = nlmsg_alloc(); if (!msg) - goto out; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_SET_STATION, 0); @@ -1478,38 +1545,33 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(ifname)); - ret = 0; - - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - (errno = nl_wait_for_ack(drv->nl_handle) < 0)) { - ret = -1; - } - + return send_and_recv_msgs(drv, msg, NULL, NULL); nla_put_failure: - nlmsg_free(msg); - - out: - return ret; + return -ENOBUFS; } -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; } @@ -1518,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); @@ -1530,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); @@ -1557,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) { @@ -1590,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; @@ -1655,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; } } @@ -1676,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; @@ -1689,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); + } } @@ -1766,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]; @@ -1799,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"); @@ -1826,42 +2033,408 @@ 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; msg = nlmsg_alloc(); if (!msg) - return -1; + return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 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); - if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || - nl_wait_for_ack(drv->nl_handle) < 0) { + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; nla_put_failure: - wpa_printf(MSG_ERROR, "Failed to set interface %s to master " - "mode.", ifname); - nlmsg_free(msg); + wpa_printf(MSG_ERROR, "Failed to set interface %s to master " + "mode.", ifname); + return ret; +} + + +#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; } - nlmsg_free(msg); + 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) { @@ -1889,7 +2462,13 @@ static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) /* * initialise generic netlink and nl80211 */ - drv->nl_handle = nl_handle_alloc(); + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + printf("Failed to allocate netlink callbacks.\n"); + return -1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); if (!drv->nl_handle) { printf("Failed to allocate netlink handle.\n"); return -1; @@ -1900,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) { @@ -1912,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)) @@ -1940,21 +2533,25 @@ static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) return -1; } - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); - if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { + memset(&ifr, 0, sizeof(ifr)); + 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; } @@ -1998,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 " @@ -2063,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)) @@ -2074,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)) { @@ -2188,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; @@ -2221,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); @@ -2265,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) { @@ -2277,13 +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; + 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: @@ -2301,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); @@ -2323,24 +2949,30 @@ static void i802_deinit(void *priv) genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); 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, @@ -2349,25 +2981,17 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sta_deauth = i802_sta_deauth, .sta_disassoc = i802_sta_disassoc, .sta_remove = i802_sta_remove, - .set_ssid = i802_set_ssid, .send_mgmt_frame = i802_send_mgmt_frame, .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_channel_flag = i802_set_channel_flag, - .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, @@ -2379,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, };