+ wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+ ifindex, mode, nl80211_iftype_str(mode));
+
+ msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+ if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (!ret)
+ return 0;
+fail:
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+ " %d (%s)", ifindex, mode, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_set_mode_impl(
+ struct i802_bss *bss,
+ enum nl80211_iftype nlmode,
+ struct hostapd_freq_params *desired_freq_params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ int i;
+ int was_ap = is_ap_interface(drv->nlmode);
+ int res;
+ int mode_switch_res;
+
+ if (TEST_FAIL())
+ return -1;
+
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+ mode_switch_res = 0;
+
+ if (mode_switch_res == 0) {
+ drv->nlmode = nlmode;
+ ret = 0;
+ goto done;
+ }
+
+ if (mode_switch_res == -ENODEV)
+ return -1;
+
+ if (nlmode == drv->nlmode) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+ "requested mode - ignore error");
+ ret = 0;
+ goto done; /* Already in the requested mode */
+ }
+
+ /* mac80211 doesn't allow mode changes while the device is up, so
+ * take the device down, try to set the mode again, and bring the
+ * device back up.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+ "interface down");
+ for (i = 0; i < 10; i++) {
+ res = i802_set_iface_flags(bss, 0);
+ if (res == -EACCES || res == -ENODEV)
+ break;
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+ "interface down");
+ os_sleep(0, 100000);
+ continue;
+ }
+
+ /*
+ * Setting the mode will fail for some drivers if the phy is
+ * on a frequency that the mode is disallowed in.
+ */
+ if (desired_freq_params) {
+ res = nl80211_set_channel(bss, desired_freq_params, 0);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set frequency on interface");
+ }
+ }
+
+ /* Try to set the mode again while the interface is down */
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res == -EBUSY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Delaying mode set while interface going down");
+ os_sleep(0, 100000);
+ continue;
+ }
+ ret = mode_switch_res;
+ break;
+ }
+
+ if (!ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+ "interface is down");
+ drv->nlmode = nlmode;
+ drv->ignore_if_down_event = 1;
+ }
+
+ /* Bring the interface back up */
+ res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set interface up after switching mode");
+ ret = -1;
+ }
+
+done:
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
+ return ret;
+ }
+
+ if (is_p2p_net_interface(nlmode)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s mode change to P2P - disable 11b rates",
+ bss->ifname);
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+ } else if (drv->disabled_11b_rates) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+ bss->ifname);
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ }
+
+ if (is_ap_interface(nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "start AP");
+ /* Setup additional AP mode functionality if needed */
+ if (nl80211_setup_ap(bss))
+ return -1;
+ } else if (was_ap) {
+ /* Remove additional AP mode functionality */
+ nl80211_teardown_ap(bss);
+ } else {
+ nl80211_mgmt_unsubscribe(bss, "mode change");
+ }
+
+ if (is_mesh_interface(nlmode) &&
+ nl80211_mgmt_subscribe_mesh(bss))
+ return -1;
+
+ if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+ !is_mesh_interface(nlmode) &&
+ nl80211_mgmt_subscribe_non_ap(bss) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+ "frame processing - ignore for now");
+
+ return 0;
+}
+
+
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode)
+{
+ return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+ struct hostapd_freq_params *freq)
+{
+ return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+ freq);
+}
+
+
+static int wpa_driver_nl80211_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ if (drv->extended_capa && drv->extended_capa_mask) {
+ capa->extended_capa = drv->extended_capa;
+ capa->extended_capa_mask = drv->extended_capa_mask;
+ capa->extended_capa_len = drv->extended_capa_len;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
+ bss->ifname, drv->operstate, state,
+ state ? "UP" : "DORMANT");
+ drv->operstate = state;
+ return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
+ state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nl80211_sta_flag_update upd;
+ int ret;
+
+ if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+ wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
+ MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (authorized)
+ upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+ nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct i802_bss *bss = priv;
+ return nl80211_set_channel(bss, freq, 0);
+}
+
+
+static inline int min_int(int a, int b)
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+ 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),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ /*
+ * TODO: validate the key index and mac address!
+ * Otherwise, there's a race condition as soon as
+ * the kernel starts sending key notifications.
+ */
+
+ if (tb[NL80211_ATTR_KEY_SEQ])
+ memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+ min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+ return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+ int idx, u8 *seq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+ NL80211_CMD_GET_KEY);
+ if (!msg ||
+ (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+ nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ memset(seq, 0, 6);
+
+ return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+}
+
+
+static int i802_set_rts(void *priv, int rts)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ u32 val;
+
+ if (rts >= 2347)
+ val = (u32) -1;
+ else
+ val = rts;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+ "%d (%s)", rts, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int i802_set_frag(void *priv, int frag)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ u32 val;
+
+ if (frag >= 2346)
+ val = (u32) -1;
+ else
+ val = frag;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+ "%d: %d (%s)", frag, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int i802_flush(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+ bss->ifname);
+
+ /*
+ * XXX: FIX! this needs to flush all VLANs too
+ */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+ res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+ "(%s)", res, strerror(-res));
+ }
+ return res;
+}
+
+
+static int get_sta_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct hostap_sta_driver_data *data = arg;
+ struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+ [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 },
+ [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+ };
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ /*
+ * TODO: validate the interface and mac address!
+ * Otherwise, there's a race condition as soon as
+ * the kernel starts sending station notifications.
+ */
+
+ if (!tb[NL80211_ATTR_STA_INFO]) {
+ wpa_printf(MSG_DEBUG, "sta stats missing!");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO],
+ stats_policy)) {
+ wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+ return NL_SKIP;
+ }
+
+ if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+ data->inactive_msec =
+ nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+ /* For backwards compatibility, fetch the 32-bit counters first. */
+ 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->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+ if (stats[NL80211_STA_INFO_RX_BYTES64] &&
+ stats[NL80211_STA_INFO_TX_BYTES64]) {
+ /*
+ * The driver supports 64-bit counters, so use them to override
+ * the 32-bit values.
+ */
+ data->rx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
+ data->tx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
+ data->bytes_64bit = 1;
+ }
+ 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]);
+ if (stats[NL80211_STA_INFO_TX_FAILED])
+ data->tx_retry_failed =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+
+ return NL_SKIP;
+}
+
+static int i802_read_sta_data(struct i802_bss *bss,
+ struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct nl_msg *msg;
+
+ os_memset(data, 0, sizeof(*data));
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *txq, *params;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
+ if (!msg)
+ return -1;
+
+ txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+ if (!txq)
+ goto fail;
+
+ /* We are only sending parameters for a single TXQ at a time */
+ params = nla_nest_start(msg, 1);
+ if (!params)
+ goto fail;
+
+ switch (queue) {
+ case 0:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+ goto fail;
+ break;
+ case 1:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+ goto fail;
+ break;
+ case 2:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+ goto fail;
+ break;
+ case 3:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+ goto fail;
+ break;
+ }
+ /* Burst time is configured in units of 0.1 msec and TXOP parameter in
+ * 32 usec, so need to convert the value here. */
+ if (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))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ nla_nest_end(msg, txq);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return 0;
+ msg = NULL;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
+ const char *ifname, int vlan_id)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
+ ", ifname=%s[%d], vlan_id=%d)",
+ bss->ifname, if_nametoindex(bss->ifname),
+ MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ 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));
+ }
+ return ret;
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+ struct hostap_sta_driver_data data;
+ int ret;
+
+ data.inactive_msec = (unsigned long) -1;
+ ret = i802_read_sta_data(priv, &data, addr);
+ if (ret == -ENOENT)
+ return -ENOENT;
+ if (ret || data.inactive_msec == (unsigned long) -1)
+ return -1;
+ return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+ /* TODO */
+#endif
+ return 0;
+}
+
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt mgmt;
+
+ if (is_mesh_interface(drv->nlmode))
+ return -1;
+
+ if (drv->device_ap_sme)
+ return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DEAUTH);
+ memcpy(mgmt.da, addr, ETH_ALEN);
+ memcpy(mgmt.sa, own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+ mgmt.u.deauth.reason_code = host_to_le16(reason);
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+ 0, NULL, 0);
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt mgmt;
+
+ if (is_mesh_interface(drv->nlmode))
+ return -1;
+
+ if (drv->device_ap_sme)
+ return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DISASSOC);
+ memcpy(mgmt.da, addr, ETH_ALEN);
+ memcpy(mgmt.sa, own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
+ 0, NULL, 0);
+}
+
+
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[200], *pos, *end;
+ int i, res;
+
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (!drv->if_indices[i])
+ continue;
+ res = os_snprintf(pos, end - pos, " %d(%d)",
+ drv->if_indices[i],
+ drv->if_indices_reason[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+
+ wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+ drv->num_if_indices, buf);
+}
+
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+ int *old, *old_reason;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
+ ifidx, ifidx_reason);
+ if (have_ifidx(drv, ifidx, ifidx_reason)) {
+ wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+ ifidx);
+ return;
+ }
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == 0) {
+ drv->if_indices[i] = ifidx;
+ drv->if_indices_reason[i] = ifidx_reason;
+ dump_ifidx(drv);
+ return;
+ }
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ old = drv->if_indices;
+ else
+ old = NULL;
+
+ if (drv->if_indices_reason != drv->default_if_indices_reason)
+ old_reason = drv->if_indices_reason;
+ else
+ old_reason = NULL;
+
+ drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+ sizeof(int));
+ drv->if_indices_reason = os_realloc_array(old_reason,
+ drv->num_if_indices + 1,
+ sizeof(int));
+ if (!drv->if_indices) {
+ if (!old)
+ drv->if_indices = drv->default_if_indices;
+ else
+ drv->if_indices = old;
+ }
+ if (!drv->if_indices_reason) {
+ if (!old_reason)
+ drv->if_indices_reason = drv->default_if_indices_reason;
+ else
+ drv->if_indices_reason = old_reason;
+ }
+ if (!drv->if_indices || !drv->if_indices_reason) {
+ wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+ "interfaces");
+ wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+ return;
+ }
+ if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
+ if (!old_reason)
+ os_memcpy(drv->if_indices_reason,
+ drv->default_if_indices_reason,
+ sizeof(drv->default_if_indices_reason));
+ drv->if_indices[drv->num_if_indices] = ifidx;
+ drv->if_indices_reason[drv->num_if_indices] = ifidx_reason;
+ drv->num_if_indices++;
+ dump_ifidx(drv);
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) &&
+ (drv->if_indices_reason[i] == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY)) {
+ drv->if_indices[i] = 0;
+ break;
+ }
+ }
+ dump_ifidx(drv);
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++)
+ if (drv->if_indices[i] == ifidx &&
+ (drv->if_indices_reason[i] == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY))
+ return 1;
+
+ return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname, char *ifname_wds)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char name[IFNAMSIZ + 1];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ if (ifname_wds)
+ os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ if (val) {
+ if (!if_nametoindex(name)) {
+ if (nl80211_create_iface(drv, name,
+ NL80211_IFTYPE_AP_VLAN,
+ bss->addr, 1, NULL, NULL, 0) <
+ 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
+ return -1;
+ }
+ if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+ "interface %s up", name);
+ }
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ if (bridge_ifname)
+ linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+ name);
+
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+ nl80211_remove_iface(drv, if_nametoindex(name));
+ return 0;
+ }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct sockaddr_ll lladdr;
+ unsigned char buf[3000];
+ int len;
+ socklen_t fromlen = sizeof(lladdr);
+
+ len = recvfrom(sock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&lladdr, &fromlen);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+ strerror(errno));
+ return;
+ }
+
+ if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ const char *brname, const char *ifname)
+{
+ int br_ifindex;
+ char in_br[IFNAMSIZ];
+
+ os_strlcpy(bss->brname, brname, IFNAMSIZ);
+ br_ifindex = if_nametoindex(brname);
+ if (br_ifindex == 0) {
+ /*
+ * Bridge was configured, but the bridge device does
+ * not exist. Try to add it now.
+ */
+ if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+ "bridge interface %s: %s",
+ brname, strerror(errno));
+ return -1;
+ }
+ bss->added_bridge = 1;
+ br_ifindex = if_nametoindex(brname);
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+ }
+ bss->br_ifindex = br_ifindex;
+
+ if (linux_br_get(in_br, ifname) == 0) {
+ if (os_strcmp(in_br, brname) == 0)
+ return 0; /* already in the bridge */
+
+ wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+ "bridge %s", ifname, in_br);
+ if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+ 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to "
+ "remove interface %s from bridge "
+ "%s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+ ifname, brname);
+ if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+ "into bridge %s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ bss->added_if_into_bridge = 1;
+
+ return 0;
+}
+
+
+static void *i802_init(struct hostapd_data *hapd,
+ struct wpa_init_params *params)
+{
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *bss;
+ size_t i;
+ char master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
+ int br_added = 0;
+
+ bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ params->global_priv, 1,
+ params->bssid, params->driver_params);
+ if (bss == NULL)
+ return NULL;
+
+ drv = bss->drv;
+
+ if (linux_br_get(master_ifname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+ params->ifname, master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ } else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+ linux_master_get(master_ifname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+ params->ifname, master_ifname);
+ /* start listening for EAPOL on the master interface */
+ add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex);
+
+ /* check if master itself is under bridge */
+ if (linux_br_get(master_ifname, master_ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
+ master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ }
+ } else {
+ master_ifname[0] = '\0';
+ }
+
+ bss->br_ifindex = br_ifindex;
+
+ for (i = 0; i < params->num_bridge; i++) {
+ if (params->bridge[i]) {
+ ifindex = if_nametoindex(params->bridge[i]);
+ if (ifindex)
+ add_ifidx(drv, ifindex, drv->ifindex);
+ if (ifindex == br_ifindex)
+ br_added = 1;
+ }
+ }
+
+ /* start listening for EAPOL on the default AP interface */
+ add_ifidx(drv, drv->ifindex, IFIDX_ANY);
+
+ if (params->num_bridge && params->bridge[0]) {
+ if (i802_check_bridge(drv, bss, params->bridge[0],
+ params->ifname) < 0)
+ goto failed;
+ if (os_strcmp(params->bridge[0], master_ifname) != 0)
+ br_added = 1;
+ }
+
+ if (!br_added && br_ifindex &&
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+
+#ifdef CONFIG_LIBNL3_ROUTE
+ if (bss->added_if_into_bridge) {
+ drv->rtnl_sk = nl_socket_alloc();
+ if (drv->rtnl_sk == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+ goto failed;
+ }
+
+ if (nl_connect(drv->rtnl_sk, NETLINK_ROUTE)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+ strerror(errno));
+ goto failed;
+ }
+ }
+#endif /* CONFIG_LIBNL3_ROUTE */
+
+ drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+ if (drv->eapol_sock < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
+ strerror(errno));
+ goto failed;
+ }
+
+ if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+ {
+ wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
+ goto failed;
+ }
+
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ params->own_addr))
+ goto failed;
+ os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
+
+ memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
+ return bss;
+
+failed:
+ wpa_driver_nl80211_deinit(bss);
+ return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_driver_nl80211_deinit(bss);
+}
+
+
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+ enum wpa_driver_if_type type)
+{
+ switch (type) {
+ case WPA_IF_STATION:
+ return NL80211_IFTYPE_STATION;
+ case WPA_IF_P2P_CLIENT:
+ case WPA_IF_P2P_GROUP:
+ return NL80211_IFTYPE_P2P_CLIENT;
+ case WPA_IF_AP_VLAN:
+ return NL80211_IFTYPE_AP_VLAN;
+ case WPA_IF_AP_BSS:
+ return NL80211_IFTYPE_AP;
+ case WPA_IF_P2P_GO:
+ return NL80211_IFTYPE_P2P_GO;
+ case WPA_IF_P2P_DEVICE:
+ return NL80211_IFTYPE_P2P_DEVICE;
+ case WPA_IF_MESH:
+ return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
+ }
+}
+
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+ struct wpa_driver_nl80211_data *drv;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
+{
+ unsigned int idx;
+
+ if (!drv->global)
+ return -1;
+
+ os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
+ for (idx = 0; idx < 64; idx++) {
+ new_addr[0] = drv->first_bss->addr[0] | 0x02;
+ new_addr[0] ^= idx << 2;
+ if (!nl80211_addr_in_use(drv->global, new_addr))
+ break;
+ }
+ if (idx == 64)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
+ MACSTR, MAC2STR(new_addr));
+
+ return 0;
+}
+
+
+struct wdev_info {
+ u64 wdev_id;
+ int wdev_id_set;
+ u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wdev_info *wi = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_WDEV]) {
+ wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+ wi->wdev_id_set = 1;
+ }
+
+ if (tb[NL80211_ATTR_MAC])
+ os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+
+ return NL_SKIP;
+}
+
+
+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,
+ char *force_ifname, u8 *if_addr,
+ const char *bridge, int use_existing,
+ int setup_ap)
+{
+ enum nl80211_iftype nlmode;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifidx;
+ int added = 1;
+
+ if (addr)
+ os_memcpy(if_addr, addr, ETH_ALEN);
+ nlmode = wpa_driver_nl80211_if_type(type);
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ struct wdev_info p2pdev_info;
+
+ os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+ ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ 0, nl80211_wdev_handler,
+ &p2pdev_info, use_existing);
+ if (!p2pdev_info.wdev_id_set || ifidx != 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
+ ifname);
+ return -1;
+ }
+
+ drv->global->if_add_wdevid = p2pdev_info.wdev_id;
+ drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
+ if (!is_zero_ether_addr(p2pdev_info.macaddr))
+ os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+ ifname,
+ (long long unsigned int) p2pdev_info.wdev_id);
+ } else {
+ ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ 0, NULL, NULL, use_existing);
+ if (use_existing && ifidx == -ENFILE) {
+ added = 0;
+ ifidx = if_nametoindex(ifname);
+ } else if (ifidx < 0) {
+ return -1;
+ }
+ }
+
+ if (!addr) {
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ os_memcpy(if_addr, bss->addr, ETH_ALEN);
+ else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
+ ifname, if_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ }
+
+ if (!addr &&
+ (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+ type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+ type == WPA_IF_STATION)) {
+ /* Enforce unique address */
+ u8 new_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (nl80211_addr_in_use(drv->global, new_addr)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+ "for interface %s type %d", ifname, type);
+ if (nl80211_vif_addr(drv, new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ }
+ os_memcpy(if_addr, new_addr, ETH_ALEN);
+ }
+
+ if (type == WPA_IF_AP_BSS && setup_ap) {
+ struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+ if (new_bss == NULL) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+ if (bridge &&
+ i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+ "interface %s to a bridge %s",
+ ifname, bridge);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
+ if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+ {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+ os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+ os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
+ new_bss->ifindex = ifidx;
+ new_bss->drv = drv;
+ new_bss->next = drv->first_bss->next;
+ new_bss->freq = drv->first_bss->freq;
+ new_bss->ctx = bss_ctx;
+ new_bss->added_if = added;
+ drv->first_bss->next = new_bss;
+ if (drv_priv)
+ *drv_priv = new_bss;
+ nl80211_init_bss(new_bss);
+
+ /* Subscribe management frames for this WPA_IF_AP_BSS */
+ if (nl80211_setup_ap(new_bss))
+ return -1;
+ }
+
+ if (drv->global)
+ drv->global->if_add_ifindex = ifidx;
+
+ /*
+ * Some virtual interfaces need to process EAPOL packets and events on
+ * the parent interface. This is used mainly with hostapd.
+ */
+ if (ifidx > 0 &&
+ (drv->hostapd ||
+ nlmode == NL80211_IFTYPE_AP_VLAN ||
+ nlmode == NL80211_IFTYPE_WDS ||
+ nlmode == NL80211_IFTYPE_MONITOR))
+ add_ifidx(drv, ifidx, IFIDX_ANY);
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifindex = if_nametoindex(ifname);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
+ __func__, type, ifname, ifindex, bss->added_if);
+ if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
+ nl80211_remove_iface(drv, ifindex);
+ else if (ifindex > 0 && !bss->added_if) {
+ struct wpa_driver_nl80211_data *drv2;
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ del_ifidx(drv2, ifindex, IFIDX_ANY);
+ del_ifidx(drv2, IFIDX_ANY, ifindex);
+ }
+ }
+
+ if (type != WPA_IF_AP_BSS)
+ return 0;
+
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "interface %s from bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+
+ if (bss != drv->first_bss) {
+ struct i802_bss *tbss;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
+ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ /* Unsubscribe management frames */
+ nl80211_teardown_ap(bss);
+ nl80211_destroy_bss(bss);
+ if (!bss->added_if)
+ i802_set_iface_flags(bss, 0);
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
+ }
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+ nl80211_teardown_ap(bss);
+ if (!bss->added_if && !drv->first_bss->next)
+ wpa_driver_nl80211_del_beacon(drv);
+ nl80211_destroy_bss(bss);
+ if (!bss->added_if)
+ i802_set_iface_flags(bss, 0);
+ if (drv->first_bss->next) {
+ drv->first_bss = drv->first_bss->next;
+ drv->ctx = drv->first_bss->ctx;
+ os_free(bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+ }
+ }
+
+ return 0;
+}
+
+
+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 nl80211_send_frame_cmd(struct i802_bss *bss,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ u64 *cookie_out, int no_cck, int no_ack,
+ int offchanok, const u16 *csa_offs,
+ size_t csa_offs_len)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret = -1;
+
+ wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+ "no_ack=%d offchanok=%d",
+ freq, wait, no_cck, no_ack, offchanok);
+ wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+ (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+ (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+ (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+ drv->test_use_roc_tx) &&
+ nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+ (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+ (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+ (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+ csa_offs_len * sizeof(u16), csa_offs)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+ goto fail;
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+ "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+ freq, wait);
+ } else {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+ "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ (long long unsigned int) cookie);
+
+ if (cookie_out)
+ *cookie_out = no_ack ? (u64) -1 : cookie;
+
+ if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop oldest pending send action cookie 0x%llx",
+ (long long unsigned int)
+ drv->send_action_cookies[0]);
+ os_memmove(&drv->send_action_cookies[0],
+ &drv->send_action_cookies[1],
+ (MAX_SEND_ACTION_COOKIES - 1) *
+ sizeof(u64));
+ drv->num_send_action_cookies--;
+ }
+ drv->send_action_cookies[drv->num_send_action_cookies] = cookie;
+ drv->num_send_action_cookies++;
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
+ unsigned int freq,
+ unsigned int wait_time,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len,
+ int no_cck)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+ "freq=%u MHz wait=%d ms no_cck=%d)",
+ drv->ifindex, freq, wait_time, no_cck);
+
+ 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 (is_ap_interface(drv->nlmode) &&
+ (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+ (int) freq == bss->freq || drv->device_ap_sme ||
+ !drv->use_monitor))
+ ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
+ 0, freq, no_cck, 1,
+ wait_time, NULL, 0);
+ else
+ ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+ 24 + data_len,
+ &drv->send_action_cookie,
+ no_cck, 0, 1, NULL, 0);
+
+ os_free(buf);
+ return ret;
+}
+
+
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
+ (long long unsigned int) cookie);
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int i;
+ u64 cookie;
+
+ /* Cancel the last pending TX cookie */
+ nl80211_frame_wait_cancel(bss, drv->send_action_cookie);
+
+ /*
+ * Cancel the other pending TX cookies, if any. This is needed since
+ * the driver may keep a list of all pending offchannel TX operations
+ * and free up the radio only once they have expired or cancelled.
+ */
+ for (i = drv->num_send_action_cookies; i > 0; i--) {
+ cookie = drv->send_action_cookies[i - 1];
+ if (cookie != drv->send_action_cookie)
+ nl80211_frame_wait_cancel(bss, cookie);
+ }
+ drv->num_send_action_cookies = 0;
+}
+
+
+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;
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+ nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ 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;
+ drv->pending_remain_on_chan = 1;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
+ 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 = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+ if (!msg ||
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ 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));
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!report) {
+ if (bss->nl_preq && drv->device_ap_sme &&
+ is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+ !bss->static_ap) {
+ /*
+ * Do not disable Probe Request reporting that was
+ * enabled in nl80211_setup_ap().
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+ "Probe Request reporting nl_preq=%p while "
+ "in AP mode", bss->nl_preq);
+ } else if (bss->nl_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
+ nl80211_destroy_eloop_handle(&bss->nl_preq);
+ }
+ return 0;
+ }
+
+ if (bss->nl_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on! nl_preq=%p", bss->nl_preq);
+ return 0;
+ }
+
+ bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+ if (bss->nl_preq == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
+
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0) < 0)
+ goto out_err;
+
+ nl80211_register_eloop_read(&bss->nl_preq,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb);
+
+ return 0;
+
+ out_err:
+ nl_destroy_handles(&bss->nl_preq);
+ return -1;
+}
+
+
+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;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+ ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+ "no NL80211_TXRATE_LEGACY constraint");
+
+ msg = nl80211_ifindex_msg(drv, ifindex, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK);
+ if (!msg)
+ return -1;
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto fail;
+
+ /*
+ * 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 ||
+ (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+ goto fail;
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ } else
+ drv->disabled_11b_rates = disabled;
+
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!is_ap_interface(drv->nlmode))
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ bss->beacon_set = 0;
+
+ /*
+ * If the P2P GO interface was dynamically added, then it is
+ * possible that the interface change to station is not possible.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+ return 0;
+
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static int wpa_driver_nl80211_stop_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!is_ap_interface(drv->nlmode))
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ bss->beacon_set = 0;
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+ return -1;
+
+ /*
+ * If the P2P Client interface was dynamically added, then it is
+ * possible that the interface change to station is not possible.
+ */
+ if (bss->if_dynamic)
+ return 0;
+
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+ struct i802_bss *bss = priv;
+ enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
+
+ if (i802_set_iface_flags(bss, 1))
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+ if (is_p2p_net_interface(nlmode))
+ nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
+}
+
+
+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;
+ struct nlattr *cqm;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+ "hysteresis=%d", threshold, hysteresis);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+ !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+ nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+ nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, cqm);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int get_channel_width(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_signal_info *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ sig_change->center_frq1 = -1;
+ sig_change->center_frq2 = -1;
+ sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+ sig_change->chanwidth = convert2width(
+ nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ sig_change->center_frq1 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ sig_change->center_frq2 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+}
+
+
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+
+ os_memset(si, 0, sizeof(*si));
+ res = nl80211_get_link_signal(drv, si);
+ if (res) {
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+ return res;
+ si->current_signal = 0;
+ }
+
+ res = nl80211_get_channel_width(drv, si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
+ 0, 0, 0, 0, NULL, 0);
+}
+
+
+static int nl80211_set_param(void *priv, const char *param)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (param == NULL)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+
+#ifdef CONFIG_P2P
+ if (os_strstr(param, "use_p2p_group_interface=1")) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+#endif /* CONFIG_P2P */
+
+ if (os_strstr(param, "use_monitor=1"))
+ drv->use_monitor = 1;
+
+ if (os_strstr(param, "force_connect_cmd=1")) {
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
+ drv->force_connect_cmd = 1;
+ }
+
+ if (os_strstr(param, "force_bss_selection=1"))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+
+ if (os_strstr(param, "no_offchannel_tx=1")) {
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ drv->test_use_roc_tx = 1;
+ }
+
+ return 0;
+}
+
+
+static void * nl80211_global_init(void *ctx)
+{
+ struct nl80211_global *global;
+ struct netlink_config *cfg;
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ global->ctx = ctx;
+ global->ioctl_sock = -1;
+ dl_list_init(&global->interfaces);
+ global->if_add_ifindex = -1;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto err;
+
+ cfg->ctx = global;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ global->netlink = netlink_init(cfg);
+ if (global->netlink == NULL) {
+ os_free(cfg);
+ goto err;
+ }
+
+ if (wpa_driver_nl80211_init_nl_global(global) < 0)
+ goto err;
+
+ global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (global->ioctl_sock < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+ strerror(errno));
+ goto err;
+ }
+
+ return global;
+
+err:
+ nl80211_global_deinit(global);
+ return NULL;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+ struct nl80211_global *global = priv;
+ if (global == NULL)
+ return;
+ if (!dl_list_empty(&global->interfaces)) {
+ wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+ "nl80211_global_deinit",
+ dl_list_len(&global->interfaces));
+ }
+
+ if (global->netlink)
+ netlink_deinit(global->netlink);
+
+ nl_destroy_handles(&global->nl);
+
+ if (global->nl_event)
+ nl80211_destroy_eloop_handle(&global->nl_event);
+
+ nl_cb_put(global->nl_cb);
+
+ if (global->ioctl_sock >= 0)
+ close(global->ioctl_sock);
+
+ os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return drv->phyname;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct nl_msg *msg;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+ (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
+ (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+ MAC2STR(bssid));
+ return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+ return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
+static void clean_survey_results(struct survey_results *survey_results)
+{
+ struct freq_survey *survey, *tmp;
+
+ if (dl_list_empty(&survey_results->survey_list))
+ return;
+
+ dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ os_free(survey);
+ }
+}
+
+
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+ struct dl_list *survey_list)
+{
+ struct freq_survey *survey;
+
+ survey = os_zalloc(sizeof(struct freq_survey));
+ if (!survey)
+ return;
+
+ survey->ifidx = ifidx;
+ survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+ survey->filled = 0;
+
+ if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+ survey->nf = (int8_t)
+ nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ survey->filled |= SURVEY_HAS_NF;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+ survey->channel_time =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+ survey->channel_time_busy =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+ survey->channel_time_rx =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+ survey->channel_time_tx =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+ survey->freq,
+ survey->nf,
+ (unsigned long int) survey->channel_time,
+ (unsigned long int) survey->channel_time_busy,
+ (unsigned long int) survey->channel_time_tx,
+ (unsigned long int) survey->channel_time_rx,
+ survey->filled);
+
+ dl_list_add_tail(survey_list, &survey->list);
+}
+
+
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+ unsigned int freq_filter)
+{
+ if (!freq_filter)
+ return 1;
+
+ return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(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_SURVEY_INFO_MAX + 1];
+ struct survey_results *survey_results;
+ u32 surveyed_freq = 0;
+ u32 ifidx;
+
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+
+ survey_results = (struct survey_results *) arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_IFINDEX])
+ return NL_SKIP;
+
+ ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO])
+ return NL_SKIP;
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy))
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+ wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+ return NL_SKIP;
+ }
+
+ surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+ if (!check_survey_ok(sinfo, surveyed_freq,
+ survey_results->freq_filter))
+ return NL_SKIP;
+
+ if (survey_results->freq_filter &&
+ survey_results->freq_filter != surveyed_freq) {
+ wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+ surveyed_freq);
+ return NL_SKIP;
+ }
+
+ add_survey(sinfo, ifidx, &survey_results->survey_list);
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int err;
+ union wpa_event_data data;
+ struct survey_results *survey_results;
+
+ os_memset(&data, 0, sizeof(data));
+ survey_results = &data.survey_results;
+
+ dl_list_init(&survey_results->survey_list);
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ if (!msg)
+ return -ENOBUFS;
+
+ if (freq)
+ data.survey_results.freq_filter = freq;
+
+ do {
+ wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+ err = send_and_recv_msgs(drv, msg, survey_handler,
+ survey_results);
+ } while (err > 0);
+
+ if (err)
+ wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+ else
+ wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
+
+ clean_survey_results(survey_results);
+ return err;
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nlattr *replay_nested;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->set_rekey_offload)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+ !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+ nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+ nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+ nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+ replay_ctr)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return;
+ }
+
+ nla_nest_end(msg, replay_nested);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ if (ret == -EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver does not support rekey offload");
+ drv->set_rekey_offload = 0;
+ }
+}
+
+
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+ const u8 *addr, int qos)
+{
+ /* send data frame to poll STA and check whether
+ * this frame is ACKed */
+ struct {
+ struct ieee80211_hdr hdr;
+ u16 qos_ctl;
+ } STRUCT_PACKED nulldata;
+ size_t size;
+
+ /* Send data frame to poll STA and check whether this frame is ACKed */
+
+ os_memset(&nulldata, 0, sizeof(nulldata));
+
+ if (qos) {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_QOS_NULL);
+ size = sizeof(nulldata);
+ } else {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_NULLFUNC);
+ size = sizeof(struct ieee80211_hdr);
+ }
+
+ nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
+ 0, 0, NULL, 0) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+ "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->poll_command_supported) {
+ nl80211_send_null_frame(bss, own_addr, addr, qos);
+ return;
+ }
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+ MACSTR " failed: ret=%d (%s)",
+ MAC2STR(addr), ret, strerror(-ret));
+ }
+}
+
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+ struct nl_msg *msg;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+ nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+ enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ struct i802_bss *bss = priv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
+ "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+
+ if (opp_ps != -1 || ctwindow != -1) {
+#ifdef ANDROID_P2P
+ wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
+#else /* ANDROID_P2P */
+ return -1; /* Not yet supported */
+#endif /* ANDROID_P2P */
+ }
+
+ if (legacy_ps == -1)
+ return 0;
+ if (legacy_ps != 0 && legacy_ps != 1)
+ return -1; /* Not yet supported */
+
+ return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+static int nl80211_start_radar_detection(void *priv,
+ struct hostapd_freq_params *freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+ freq->freq, freq->ht_enabled, freq->vht_enabled,
+ freq->bandwidth, freq->center_freq1, freq->center_freq2);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
+ "detection");
+ return -1;
+ }
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+ nl80211_put_freq_params(msg, freq) < 0) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+ "%d (%s)", ret, strerror(-ret));
+ return -1;
+}
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capab, int initiator, const u8 *buf,
+ size_t len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (!dst)
+ return -EINVAL;