+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u64 *cookie = arg;
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_COOKIE])
+ *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ struct nl_msg *msg;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+ u64 cookie;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
+ drv->ifindex);
+
+ buf = os_zalloc(24 + data_len);
+ if (buf == NULL)
+ return ret;
+ os_memcpy(buf + 24, data, data_len);
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+ os_memcpy(hdr->addr1, dst, ETH_ALEN);
+ os_memcpy(hdr->addr2, src, ETH_ALEN);
+ os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+ if (drv->nlmode == NL80211_IFTYPE_AP) {
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ os_free(buf);
+ return ret;
+ }
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ os_free(buf);
+ return -1;
+ }
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_ACTION, 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);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
+ "cookie 0x%llx", (long long unsigned int) cookie);
+ drv->send_action_cookie = cookie;
+ ret = 0;
+
+nla_put_failure:
+ os_free(buf);
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+ unsigned int duration)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ u64 cookie;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+ "0x%llx for freq=%u MHz duration=%u",
+ (long long unsigned int) cookie, freq, duration);
+ drv->remain_on_chan_cookie = cookie;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->pending_remain_on_chan) {
+ wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+ "to cancel");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+ "0x%llx",
+ (long long unsigned int) drv->remain_on_chan_cookie);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ if (drv->monitor_ifidx < 0)
+ return; /* monitor interface already removed */
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION)
+ return; /* not in station mode anymore */
+
+ if (drv->probe_req_report)
+ return; /* reporting enabled */
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+ "Probe Request reporting needed anymore");
+ nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION) {
+ wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+ "allowed in station mode (iftype=%d)",
+ drv->nlmode);
+ return -1;
+ }
+ drv->probe_req_report = report;
+
+ if (report) {
+ eloop_cancel_timeout(
+ wpa_driver_nl80211_probe_req_report_timeout,
+ drv, NULL);
+ if (drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv))
+ return -1;
+ } else {
+ /*
+ * It takes a while to remove the monitor interface, so try to
+ * avoid doing this if it is needed again shortly. Instead,
+ * schedule the interface to be removed later if no need for it
+ * is seen.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+ "to be removed after 10 seconds of no use");
+ eloop_register_timeout(
+ 10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+ drv, NULL);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled)
+{
+ struct nl_msg *msg;
+ struct nlattr *bands, *band;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto nla_put_failure;
+
+ /*
+ * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+ * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+ * rates. All 5 GHz rates are left enabled.
+ */
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ if (!band)
+ goto nla_put_failure;
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ }
+
+ return ret;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ drv->disable_11b_rates = disabled;
+ return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_AP)
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
+ "resume event");
+ }
+}
+
+
+static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+ u8 *data, *pos;
+ size_t data_len;
+ u8 own_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+ return -1;
+
+ if (action != 1) {
+ wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
+ "action %d", action);
+ return -1;
+ }
+
+ /*
+ * Action frame payload:
+ * Category[1] = 6 (Fast BSS Transition)
+ * Action[1] = 1 (Fast BSS Transition Request)
+ * STA Address
+ * Target AP Address
+ * FT IEs
+ */
+
+ data_len = 2 + 2 * ETH_ALEN + ies_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ *pos++ = 0x06; /* FT Action category */
+ *pos++ = action;
+ os_memcpy(pos, own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, target_ap, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, ies, ies_len);
+
+ ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
+ own_addr, drv->bssid,
+ data, data_len);
+ os_free(data);
+
+ return ret;
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg, *cqm = NULL;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+ "hysteresis=%d", threshold, hysteresis);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_CQM, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ cqm = nlmsg_alloc();
+ if (cqm == NULL)
+ return -1;
+
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+ nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return 0;
+ msg = NULL;
+
+nla_put_failure:
+ if (cqm)
+ nlmsg_free(cqm);
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+