X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fdrivers%2Fdriver_nl80211.c;h=9823afd3537bce418356a4714d6f0249a93eb300;hb=bd94971e115dc03c03eba1b538a52e0b76341df5;hp=70a0d07b7002b0ff5fbf13d508e850db1fc71284;hpb=b91ab76e8ca3dceb6b0d9262988bbff12e722220;p=libeap.git diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 70a0d07..9823afd 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "nl80211_copy.h" @@ -33,13 +34,54 @@ #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" +#include "rfkill.h" #include "driver.h" #ifdef CONFIG_LIBNL20 /* libnl 2.0 compatibility code */ #define nl_handle nl_sock -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} #endif /* CONFIG_LIBNL20 */ @@ -72,6 +114,8 @@ struct wpa_driver_nl80211_data { char brname[IFNAMSIZ]; int ifindex; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; int has_capability; @@ -101,7 +145,6 @@ struct wpa_driver_nl80211_data { int disable_11b_rates; unsigned int pending_remain_on_chan:1; - unsigned int pending_send_action:1; unsigned int added_bridge:1; unsigned int added_if_into_bridge:1; @@ -132,19 +175,26 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code); + const u8 *addr, int cmd, u16 reason_code, + int local_state_change); static void nl80211_remove_monitor_interface( struct wpa_driver_nl80211_data *drv); #ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int wpa_driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, const char *ifname); +#else /* HOSTAPD */ +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + return 0; +} #endif /* HOSTAPD */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, void *timeout_ctx); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, @@ -388,10 +438,12 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len)) { - wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", - ifi->ifi_index); + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && + !have_ifidx(drv, ifi->ifi_index)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " + "ifindex %d", ifi->ifi_index); return; } @@ -402,6 +454,19 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -423,9 +488,21 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been added to bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", + brid, namebuf); + add_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -436,6 +513,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, struct wpa_driver_nl80211_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + u32 brid = 0; attrlen = len; attr = (struct rtattr *) buf; @@ -447,9 +525,21 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); - } + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); attr = RTA_NEXT(attr, attrlen); } + +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been removed from bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " + "%s", brid, namebuf); + del_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } @@ -638,10 +728,11 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, return; cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s", + wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s " + "(ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)"); + " (match)" : " (unknown)", ack != NULL); if (cookie_val != drv->send_action_cookie) return; @@ -659,6 +750,63 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, } +static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + const u8 *bssid = NULL; + u16 reason_code = 0; + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); + return; + } + } + + drv->associated = 0; + os_memset(&event, 0, sizeof(event)); + + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_DISASSOC) { + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + static void mlme_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, @@ -688,17 +836,17 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DEAUTHENTICATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); break; case NL80211_CMD_DISASSOCIATE: - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION: + case NL80211_CMD_FRAME: mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); break; - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event_action_tx_status(drv, cookie, nla_data(frame), nla_len(frame), ack); break; @@ -849,6 +997,53 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } +static int get_link_signal(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + }; + int *sig = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; + + *sig = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + return NL_SKIP; +} + + +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + int *sig) +{ + struct nl_msg *msg; + + *sig = -9999; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + return -ENOBUFS; +} + + static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { @@ -860,6 +1055,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; union wpa_event_data ed; + int sig, res; if (tb[NL80211_ATTR_CQM] == NULL || nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], @@ -885,6 +1081,12 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, } else return; + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm", sig); + } + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } @@ -894,13 +1096,14 @@ static int process_event(struct nl_msg *msg, void *arg) struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data data; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { + if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" " for foreign interface (ifindex %d)", gnlh->cmd, ifindex); @@ -941,8 +1144,8 @@ static int process_event(struct nl_msg *msg, void *arg) case NL80211_CMD_ASSOCIATE: case NL80211_CMD_DEAUTHENTICATE: case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_ACTION: - case NL80211_CMD_ACTION_TX_STATUS: + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], @@ -967,7 +1170,11 @@ static int process_event(struct nl_msg *msg, void *arg) break; } drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_REASON_CODE]) + data.disassoc_info.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); break; case NL80211_CMD_MICHAEL_MIC_FAILURE: mlme_event_michael_mic_failure(drv, tb); @@ -1156,14 +1363,15 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) } drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + drv->capa.max_remain_on_chan = 5000; return 0; } #endif /* HOSTAPD */ -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, - void *ctx) +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) { int ret; @@ -1176,14 +1384,14 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, goto err1; } - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks"); goto err2; } - drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); + drv->nl_handle_event = nl80211_handle_alloc(drv->nl_cb); if (drv->nl_handle_event == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " "callbacks (event)"); @@ -1257,7 +1465,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, } eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), - wpa_driver_nl80211_event_receive, drv, ctx); + wpa_driver_nl80211_event_receive, drv, NULL); return 0; @@ -1266,9 +1474,9 @@ err4: err3b: nl_cache_free(drv->nl_cache); err3: - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle_event); err2b: - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); err2: nl_cb_put(drv->nl_cb); err1: @@ -1276,6 +1484,29 @@ err1: } +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + /** * wpa_driver_nl80211_init - Initialize nl80211 driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -1287,6 +1518,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) { struct wpa_driver_nl80211_data *drv; struct netlink_config *cfg; + struct rfkill_config *rcfg; struct i802_bss *bss; drv = os_zalloc(sizeof(*drv)); @@ -1300,7 +1532,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) drv->monitor_sock = -1; drv->ioctl_sock = -1; - if (wpa_driver_nl80211_init_nl(drv, ctx)) { + if (wpa_driver_nl80211_init_nl(drv)) { os_free(drv); return NULL; } @@ -1322,19 +1554,34 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) os_free(cfg); goto failed; } + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } + if (wpa_driver_nl80211_finish_drv_init(drv)) goto failed; return bss; failed: + rfkill_deinit(drv->rfkill); netlink_deinit(drv->netlink); if (drv->ioctl_sock >= 0) close(drv->ioctl_sock); genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); @@ -1343,8 +1590,8 @@ failed: } -static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, - const u8 *match, size_t match_len) +static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv, + u16 type, const u8 *match, size_t match_len) { struct nl_msg *msg; int ret = -1; @@ -1357,6 +1604,7 @@ static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, NL80211_CMD_REGISTER_ACTION, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); @@ -1375,12 +1623,40 @@ nla_put_failure: } +static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(drv, type, match, match_len); +} + + static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) { - if (0) { - /* Public Action frames */ - return nl80211_register_action_frame(drv, (u8 *) "\x04", 1); - } +#ifdef CONFIG_P2P + /* GAS Initial Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0) + return -1; + /* P2P Public Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(drv, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ /* FT Action frames */ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) @@ -1393,10 +1669,17 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) } +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { struct i802_bss *bss = &drv->first_bss; + int send_rfkill_event = 0; drv->ifindex = if_nametoindex(bss->ifname); drv->first_bss.ifindex = drv->ifindex; @@ -1408,9 +1691,17 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) } if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_ERROR, "Could not set interface '%s' UP", - bss->ifname); - return -1; + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } } if (wpa_driver_nl80211_capa(drv)) @@ -1430,6 +1721,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) */ } + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); + } + return 0; } @@ -1506,6 +1802,7 @@ static void wpa_driver_nl80211_deinit(void *priv) netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); @@ -1519,8 +1816,8 @@ static void wpa_driver_nl80211_deinit(void *priv) genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); nl_cache_free(drv->nl_cache_event); - nl_handle_destroy(drv->nl_handle); - nl_handle_destroy(drv->nl_handle_event); + nl80211_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle_event); nl_cb_put(drv->nl_cb); eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, @@ -1845,7 +2142,7 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, "mismatch (" MACSTR ")", MAC2STR(addr)); wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID); + WLAN_REASON_PREV_AUTH_NOT_VALID, 1); } } @@ -2076,9 +2373,12 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, */ if (ret || !set_tx || alg == WPA_ALG_NONE) return ret; -#ifdef HOSTAPD /* FIX: is this needed? */ +#ifdef HOSTAPD if (addr) return ret; +#else /* HOSTAPD */ + if (drv->nlmode == NL80211_IFTYPE_AP && addr) + return ret; #endif /* HOSTAPD */ msg = nlmsg_alloc(); @@ -2216,7 +2516,8 @@ nla_put_failure: static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code) + const u8 *addr, int cmd, u16 reason_code, + int local_state_change) { int ret = -1; struct nl_msg *msg; @@ -2230,6 +2531,8 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (local_state_change) + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -2249,10 +2552,11 @@ nla_put_failure: static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, const u8 *addr, int reason_code) { - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, - reason_code); + reason_code, 0); } @@ -2263,10 +2567,11 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, addr, reason_code); - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code); + reason_code, 0); } @@ -2280,7 +2585,7 @@ static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, wpa_printf(MSG_DEBUG, "%s", __func__); drv->associated = 0; return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, - reason_code); + reason_code, 0); } @@ -2350,10 +2655,6 @@ retry: wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); if (params->ie) NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); - /* - * TODO: if multiple auth_alg options enabled, try them one by one if - * the AP rejects authentication due to unknown auth alg - */ if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; else if (params->auth_alg & WPA_AUTH_ALG_SHARED) @@ -2366,6 +2667,10 @@ retry: goto nla_put_failure; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + if (params->local_state_change) { + wpa_printf(MSG_DEBUG, " * Local state change only"); + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -2373,7 +2678,8 @@ retry: wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " "(%s)", ret, strerror(-ret)); count++; - if (ret == -EALREADY && count == 1 && params->bssid) { + if (ret == -EALREADY && count == 1 && params->bssid && + !params->local_state_change) { /* * mac80211 does not currently accept new * authentication if we are already authenticated. As a @@ -3532,6 +3838,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params) { + if (params->p2p) + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { nl80211_remove_monitor_interface(drv); @@ -3866,6 +4175,9 @@ static int wpa_driver_nl80211_associate( params->prev_bssid); } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -3915,6 +4227,7 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1; int nlmode; + int i; switch (mode) { case 0: @@ -3947,11 +4260,23 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode) * take the device down, try to set the mode again, and bring the * device back up. */ - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) { - /* Try to set the mode again while the interface is down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) - ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == + 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, + 1)) + ret = -1; + if (!ret) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); } if (!ret) { @@ -4067,7 +4392,9 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) "interfaces"); wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); return; - } + } else if (!old) + os_memcpy(drv->if_indices, drv->default_if_indices, + sizeof(drv->default_if_indices)); drv->if_indices[drv->num_if_indices] = ifidx; drv->num_if_indices++; } @@ -4183,6 +4510,8 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, return -ENOBUFS; } +#endif /* HOSTAPD */ + /* Set kernel driver on given frequency (MHz) */ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) @@ -4194,6 +4523,8 @@ static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) } +#ifdef HOSTAPD + static int i802_set_rts(void *priv, int rts) { struct i802_bss *bss = priv; @@ -4455,6 +4786,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) @@ -4469,9 +4801,15 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr, NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" + MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", + MAC2STR(addr), ifname, vlan_id, ret, + strerror(-ret)); + } nla_put_failure: - return -ENOBUFS; + return ret; } @@ -4718,7 +5056,7 @@ failed: genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); + nl80211_handle_destroy(drv->nl_handle); nl_cb_put(drv->nl_cb); os_free(drv); @@ -4739,10 +5077,13 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( { switch (type) { case WPA_IF_STATION: + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: return NL80211_IFTYPE_STATION; case WPA_IF_AP_VLAN: return NL80211_IFTYPE_AP_VLAN; case WPA_IF_AP_BSS: + case WPA_IF_P2P_GO: return NL80211_IFTYPE_AP; } return -1; @@ -4751,7 +5092,8 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, - void *bss_ctx, void **drv_priv) + void *bss_ctx, void **drv_priv, + char *force_ifname, u8 *if_addr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -4766,6 +5108,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } #endif /* HOSTAPD */ + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); ifidx = nl80211_create_iface(drv, ifname, wpa_driver_nl80211_if_type(type), addr, 0); @@ -4776,6 +5120,10 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, return -1; } + if (!addr && + linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) + return -1; + #ifdef HOSTAPD if (type == WPA_IF_AP_BSS) { if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { @@ -4880,16 +5228,19 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, } msg = nlmsg_alloc(); - if (!msg) + if (!msg) { + os_free(buf); return -1; + } genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_ACTION, 0); + NL80211_CMD_FRAME, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf); os_free(buf); + buf = NULL; cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); @@ -4902,10 +5253,10 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " "cookie 0x%llx", (long long unsigned int) cookie); drv->send_action_cookie = cookie; - drv->pending_send_action = 1; ret = 0; nla_put_failure: + os_free(buf); nlmsg_free(msg); return ret; } @@ -4941,7 +5292,8 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: return -1; } @@ -5041,35 +5393,6 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report) } -static int wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr, - char *ifname) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - - if (ifname) - ifname[0] = '\0'; - - if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, addr) < 0) - return -1; - - if (addr[0] & 0x02) { - /* TODO: add support for generating multiple addresses */ - addr[0] ^= 0x80; - } else - addr[0] = 0x02; /* locally administered */ - - return 0; -} - - -static void wpa_driver_nl80211_release_interface_addr(void *priv, - const u8 *addr) -{ - /* TODO: keep list of allocated address and release them here */ -} - - static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled) { @@ -5246,6 +5569,28 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, } +static int nl80211_set_intra_bss(void *priv, int enabled) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + 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_BSS, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -5283,7 +5628,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sta_disassoc = i802_sta_disassoc, .get_inact_sec = i802_get_inact_sec, .sta_clear_stats = i802_sta_clear_stats, - .set_freq = i802_set_freq, .set_rts = i802_set_rts, .set_frag = i802_set_frag, .set_rate_sets = i802_set_rate_sets, @@ -5294,17 +5638,17 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_sta_vlan = i802_set_sta_vlan, .set_wds_sta = i802_set_wds_sta, #endif /* HOSTAPD */ + .set_freq = i802_set_freq, .send_action = wpa_driver_nl80211_send_action, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, .probe_req_report = wpa_driver_nl80211_probe_req_report, - .alloc_interface_addr = wpa_driver_nl80211_alloc_interface_addr, - .release_interface_addr = wpa_driver_nl80211_release_interface_addr, .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates, .deinit_ap = wpa_driver_nl80211_deinit_ap, .resume = wpa_driver_nl80211_resume, .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, .send_frame = nl80211_send_frame, + .set_intra_bss = nl80211_set_intra_bss, };