Remove src/drivers/scan_helpers.c
[libeap.git] / src / drivers / driver_nl80211.c
index 9982c44..89acdae 100644 (file)
@@ -95,8 +95,12 @@ struct wpa_driver_nl80211_data {
 
        int monitor_sock;
        int monitor_ifidx;
+       int probe_req_report;
 
        unsigned int beacon_set:1;
+       unsigned int pending_remain_on_chan:1;
+
+       u64 remain_on_chan_cookie;
 
 #ifdef HOSTAPD
        int eapol_sock; /* socket for EAPOL frames */
@@ -134,6 +138,9 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                                        const char *ifname);
 #endif /* HOSTAPD */
 
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -262,7 +269,6 @@ nla_put_failure:
 }
 
 
-#ifdef HOSTAPD
 static int get_ifhwaddr(struct wpa_driver_nl80211_data *drv,
                        const char *ifname, u8 *addr)
 {
@@ -305,7 +311,6 @@ static int set_ifhwaddr(struct wpa_driver_nl80211_data *drv,
 
        return 0;
 }
-#endif /* HOSTAPD */
 
 
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
@@ -707,6 +712,53 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+                                        int cancel_event, struct nlattr *tb[])
+{
+       unsigned int freq, chan_type, duration;
+       union wpa_event_data data;
+       u64 cookie;
+
+       if (tb[NL80211_ATTR_WIPHY_FREQ])
+               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       else
+               freq = 0;
+
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+       else
+               chan_type = 0;
+
+       if (tb[NL80211_ATTR_DURATION])
+               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+       else
+               duration = 0;
+
+       if (tb[NL80211_ATTR_COOKIE])
+               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       else
+               cookie = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+                  cancel_event, freq, chan_type, duration,
+                  (long long unsigned int) cookie,
+                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+       if (cookie != drv->remain_on_chan_cookie)
+               return; /* not for us */
+
+       drv->pending_remain_on_chan = !cancel_event;
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, cancel_event ?
+                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
+                            EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
                            struct nlattr *tb[])
 {
@@ -829,6 +881,12 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_JOIN_IBSS:
                mlme_event_join_ibss(drv, tb);
                break;
+       case NL80211_CMD_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 0, tb);
+               break;
+       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 1, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -1292,10 +1350,6 @@ static void wpa_driver_nl80211_deinit(void *priv)
        struct wpa_driver_nl80211_data *drv = priv;
 
        nl80211_remove_monitor_interface(drv);
-       if (drv->monitor_sock >= 0) {
-               eloop_unregister_read_sock(drv->monitor_sock);
-               close(drv->monitor_sock);
-       }
 
        if (drv->nlmode == NL80211_IFTYPE_AP)
                wpa_driver_nl80211_del_beacon(drv);
@@ -1339,6 +1393,9 @@ static void wpa_driver_nl80211_deinit(void *priv)
        nl_handle_destroy(drv->nl_handle_event);
        nl_cb_put(drv->nl_cb);
 
+       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+                            drv, NULL);
+
        os_free(drv);
 }
 
@@ -1625,6 +1682,20 @@ static void wpa_driver_nl80211_check_bss_status(
 }
 
 
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
 /**
  * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
  * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
@@ -2658,13 +2729,13 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
 #ifdef HOSTAPD
        /* start listening for EAPOL on this interface */
        add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
 
-       if (addr && iftype == NL80211_IFTYPE_AP &&
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
            set_ifhwaddr(drv, ifname, addr)) {
                nl80211_remove_iface(drv, ifidx);
                return -1;
        }
-#endif /* HOSTAPD */
 
        return ifidx;
 }
@@ -2715,19 +2786,18 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
 
 
 static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-                            struct ieee80211_hdr *hdr, size_t len)
