/* * * Wireless daemon for Linux * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "linux/nl80211.h" #include "src/missing.h" #include "src/iwd.h" #include "src/wiphy.h" #include "src/scan.h" #include "src/p2putil.h" #include "src/ie.h" #include "src/util.h" #include "src/dbus.h" #include "src/netdev.h" #include "src/mpdu.h" #include "src/common.h" #include "src/wsc.h" #include "src/handshake.h" #include "src/crypto.h" #include "src/module.h" #include "src/frame-xchg.h" #include "src/nl80211util.h" #include "src/netconfig.h" #include "src/p2p.h" struct p2p_device { uint64_t wdev_id; uint8_t addr[6]; struct l_genl_family *nl80211; struct wiphy *wiphy; unsigned int connections_left; struct p2p_capability_attr capability; struct p2p_device_info_attr device_info; uint32_t start_stop_cmd_id; l_dbus_property_complete_cb_t pending_complete; struct l_dbus_message *pending_message; uint32_t xchg_id; uint8_t listen_country[3]; uint8_t listen_oper_class; uint32_t listen_channel; unsigned int scan_interval; time_t next_scan_ts; struct l_timeout *scan_timeout; uint32_t scan_id; unsigned int chans_per_scan; unsigned int scan_chan_idx; uint64_t roc_cookie; unsigned int listen_duration; struct l_queue *discovery_users; struct l_queue *peer_list; struct p2p_peer *conn_peer; uint16_t conn_config_method; char *conn_pin; uint8_t conn_addr[6]; uint16_t conn_password_id; unsigned int conn_num; struct scan_bss *conn_wsc_bss; struct netdev *conn_netdev; uint32_t conn_netdev_watch_id; uint32_t conn_new_intf_cmd_id; struct wsc_enrollee *conn_enrollee; struct netconfig *conn_netconfig; struct l_timeout *conn_dhcp_timeout; struct p2p_wfd_properties *conn_own_wfd; struct l_timeout *config_timeout; unsigned long go_config_delay; struct l_timeout *go_neg_req_timeout; uint8_t go_dialog_token; unsigned int go_scan_retry; uint32_t go_oper_freq; struct p2p_group_id_attr go_group_id; uint8_t go_interface_addr[6]; bool enabled : 1; bool have_roc_cookie : 1; /* * We need to track @disconnecting because while a connect action is * always triggered by a DBus message, meaning that @pending_message * is going to be non-NULL, a disconnect may also be a result of an * error at a layer higher than netdev and may last until * netdev_disconnect, or similar, finishes. */ bool disconnecting : 1; }; struct p2p_discovery_user { char *client; struct p2p_device *dev; unsigned int disconnect_watch; }; struct p2p_peer { struct scan_bss *bss; struct p2p_device *dev; struct wsc_dbus wsc; char *name; struct wsc_primary_device_type primary_device_type; const uint8_t *device_addr; struct p2p_wfd_properties *wfd; /* Whether peer is currently a GO */ bool group; }; struct p2p_wfd_properties { bool available; bool source; bool sink; uint16_t port; uint16_t throughput; bool audio; bool uibc; bool cp; bool r2; }; static struct l_queue *p2p_device_list; static struct l_settings *p2p_dhcp_settings; static struct p2p_wfd_properties *p2p_own_wfd; static unsigned int p2p_wfd_disconnect_watch; /* * For now we only scan the common 2.4GHz channels, to be replaced with * a query of actual allowed channels per band and reg-domain. */ static const int channels_social[] = { 1, 6, 11 }; static const int channels_scan_2_4_other[] = { 2, 3, 4, 5, 7, 8, 9, 10 }; enum { FRAME_GROUP_DEFAULT = 0, FRAME_GROUP_LISTEN, FRAME_GROUP_CONNECT, }; static bool p2p_device_match(const void *a, const void *b) { const struct p2p_device *dev = a; const uint64_t *wdev_id = b; return dev->wdev_id == *wdev_id; } struct p2p_device *p2p_device_find(uint64_t wdev_id) { return l_queue_find(p2p_device_list, p2p_device_match, &wdev_id); } static const char *p2p_device_get_path(const struct p2p_device *dev) { return wiphy_get_path(dev->wiphy); } static bool p2p_discovery_user_match(const void *a, const void *b) { const struct p2p_discovery_user *user = a; return !strcmp(user->client, b); } static void p2p_discovery_user_free(void *data) { struct p2p_discovery_user *user = data; if (user->disconnect_watch) l_dbus_remove_watch(dbus_get_bus(), user->disconnect_watch); l_free(user->client); l_free(user); } static inline bool p2p_peer_operational(struct p2p_peer *peer) { return peer && peer->dev->conn_netdev && !peer->dev->conn_wsc_bss && !peer->dev->conn_dhcp_timeout && !peer->wsc.pending_connect && !peer->dev->disconnecting; } static bool p2p_peer_match(const void *a, const void *b) { const struct p2p_peer *peer = a; const uint8_t *addr = b; return !memcmp(peer->bss->addr, addr, 6); } static const char *p2p_peer_get_path(const struct p2p_peer *peer) { static char path[256]; snprintf(path, sizeof(path), "%s/p2p_peers/%02x_%02x_%02x_%02x_%02x_%02x", p2p_device_get_path(peer->dev), peer->bss->addr[0], peer->bss->addr[1], peer->bss->addr[2], peer->bss->addr[3], peer->bss->addr[4], peer->bss->addr[5]); return path; } static void p2p_peer_free(void *user_data) { struct p2p_peer *peer = user_data; scan_bss_free(peer->bss); l_free(peer->name); l_free(peer); } static void p2p_peer_put(void *user_data) { struct p2p_peer *peer = user_data; /* * Removes all interfaces with one call, no need to call * wsc_dbus_remove_interface. */ l_dbus_unregister_object(dbus_get_bus(), p2p_peer_get_path(peer)); p2p_peer_free(peer); } static void p2p_device_discovery_start(struct p2p_device *dev); static void p2p_device_discovery_stop(struct p2p_device *dev); /* Callers should reserve 32 bytes */ static size_t p2p_build_wfd_ie(const struct p2p_wfd_properties *wfd, uint8_t *buf) { /* * Wi-Fi Display Technical Specification v2.1.0 * Probe req: Section 5.2.2 * Negotiation req: Section 5.2.6.1 * Negotiation resp: Section 5.2.6.2 * Negotiation confirm: Section 5.2.6.3 * Provision disc req: Section 5.2.6.6 */ size_t size = 0; uint16_t dev_type = wfd->source ? (wfd->sink ? WFD_DEV_INFO_TYPE_DUAL_ROLE : WFD_DEV_INFO_TYPE_SOURCE) : WFD_DEV_INFO_TYPE_PRIMARY_SINK; buf[size++] = IE_TYPE_VENDOR_SPECIFIC; size++; /* IE Data length */ buf[size++] = wifi_alliance_oui[0]; buf[size++] = wifi_alliance_oui[1]; buf[size++] = wifi_alliance_oui[2]; buf[size++] = 0x0a; /* OUI Type: WFD */ buf[size++] = WFD_SUBELEM_WFD_DEVICE_INFORMATION; buf[size++] = 0; /* WFD Subelement length */ buf[size++] = 6; buf[size++] = 0x00; /* WFD Device Information bitmap: */ buf[size++] = dev_type | (wfd->available ? WFD_DEV_INFO_SESSION_AVAILABLE : WFD_DEV_INFO_SESSION_NOT_AVAILABLE) | (wfd->audio ? 0 : WFD_DEV_INFO_NO_AUDIO_AT_PRIMARY_SINK) | (wfd->cp ? WFD_DEV_INFO_CONTENT_PROTECTION_SUPPORT : 0); buf[size++] = wfd->port >> 8; /* Session Mgmt Ctrl Port */ buf[size++] = wfd->port & 255; buf[size++] = wfd->throughput >> 8; /* Maximum throughput */ buf[size++] = wfd->throughput & 255; if (wfd->uibc) { buf[size++] = WFD_SUBELEM_EXTENDED_CAPABILITY; buf[size++] = 0; /* WFD Subelement length */ buf[size++] = 2; buf[size++] = 0x00; /* WFD Extended Capability Bitmap: */ buf[size++] = 0x01; /* UIBC Support */ } if (wfd->r2) { buf[size++] = WFD_SUBELEM_R2_DEVICE_INFORMATION; buf[size++] = 0; /* WFD Subelement length */ buf[size++] = 2; buf[size++] = 0x00; /* WFD R2 Device Information bitmap: */ buf[size++] = wfd->source ? wfd->sink ? 3 : 0 : 1; } buf[1] = size - 2; return size; } /* TODO: convert to iovecs */ static uint8_t *p2p_build_scan_ies(struct p2p_device *dev, uint8_t *buf, size_t buf_len, size_t *out_len) { struct p2p_probe_req p2p_info = {}; struct wsc_probe_request wsc_info = {}; L_AUTO_FREE_VAR(uint8_t *, p2p_ie) = NULL; size_t p2p_ie_size; uint8_t *wsc_data; size_t wsc_data_size; L_AUTO_FREE_VAR(uint8_t *, wsc_ie) = NULL; size_t wsc_ie_size; uint8_t wfd_ie[32]; size_t wfd_ie_size; p2p_info.capability = dev->capability; memcpy(p2p_info.listen_channel.country, dev->listen_country, 3); p2p_info.listen_channel.oper_class = dev->listen_oper_class; p2p_info.listen_channel.channel_num = dev->listen_channel; /* * Note that through an attribute we can also request Group Owners * to send us info on clients within their groups and could also * show those on D-Bus. Doesn't seem useful at this time but may * be desired at some point. */ p2p_ie = p2p_build_probe_req(&p2p_info, &p2p_ie_size); if (!p2p_ie) return NULL; wsc_info.version2 = true; wsc_info.request_type = WSC_REQUEST_TYPE_ENROLLEE_INFO; wsc_info.config_methods = dev->device_info.wsc_config_methods; if (!wsc_uuid_from_addr(dev->addr, wsc_info.uuid_e)) return NULL; wsc_info.primary_device_type = dev->device_info.primary_device_type; wsc_info.rf_bands = WSC_RF_BAND_2_4_GHZ; wsc_info.association_state = WSC_ASSOCIATION_STATE_NOT_ASSOCIATED; wsc_info.configuration_error = WSC_CONFIGURATION_ERROR_NO_ERROR; wsc_info.device_password_id = WSC_DEVICE_PASSWORD_ID_DEFAULT; l_strlcpy(wsc_info.device_name, dev->device_info.device_name, sizeof(wsc_info.device_name)); wsc_data = wsc_build_probe_request(&wsc_info, &wsc_data_size); if (!wsc_data) return NULL; wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size, &wsc_ie_size); l_free(wsc_data); if (!wsc_ie) return NULL; if (p2p_own_wfd) wfd_ie_size = p2p_build_wfd_ie(p2p_own_wfd, wfd_ie); else wfd_ie_size = 0; if (buf_len < wsc_ie_size + p2p_ie_size + wfd_ie_size) return NULL; memcpy(buf + 0, wsc_ie, wsc_ie_size); memcpy(buf + wsc_ie_size, p2p_ie, p2p_ie_size); if (wfd_ie_size) memcpy(buf + wsc_ie_size + p2p_ie_size, wfd_ie, wfd_ie_size); *out_len = wsc_ie_size + p2p_ie_size + wfd_ie_size; return buf; } static void p2p_connection_reset(struct p2p_device *dev) { struct p2p_peer *peer = dev->conn_peer; if (!peer) return; /* * conn_peer is currently not refcounted and we make sure it's always * on the dev->peer_list so we can just drop our reference. Since we * may not have been scanning for a while, don't drop the peer object * now just because it's not been seen in scan results recently, its * age will be checked on the next scan. */ dev->conn_peer = NULL; dev->disconnecting = false; dev->connections_left++; if (dev->conn_pin) { explicit_bzero(dev->conn_pin, strlen(dev->conn_pin)); l_free(dev->conn_pin); dev->conn_pin = NULL; } l_dbus_property_changed(dbus_get_bus(), p2p_device_get_path(dev), IWD_P2P_INTERFACE, "AvailableConnections"); l_timeout_remove(dev->config_timeout); l_timeout_remove(dev->go_neg_req_timeout); l_timeout_remove(dev->conn_dhcp_timeout); if (dev->conn_netconfig) { netconfig_destroy(dev->conn_netconfig); dev->conn_netconfig = NULL; } if (dev->conn_new_intf_cmd_id) /* * Note this may result in the interface being created * and unused, we don't have its ifindex or wdev_id here * to be able to delete it. Could use a separate netlink * socket for each connection or disallowing .Disconnect * calls while this command runs. */ l_genl_family_cancel(dev->nl80211, dev->conn_new_intf_cmd_id); if (dev->conn_enrollee) wsc_enrollee_cancel(dev->conn_enrollee, false); if (dev->conn_netdev) { struct l_genl_msg *msg; uint64_t wdev_id = netdev_get_wdev_id(dev->conn_netdev); msg = l_genl_msg_new(NL80211_CMD_DEL_INTERFACE); l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &wdev_id); if (!l_genl_family_send(dev->nl80211, msg, NULL, NULL, NULL)) { l_genl_msg_unref(msg); l_error("Sending DEL_INTERFACE for %s failed", netdev_get_name(dev->conn_netdev)); } netdev_destroy(dev->conn_netdev); dev->conn_netdev = NULL; } /* * Removing the netdev above makes sure that both the WSC connection * and the final WPA2 connection (wsc.c and netdev.c) no longer need * the bss so we can free it now -- if it wasn't freed as a result * of wsc_enrollee_cancel or netdev_destroy triggering * p2p_peer_provision_done in the first place. */ if (dev->conn_wsc_bss) { scan_bss_free(dev->conn_wsc_bss); dev->conn_wsc_bss = NULL; } netdev_watch_remove(dev->conn_netdev_watch_id); frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_CONNECT); if (dev->xchg_id) { frame_xchg_cancel(dev->xchg_id); dev->xchg_id = 0; } if (!dev->enabled || (dev->enabled && dev->start_stop_cmd_id)) { /* * The device has been disabled in the mean time, all peers * have been removed except this one. Now it's safe to * drop this peer from the scan results too. */ l_queue_destroy(dev->peer_list, p2p_peer_put); dev->peer_list = NULL; } if (dev->conn_own_wfd) { l_free(dev->conn_own_wfd); dev->conn_own_wfd = NULL; p2p_own_wfd->available = true; } if (dev->enabled && !dev->start_stop_cmd_id && !l_queue_isempty(dev->discovery_users)) p2p_device_discovery_start(dev); } static void p2p_connect_failed(struct p2p_device *dev) { struct p2p_peer *peer = dev->conn_peer; if (!peer) return; /* Are we in the scan for the WSC provision bss */ if (dev->scan_id) scan_cancel(dev->wdev_id, dev->scan_id); if (peer->wsc.pending_connect) dbus_pending_reply(&peer->wsc.pending_connect, dbus_error_failed(peer->wsc.pending_connect)); p2p_connection_reset(dev); } static void p2p_peer_frame_xchg(struct p2p_peer *peer, struct iovec *tx_body, const uint8_t *bssid, unsigned int retry_interval, unsigned int resp_timeout, unsigned int retries_on_ack, bool own_channel, uint32_t group_id, frame_xchg_cb_t cb, ...) { struct p2p_device *dev = peer->dev; struct iovec *frame; const struct iovec *iov; struct mmpdu_header *header; uint8_t header_buf[32] __attribute__ ((aligned)); int iov_cnt; uint32_t freq; va_list args; /* Header */ memset(header_buf, 0, sizeof(header_buf)); header = (void *) header_buf; header->fc.protocol_version = 0; header->fc.type = MPDU_TYPE_MANAGEMENT; header->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_ACTION; /* Section 2.4.3 */ memcpy(header->address_1, peer->device_addr, 6); /* DA */ memcpy(header->address_2, dev->addr, 6); /* SA */ memcpy(header->address_3, bssid, 6); /* BSSID */ for (iov = tx_body, iov_cnt = 0; iov->iov_base; iov++) iov_cnt++; frame = l_new(struct iovec, iov_cnt + 2); frame[0].iov_base = header_buf; frame[0].iov_len = (const uint8_t *) mmpdu_body(header) - header_buf; memcpy(frame + 1, tx_body, sizeof(struct iovec) * iov_cnt); freq = own_channel ? scan_channel_to_freq(dev->listen_channel, SCAN_BAND_2_4_GHZ) : peer->bss->frequency; va_start(args, cb); dev->xchg_id = frame_xchg_startv(dev->wdev_id, frame, freq, retry_interval, resp_timeout, retries_on_ack, group_id, cb, dev, NULL, args); va_end(args); l_free(frame); } static const struct frame_xchg_prefix p2p_frame_go_neg_req = { /* Management -> Public Action -> P2P -> GO Negotiation Request */ .data = (uint8_t []) { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, P2P_ACTION_GO_NEGOTIATION_REQ }, .len = 7, }; static const struct frame_xchg_prefix p2p_frame_go_neg_resp = { /* Management -> Public Action -> P2P -> GO Negotiation Response */ .data = (uint8_t []) { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, P2P_ACTION_GO_NEGOTIATION_RESP }, .len = 7, }; static const struct frame_xchg_prefix p2p_frame_go_neg_confirm = { /* Management -> Public Action -> P2P -> GO Negotiation Confirm */ .data = (uint8_t []) { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, P2P_ACTION_GO_NEGOTIATION_CONFIRM }, .len = 7, }; static const struct frame_xchg_prefix p2p_frame_pd_resp = { /* Management -> Public Action -> P2P -> Provision Discovery Response */ .data = (uint8_t []) { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, P2P_ACTION_PROVISION_DISCOVERY_RESP }, .len = 7, }; static void p2p_netconfig_event_handler(enum netconfig_event event, void *user_data) { struct p2p_device *dev = user_data; struct p2p_peer *peer = dev->conn_peer; switch (event) { case NETCONFIG_EVENT_CONNECTED: l_timeout_remove(dev->conn_dhcp_timeout); dbus_pending_reply(&peer->wsc.pending_connect, l_dbus_message_new_method_return( peer->wsc.pending_connect)); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "Connected"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "ConnectedInterface"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "ConnectedIP"); break; default: l_error("station: Unsupported netconfig event: %d.", event); p2p_connect_failed(dev); break; } } static void p2p_dhcp_timeout(struct l_timeout *timeout, void *user_data) { struct p2p_device *dev = user_data; l_debug(""); p2p_connect_failed(dev); } static void p2p_dhcp_timeout_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->conn_dhcp_timeout = NULL; } static void p2p_start_dhcp(struct p2p_device *dev) { uint32_t ifindex = netdev_get_ifindex(dev->conn_netdev); unsigned int dhcp_timeout_val; if (!l_settings_get_uint(iwd_get_config(), "P2P", "DHCPTimeout", &dhcp_timeout_val)) dhcp_timeout_val = 10; /* 10s default */ dev->conn_netconfig = netconfig_new(ifindex); if (!dev->conn_netconfig) { p2p_connect_failed(dev); return; } netconfig_configure(dev->conn_netconfig, p2p_dhcp_settings, dev->conn_addr, p2p_netconfig_event_handler, dev); dev->conn_dhcp_timeout = l_timeout_create(dhcp_timeout_val, p2p_dhcp_timeout, dev, p2p_dhcp_timeout_destroy); } static void p2p_netdev_connect_cb(struct netdev *netdev, enum netdev_result result, void *event_data, void *user_data) { struct p2p_device *dev = user_data; struct p2p_peer *peer = dev->conn_peer; l_debug("result: %i", result); if (!peer->wsc.pending_connect || dev->disconnecting) { /* Shouldn't happen except maybe in the ABORTED case */ return; } switch (result) { case NETDEV_RESULT_OK: p2p_start_dhcp(dev); break; case NETDEV_RESULT_AUTHENTICATION_FAILED: case NETDEV_RESULT_ASSOCIATION_FAILED: case NETDEV_RESULT_HANDSHAKE_FAILED: case NETDEV_RESULT_KEY_SETTING_FAILED: /* * In the AUTHENTICATION_FAILED and ASSOCIATION_FAILED * cases there's nothing to disconnect. In the * HANDSHAKE_FAILED and KEY_SETTINGS failed cases * netdev disconnects from the GO automatically and we are * called already from within the disconnect callback, * so we can directly free the netdev. */ p2p_connect_failed(dev); break; case NETDEV_RESULT_ABORTED: /* * This case can only be triggered by netdev_disconnect so * we'll wait for its callback before freeing the netdev. * We will also have already replied to * @peer->wsc.pending_connect so we have nothing to do here. */ break; } } static void p2p_netdev_event(struct netdev *netdev, enum netdev_event event, void *event_data, void *user_data) { struct p2p_device *dev = user_data; switch (event) { case NETDEV_EVENT_DISCONNECT_BY_AP: case NETDEV_EVENT_DISCONNECT_BY_SME: /* * We may get a DISCONNECT_BY_SME as a result of a * netdev_disconnect(). In that case let the callback handle * that. */ if (dev->disconnecting) break; /* If we're not connected, .Connected is already False */ if (!p2p_peer_operational(dev->conn_peer)) { p2p_connect_failed(dev); break; } l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "Connected"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "ConnectedInterface"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(dev->conn_peer), IWD_P2P_PEER_INTERFACE, "ConnectedIP"); p2p_connection_reset(dev); break; default: break; }; } static void p2p_handshake_event(struct handshake_state *hs, enum handshake_event event, void *user_data, ...) { va_list args; va_start(args, user_data); switch (event) { case HANDSHAKE_EVENT_FAILED: netdev_handshake_failed(hs, va_arg(args, int)); break; default: break; } va_end(args); } static void p2p_peer_provision_done(int err, struct wsc_credentials_info *creds, unsigned int n_creds, void *user_data) { struct p2p_peer *peer = user_data; struct p2p_device *dev = peer->dev; struct scan_bss *bss = dev->conn_wsc_bss; struct handshake_state *hs = NULL; struct iovec ie_iov[16]; int ie_num = 0; int r = -EOPNOTSUPP; struct p2p_association_req info = {}; struct ie_rsn_info bss_info = {}; struct ie_rsn_info rsn_info = {}; uint8_t rsne_buf[256]; uint8_t wfd_ie[32]; l_debug("err=%i n_creds=%u", err, n_creds); dev->conn_wsc_bss = NULL; dev->conn_enrollee = NULL; l_timeout_remove(dev->config_timeout); l_timeout_remove(dev->go_neg_req_timeout); if (err < 0) { if (err == -ECANCELED && peer->wsc.pending_cancel) { dbus_pending_reply(&peer->wsc.pending_cancel, l_dbus_message_new_method_return( peer->wsc.pending_cancel)); p2p_connection_reset(dev); } else p2p_connect_failed(dev); goto done; } if (strlen(creds[0].ssid) != bss->ssid_len || memcmp(creds[0].ssid, bss->ssid, bss->ssid_len)) { l_error("Unsupported: the SSID from the P2P peer's WSC " "credentials doesn't match the SSID from the " "Probe Response IEs"); goto not_supported; } /* * Apparently some implementations send the intended client's address * here (i.e. our), and some send the target BSS's (their own). */ if (memcmp(creds[0].addr, netdev_get_address(dev->conn_netdev), 6) && memcmp(creds[0].addr, bss->addr, 6)) { char addr1[32], addr2[32]; l_strlcpy(addr1, util_address_to_string(creds[0].addr), sizeof(addr1)); l_strlcpy(addr2, util_address_to_string( netdev_get_address(dev->conn_netdev)), sizeof(addr2)); l_error("Error: WSC credentials are not for our client " "interface (%s vs. %s)", addr1, addr2); goto error; } if (!bss->rsne || creds[0].security != SECURITY_PSK) goto not_supported; info.capability = dev->capability; info.device_info = dev->device_info; ie_iov[0].iov_base = p2p_build_association_req(&info, &ie_iov[0].iov_len); L_WARN_ON(!ie_iov[0].iov_base); ie_num = 1; if (dev->conn_own_wfd) { ie_iov[ie_num].iov_base = wfd_ie; ie_iov[ie_num].iov_len = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); ie_num++; } scan_bss_get_rsn_info(bss, &bss_info); rsn_info.akm_suites = wiphy_select_akm(dev->wiphy, bss, false); if (!rsn_info.akm_suites) goto not_supported; rsn_info.pairwise_ciphers = wiphy_select_cipher(dev->wiphy, bss_info.pairwise_ciphers); rsn_info.group_cipher = wiphy_select_cipher(dev->wiphy, bss_info.group_cipher); if (!rsn_info.pairwise_ciphers || !rsn_info.group_cipher) goto not_supported; rsn_info.group_management_cipher = wiphy_select_cipher(dev->wiphy, bss_info.group_management_cipher); rsn_info.mfpc = rsn_info.group_management_cipher != 0; ie_build_rsne(&rsn_info, rsne_buf); hs = netdev_handshake_state_new(dev->conn_netdev); if (!handshake_state_set_authenticator_ie(hs, bss->rsne)) goto not_supported; if (!handshake_state_set_supplicant_ie(hs, rsne_buf)) goto not_supported; handshake_state_set_event_func(hs, p2p_handshake_event, dev); handshake_state_set_ssid(hs, bss->ssid, bss->ssid_len); if (creds[0].has_passphrase) { uint8_t psk[32]; if (crypto_psk_from_passphrase(creds[0].passphrase, bss->ssid, bss->ssid_len, psk) < 0) goto error; handshake_state_set_pmk(hs, psk, 32); } else handshake_state_set_pmk(hs, creds[0].psk, 32); r = netdev_connect(dev->conn_netdev, bss, hs, ie_iov, ie_num, p2p_netdev_event, p2p_netdev_connect_cb, dev); if (r == 0) goto done; l_error("netdev_connect error: %s (%i)", strerror(-err), -err); error: not_supported: if (r < 0) { if (hs) handshake_state_free(hs); p2p_connect_failed(dev); } done: if (ie_num) l_free(ie_iov[0].iov_base); scan_bss_free(bss); } static void p2p_provision_connect(struct p2p_device *dev) { struct iovec iov[16]; int iov_num; uint8_t wfd_ie[32]; struct p2p_association_req info = {}; /* Ready to start the provisioning */ info.capability = dev->capability; info.device_info = dev->device_info; iov[0].iov_base = p2p_build_association_req(&info, &iov[0].iov_len); L_WARN_ON(!iov[0].iov_base); iov_num = 1; if (dev->conn_own_wfd) { iov[iov_num].iov_base = wfd_ie; iov[iov_num].iov_len = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); iov_num++; } dev->conn_enrollee = wsc_enrollee_new(dev->conn_netdev, dev->conn_wsc_bss, dev->conn_pin, iov, iov_num, p2p_peer_provision_done, dev->conn_peer); l_free(iov[0].iov_base); } static void p2p_device_netdev_watch_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->conn_netdev_watch_id = 0; } static void p2p_device_netdev_notify(struct netdev *netdev, enum netdev_watch_event event, void *user_data) { struct p2p_device *dev = user_data; if (dev->conn_netdev != netdev) return; switch (event) { case NETDEV_WATCH_EVENT_UP: case NETDEV_WATCH_EVENT_NEW: if (!dev->conn_wsc_bss || dev->conn_enrollee || !netdev_get_is_up(netdev)) break; p2p_provision_connect(dev); break; case NETDEV_WATCH_EVENT_DEL: dev->conn_netdev = NULL; /* Fall through */ case NETDEV_WATCH_EVENT_DOWN: case NETDEV_WATCH_EVENT_ADDRESS_CHANGE: p2p_connect_failed(dev); break; default: break; } } static void p2p_device_new_interface_cb(struct l_genl_msg *msg, void *user_data) { struct p2p_device *dev = user_data; l_debug(""); if (l_genl_msg_get_error(msg) < 0) { l_error("NEW_INTERFACE failed: %s", strerror(-l_genl_msg_get_error(msg))); p2p_connect_failed(dev); return; } /* Create the netdev so we don't have to parse the message ourselves */ dev->conn_netdev = netdev_create_from_genl(msg, dev->conn_addr); if (!dev->conn_netdev) { p2p_connect_failed(dev); return; } /* * Register a watch for each connection rather than having one * global watch. Each connection's watch will receive events * related to all other connections too, and will check that its * conn_netdev != netdev and exit immediately. This is not ideal * but it's the same complexity (n^2) as that of one global watch * that receives all events and iterates over p2p_device_list to * find the connection. */ dev->conn_netdev_watch_id = netdev_watch_add(p2p_device_netdev_notify, dev, p2p_device_netdev_watch_destroy); } static void p2p_device_new_interface_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->conn_new_intf_cmd_id = 0; } static void p2p_device_interface_create(struct p2p_device *dev) { uint32_t iftype = NL80211_IFTYPE_P2P_CLIENT; char ifname[32]; uint32_t wiphy_id = dev->wdev_id >> 32; struct l_genl_msg *msg; snprintf(ifname, sizeof(ifname), "wlan%i-p2p-cl%i", wiphy_id, dev->conn_num++); l_debug("creating %s", ifname); msg = l_genl_msg_new(NL80211_CMD_NEW_INTERFACE); l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy_id); l_genl_msg_append_attr(msg, NL80211_ATTR_IFTYPE, 4, &iftype); l_genl_msg_append_attr(msg, NL80211_ATTR_IFNAME, strlen(ifname) + 1, ifname); l_genl_msg_append_attr(msg, NL80211_ATTR_4ADDR, 1, "\0"); l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, ""); dev->conn_new_intf_cmd_id = l_genl_family_send(dev->nl80211, msg, p2p_device_new_interface_cb, dev, p2p_device_new_interface_destroy); if (!dev->conn_new_intf_cmd_id) { l_genl_msg_unref(msg); l_error("Error sending NEW_INTERFACE for %s", ifname); p2p_connect_failed(dev); } } static void p2p_scan_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->scan_id = 0; } static void p2p_provision_scan_start(struct p2p_device *dev); static bool p2p_provision_scan_notify(int err, struct l_queue *bss_list, void *user_data) { struct p2p_device *dev = user_data; const struct l_queue_entry *entry; static const uint8_t wildcard_addr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; l_debug("err=%i, len(bss_list)=%i", err, l_queue_length(bss_list)); if (err) { l_error("P2P provision scan failed: %s (%i)", strerror(-err), -err); p2p_connect_failed(dev); return false; } for (entry = l_queue_get_entries(bss_list); entry; entry = entry->next) { struct scan_bss *bss = entry->data; const uint8_t *group_id; bool selected_reg; struct p2p_capability_attr *capability; enum wsc_device_password_id device_password_id; const uint8_t *amacs; /* * Check if we found our target GO, some of these checks may * need to be gradually relaxed as we discover non-compliant * implementations but at least print a debug statement when * something doesn't match. */ if (strncmp((const char *) bss->ssid, dev->go_group_id.ssid, bss->ssid_len)) continue; if (dev->go_group_id.ssid[bss->ssid_len] != '\0') continue; if (!util_mem_is_zero(dev->go_interface_addr, 6) && memcmp(bss->addr, dev->go_interface_addr, 6)) l_debug("SSID matched but BSSID didn't match the GO's " "intended interface addr, proceeding anyway"); if (!bss->wsc) { l_error("SSID matched but no valid WSC IE"); continue; } if (bss->source_frame == SCAN_BSS_PROBE_RESP) { struct wsc_probe_response wsc_info; if (!bss->p2p_probe_resp_info) { l_error("SSID matched but no valid P2P IE"); continue; } if (wsc_parse_probe_response(bss->wsc, bss->wsc_size, &wsc_info) < 0) { l_error("SSID matched but can't parse WSC " "Probe Response info"); continue; } group_id = bss->p2p_probe_resp_info-> device_info.device_addr; selected_reg = wsc_info.selected_registrar; capability = &bss->p2p_probe_resp_info->capability; device_password_id = wsc_info.device_password_id; amacs = wsc_info.authorized_macs; } else if (bss->source_frame == SCAN_BSS_BEACON) { struct wsc_beacon wsc_info; if (!bss->p2p_beacon_info) { l_error("SSID matched but no valid P2P IE"); continue; } if (wsc_parse_beacon(bss->wsc, bss->wsc_size, &wsc_info) < 0) { l_error("SSID matched but can't parse WSC " "Beacon info"); continue; } group_id = bss->p2p_beacon_info->device_addr; selected_reg = wsc_info.selected_registrar; capability = &bss->p2p_beacon_info->capability; device_password_id = wsc_info.device_password_id; amacs = wsc_info.authorized_macs; } else continue; if (memcmp(group_id, dev->go_group_id.device_addr, 6)) { l_error("SSID matched but Group ID address didn't"); continue; } if (!selected_reg) { /* * Debug level because this will sometimes happen * while the target is setting up the GO mode in the * course of normal operation, and gets set to true * in a few seconds, we just need to keep scanning. */ l_debug("SSID matched but not a Selected Reg"); continue; } if (dev->conn_peer->group && (capability->group_caps & P2P_GROUP_CAP_GROUP_FORMATION)) { l_error("SSID matched but not in Group Formation"); continue; } if (!dev->conn_peer->group && !(capability->group_caps & P2P_GROUP_CAP_GROUP_FORMATION)) /* * We have to ignore this one for interoperability * with some devices. */ l_debug("SSID matched but GO not in Group Formation, " "proceeding anyway"); if (capability->group_caps & P2P_GROUP_CAP_GROUP_LIMIT) { l_error("SSID matched but group already full"); continue; } if (device_password_id != dev->conn_password_id) { l_error("SSID matched wrong Password ID"); continue; } if (!util_mem_is_zero(amacs, 30)) { bool amacs_match = false; int i; for (i = 0; i < 5; i++, amacs += 6) if (!memcmp(amacs, dev->addr, 6) || !memcmp(amacs, wildcard_addr, 6)) amacs_match = true; if (!amacs_match) { l_error("SSID matched we're not in AMacs"); continue; } } l_debug("GO found in the scan results"); dev->conn_wsc_bss = bss; p2p_device_interface_create(dev); l_queue_remove(bss_list, bss); l_queue_destroy(bss_list, (l_queue_destroy_func_t) scan_bss_free); return true; } /* Retry a few times if the WSC AP not found or not ready */ dev->go_scan_retry++; if (dev->go_scan_retry > 15) { p2p_connect_failed(dev); return false; } p2p_provision_scan_start(dev); return false; } static void p2p_provision_scan_start(struct p2p_device *dev) { struct scan_parameters params = {}; uint8_t buf[256]; params.flush = true; params.no_cck_rates = true; params.ssid = dev->go_group_id.ssid; params.extra_ie = p2p_build_scan_ies(dev, buf, sizeof(buf), ¶ms.extra_ie_size); L_WARN_ON(!params.extra_ie); /* * Initially scan just the Operating Channel the GO reported * during the negotiation. In theory there's no guarantee that * it is going to be on that channel so we should fall back * to scanning all the channels listed in the Channel List * attribute. For simplicity we just do a full scan in that * scenario -- for most target P2P devices we wouldn't be saving * ourselves any work anyway as the Channel List is going to * contain all of the 2.4 and 5G channels. */ if (dev->go_scan_retry < 12) { params.freqs = scan_freq_set_new(); scan_freq_set_add(params.freqs, dev->go_oper_freq); } dev->scan_id = scan_active_full(dev->wdev_id, ¶ms, NULL, p2p_provision_scan_notify, dev, p2p_scan_destroy); if (params.freqs) scan_freq_set_free(params.freqs); } static void p2p_start_client_provision(struct p2p_device *dev) { char bssid_str[18]; memcpy(bssid_str, util_address_to_string(dev->go_interface_addr), 18); l_debug("freq=%u ssid=%s group_addr=%s bssid=%s", dev->go_oper_freq, dev->go_group_id.ssid, util_address_to_string(dev->go_group_id.device_addr), bssid_str); dev->go_scan_retry = 0; p2p_provision_scan_start(dev); } static void p2p_config_timeout_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->config_timeout = NULL; } static void p2p_config_timeout(struct l_timeout *timeout, void *user_data) { struct p2p_device *dev = user_data; l_timeout_remove(dev->config_timeout); /* Ready to start WSC */ p2p_start_client_provision(dev); } static void p2p_go_negotiation_resp_done(int error, void *user_data) { struct p2p_device *dev = user_data; if (error) l_error("Sending the GO Negotiation Response failed: %s (%i)", strerror(-error), -error); else l_error("No GO Negotiation Confirmation frame received"); dev->xchg_id = 0; p2p_connect_failed(dev); } static void p2p_go_negotiation_resp_err_done(int error, void *user_data) { struct p2p_device *dev = user_data; dev->xchg_id = 0; if (error) l_error("Sending the GO Negotiation Response failed: %s (%i)", strerror(-error), -error); } /* * Called by GO Negotiation Response and Confirmation receive handlers, * in both cases the channel lists are required to be subsets of our * own supported channels and the Operating Channel must appear in the * channel list. */ static bool p2p_device_validate_channel_list(struct p2p_device *dev, const struct p2p_channel_list_attr *attr, const struct p2p_channel_attr *oper_channel) { if (l_queue_isempty(attr->channel_entries)) return false; /* TODO */ return true; } /* * It seems that sending more than about 42 channels in a frame's Channel * List attribute will baffle some devices enough that they will ignore * the frame. */ #define MAX_CHANNELS 40 static void p2p_add_freq_func(uint32_t freq, void *user_data) { struct p2p_channel_entries *channel_entry = user_data; uint8_t channel; enum scan_band band; if (channel_entry->n_channels >= MAX_CHANNELS) return; channel = scan_freq_to_channel(freq, &band); if (band != scan_oper_class_to_band((const uint8_t *) "XX\x4", channel_entry->oper_class)) return; channel_entry->channels[channel_entry->n_channels++] = channel; } static void p2p_device_fill_channel_list(struct p2p_device *dev, struct p2p_channel_list_attr *attr) { struct p2p_channel_entries *channel_entry; unsigned int total_channels; memcpy(attr->country, dev->listen_country, 3); attr->channel_entries = l_queue_new(); channel_entry = l_malloc(sizeof(struct p2p_channel_entries) + MAX_CHANNELS); channel_entry->oper_class = 81; channel_entry->n_channels = 0; scan_freq_set_foreach(wiphy_get_supported_freqs(dev->wiphy), p2p_add_freq_func, channel_entry); l_queue_push_tail(attr->channel_entries, channel_entry); total_channels = channel_entry->n_channels; if (total_channels >= MAX_CHANNELS) return; channel_entry = l_malloc(sizeof(struct p2p_channel_entries) + MAX_CHANNELS); channel_entry->oper_class = 115; channel_entry->n_channels = 0; scan_freq_set_foreach(wiphy_get_supported_freqs(dev->wiphy), p2p_add_freq_func, channel_entry); if (total_channels + channel_entry->n_channels > MAX_CHANNELS) channel_entry->n_channels = MAX_CHANNELS - total_channels; l_queue_push_tail(attr->channel_entries, channel_entry); } static bool p2p_extract_wfd_properties(const uint8_t *ie, size_t ie_size, struct p2p_wfd_properties *out) { struct wfd_subelem_iter iter; const uint8_t *devinfo = NULL; const uint8_t *ext_caps = NULL; const uint8_t *r2 = NULL; if (!ie) return false; wfd_subelem_iter_init(&iter, ie, ie_size); while (wfd_subelem_iter_next(&iter)) { enum wfd_subelem_type type = wfd_subelem_iter_get_type(&iter); size_t len = wfd_subelem_iter_get_length(&iter); const uint8_t *data = wfd_subelem_iter_get_data(&iter); switch (type) { #define SUBELEM_CHECK(var, expected_len, name) \ if (len != expected_len) { \ l_debug(name " length wrong in WFD IE");\ return false; \ } \ \ if (var) { \ l_debug("Duplicate" name " in WFD IE");\ return false; \ } \ \ var = data; case WFD_SUBELEM_WFD_DEVICE_INFORMATION: SUBELEM_CHECK(devinfo, 6, "Device Information"); break; case WFD_SUBELEM_EXTENDED_CAPABILITY: SUBELEM_CHECK(ext_caps, 2, "Extended Capability"); break; case WFD_SUBELEM_R2_DEVICE_INFORMATION: SUBELEM_CHECK(r2, 2, "R2 Device Information"); break; #undef SUBELEM_CHECK default: /* Skip unknown IEs */ break; } } if (devinfo) { uint16_t capability = l_get_be16(devinfo + 0); bool source; bool sink; uint16_t port; source = (capability & WFD_DEV_INFO_DEVICE_TYPE) == WFD_DEV_INFO_TYPE_SOURCE || (capability & WFD_DEV_INFO_DEVICE_TYPE) == WFD_DEV_INFO_TYPE_DUAL_ROLE; sink = (capability & WFD_DEV_INFO_DEVICE_TYPE) == WFD_DEV_INFO_TYPE_PRIMARY_SINK || (capability & WFD_DEV_INFO_DEVICE_TYPE) == WFD_DEV_INFO_TYPE_DUAL_ROLE; if (!source && !sink) return false; port = l_get_be16(devinfo + 2); if (source && port == 0) { l_debug("0 port number in WFD IE Device Information"); return false; } memset(out, 0, sizeof(*out)); out->available = (capability & WFD_DEV_INFO_SESSION_AVAILABILITY) == WFD_DEV_INFO_SESSION_AVAILABLE; out->source = source; out->sink = sink; out->port = port; out->cp = capability & WFD_DEV_INFO_CONTENT_PROTECTION_SUPPORT; out->audio = !sink || !(capability & WFD_DEV_INFO_NO_AUDIO_AT_PRIMARY_SINK); } else { l_error("Device Information missing in WFD IE"); return false; } if (ext_caps && (l_get_be16(ext_caps) & 1)) out->uibc = 1; if (r2) { uint8_t role = l_get_be16(r2) & 3; if ((out->source && role != 0 && role != 3) || (out->sink && role != 1 && role != 3)) l_debug("Invalid role in WFD R2 Device Information"); else out->r2 = true; } return true; } static bool p2p_device_validate_conn_wfd(struct p2p_device *dev, const uint8_t *ie, ssize_t ie_size) { struct p2p_wfd_properties wfd; if (!dev->conn_own_wfd) return true; /* * WFD IEs are optional in Association Request/Response and P2P Public * Action frames for R2 devices and required for R1 devices. * Wi-Fi Display Technical Specification v2.1.0 section 5.2: * "A WFD R2 Device shall include the WFD IE in Beacon, Probe * Request/Response, Association Request/Response and P2P Public Action * frames in order to be interoperable with R1 devices. If a WFD R2 * Device discovers that the peer device is also a WFD R2 Device, then * it may include the WFD IE in Association Request/Response and P2P * Public Action frames." */ if (!ie) return dev->conn_own_wfd->r2; if (!p2p_extract_wfd_properties(ie, ie_size, &wfd)) { l_error("Could not parse the WFD IE contents"); return false; } if ((dev->conn_own_wfd->source && !wfd.sink) || (dev->conn_own_wfd->sink && !wfd.source)) { l_error("Wrong role in peer's WFD IE"); return false; } if (wfd.r2 != dev->conn_own_wfd->r2) { l_error("Wrong version in peer's WFD IE"); return false; } /* * Ignore the session available state because it's not 100% clear * at what point the peer switches to SESSION_NOT_AVAILABLE in its * Device Information. * But we might want to check that other bits have not changed from * what the peer reported during discovery. * Wi-Fi Display Technical Specification v2.1.0 section 4.5.2.1: * "The content of the WFD Device Information subelement should be * immutable during the period of P2P connection establishment, with * [...] exceptions [...]" */ return true; } static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, struct p2p_device *dev) { struct p2p_go_negotiation_confirmation info; int r; enum scan_band band; uint32_t frequency; l_debug(""); dev->xchg_id = 0; if (body_len < 8) { l_error("GO Negotiation Confirmation frame too short"); p2p_connect_failed(dev); return true; } r = p2p_parse_go_negotiation_confirmation(body + 7, body_len - 7, &info); if (r < 0) { l_error("GO Negotiation Confirmation parse error %s (%i)", strerror(-r), -r); p2p_connect_failed(dev); return true; } if (info.dialog_token != dev->go_dialog_token) { l_error("GO Negotiation Response dialog token doesn't match"); p2p_connect_failed(dev); return true; } if (info.status != P2P_STATUS_SUCCESS) { l_error("GO Negotiation Confirmation status %i", info.status); p2p_connect_failed(dev); return true; } if (!p2p_device_validate_channel_list(dev, &info.channel_list, &info.operating_channel)) return true; band = scan_oper_class_to_band( (const uint8_t *) info.operating_channel.country, info.operating_channel.oper_class); frequency = scan_channel_to_freq(info.operating_channel.channel_num, band); if (!frequency) { l_error("Bad operating channel in GO Negotiation Confirmation"); p2p_connect_failed(dev); return true; } /* Check whether WFD IE is required, validate it if present */ if (!p2p_device_validate_conn_wfd(dev, info.wfd, info.wfd_size)) { p2p_connect_failed(dev); return true; } dev->go_oper_freq = frequency; memcpy(&dev->go_group_id, &info.group_id, sizeof(struct p2p_group_id_attr)); /* * Confirmation received. For simplicity wait idly the maximum amount * of time indicated by the peer in the GO Negotiation Response's * Configuration Timeout attribute and start the provisioning phase. */ dev->config_timeout = l_timeout_create_ms(dev->go_config_delay, p2p_config_timeout, dev, p2p_config_timeout_destroy); return true; } static void p2p_device_go_negotiation_req_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, void *user_data) { struct p2p_device *dev = user_data; struct p2p_go_negotiation_req req_info; struct p2p_go_negotiation_resp resp_info = {}; int r; uint8_t *resp_body; size_t resp_len; uint8_t wfd_ie[32]; struct iovec iov[16]; int iov_len = 0; struct p2p_peer *peer; enum p2p_attr_status_code status = P2P_STATUS_SUCCESS; bool tie_breaker = false; l_debug(""); /* * Check the Destination Address and the BSSID. Section 2.4.3: * "When communication is not within a P2P Group, e.g. during * [...] GO Negotiation [...], a P2P Device shall use the * P2P Device Address of the intended destination as the BSSID in * Request, or Confirmation frames and its own P2P Device Address * as the BSSID in Response frames." * * Some drivers (brcmfmac) will report the BSSID as all zeros and * some Wi-Fi Display dongles will pass their own address as the * BSSID in the GO Negotiation Request so allow all three possible * values. */ if (memcmp(mpdu->address_1, dev->addr, 6) || (memcmp(mpdu->address_3, dev->addr, 6) && memcmp(mpdu->address_3, mpdu->address_2, 6) && !util_mem_is_zero(mpdu->address_3, 6))) return; peer = l_queue_find(dev->peer_list, p2p_peer_match, mpdu->address_2); if (!peer) return; if (body_len < 8) return; if (!dev->go_neg_req_timeout || peer != dev->conn_peer) { status = P2P_STATUS_FAIL_INFO_NOT_AVAIL; goto respond; } if (memcmp(mpdu->address_2, dev->conn_peer->bss->addr, 6)) { status = P2P_STATUS_FAIL_UNABLE_TO_ACCOMMODATE_REQUEST; goto respond; } r = p2p_parse_go_negotiation_req(body + 7, body_len - 7, &req_info); if (r < 0) { l_error("GO Negotiation Request parse error %s (%i)", strerror(-r), -r); p2p_connect_failed(dev); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); status = P2P_STATUS_FAIL_INVALID_PARAMS; goto respond; } if (req_info.go_intent == 0 && !req_info.go_tie_breaker) { l_error("Can't negotiate client role and GO operation not " "supported"); if (peer->wsc.pending_connect) { struct l_dbus_message *reply = dbus_error_not_supported( peer->wsc.pending_connect); dbus_pending_reply(&peer->wsc.pending_connect, reply); } p2p_connect_failed(dev); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); status = P2P_STATUS_FAIL_INCOMPATIBLE_PARAMS; goto p2p_free; } if (req_info.capability.group_caps & P2P_GROUP_CAP_PERSISTENT_GROUP) { if (peer->wsc.pending_connect) { struct l_dbus_message *reply = dbus_error_not_supported( peer->wsc.pending_connect); dbus_pending_reply(&peer->wsc.pending_connect, reply); } p2p_connect_failed(dev); l_error("Persistent groups not supported"); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); status = P2P_STATUS_FAIL_INCOMPATIBLE_PARAMS; goto p2p_free; } if (req_info.device_password_id != dev->conn_password_id) { p2p_connect_failed(dev); l_error("Incompatible Password ID in the GO Negotiation Req"); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); status = P2P_STATUS_FAIL_INCOMPATIBLE_PROVISIONING; goto p2p_free; } /* Check whether WFD IE is required, validate it if present */ if (!p2p_device_validate_conn_wfd(dev, req_info.wfd, req_info.wfd_size)) { p2p_connect_failed(dev); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); status = P2P_STATUS_FAIL_INCOMPATIBLE_PARAMS; goto p2p_free; } l_timeout_remove(dev->go_neg_req_timeout); p2p_device_discovery_stop(dev); dev->go_dialog_token = req_info.dialog_token; dev->go_config_delay = req_info.config_timeout.go_config_timeout * 10; memcpy(dev->go_interface_addr, req_info.intended_interface_addr, 6); p2p_free: tie_breaker = !req_info.go_tie_breaker; p2p_clear_go_negotiation_req(&req_info); respond: /* Build and send the GO Negotiation Response */ resp_info.dialog_token = dev->go_dialog_token; resp_info.status = status; resp_info.capability.device_caps = dev->capability.device_caps; resp_info.capability.group_caps = 0; /* Reserved */ resp_info.go_intent = 0; /* Don't want to be the GO */ resp_info.go_tie_breaker = tie_breaker; resp_info.config_timeout.go_config_timeout = 50; /* 500ms */ resp_info.config_timeout.client_config_timeout = 50; /* 500ms */ if (dev->conn_peer) memcpy(resp_info.intended_interface_addr, dev->conn_addr, 6); p2p_device_fill_channel_list(dev, &resp_info.channel_list); resp_info.device_info = dev->device_info; resp_info.device_password_id = dev->conn_password_id; if (dev->conn_own_wfd) { resp_info.wfd = wfd_ie; resp_info.wfd_size = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); } resp_body = p2p_build_go_negotiation_resp(&resp_info, &resp_len); resp_info.wfd = NULL; p2p_clear_go_negotiation_resp(&resp_info); if (!resp_body) { p2p_connect_failed(dev); return; } iov[iov_len].iov_base = resp_body; iov[iov_len].iov_len = resp_len; iov_len++; iov[iov_len].iov_base = NULL; if (status == P2P_STATUS_SUCCESS) p2p_peer_frame_xchg(peer, iov, dev->addr, 0, 600, 0, true, FRAME_GROUP_CONNECT, p2p_go_negotiation_resp_done, &p2p_frame_go_neg_confirm, p2p_go_negotiation_confirm_cb, NULL); else p2p_peer_frame_xchg(peer, iov, dev->addr, 0, 0, 0, true, FRAME_GROUP_CONNECT, p2p_go_negotiation_resp_err_done, NULL); l_debug("GO Negotiation Response sent with status %i", status); } static void p2p_go_negotiation_confirm_done(int error, void *user_data) { struct p2p_device *dev = user_data; if (error) { /* TODO: we should probably ignore the missing ACK error */ l_error("Sending the GO Negotiation Confirm failed: %s (%i)", strerror(-error), -error); p2p_connect_failed(dev); return; } /* * Frame was ACKed. For simplicity wait idly the maximum amount of * time indicated by the peer in the GO Negotiation Response's * Configuration Timeout attribute and start the provisioning phase. */ dev->config_timeout = l_timeout_create_ms(dev->go_config_delay, p2p_config_timeout, dev, p2p_config_timeout_destroy); } static void p2p_go_neg_req_timeout_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->go_neg_req_timeout = NULL; } static void p2p_go_neg_req_timeout(struct l_timeout *timeout, void *user_data) { struct p2p_device *dev = user_data; l_debug(""); p2p_connect_failed(dev); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); } static bool p2p_go_negotiation_resp_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, struct p2p_device *dev) { struct p2p_go_negotiation_resp resp_info; struct p2p_go_negotiation_confirmation confirm_info = {}; uint8_t *confirm_body; size_t confirm_len; uint8_t wfd_ie[32]; int r; struct iovec iov[16]; int iov_len = 0; enum scan_band band; uint32_t frequency; l_debug(""); if (!dev->conn_peer) return true; if (body_len < 8) { l_error("GO Negotiation Response frame too short"); p2p_connect_failed(dev); return true; } r = p2p_parse_go_negotiation_resp(body + 7, body_len - 7, &resp_info); if (r < 0) { l_error("GO Negotiation Response parse error %s (%i)", strerror(-r), -r); p2p_connect_failed(dev); return true; } if (resp_info.dialog_token != 1) { l_error("GO Negotiation Response dialog token doesn't match"); p2p_connect_failed(dev); return true; } if (resp_info.status != P2P_STATUS_SUCCESS) { if (resp_info.status == P2P_STATUS_FAIL_INFO_NOT_AVAIL) { /* Give the peer 120s to restart the GO Negotiation */ l_error("P2P_STATUS_FAIL_INFO_NOT_AVAIL: Will wait for " "a new GO Negotiation Request before declaring " "failure"); dev->go_neg_req_timeout = l_timeout_create(120, p2p_go_neg_req_timeout, dev, p2p_go_neg_req_timeout_destroy); p2p_device_discovery_start(dev); return true; } l_error("GO Negotiation Response status %i", resp_info.status); p2p_connect_failed(dev); return true; } /* * 3.1.4.2: "The Tie breaker bit in a GO Negotiation Response frame * shall be toggled from the corresponding GO Negotiation Request * frame." */ if (!resp_info.go_tie_breaker) { l_error("GO Negotiation Response tie breaker value wrong"); if (resp_info.go_intent == 0) { /* Can't continue */ p2p_connect_failed(dev); return true; } } if (resp_info.capability.group_caps & P2P_GROUP_CAP_PERSISTENT_GROUP) { l_error("Persistent groups not supported"); p2p_connect_failed(dev); return true; } if (resp_info.device_password_id != dev->conn_password_id) { l_error("GO Negotiation Response WSC device password ID wrong"); p2p_connect_failed(dev); return true; } if (!p2p_device_validate_channel_list(dev, &resp_info.channel_list, &resp_info.operating_channel)) return true; /* Check whether WFD IE is required, validate it if present */ if (!p2p_device_validate_conn_wfd(dev, resp_info.wfd, resp_info.wfd_size)) { p2p_connect_failed(dev); return true; } band = scan_oper_class_to_band( (const uint8_t *) resp_info.operating_channel.country, resp_info.operating_channel.oper_class); frequency = scan_channel_to_freq( resp_info.operating_channel.channel_num, band); if (!frequency) { l_error("Bad operating channel in GO Negotiation Response"); p2p_connect_failed(dev); return true; } dev->go_config_delay = resp_info.config_timeout.go_config_timeout * 10; dev->go_oper_freq = frequency; memcpy(&dev->go_group_id, &resp_info.group_id, sizeof(struct p2p_group_id_attr)); memcpy(dev->go_interface_addr, resp_info.intended_interface_addr, 6); /* Build and send the GO Negotiation Confirmation */ confirm_info.dialog_token = resp_info.dialog_token; confirm_info.status = P2P_STATUS_SUCCESS; confirm_info.capability.device_caps = 0; /* Reserved */ confirm_info.capability.group_caps = 0; /* Reserved */ confirm_info.channel_list = resp_info.channel_list; confirm_info.operating_channel = resp_info.operating_channel; if (dev->conn_own_wfd) { confirm_info.wfd = wfd_ie; confirm_info.wfd_size = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); } confirm_body = p2p_build_go_negotiation_confirmation(&confirm_info, &confirm_len); confirm_info.wfd = NULL; p2p_clear_go_negotiation_resp(&resp_info); if (!confirm_body) { p2p_connect_failed(dev); return true; } iov[iov_len].iov_base = confirm_body; iov[iov_len].iov_len = confirm_len; iov_len++; iov[iov_len].iov_base = NULL; p2p_peer_frame_xchg(dev->conn_peer, iov, dev->conn_peer->device_addr, 0, 0, 0, false, FRAME_GROUP_CONNECT, p2p_go_negotiation_confirm_done, NULL); return true; } static void p2p_go_negotiation_req_done(int error, void *user_data) { struct p2p_device *dev = user_data; if (error) l_error("Sending the GO Negotiation Req failed: %s (%i)", strerror(-error), -error); else l_error("No GO Negotiation Response after Request ACKed"); p2p_connect_failed(dev); } static void p2p_start_go_negotiation(struct p2p_device *dev) { struct p2p_go_negotiation_req info = {}; uint8_t *req_body; size_t req_len; uint8_t wfd_ie[32]; struct iovec iov[16]; int iov_len = 0; /* * Devices should respond within 100ms but times of ~400ms are * often seen instead. * * 3.1.4.2: "The P2P Device that sent the Group Owner Negotiation * frame shall assume that Group Owner Negotiation failed and is * complete if it does not receive the next frame in the exchange * within 100 milliseconds of receiving an acknowledgement frame." */ unsigned int resp_timeout = 600; info.dialog_token = 1; info.capability = dev->capability; info.go_intent = 0; /* Don't want to be the GO */ info.go_tie_breaker = 0; info.config_timeout.go_config_timeout = 50; /* 500ms */ info.config_timeout.client_config_timeout = 50; /* 500ms */ memcpy(info.listen_channel.country, dev->listen_country, 3); info.listen_channel.oper_class = dev->listen_oper_class; info.listen_channel.channel_num = dev->listen_channel; memcpy(info.intended_interface_addr, dev->conn_addr, 6); /* * In theory we support an empty set of operating channels for * our potential group as a GO but we have to include our * supported channels because the peer can only choose their * own channels from our list. Use the listen channel as the * preferred operating channel because we have no preference. */ p2p_device_fill_channel_list(dev, &info.channel_list); memcpy(info.operating_channel.country, dev->listen_country, 3); info.operating_channel.oper_class = dev->listen_oper_class; info.operating_channel.channel_num = dev->listen_channel; info.device_info = dev->device_info; info.device_password_id = dev->conn_password_id; if (dev->conn_own_wfd) { info.wfd = wfd_ie; info.wfd_size = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); } req_body = p2p_build_go_negotiation_req(&info, &req_len); info.wfd = NULL; p2p_clear_go_negotiation_req(&info); if (!req_body) { p2p_connect_failed(dev); return; } iov[iov_len].iov_base = req_body; iov[iov_len].iov_len = req_len; iov_len++; iov[iov_len].iov_base = NULL; p2p_peer_frame_xchg(dev->conn_peer, iov, dev->conn_peer->device_addr, 100, resp_timeout, 256, false, FRAME_GROUP_CONNECT, p2p_go_negotiation_req_done, &p2p_frame_go_neg_resp, p2p_go_negotiation_resp_cb, NULL); } static bool p2p_provision_disc_resp_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, struct p2p_device *dev) { struct p2p_provision_discovery_resp info; int r; l_debug(""); if (!dev->conn_peer) return true; if (body_len < 8) { l_error("Provision Discovery Response frame too short"); p2p_connect_failed(dev); return true; } r = p2p_parse_provision_disc_resp(body + 7, body_len - 7, &info); if (r < 0) { l_error("Provision Discovery Response parse error %s (%i)", strerror(-r), -r); p2p_connect_failed(dev); return true; } if (info.dialog_token != 2) { l_error("Provision Discovery Response dialog token doesn't " "match"); p2p_connect_failed(dev); return true; } if (info.wsc_config_method != dev->conn_config_method) { l_error("Provision Discovery Response WSC device password ID " "wrong"); p2p_connect_failed(dev); return true; } /* Check whether WFD IE is required, validate it if present */ if (!p2p_device_validate_conn_wfd(dev, info.wfd, info.wfd_size)) { p2p_connect_failed(dev); return true; } /* * If we're not joining an existing group, continue with Group * Formation now. */ if (!dev->conn_peer->group) { p2p_start_go_negotiation(dev); return true; } /* * Indended P2P Interface address is optional, we don't have the * BSSID of the group here. * * We might want to make sure that Group Formation is false but the * Capability attribute is also optional. */ dev->go_oper_freq = dev->conn_peer->bss->frequency; memset(dev->go_interface_addr, 0, 6); memcpy(dev->go_group_id.device_addr, dev->conn_peer->device_addr, 6); l_strlcpy(dev->go_group_id.ssid, (const char *) dev->conn_peer->bss->ssid, dev->conn_peer->bss->ssid_len + 1); /* Ready to start WSC */ p2p_start_client_provision(dev); return true; } static void p2p_provision_disc_req_done(int error, void *user_data) { struct p2p_device *dev = user_data; if (error) l_error("Sending the Provision Discovery Req failed: %s (%i)", strerror(-error), -error); else l_error("No Provision Discovery Response after Request ACKed"); p2p_connect_failed(dev); } static void p2p_start_provision_discovery(struct p2p_device *dev) { struct p2p_provision_discovery_req info = { .status = -1 }; uint8_t *req_body; size_t req_len; uint8_t wfd_ie[32]; struct iovec iov[16]; int iov_len = 0; /* This frame is pretty simple when P2Ps isn't supported */ info.dialog_token = 2; info.capability = dev->capability; info.device_info = dev->device_info; if (dev->conn_peer->group) { memcpy(info.group_id.device_addr, dev->conn_peer->bss->addr, 6); memcpy(info.group_id.ssid, dev->conn_peer->bss->ssid, dev->conn_peer->bss->ssid_len); } info.wsc_config_method = dev->conn_config_method; if (dev->conn_own_wfd) { info.wfd = wfd_ie; info.wfd_size = p2p_build_wfd_ie(dev->conn_own_wfd, wfd_ie); } req_body = p2p_build_provision_disc_req(&info, &req_len); info.wfd = NULL; p2p_clear_provision_disc_req(&info); if (!req_body) { p2p_connect_failed(dev); return; } iov[iov_len].iov_base = req_body; iov[iov_len].iov_len = req_len; iov_len++; iov[iov_len].iov_base = NULL; /* * Section 3.2.3: "The Provision Discovery Request frame shall be * sent to the P2P Device Address of the P2P Group Owner" */ p2p_peer_frame_xchg(dev->conn_peer, iov, dev->conn_peer->device_addr, 200, 600, 8, false, FRAME_GROUP_CONNECT, p2p_provision_disc_req_done, &p2p_frame_pd_resp, p2p_provision_disc_resp_cb, NULL); } static bool p2p_peer_get_info(struct p2p_peer *peer, uint16_t *wsc_config_methods, struct p2p_capability_attr **capability) { struct wsc_probe_request wsc_info; switch (peer->bss->source_frame) { case SCAN_BSS_PROBE_RESP: if (!peer->bss->p2p_probe_resp_info) return false; if (wsc_config_methods) *wsc_config_methods = peer->bss->p2p_probe_resp_info-> device_info.wsc_config_methods; *capability = &peer->bss->p2p_probe_resp_info->capability; return true; case SCAN_BSS_PROBE_REQ: if (!peer->bss->p2p_probe_req_info || !peer->bss->wsc) return false; if (wsc_parse_probe_request(peer->bss->wsc, peer->bss->wsc_size, &wsc_info) < 0) return false; if (wsc_config_methods) *wsc_config_methods = wsc_info.config_methods; *capability = &peer->bss->p2p_probe_req_info->capability; return true; case SCAN_BSS_BEACON: if (!peer->bss->p2p_beacon_info || !peer->bss->wsc) return false; if (wsc_parse_probe_request(peer->bss->wsc, peer->bss->wsc_size, &wsc_info) < 0) return false; if (wsc_config_methods) *wsc_config_methods = wsc_info.config_methods; *capability = &peer->bss->p2p_beacon_info->capability; break; } return false; } static void p2p_peer_connect(struct p2p_peer *peer, const char *pin) { struct p2p_device *dev = peer->dev; uint16_t wsc_config_methods; struct p2p_capability_attr *capability; struct l_dbus_message *message = peer->wsc.pending_connect; struct l_dbus_message *reply; if (dev->conn_peer) { reply = dbus_error_busy(message); goto send_error; } /* * Step 1, check if the device indicates it supports our WSC method * and check other flags to make sure a connection is possible. */ if (!p2p_peer_get_info(peer, &wsc_config_methods, &capability)) { reply = dbus_error_failed(message); goto send_error; } dev->conn_config_method = pin ? WSC_CONFIGURATION_METHOD_KEYPAD : WSC_CONFIGURATION_METHOD_PUSH_BUTTON; dev->conn_password_id = pin ? (strlen(pin) == 4 || wsc_pin_is_checksum_valid(pin) ? WSC_DEVICE_PASSWORD_ID_DEFAULT : WSC_DEVICE_PASSWORD_ID_USER_SPECIFIED) : WSC_DEVICE_PASSWORD_ID_PUSH_BUTTON; if (!(wsc_config_methods & dev->conn_config_method)) { reply = dbus_error_not_supported(message); goto send_error; } if (capability->device_caps & P2P_DEVICE_CAP_DEVICE_LIMIT) { reply = dbus_error_not_supported(message); goto send_error; } if (capability->group_caps & P2P_GROUP_CAP_GROUP_LIMIT) { reply = dbus_error_not_supported(message); goto send_error; } if (capability->group_caps & P2P_GROUP_CAP_GROUP_FORMATION) { reply = dbus_error_busy(message); goto send_error; } /* * Check WFD role compatibility. At least one of the devices * (device A) must be non-dual-role and device B must implement the * role that A does not. peer->wfd and p2p_own_wfd have both been * validated and we know each device implements at least one role. */ if (p2p_own_wfd && p2p_own_wfd->available && peer->wfd) { if (!( (!peer->wfd->source && p2p_own_wfd->source) || (!peer->wfd->sink && p2p_own_wfd->sink) || (!p2p_own_wfd->source && peer->wfd->source) || (!p2p_own_wfd->sink && peer->wfd->sink))) { l_error("Incompatible WFD roles"); reply = dbus_error_not_supported(message); goto send_error; } } p2p_device_discovery_stop(dev); /* Generate the interface address for our P2P-Client connection */ wiphy_generate_random_address(dev->wiphy, dev->conn_addr); dev->conn_peer = peer; /* No ref counting so just set the pointer */ dev->conn_pin = l_strdup(pin); dev->connections_left--; l_dbus_property_changed(dbus_get_bus(), p2p_device_get_path(dev), IWD_P2P_INTERFACE, "AvailableConnections"); if (p2p_own_wfd && p2p_own_wfd->available && peer->wfd) { dev->conn_own_wfd = l_memdup(p2p_own_wfd, sizeof(*p2p_own_wfd)); /* * From now on we'll appear as SESSION_NOT_AVAILABLE to other * peers but as SESSION_AVAILABLE to conn_peer. */ p2p_own_wfd->available = false; /* If peer is R1, fall back to R1 as well */ dev->conn_own_wfd->r2 = p2p_own_wfd->r2 && peer->wfd->r2; /* * If we're a dual-role device, we have to select our role * for this connection now. */ if (p2p_own_wfd->source && p2p_own_wfd->sink) { dev->conn_own_wfd->source = !peer->wfd->source; dev->conn_own_wfd->sink = !peer->wfd->sink; } } /* * Step 2, if peer is already a GO then send the Provision Discovery * before doing WSC. If it's not then do Provision Discovery * optionally as seems to be required by some implementations, and * start GO negotiation following that. * TODO: Add a AlwaysUsePD config setting. */ if (dev->conn_peer->group) p2p_start_provision_discovery(dev); else p2p_start_go_negotiation(dev); return; send_error: dbus_pending_reply(&peer->wsc.pending_connect, reply); } static void p2p_peer_disconnect_cb(struct netdev *netdev, bool result, void *user_data) { struct p2p_peer *peer = user_data; struct p2p_device *dev = peer->dev; if (!peer->wsc.pending_cancel || !dev->disconnecting) return; dbus_pending_reply(&peer->wsc.pending_cancel, l_dbus_message_new_method_return( peer->wsc.pending_cancel)); /* Independent of the result this will just drop the whole netdev */ p2p_connection_reset(dev); } static void p2p_peer_disconnect(struct p2p_peer *peer) { struct p2p_device *dev = peer->dev; struct l_dbus_message *message = peer->wsc.pending_cancel; struct l_dbus_message *reply; if (dev->conn_peer != peer) { reply = dbus_error_not_connected(message); goto send_reply; } if (dev->disconnecting) { reply = dbus_error_busy(message); goto send_reply; } if (peer->wsc.pending_connect) dbus_pending_reply(&peer->wsc.pending_connect, dbus_error_aborted(peer->wsc.pending_connect)); if (p2p_peer_operational(peer)) { l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_PEER_INTERFACE, "Connected"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_PEER_INTERFACE, "ConnectedInterface"); l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_PEER_INTERFACE, "ConnectedIP"); } dev->disconnecting = true; if (dev->conn_enrollee) { wsc_enrollee_cancel(dev->conn_enrollee, true); return; } if (dev->conn_netdev && !dev->conn_wsc_bss) { /* Note: in theory we need to add the P2P IEs here too */ if (netdev_disconnect(dev->conn_netdev, p2p_peer_disconnect_cb, peer) == 0) return; l_error("netdev_disconnect failed"); } p2p_connection_reset(dev); reply = l_dbus_message_new_method_return(message); send_reply: dbus_pending_reply(&peer->wsc.pending_cancel, reply); } #define SCAN_INTERVAL_MAX 3 #define SCAN_INTERVAL_STEP 1 #define CHANS_PER_SCAN_INITIAL 2 #define CHANS_PER_SCAN 2 static bool p2p_device_scan_start(struct p2p_device *dev); static void p2p_device_roc_start(struct p2p_device *dev); static void p2p_device_roc_timeout(struct l_timeout *timeout, void *user_data) { struct p2p_device *dev = user_data; l_timeout_remove(dev->scan_timeout); if (time(NULL) < dev->next_scan_ts) { /* * dev->scan_timeout destroy function will have been called * by now so it won't overwrite the new timeout set by * p2p_device_roc_start. */ p2p_device_roc_start(dev); return; } p2p_device_scan_start(dev); } static void p2p_device_roc_cancel(struct p2p_device *dev) { struct l_genl_msg *msg; if (!dev->have_roc_cookie) return; l_debug(""); msg = l_genl_msg_new_sized(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 32); l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dev->wdev_id); l_genl_msg_append_attr(msg, NL80211_ATTR_COOKIE, 8, &dev->roc_cookie); l_genl_family_send(dev->nl80211, msg, NULL, NULL, NULL); dev->have_roc_cookie = false; } static void p2p_scan_timeout_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->scan_timeout = NULL; if (dev->nl80211) { /* * Most likely when the timer expires the ROC period * has finished but send a cancel command to make sure, * as well as handle situations like disabling P2P. */ p2p_device_roc_cancel(dev); } } static void p2p_device_roc_cb(struct l_genl_msg *msg, void *user_data) { struct p2p_device *dev = user_data; uint64_t cookie; int error = l_genl_msg_get_error(msg); l_debug("ROC: %s (%i)", strerror(-error), -error); if (error) return; if (nl80211_parse_attrs(msg, NL80211_ATTR_COOKIE, &cookie, NL80211_ATTR_UNSPEC) < 0) return; dev->roc_cookie = cookie; dev->have_roc_cookie = true; /* * Has the command taken so long that P2P has been since disabled * or the timeout otherwise ran out? */ if (!dev->scan_timeout) p2p_device_roc_cancel(dev); } static void p2p_device_roc_start(struct p2p_device *dev) { struct l_genl_msg *msg; uint32_t listen_freq; uint32_t duration; uint32_t cmd_id; l_debug(""); /* * One second granularity is fine here because some randomess * is desired and the intervals don't have strictly defined * limits. */ duration = (dev->next_scan_ts - time(NULL)) * 1000; if (duration < 200) duration = 200; /* * Driver max duration seems to be 5000ms or more for all drivers * except mac80211_hwsim where it is only 1000ms. */ if (duration > wiphy_get_max_roc_duration(dev->wiphy)) duration = wiphy_get_max_roc_duration(dev->wiphy); /* * Some drivers seem to miss fewer frames if we start new requests * often. */ if (duration > 1000) duration = 1000; /* * Be on our listen channel, even if we're still in the 120s * waiting period after a locally-initiated GO Negotiation and * waiting for the peer's GO Negotiation Request. It's not * totally clear that this is how the spec intended this * mechanism to work. On one hand 3.1.4.1 says this: * "A P2P Device may start Group Owner Negotiation by sending a * GO Negotiation Request frame after receiving a Probe Request * frame from the target P2P Device." * and the Appendix D. scenarios also show GO Negotiation happening * on the initiator's listen channel directly after the reception * of the Probe Request from the target. But: * 1. in 3.1.4.1 that is a MAY and doesn't exclude starting GO * Negotiation also on the target's listen channel. * 2. not all devices use the search state so we may never * receive a Probe Request and may end up waiting indefinitely. * 3. the time the peer spends on each channel in the scan state * may be too short for the peer to receive the GO Negotiation * Request after the Probe Request before moving to the next * channel. * 4. since we know the target is going to spend some time on * their own listen channel, using that channel should work in * every case. * * We also have this in 3.1.4.1: * "When the P2P Devices arrive on a common channel and begin Group * Owner Negotiation, they shall stay on that channel until Group * Owner Negotiation completes." * telling us that the whole negotiation should be happening on * one channel seemingly supporting the new GO Negotiation being on * the same channel as the original failed GO Negotiation. * However the rest of the spec makes it clear they are not treated * as a single GO Negotiation: * 3.1.4.2: * "Group Owner Negotiation is a three way frame exchange" * 3.1.4.2.2: * "Group Formation ends on transmission or reception of a GO * Negotiation Response frame with the Status Code set to a value * other than Success." * * 3.1.4.1 implies frame exchanges happen on the target device's * Listen Channel, not our Listen Channel: * "Prior to beginning the Group Formation Procedure the P2P Device * shall arrive on a common channel with the target P2P Device. * The Find Phase in In-band Device Discovery or Out-of-Band Device * Discovery may be used for this purpose. In the former case, the * P2P Device only needs to scan the Listen Channel of the target * P2P Device, as opposed to all of the Social Channels." * * All in all we transmit our Negotiation Requests on the peer's * listen channel since it is bound to spend more time on that * channel than on any other channel and then we listen for a * potential GO Negotiation restart on our listen channel. */ listen_freq = scan_channel_to_freq(dev->listen_channel, SCAN_BAND_2_4_GHZ); msg = l_genl_msg_new_sized(NL80211_CMD_REMAIN_ON_CHANNEL, 64); l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dev->wdev_id); l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &listen_freq); l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &duration); cmd_id = l_genl_family_send(dev->nl80211, msg, p2p_device_roc_cb, dev, NULL); if (!cmd_id) l_genl_msg_unref(msg); /* * Time out after @duration ms independent of whether we were able to * start the ROC command. If we receive the CMD_REMAIN_ON_CHANNEL * event we'll update the timeout to give the ROC command enough time * to finish. On an error or if we time out before the ROC command * even starts, we'll just retry after @duration ms so we don't even * need to handle errors specifically. */ dev->scan_timeout = l_timeout_create_ms(duration, p2p_device_roc_timeout, dev, p2p_scan_timeout_destroy); dev->listen_duration = duration; dev->have_roc_cookie = false; l_debug("started a ROC command on channel %i for %i ms", (int) dev->listen_channel, (int) duration); } static void p2p_peer_update_wfd(struct p2p_peer *peer, struct p2p_wfd_properties *new_wfd) { struct p2p_wfd_properties *orig_wfd = peer->wfd; if (!orig_wfd && !new_wfd) return; peer->wfd = new_wfd ? l_memdup(new_wfd, sizeof(*new_wfd)) : NULL; if (!orig_wfd && new_wfd) { l_dbus_object_add_interface(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, peer); return; } else if (orig_wfd && !new_wfd) { l_free(orig_wfd); l_dbus_object_remove_interface(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE); return; } if (orig_wfd->source != new_wfd->source) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "Source"); if (orig_wfd->sink != new_wfd->sink) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "Sink"); if (orig_wfd->port != new_wfd->port) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "Port"); if (orig_wfd->audio != new_wfd->audio) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "HasAudio"); if (orig_wfd->uibc != new_wfd->uibc) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "HasUIBC"); if (orig_wfd->cp != new_wfd->cp) l_dbus_property_changed(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_WFD_INTERFACE, "HasContentProtection"); l_free(orig_wfd); } static const char *p2p_peer_wsc_get_path(struct wsc_dbus *wsc) { return p2p_peer_get_path(l_container_of(wsc, struct p2p_peer, wsc)); } static void p2p_peer_wsc_connect(struct wsc_dbus *wsc, const char *pin) { p2p_peer_connect(l_container_of(wsc, struct p2p_peer, wsc), pin); } static void p2p_peer_wsc_cancel(struct wsc_dbus *wsc) { p2p_peer_disconnect(l_container_of(wsc, struct p2p_peer, wsc)); } static void p2p_peer_wsc_remove(struct wsc_dbus *wsc) { /* * The WSC removal is triggered in p2p_peer_put so we call * p2p_peer_free directly from there too. */ } static bool p2p_device_peer_add(struct p2p_device *dev, struct p2p_peer *peer) { struct p2p_wfd_properties wfd; if (!strlen(peer->name) || !l_utf8_validate( peer->name, strlen(peer->name), NULL)) { l_debug("Device name doesn't validate for bssid %s", util_address_to_string(peer->bss->addr)); return false; } if (!l_dbus_object_add_interface(dbus_get_bus(), p2p_peer_get_path(peer), IWD_P2P_PEER_INTERFACE, peer)) { l_debug("Unable to add the %s interface to %s", IWD_P2P_PEER_INTERFACE, p2p_peer_get_path(peer)); return false; } if (!l_dbus_object_add_interface(dbus_get_bus(), p2p_peer_get_path(peer), L_DBUS_INTERFACE_PROPERTIES, NULL)) { l_dbus_unregister_object(dbus_get_bus(), p2p_peer_get_path(peer)); l_debug("Unable to add the %s interface to %s", L_DBUS_INTERFACE_PROPERTIES, p2p_peer_get_path(peer)); return false; } peer->wsc.get_path = p2p_peer_wsc_get_path; peer->wsc.connect = p2p_peer_wsc_connect; peer->wsc.cancel = p2p_peer_wsc_cancel; peer->wsc.remove = p2p_peer_wsc_remove; if (!wsc_dbus_add_interface(&peer->wsc)) { l_dbus_unregister_object(dbus_get_bus(), p2p_peer_get_path(peer)); return false; } /* * We need to either only show peers that are available for a WFD * session, or expose the availability information through a property, * which we are not doing right now. */ if (p2p_own_wfd && p2p_extract_wfd_properties(peer->bss->wfd, peer->bss->wfd_size, &wfd) && wfd.available) p2p_peer_update_wfd(peer, &wfd); l_queue_push_tail(dev->peer_list, peer); return true; } struct p2p_peer_move_data { struct l_queue *new_list; struct p2p_peer *conn_peer; uint64_t now; }; static bool p2p_peer_move_recent(void *data, void *user_data) { struct p2p_peer *peer = data; struct p2p_peer_move_data *move_data = user_data; if (move_data->now > peer->bss->time_stamp + 30 * L_USEC_PER_SEC && peer != move_data->conn_peer) return false; /* Old, keep on the list */ /* Recently seen or currently connected, move to the new list */ l_queue_push_tail(move_data->new_list, peer); return true; } static bool p2p_peer_update_existing(struct scan_bss *bss, struct l_queue *old_list, struct l_queue *new_list) { struct p2p_peer *peer; struct p2p_wfd_properties wfd; peer = l_queue_remove_if(old_list, p2p_peer_match, bss->addr); if (!peer) return false; /* * We've seen this peer already, only update the scan_bss object * and WFD state. We can update peer->bss even if * peer == peer->dev->conn_peer because its .bss is not used by * .conn_netdev or .conn_enrollee. .conn_wsc_bss is used for * both connections and it doesn't come from the discovery scan * results. * Some property changes may need to be notified here. */ if (peer->device_addr == peer->bss->addr) peer->device_addr = bss->addr; else peer->device_addr = bss->p2p_probe_resp_info->device_info.device_addr; scan_bss_free(peer->bss); peer->bss = bss; if (p2p_own_wfd && p2p_extract_wfd_properties(bss->wfd, bss->wfd_size, &wfd) && wfd.available) p2p_peer_update_wfd(peer, &wfd); else if (peer->wfd) p2p_peer_update_wfd(peer, NULL); l_queue_push_tail(new_list, peer); return true; } static bool p2p_scan_notify(int err, struct l_queue *bss_list, void *user_data) { struct p2p_device *dev = user_data; const struct l_queue_entry *entry; struct l_queue *old_peer_list = dev->peer_list; struct p2p_peer_move_data move_data; if (err) { l_debug("P2P scan failed: %s (%i)", strerror(-err), -err); goto schedule; } dev->peer_list = l_queue_new(); for (entry = l_queue_get_entries(bss_list); entry; entry = entry->next) { struct scan_bss *bss = entry->data; struct p2p_peer *peer; if (bss->source_frame != SCAN_BSS_PROBE_RESP || !bss->p2p_probe_resp_info) { scan_bss_free(bss); continue; } if (p2p_peer_update_existing(bss, old_peer_list, dev->peer_list)) continue; peer = l_new(struct p2p_peer, 1); peer->dev = dev; peer->bss = bss; peer->name = l_strdup(bss->p2p_probe_resp_info-> device_info.device_name); peer->primary_device_type = bss->p2p_probe_resp_info->device_info.primary_device_type; peer->group = !!(bss->p2p_probe_resp_info->capability.group_caps & P2P_GROUP_CAP_GO); /* * Both P2P Devices and GOs can send Probe Responses so the * frame's source address may not necessarily be the Device * Address, use what's in the obligatory Device Info. */ peer->device_addr = bss->p2p_probe_resp_info->device_info.device_addr; if (!p2p_device_peer_add(dev, peer)) p2p_peer_free(peer); } /* * old_peer_list now only contains peers not present in the new * results. Move any peers seen in the last 30 secs to the new * dev->peer_list and unref only the remaining peers. */ move_data.new_list = dev->peer_list; move_data.conn_peer = dev->conn_peer; move_data.now = l_time_now(); l_queue_foreach_remove(old_peer_list, p2p_peer_move_recent, &move_data); l_queue_destroy(old_peer_list, p2p_peer_put); l_queue_destroy(bss_list, NULL); schedule: /* * Calculate interval between now and when we want the next active * scan to start. Keep issuing Remain-on-Channel commands of * maximum duration until it's time to start the new scan. * The listen periods are actually like a passive scan except that * instead of listening for Beacons only, we also look at Probe * Requests and Probe Responses because they, too, carry P2P IEs * with all the information we need about peer devices. Beacons * also do, in case of GOs, but we will already get the same * information from the Probe Responses and (even if we can * receive the beacons in userspace in the first place) we don't * want to handle so many frames. * * According to 3.1.2.1.1 we shall be available in listen state * during Find for at least 500ms continuously at least once in * every 5s. According to 3.1.2.1.3, the Listen State lasts for * between 1 and 3 one-hundred TU Intervals. * * The Search State duration is implementation dependent. */ if (dev->scan_interval < SCAN_INTERVAL_MAX) dev->scan_interval += SCAN_INTERVAL_STEP; dev->next_scan_ts = time(NULL) + dev->scan_interval; p2p_device_roc_start(dev); return true; } static bool p2p_device_scan_start(struct p2p_device *dev) { struct scan_parameters params = {}; uint8_t buf[256]; unsigned int i; wiphy_get_reg_domain_country(dev->wiphy, (char *) dev->listen_country); dev->listen_country[2] = 4; /* Table E-4 */ dev->listen_oper_class = 81; /* 2.4 band */ params.extra_ie = p2p_build_scan_ies(dev, buf, sizeof(buf), ¶ms.extra_ie_size); L_WARN_ON(!params.extra_ie); params.flush = true; /* P2P Wildcard SSID because we don't need legacy networks to reply */ params.ssid = "DIRECT-"; /* * Must send probe requests at 6Mb/s, OFDM only. The no-CCK rates * flag forces the drivers to do exactly this for 2.4GHz frames. * * "- P2P Devices shall not use 11b rates (1, 2, 5.5, 11 Mbps) for data * and management frames except: * * Probe Request frames sent to both P2P Devices and non-P2P * Devices. * - P2P Devices shall not respond to Probe Request frames that indicate * support for 11b rates only. * Note 1 - This means that the P2P Group Owner transmits Beacon frames * using OFDM. * Note 2 - This means that the P2P Group Owner transmits Probe Response * frames using OFDM, including frames sent in response to Probe * Requests received at 11b rates from non 11b-only devices. * Note 3 - P2P Devices shall not include 11b rates in the list of * supported rates in Probe Request frame intended only for P2P Devices. * 11b rates may be included in the list of supported rates in Probe * Request frames intended for both P2P Devices and non-P2P Devices." */ params.no_cck_rates = true; params.freqs = scan_freq_set_new(); for (i = 0; i < L_ARRAY_SIZE(channels_social); i++) { int chan = channels_social[i]; uint32_t freq = scan_channel_to_freq(chan, SCAN_BAND_2_4_GHZ); scan_freq_set_add(params.freqs, freq); } /* * Instead of doing a single Scan Phase at the beginning of the Device * Discovery and then strictly a Find Phase loop as defined in the * spec, mix both to keep watching for P2P groups on the non-social * channels, slowly going through a few channels at a time in each * Scan State iteration. Scan dev->chans_per_scan channels each time, * use dev->scan_chan_idx to keep track of which channels we've * visited recently. */ for (i = 0; i < dev->chans_per_scan; i++) { int idx = dev->scan_chan_idx++; int chan = channels_scan_2_4_other[idx]; uint32_t freq = scan_channel_to_freq(chan, SCAN_BAND_2_4_GHZ); if (dev->scan_chan_idx >= L_ARRAY_SIZE(channels_scan_2_4_other)) { dev->scan_chan_idx = 0; /* * Do fewer channels per scan after we've initially * gone through the 2.4 band. */ dev->chans_per_scan = CHANS_PER_SCAN; } scan_freq_set_add(params.freqs, freq); } dev->scan_id = scan_active_full(dev->wdev_id, ¶ms, NULL, p2p_scan_notify, dev, p2p_scan_destroy); scan_freq_set_free(params.freqs); return dev->scan_id != 0; } static void p2p_probe_resp_done(int error, void *user_data) { if (error) l_error("Sending the Probe Response failed: %s (%i)", strerror(-error), -error); } static void p2p_device_send_probe_resp(struct p2p_device *dev, const uint8_t *dest_addr) { uint8_t resp_buf[64] __attribute__ ((aligned)); size_t resp_len = 0; struct p2p_probe_resp resp_info = {}; uint8_t *p2p_ie; size_t p2p_ie_size; struct wsc_probe_response wsc_info = {}; uint8_t *wsc_data; size_t wsc_data_size; uint8_t *wsc_ie; size_t wsc_ie_size; uint8_t wfd_ie[32]; struct iovec iov[16]; int iov_len = 0; /* TODO: extract some of these from wiphy features */ uint16_t capability = IE_BSS_CAP_PRIVACY | IE_BSS_CAP_SHORT_PREAMBLE; struct mmpdu_header *header; uint32_t freq; /* Header */ memset(resp_buf, 0, sizeof(resp_buf)); header = (void *) resp_buf; header->fc.protocol_version = 0; header->fc.type = MPDU_TYPE_MANAGEMENT; header->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE; memcpy(header->address_1, dest_addr, 6); /* DA */ memcpy(header->address_2, dev->addr, 6); /* SA */ memcpy(header->address_3, dev->addr, 6); /* BSSID */ resp_len = (const uint8_t *) mmpdu_body(header) - resp_buf; resp_len += 8; /* Timestamp */ resp_buf[resp_len++] = 0x64; /* Beacon Interval: 100 TUs */ resp_buf[resp_len++] = 0x00; resp_buf[resp_len++] = capability >> 0; resp_buf[resp_len++] = capability >> 8; resp_buf[resp_len++] = IE_TYPE_SSID; resp_buf[resp_len++] = 7; resp_buf[resp_len++] = 'D'; resp_buf[resp_len++] = 'I'; resp_buf[resp_len++] = 'R'; resp_buf[resp_len++] = 'E'; resp_buf[resp_len++] = 'C'; resp_buf[resp_len++] = 'T'; resp_buf[resp_len++] = '-'; resp_buf[resp_len++] = IE_TYPE_SUPPORTED_RATES; resp_buf[resp_len++] = 8; resp_buf[resp_len++] = 0x8c; resp_buf[resp_len++] = 0x12; resp_buf[resp_len++] = 0x18; resp_buf[resp_len++] = 0x24; resp_buf[resp_len++] = 0x30; resp_buf[resp_len++] = 0x48; resp_buf[resp_len++] = 0x60; resp_buf[resp_len++] = 0x6c; resp_info.capability = dev->capability; resp_info.device_info = dev->device_info; p2p_ie = p2p_build_probe_resp(&resp_info, &p2p_ie_size); if (!p2p_ie) { l_error("Can't build our Probe Response P2P IE"); return; } wsc_info.state = WSC_STATE_CONFIGURED; wsc_info.response_type = WSC_RESPONSE_TYPE_ENROLLEE_OPEN_8021X; wsc_info.uuid_e[15] = 0x01; wsc_info.serial_number[0] = '0'; wsc_info.primary_device_type = dev->device_info.primary_device_type; l_strlcpy(wsc_info.device_name, dev->device_info.device_name, sizeof(wsc_info.device_name)); wsc_info.config_methods = dev->device_info.wsc_config_methods; wsc_info.rf_bands = 0x01; /* 2.4GHz */ wsc_info.version2 = true; wsc_data = wsc_build_probe_response(&wsc_info, &wsc_data_size); if (!wsc_data) { l_free(p2p_ie); l_error("Can't build our Probe Response WSC payload"); return; } wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_size, &wsc_ie_size); l_free(wsc_data); if (!wsc_ie) { l_free(p2p_ie); l_error("Can't build our Probe Response WSC IE"); return; } iov[iov_len].iov_base = resp_buf; iov[iov_len].iov_len = resp_len; iov_len++; iov[iov_len].iov_base = p2p_ie; iov[iov_len].iov_len = p2p_ie_size; iov_len++; iov[iov_len].iov_base = wsc_ie; iov[iov_len].iov_len = wsc_ie_size; iov_len++; if (p2p_own_wfd) { iov[iov_len].iov_base = wfd_ie; iov[iov_len].iov_len = p2p_build_wfd_ie(p2p_own_wfd, wfd_ie); iov_len++; } iov[iov_len].iov_base = NULL; freq = scan_channel_to_freq(dev->listen_channel, SCAN_BAND_2_4_GHZ); frame_xchg_start(dev->wdev_id, iov, freq, 0, 0, false, 0, p2p_probe_resp_done, dev, NULL); l_debug("Probe Response tx queued"); l_free(p2p_ie); l_free(wsc_ie); } static void p2p_device_probe_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, void *user_data) { struct p2p_device *dev = user_data; struct p2p_peer *peer; struct p2p_probe_req p2p_info; struct wsc_probe_request wsc_info; int r; uint8_t *wsc_payload; ssize_t wsc_len; struct scan_bss *bss; struct p2p_channel_attr *channel; enum scan_band band; uint32_t frequency; bool from_conn_peer; l_debug(""); if (!dev->scan_timeout && !dev->scan_id) return; from_conn_peer = dev->go_neg_req_timeout && dev->conn_peer && !memcmp(mpdu->address_2, dev->conn_peer->bss->addr, 6); wsc_payload = ie_tlv_extract_wsc_payload(body, body_len, &wsc_len); if (!wsc_payload) /* Not a P2P Probe Req, ignore */ return; r = wsc_parse_probe_request(wsc_payload, wsc_len, &wsc_info); l_free(wsc_payload); if (r < 0) { l_error("Probe Request WSC IE parse error %s (%i)", strerror(-r), -r); /* * Ignore requests with erroneous WSC IEs except if they * come from the peer we're currently connecting to as a * workaround for implementations sending invalid Probe * Requests. */ if (!from_conn_peer) return; } r = p2p_parse_probe_req(body, body_len, &p2p_info); if (r < 0) { if (r == -ENOENT) /* Not a P2P Probe Req, ignore */ return; l_error("Probe Request P2P IE parse error %s (%i)", strerror(-r), -r); return; } /* * We don't currently have a use case for replying to Probe Requests * except when waiting for a GO Negotiation Request from our target * peer. Some of those peers (seemingly running ancient and/or * hw-manufacturer-provided versions of wpa_s) will only send us GO * Negotiation Requests each time they receive our Probe Response * frame, even if that frame's body is unparsable. */ if (from_conn_peer) { /* * TODO: use ap.c code to check if we match the SSID, BSSID, * DSSS Channel etc. in the Probe Request, and to build the * Response body. */ p2p_device_send_probe_resp(dev, mpdu->address_2); goto p2p_free; } /* * The peer's listen frequency may be different from ours. * The Listen Channel attribute is optional but if neither * it nor the Operating Channel are set then we have no way * to contact that peer. Ignore such peers. */ if (p2p_info.listen_channel.country[0]) channel = &p2p_info.listen_channel; else if (p2p_info.operating_channel.country[0]) channel = &p2p_info.operating_channel; else goto p2p_free; band = scan_oper_class_to_band((const uint8_t *) channel->country, channel->oper_class); frequency = scan_channel_to_freq(channel->channel_num, band); if (!frequency) goto p2p_free; bss = scan_bss_new_from_probe_req(mpdu, body, body_len, frequency, rssi); if (!bss) goto p2p_free; bss->time_stamp = l_time_now(); if (p2p_peer_update_existing(bss, dev->peer_list, dev->peer_list)) goto p2p_free; peer = l_new(struct p2p_peer, 1); peer->dev = dev; peer->bss = bss; peer->name = l_strdup(wsc_info.device_name); peer->primary_device_type = wsc_info.primary_device_type; peer->group = !!(p2p_info.capability.group_caps & P2P_GROUP_CAP_GO); /* * The Device Info attribute is present conditionally so we can't get * the Device Address from there. In theory only P2P Devices send * out Probe Requests, not P2P GOs, so we assume the source address * is the Device Address. */ peer->device_addr = bss->addr; if (!p2p_device_peer_add(dev, peer)) p2p_peer_free(peer); /* * TODO: check SSID/BSSID are wildcard values if present and * reply with a Probe Response -- not useful in our current usage * scenarios but required by the spec. */ p2p_free: p2p_clear_probe_req(&p2p_info); } static void p2p_device_discovery_start(struct p2p_device *dev) { if (dev->scan_timeout || dev->scan_id) return; dev->scan_interval = 1; dev->chans_per_scan = CHANS_PER_SCAN_INITIAL; dev->scan_chan_idx = 0; /* * 3.1.2.1.1: "The Listen Channel shall be chosen at the beginning of * the In-band Device Discovery" * * But keep the old channel if we're still waiting for the peer to * restart the GO Negotiation because there may not be enough time * for the peer to update our Listen Channel value before the user * accepts the connection. In that case the GO Negotiation Request * would be sent on the old channel. */ if (!(dev->listen_channel && dev->conn_peer)) dev->listen_channel = channels_social[l_getrandom_uint32() % L_ARRAY_SIZE(channels_social)]; frame_watch_add(dev->wdev_id, FRAME_GROUP_LISTEN, 0x0040, (uint8_t *) "", 0, p2p_device_probe_cb, dev, NULL); frame_watch_add(dev->wdev_id, FRAME_GROUP_LISTEN, 0x00d0, p2p_frame_go_neg_req.data, p2p_frame_go_neg_req.len, p2p_device_go_negotiation_req_cb, dev, NULL); p2p_device_scan_start(dev); } static void p2p_device_discovery_stop(struct p2p_device *dev) { dev->scan_interval = 0; if (dev->scan_id) scan_cancel(dev->wdev_id, dev->scan_id); if (dev->scan_timeout) l_timeout_remove(dev->scan_timeout); p2p_device_roc_cancel(dev); frame_watch_group_remove(dev->wdev_id, FRAME_GROUP_LISTEN); } static void p2p_device_enable_cb(struct l_genl_msg *msg, void *user_data) { struct p2p_device *dev = user_data; int error = l_genl_msg_get_error(msg); struct l_dbus_message *message = dev->pending_message; l_debug("START/STOP_P2P_DEVICE: %s (%i)", strerror(-error), -error); if (error) goto done; dev->enabled = !dev->enabled; if (dev->enabled && !l_queue_isempty(dev->discovery_users)) p2p_device_discovery_start(dev); done: dev->pending_complete(dbus_get_bus(), message, error ? dbus_error_failed(message) : NULL); dev->pending_message = NULL; dev->pending_complete = NULL; if (!error) l_dbus_property_changed(dbus_get_bus(), p2p_device_get_path(dev), IWD_P2P_INTERFACE, "Enabled"); } static void p2p_device_enable_destroy(void *user_data) { struct p2p_device *dev = user_data; dev->start_stop_cmd_id = 0; } static bool p2p_peer_remove_disconnected(void *peer, void *conn_peer) { if (peer == conn_peer) return false; p2p_peer_put(peer); return true; } static void p2p_device_start_stop(struct p2p_device *dev, l_dbus_property_complete_cb_t complete, struct l_dbus_message *message) { struct l_genl_msg *cmd; if (dev->enabled) p2p_device_discovery_stop(dev); if (!dev->enabled) cmd = l_genl_msg_new_sized(NL80211_CMD_START_P2P_DEVICE, 16); else cmd = l_genl_msg_new_sized(NL80211_CMD_STOP_P2P_DEVICE, 16); l_genl_msg_append_attr(cmd, NL80211_ATTR_WDEV, 8, &dev->wdev_id); dev->start_stop_cmd_id = l_genl_family_send(dev->nl80211, cmd, p2p_device_enable_cb, dev, p2p_device_enable_destroy); if (!dev->start_stop_cmd_id) { l_genl_msg_unref(cmd); complete(dbus_get_bus(), message, dbus_error_failed(message)); return; } dev->pending_message = message; dev->pending_complete = complete; if (dev->enabled) { /* * Stopping the P2P device, drop all peers as we can't start * new connections from now on. Check if we have a connection * being set up without a .conn_netdev and without * .conn_wsc_bss -- this will mean the connection is still in * the PD or GO Negotiation phase or inside the scan. Those * phases happen on the device interface so the connection * gets immediately aborted. */ if (dev->conn_peer && !dev->conn_netdev && !dev->conn_wsc_bss) p2p_connect_failed(dev); if (!dev->conn_peer) { l_queue_destroy(dev->peer_list, p2p_peer_put); dev->peer_list = NULL; } else /* * If the connection already depends on its own * netdev only, we can let it continue until the user * decides to disconnect. */ l_queue_foreach_remove(dev->peer_list, p2p_peer_remove_disconnected, dev->conn_peer); } } static void p2p_mlme_notify(struct l_genl_msg *msg, void *user_data) { struct p2p_device *dev = user_data; uint64_t wdev_id; uint64_t cookie; if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id, NL80211_ATTR_COOKIE, &cookie, NL80211_ATTR_UNSPEC) < 0 || wdev_id != dev->wdev_id) return; switch (l_genl_msg_get_command(msg)) { case NL80211_CMD_REMAIN_ON_CHANNEL: if (!dev->have_roc_cookie || cookie != dev->roc_cookie) break; if (!dev->scan_timeout) break; /* * The Listen phase is actually starting here, update the * timeout so we know more or less when it ends. */ l_debug("ROC started"); l_timeout_modify_ms(dev->scan_timeout, dev->listen_duration); break; case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: /* TODO */ break; } } #define P2P_SUPPORTED_METHODS ( \ WSC_CONFIGURATION_METHOD_LABEL | \ WSC_CONFIGURATION_METHOD_KEYPAD | \ WSC_CONFIGURATION_METHOD_VIRTUAL_PUSH_BUTTON | \ WSC_CONFIGURATION_METHOD_PHYSICAL_PUSH_BUTTON | \ WSC_CONFIGURATION_METHOD_P2P | \ WSC_CONFIGURATION_METHOD_VIRTUAL_DISPLAY_PIN | \ WSC_CONFIGURATION_METHOD_PHYSICAL_DISPLAY_PIN) struct p2p_device *p2p_device_update_from_genl(struct l_genl_msg *msg, bool create) { const uint8_t *ifaddr; uint32_t iftype; uint64_t wdev_id; uint32_t wiphy_id; struct wiphy *wiphy; struct p2p_device *dev; char hostname[HOST_NAME_MAX + 1]; char *str; unsigned int uint_val; if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id, NL80211_ATTR_WIPHY, &wiphy_id, NL80211_ATTR_IFTYPE, &iftype, NL80211_ATTR_MAC, &ifaddr, NL80211_ATTR_UNSPEC) < 0 || L_WARN_ON(!(wiphy = wiphy_find(wiphy_id))) || L_WARN_ON(iftype != NL80211_IFTYPE_P2P_DEVICE)) { l_warn("Unable to parse interface information"); return NULL; } if (create) { if (p2p_device_find(wdev_id)) { l_debug("Duplicate p2p device %" PRIx64, wdev_id); return NULL; } } else { dev = p2p_device_find(wdev_id); if (!dev) return NULL; memcpy(dev->addr, ifaddr, ETH_ALEN); return NULL; } dev = l_new(struct p2p_device, 1); dev->wdev_id = wdev_id; memcpy(dev->addr, ifaddr, ETH_ALEN); dev->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME); dev->wiphy = wiphy; gethostname(hostname, sizeof(hostname)); dev->connections_left = 1; /* TODO: allow masking capability bits through a setting? */ dev->capability.device_caps = P2P_DEVICE_CAP_CONCURRENT_OP; dev->capability.group_caps = 0; memcpy(dev->device_info.device_addr, dev->addr, 6); dev->device_info.wsc_config_methods = WSC_CONFIGURATION_METHOD_P2P | WSC_CONFIGURATION_METHOD_PUSH_BUTTON; dev->device_info.primary_device_type.category = 1; /* Computer */ memcpy(dev->device_info.primary_device_type.oui, microsoft_oui, 3); dev->device_info.primary_device_type.oui_type = 0x04; dev->device_info.primary_device_type.subcategory = 1; /* PC */ l_strlcpy(dev->device_info.device_name, hostname, sizeof(dev->device_info.device_name)); if (l_settings_get_uint(iwd_get_config(), "P2P", "ConfigurationMethods", &uint_val)) { if (!(uint_val & P2P_SUPPORTED_METHODS)) l_error("[P2P].ConfigurationMethods must contain " "at least one supported method"); else if (uint_val & ~0xffff) l_error("[P2P].ConfigurationMethods should be a " "16-bit integer"); else dev->device_info.wsc_config_methods = uint_val & P2P_SUPPORTED_METHODS; } str = l_settings_get_string(iwd_get_config(), "P2P", "DeviceType"); /* * Standard WSC subcategories are unique and more specific than * categories so there's no point for the user to specify the * category if they choose to use the string format. * * As an example our default value (Computer - PC) can be * encoded as either of: * * DeviceType=pc * DeviceType=0x00010050f2040001 */ if (str && !wsc_device_type_from_subcategory_str( &dev->device_info.primary_device_type, str)) { unsigned long long u; char *endp; u = strtoull(str, &endp, 0); /* * Accept any custom category, OUI and subcategory values but * require non-zero category as a sanity check. */ if (*endp != '\0' || (u & 0xffff000000000000ll) == 0) l_error("[P2P].DeviceType must be a subcategory string " "or a 64-bit integer encoding the full Primary" " Device Type attribute: " "|||"); else { dev->device_info.primary_device_type.category = u >> 48; dev->device_info.primary_device_type.oui[0] = u >> 40; dev->device_info.primary_device_type.oui[1] = u >> 32; dev->device_info.primary_device_type.oui[2] = u >> 24; dev->device_info.primary_device_type.oui_type = u >> 16; dev->device_info.primary_device_type.subcategory = u; } } l_queue_push_tail(p2p_device_list, dev); l_debug("Created P2P device %" PRIx64, dev->wdev_id); scan_wdev_add(dev->wdev_id); if (!l_genl_family_register(dev->nl80211, NL80211_MULTICAST_GROUP_MLME, p2p_mlme_notify, dev, NULL)) l_error("Registering for MLME notifications failed"); if (!l_dbus_object_add_interface(dbus_get_bus(), p2p_device_get_path(dev), IWD_P2P_INTERFACE, dev)) l_info("Unable to add the %s interface to %s", IWD_P2P_INTERFACE, p2p_device_get_path(dev)); return dev; } static void p2p_device_free(void *user_data) { struct p2p_device *dev = user_data; if (dev->pending_message) { struct l_dbus_message *reply = dbus_error_aborted(dev->pending_message); dev->pending_complete(dbus_get_bus(), dev->pending_message, reply); dev->pending_message = NULL; dev->pending_complete = NULL; } p2p_device_discovery_stop(dev); p2p_connection_reset(dev); l_dbus_unregister_object(dbus_get_bus(), p2p_device_get_path(dev)); l_queue_destroy(dev->peer_list, p2p_peer_put); l_queue_destroy(dev->discovery_users, p2p_discovery_user_free); l_genl_family_free(dev->nl80211); /* Cancels dev->start_stop_cmd_id */ scan_wdev_remove(dev->wdev_id); l_free(dev); } bool p2p_device_destroy(struct p2p_device *dev) { if (!l_queue_remove(p2p_device_list, dev)) return false; p2p_device_free(dev); return true; } static bool p2p_device_get_enabled(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_device *dev = user_data; bool enabled = dev->enabled; l_dbus_message_builder_append_basic(builder, 'b', &enabled); return true; } static struct l_dbus_message *p2p_device_set_enabled(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_iter *new_value, l_dbus_property_complete_cb_t complete, void *user_data) { struct p2p_device *dev = user_data; bool new_enabled; if (!l_dbus_message_iter_get_variant(new_value, "b", &new_enabled)) return dbus_error_invalid_args(message); if (dev->start_stop_cmd_id || dev->pending_message) return dbus_error_busy(message); if (dev->enabled == new_enabled) { complete(dbus, message, NULL); return NULL; } p2p_device_start_stop(dev, complete, message); return NULL; } static bool p2p_device_get_name(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_device *dev = user_data; l_dbus_message_builder_append_basic(builder, 's', dev->device_info.device_name); return true; } static struct l_dbus_message *p2p_device_set_name(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_iter *new_value, l_dbus_property_complete_cb_t complete, void *user_data) { struct p2p_device *dev = user_data; const char *new_name; bool changed = false; if (!l_dbus_message_iter_get_variant(new_value, "s", &new_name)) return dbus_error_invalid_args(message); if (!strcmp(new_name, dev->device_info.device_name)) goto done; if (strlen(new_name) > sizeof(dev->device_info.device_name) - 1) return dbus_error_invalid_args(message); changed = true; l_strlcpy(dev->device_info.device_name, new_name, sizeof(dev->device_info.device_name)); done: complete(dbus, message, NULL); if (changed) l_dbus_property_changed(dbus, p2p_device_get_path(dev), IWD_P2P_INTERFACE, "Name"); return NULL; } static bool p2p_device_get_avail_conns(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_device *dev = user_data; uint16_t avail_conns = dev->connections_left; l_dbus_message_builder_append_basic(builder, 'q', &avail_conns); return true; } static struct l_dbus_message *p2p_device_get_peers(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct p2p_device *dev = user_data; struct l_dbus_message *reply; struct l_dbus_message_builder *builder; const struct l_queue_entry *entry; if (!l_dbus_message_get_arguments(message, "")) return dbus_error_invalid_args(message); reply = l_dbus_message_new_method_return(message); builder = l_dbus_message_builder_new(reply); l_dbus_message_builder_enter_array(builder, "(on)"); for (entry = l_queue_get_entries(dev->peer_list); entry; entry = entry->next) { const struct p2p_peer *peer = entry->data; int16_t signal_strength = peer->bss->signal_strength; l_dbus_message_builder_enter_struct(builder, "on"); l_dbus_message_builder_append_basic(builder, 'o', p2p_peer_get_path(peer)); l_dbus_message_builder_append_basic(builder, 'n', &signal_strength); l_dbus_message_builder_leave_struct(builder); } l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); return reply; } static void p2p_device_discovery_disconnect(struct l_dbus *dbus, void *user_data) { struct p2p_discovery_user *user = user_data; struct p2p_device *dev = user->dev; l_debug("P2P Device Discovery user %s disconnected", user->client); l_queue_remove(dev->discovery_users, user); p2p_discovery_user_free(user); if (l_queue_isempty(dev->discovery_users)) p2p_device_discovery_stop(dev); } static void p2p_device_discovery_destroy(void *user_data) { struct p2p_discovery_user *user = user_data; user->disconnect_watch = 0; } static struct l_dbus_message *p2p_device_request_discovery(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct p2p_device *dev = user_data; struct p2p_discovery_user *user; bool first_user = l_queue_isempty(dev->discovery_users); if (!l_dbus_message_get_arguments(message, "")) return dbus_error_invalid_args(message); if (l_queue_find(dev->discovery_users, p2p_discovery_user_match, l_dbus_message_get_sender(message))) return dbus_error_already_exists(message); if (!dev->discovery_users) dev->discovery_users = l_queue_new(); user = l_new(struct p2p_discovery_user, 1); user->client = l_strdup(l_dbus_message_get_sender(message)); user->dev = dev; user->disconnect_watch = l_dbus_add_disconnect_watch(dbus, user->client, p2p_device_discovery_disconnect, user, p2p_device_discovery_destroy); l_queue_push_tail(dev->discovery_users, user); if (first_user && !dev->conn_peer && dev->enabled) p2p_device_discovery_start(dev); return l_dbus_message_new_method_return(message); } static struct l_dbus_message *p2p_device_release_discovery(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct p2p_device *dev = user_data; struct p2p_discovery_user *user; if (!l_dbus_message_get_arguments(message, "")) return dbus_error_invalid_args(message); user = l_queue_remove_if(dev->discovery_users, p2p_discovery_user_match, l_dbus_message_get_sender(message)); if (!user) return dbus_error_not_found(message); p2p_discovery_user_free(user); /* * If dev->conn_peer is non-NULL, we may be scanning as a way to * listen for a GO Negotiation Request from the target peer. In * that case we don't stop the device discovery when the list * becomes empty. */ if (l_queue_isempty(dev->discovery_users) && !dev->conn_peer) p2p_device_discovery_stop(dev); return l_dbus_message_new_method_return(message); } static void p2p_interface_setup(struct l_dbus_interface *interface) { l_dbus_interface_property(interface, "Enabled", 0, "b", p2p_device_get_enabled, p2p_device_set_enabled); l_dbus_interface_property(interface, "Name", 0, "s", p2p_device_get_name, p2p_device_set_name); l_dbus_interface_property(interface, "AvailableConnections", 0, "q", p2p_device_get_avail_conns, NULL); l_dbus_interface_method(interface, "GetPeers", 0, p2p_device_get_peers, "a(on)", "", "peers"); l_dbus_interface_method(interface, "RequestDiscovery", 0, p2p_device_request_discovery, "", ""); l_dbus_interface_method(interface, "ReleaseDiscovery", 0, p2p_device_release_discovery, "", ""); } static bool p2p_peer_get_name(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 's', peer->name); return true; } static bool p2p_peer_get_device(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 'o', p2p_device_get_path(peer->dev)); return true; } static bool p2p_peer_get_category(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; const char *category; if (!wsc_device_type_to_dbus_str(&peer->primary_device_type, &category, NULL) || !category) category = "unknown-device"; l_dbus_message_builder_append_basic(builder, 's', category); return true; } static bool p2p_peer_get_subcategory(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; const char *subcategory; /* * Should we generate subcategory strings with the numerical * values for the subcategories we don't know, such as * "Vendor-specific 00:11:22:33 44" ? */ if (!wsc_device_type_to_dbus_str(&peer->primary_device_type, NULL, &subcategory) || !subcategory) return false; l_dbus_message_builder_append_basic(builder, 's', subcategory); return true; } static bool p2p_peer_get_connected(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; bool connected = p2p_peer_operational(peer) && peer->dev->conn_peer == peer; l_dbus_message_builder_append_basic(builder, 'b', &connected); return true; } static bool p2p_peer_get_connected_if(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; const char *ifname = netdev_get_name(peer->dev->conn_netdev); if (!p2p_peer_operational(peer)) return false; l_dbus_message_builder_append_basic(builder, 's', ifname); return true; } static bool p2p_peer_get_connected_ip(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; char *server_ip; if (!p2p_peer_operational(peer)) return false; server_ip = netconfig_get_dhcp_server_ipv4(peer->dev->conn_netconfig); l_dbus_message_builder_append_basic(builder, 's', server_ip); l_free(server_ip); return true; } static struct l_dbus_message *p2p_peer_dbus_disconnect(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { struct p2p_peer *peer = user_data; if (!l_dbus_message_get_arguments(message, "")) return dbus_error_invalid_args(message); /* * Save the message for both WSC.Cancel and Peer.Disconnect the * same way. */ peer->wsc.pending_cancel = l_dbus_message_ref(message); p2p_peer_disconnect(peer); return NULL; } static void p2p_peer_interface_setup(struct l_dbus_interface *interface) { l_dbus_interface_property(interface, "Name", 0, "s", p2p_peer_get_name, NULL); l_dbus_interface_property(interface, "Device", 0, "o", p2p_peer_get_device, NULL); l_dbus_interface_property(interface, "DeviceCategory", 0, "s", p2p_peer_get_category, NULL); l_dbus_interface_property(interface, "DeviceSubcategory", 0, "s", p2p_peer_get_subcategory, NULL); l_dbus_interface_property(interface, "Connected", 0, "b", p2p_peer_get_connected, NULL); l_dbus_interface_property(interface, "ConnectedInterface", 0, "s", p2p_peer_get_connected_if, NULL); l_dbus_interface_property(interface, "ConnectedIP", 0, "s", p2p_peer_get_connected_ip, NULL); l_dbus_interface_method(interface, "Disconnect", 0, p2p_peer_dbus_disconnect, "", ""); } static bool p2p_peer_get_wfd_source(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 'b', &peer->wfd->source); return true; } static bool p2p_peer_get_wfd_sink(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 'b', &peer->wfd->sink); return true; } static bool p2p_peer_get_wfd_port(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; if (!peer->wfd->source) return false; l_dbus_message_builder_append_basic(builder, 'q', &peer->wfd->port); return true; } static bool p2p_peer_get_wfd_has_audio(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; if (!peer->wfd->sink) return false; l_dbus_message_builder_append_basic(builder, 'b', &peer->wfd->audio); return true; } static bool p2p_peer_get_wfd_has_uibc(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 'b', &peer->wfd->uibc); return true; } static bool p2p_peer_get_wfd_has_cp(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, void *user_data) { struct p2p_peer *peer = user_data; l_dbus_message_builder_append_basic(builder, 'b', &peer->wfd->cp); return true; } static void p2p_wfd_interface_setup(struct l_dbus_interface *interface) { l_dbus_interface_property(interface, "Source", 0, "b", p2p_peer_get_wfd_source, NULL); l_dbus_interface_property(interface, "Sink", 0, "b", p2p_peer_get_wfd_sink, NULL); l_dbus_interface_property(interface, "Port", 0, "q", p2p_peer_get_wfd_port, NULL); l_dbus_interface_property(interface, "HasAudio", 0, "b", p2p_peer_get_wfd_has_audio, NULL); l_dbus_interface_property(interface, "HasUIBC", 0, "b", p2p_peer_get_wfd_has_uibc, NULL); l_dbus_interface_property(interface, "HasContentProtection", 0, "b", p2p_peer_get_wfd_has_cp, NULL); } static void p2p_own_wfd_free(void) { const struct l_queue_entry *entry; l_free(p2p_own_wfd); p2p_own_wfd = NULL; for (entry = l_queue_get_entries(p2p_device_list); entry; entry = entry->next) { struct p2p_device *dev = entry->data; if (dev->conn_own_wfd) p2p_connect_failed(dev); } } static void p2p_wfd_disconnect_watch_cb(struct l_dbus *dbus, void *user_data) { l_debug("P2P WFD service disconnected"); if (L_WARN_ON(unlikely(!p2p_own_wfd))) return; p2p_own_wfd_free(); } static void p2p_wfd_disconnect_watch_destroy(void *user_data) { p2p_wfd_disconnect_watch = 0; } static struct l_dbus_message *p2p_wfd_register(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { const char *prop_name; struct l_dbus_message_iter prop_iter; struct l_dbus_message_iter prop_variant; struct p2p_wfd_properties props = {}; bool have_source = false; bool have_sink = false; bool have_port = false; bool have_has_audio = false; bool have_has_uibc = false; bool have_has_cp = false; if (!l_dbus_message_get_arguments(message, "a{sv}", &prop_iter)) return dbus_error_invalid_args(message); while (l_dbus_message_iter_next_entry(&prop_iter, &prop_name, &prop_variant)) { if (!strcmp(prop_name, "Source")) { if (have_source) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "b", &props.source)) return dbus_error_invalid_args(message); have_source = true; } else if (!strcmp(prop_name, "Sink")) { if (have_sink) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "b", &props.sink)) return dbus_error_invalid_args(message); have_sink = true; } else if (!strcmp(prop_name, "Port")) { if (have_port) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "q", &props.port)) return dbus_error_invalid_args(message); have_port = true; } else if (!strcmp(prop_name, "HasAudio")) { if (have_has_audio) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "b", &props.audio)) return dbus_error_invalid_args(message); have_has_audio = true; } else if (!strcmp(prop_name, "HasUIBC")) { if (have_has_uibc) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "b", &props.uibc)) return dbus_error_invalid_args(message); have_has_uibc = true; } else if (!strcmp(prop_name, "HasContentProtection")) { if (have_has_cp) return dbus_error_invalid_args(message); if (!l_dbus_message_iter_get_variant(&prop_variant, "b", &props.cp)) return dbus_error_invalid_args(message); have_has_cp = true; } else return dbus_error_invalid_args(message); } if ((!have_source || !props.source) && (!have_sink || !props.sink)) return dbus_error_invalid_args(message); if (!have_source) props.source = !props.sink; else if (!have_sink) props.sink = !props.source; if (have_port && (!props.source || props.port == 0)) return dbus_error_invalid_args(message); if (props.source && !have_port) props.port = 7236; if (have_has_audio && !props.sink) return dbus_error_invalid_args(message); else if (!have_has_audio && props.sink) props.audio = true; /* * Should this be calculated based on Wi-Fi connection capacity? * Wi-Fi Display Technical Specification v2.1.0 only mentions this * in the context of the video format selection on the source (D.1.1): * "A WFD Source should determine averaged encoded video data rate * not to exceed the value indicated in the WFD Device Maximum * throughput field at WFD Device Information subelement transmitted * by the targeted WFD Sink [...]" */ props.throughput = 10; if (p2p_own_wfd) return dbus_error_already_exists(message); p2p_wfd_disconnect_watch = l_dbus_add_disconnect_watch(dbus, l_dbus_message_get_sender(message), p2p_wfd_disconnect_watch_cb, NULL, p2p_wfd_disconnect_watch_destroy); p2p_own_wfd = l_memdup(&props, sizeof(props)); return l_dbus_message_new_method_return(message); } static struct l_dbus_message *p2p_wfd_unregister(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { if (!l_dbus_message_get_arguments(message, "")) return dbus_error_invalid_args(message); if (!p2p_own_wfd) return dbus_error_not_found(message); /* TODO: possibly check sender */ l_dbus_remove_watch(dbus, p2p_wfd_disconnect_watch); p2p_own_wfd_free(); return l_dbus_message_new_method_return(message); } static void p2p_service_manager_interface_setup( struct l_dbus_interface *interface) { l_dbus_interface_method(interface, "RegisterDisplayService", 0, p2p_wfd_register, "", "a{sv}", "properties"); l_dbus_interface_method(interface, "UnregisterDisplayService", 0, p2p_wfd_unregister, "", ""); } static void p2p_service_manager_destroy_cb(void *user_data) { if (p2p_own_wfd) { l_dbus_remove_watch(dbus_get_bus(), p2p_wfd_disconnect_watch); p2p_own_wfd_free(); } } static int p2p_init(void) { struct l_dbus *dbus = dbus_get_bus(); if (!l_dbus_register_interface(dbus, IWD_P2P_INTERFACE, p2p_interface_setup, NULL, false)) l_error("Unable to register the %s interface", IWD_P2P_INTERFACE); if (!l_dbus_register_interface(dbus, IWD_P2P_PEER_INTERFACE, p2p_peer_interface_setup, NULL, false)) l_error("Unable to register the %s interface", IWD_P2P_PEER_INTERFACE); p2p_dhcp_settings = l_settings_new(); p2p_device_list = l_queue_new(); if (!l_dbus_register_interface(dbus, IWD_P2P_WFD_INTERFACE, p2p_wfd_interface_setup, NULL, false)) l_error("Unable to register the %s interface", IWD_P2P_WFD_INTERFACE); if (!l_dbus_register_interface(dbus, IWD_P2P_SERVICE_MANAGER_INTERFACE, p2p_service_manager_interface_setup, p2p_service_manager_destroy_cb, false)) l_error("Unable to register the %s interface", IWD_P2P_SERVICE_MANAGER_INTERFACE); else if (!l_dbus_object_add_interface(dbus, IWD_P2P_SERVICE_MANAGER_PATH, IWD_P2P_SERVICE_MANAGER_INTERFACE, NULL)) l_error("Unable to register the P2P Service Manager object"); return 0; } static void p2p_exit(void) { struct l_dbus *dbus = dbus_get_bus(); l_dbus_unregister_interface(dbus, IWD_P2P_INTERFACE); l_dbus_unregister_interface(dbus, IWD_P2P_PEER_INTERFACE); l_dbus_unregister_interface(dbus, IWD_P2P_WFD_INTERFACE); l_dbus_unregister_interface(dbus, IWD_P2P_SERVICE_MANAGER_INTERFACE); l_queue_destroy(p2p_device_list, p2p_device_free); p2p_device_list = NULL; l_settings_free(p2p_dhcp_settings); p2p_dhcp_settings = NULL; } IWD_MODULE(p2p, p2p_init, p2p_exit) IWD_MODULE_DEPENDS(p2p, wiphy) IWD_MODULE_DEPENDS(p2p, scan) IWD_MODULE_DEPENDS(p2p, netconfig)