+                            u8 *buf, size_t len)
 {
        union wpa_event_data event;
        os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.hdr = hdr;
+       event.rx_from_unknown.frame = buf;
        event.rx_from_unknown.len = len;
        wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
 
 static void handle_frame(struct wpa_driver_nl80211_data *drv,
-                        u8 *buf, size_t len,
-                        struct hostapd_frame_info *hfi)
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
 {
        struct ieee80211_hdr *hdr;
        u16 fc;
@@ -2741,16 +2811,17 @@ static void handle_frame(struct wpa_driver_nl80211_data *drv,
                os_memset(&event, 0, sizeof(event));
                event.rx_mgmt.frame = buf;
                event.rx_mgmt.frame_len = len;
-               event.rx_mgmt.fi = hfi;
+               event.rx_mgmt.datarate = datarate;
+               event.rx_mgmt.ssi_signal = ssi_signal;
                wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
                break;
        case WLAN_FC_TYPE_CTRL:
                /* can only get here with PS-Poll frames */
                wpa_printf(MSG_DEBUG, "CTRL");
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        case WLAN_FC_TYPE_DATA:
-               from_unknown_sta(drv, hdr, len);
+               from_unknown_sta(drv, buf, len);
                break;
        }
 }
@@ -2763,7 +2834,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
        unsigned char buf[3000];
        struct ieee80211_radiotap_iterator iter;
        int ret;
-       struct hostapd_frame_info hfi;
+       int datarate = 0, ssi_signal = 0;
        int injected = 0, failed = 0, rxflags = 0;
 
        len = recv(sock, buf, sizeof(buf), 0);
@@ -2772,13 +2843,17 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
+       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+                          "frame since Probe Request reporting is disabled");
+               return;
+       }
+
        if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
                printf("received invalid radiotap frame\n");
                return;
        }
 
-       memset(&hfi, 0, sizeof(hfi));
-
        while (1) {
                ret = ieee80211_radiotap_iterator_next(&iter);
                if (ret == -ENOENT)
@@ -2803,15 +2878,13 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                case IEEE80211_RADIOTAP_DATA_RETRIES:
                        break;
                case IEEE80211_RADIOTAP_CHANNEL:
-                       /* TODO convert from freq/flags to channel number
-                       hfi.channel = XXX;
-                        */
+                       /* TODO: convert from freq/flags to channel number */
                        break;
                case IEEE80211_RADIOTAP_RATE:
-                       hfi.datarate = *iter.this_arg * 5;
+                       datarate = *iter.this_arg * 5;
                        break;
                case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
-                       hfi.ssi_signal = *iter.this_arg;
+                       ssi_signal = *iter.this_arg;
                        break;
                }
        }
@@ -2821,7 +2894,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
 
        if (!injected)
                handle_frame(drv, buf + iter.max_length,
-                            len - iter.max_length, &hfi);
+                            len - iter.max_length, datarate, ssi_signal);
        else
                handle_tx_callback(drv->ctx, buf + iter.max_length,
                                   len - iter.max_length, !failed);
@@ -2979,6 +3052,11 @@ static void nl80211_remove_monitor_interface(
                nl80211_remove_iface(drv, drv->monitor_ifidx);
                drv->monitor_ifidx = -1;
        }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
 }
 
 
@@ -4169,13 +4247,8 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
-       if (have_ifidx(drv, lladdr.sll_ifindex)) {
-               void *ctx;
-               ctx = hostapd_sta_get_bss(drv->ctx, lladdr.sll_addr);
-               if (!ctx)
-                       return;
-               hostapd_eapol_receive(ctx, lladdr.sll_addr, buf, len);
-       }
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
 }
 
 
@@ -4407,6 +4480,170 @@ static int wpa_driver_nl80211_if_remove(void *priv,
 }
 
 
+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_remain_on_channel(void *priv, unsigned int freq,
+                                               unsigned int duration)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       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): %d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       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 wpa_driver_nl80211_data *drv = priv;
+
+       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 wpa_driver_nl80211_alloc_interface_addr(void *priv, u8 *addr)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+
+       if (get_ifhwaddr(drv, drv->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 */
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -4455,4 +4692,10 @@ 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 */
+       .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,
 };