diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 88412ba71..b70cf7a37 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -101,6 +101,8 @@ extern "C" { #define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " #define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " #define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " /* parameters: */ #define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " /* parameters: */ diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 9fc6abff6..86261cac2 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1612,6 +1612,7 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, u8 *lpos; size_t tmplen; int res; + u8 group_capab; if (p2p_ie == NULL) return 0; /* WLAN AP is not a P2P manager */ @@ -1627,7 +1628,15 @@ static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, return -1; lpos = p2p_buf_add_ie_hdr(tmp); - p2p_buf_add_capability(tmp, p2p->dev_capab, 0); + group_capab = 0; + if (p2p->num_groups > 0) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) p2p_buf_add_p2p_interface(tmp, p2p); @@ -2960,3 +2969,14 @@ void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " "filter for " MACSTR, MAC2STR(p2p->peer_filter)); } + + +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) +{ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", + enabled ? "enabled" : "disabled"); + if (p2p->cross_connect == enabled) + return; + p2p->cross_connect = enabled; + /* TODO: may need to tear down any action group where we are GO(?) */ +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index e108172a0..8be8aead4 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -1170,6 +1170,13 @@ int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); */ u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); +/** + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection + * @p2p_ie: P2P IE(s) contents + * Returns: 0 if cross connection is allow, 1 if not + */ +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); + /** * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data * @p2p_ie: P2P IE(s) contents @@ -1216,4 +1223,11 @@ int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); +/** + * p2p_set_cross_connect - Set cross connection capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether cross connection will be enabled + */ +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 4686007db..227b47c80 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -158,6 +158,8 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, group_capab = 0; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | p2p->next_tie_breaker); @@ -242,8 +244,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); group_capab = 0; - if (peer && (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)) - group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer && peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, 100, 20); @@ -631,8 +637,12 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_status(buf, status); group_capab = 0; - if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) - group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); if (go || resp_chan == NULL) p2p_buf_add_operating_channel(buf, p2p->cfg->country, diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index dd938d6ed..17837cbae 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -145,6 +145,8 @@ static void p2p_group_add_common_ies(struct p2p_group *group, group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; if (group->group_formation) group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION; + if (group->p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; p2p_buf_add_capability(ie, dev_capab, group_capab); } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 5c9d0ebda..f349bc7b6 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -365,6 +365,8 @@ struct p2p_data { unsigned int ext_listen_interval_usec; u8 peer_filter[ETH_ALEN]; + + int cross_connect; }; /** diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index 07b9b51d7..b82848e49 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -650,6 +650,21 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) } +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.manageability) + return 0; + + return !(msg.capability[0] & P2P_MAN_CROSS_CONNECTIION_PERMITTED); +} + + u8 p2p_get_group_capab(const struct wpabuf *p2p_ie) { struct p2p_message msg; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 835575747..739b9e903 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -2476,6 +2476,9 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) return 0; } + if (os_strcmp(cmd, "cross_connect") == 0) + return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh index ee645cb4e..38fd8d65d 100755 --- a/wpa_supplicant/examples/p2p-action.sh +++ b/wpa_supplicant/examples/p2p-action.sh @@ -54,3 +54,24 @@ if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then ifconfig $GIFNAME 0.0.0.0 fi fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # enable NAT/masquarade $GIFNAME -> $UPLINK + iptables -P FORWARD DROP + iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE + iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT + sysctl net.ipv4.ip_forward=1 +fi + +if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then + GIFNAME=$3 + UPLINK=$4 + # disable NAT/masquarade $GIFNAME -> $UPLINK + sysctl net.ipv4.ip_forward=0 + iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE + iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT +fi diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index ac65b4f0d..c9a1b4b1b 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -24,6 +24,7 @@ #include "dbus/dbus_new.h" #include "driver_i.h" #include "scan.h" +#include "p2p_supplicant.h" #include "notify.h" int wpas_notify_supplicant_initialized(struct wpa_global *global) @@ -81,6 +82,13 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); + +#ifdef CONFIG_P2P + if (new_state == WPA_COMPLETED) + wpas_p2p_notif_connected(wpa_s); + else if (new_state < WPA_ASSOCIATED) + wpas_p2p_notif_disconnected(wpa_s); +#endif /* CONFIG_P2P */ } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index b25765432..cbe1c4f26 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -45,6 +45,7 @@ static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, const u8 *dev_addr, enum p2p_wps_method wps_method); static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s); +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s); static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, @@ -201,6 +202,12 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s) gtype = "client"; } else gtype = "GO"; + if (wpa_s->cross_connect_in_use) { + wpa_s->cross_connect_in_use = 0; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + } wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s", wpa_s->ifname, gtype); if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { @@ -441,6 +448,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, "%s GO ssid=\"%s\" psk=%s go_dev_addr=" MACSTR "%s", wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : ""); + wpas_p2p_cross_connect_setup(wpa_s); } else { wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr=" @@ -449,6 +457,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, ssid && ssid->passphrase ? ssid->passphrase : "", MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : ""); + wpas_p2p_cross_connect_setup(wpa_s); } if (persistent) @@ -717,6 +726,7 @@ static void p2p_go_configured(void *ctx, void *data) wpas_p2p_store_persistent_group( wpa_s->parent, ssid, wpa_s->parent->own_addr); + wpas_p2p_cross_connect_setup(wpa_s); return; } @@ -3307,3 +3317,135 @@ int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start, duration); } + + +int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) +{ + if (wpa_s->global->p2p_disabled) + return -1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + + wpa_s->global->cross_connection = enabled; + p2p_set_cross_connect(wpa_s->global->p2p, enabled); + + if (!enabled) { + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (iface->cross_connect_enabled == 0) + continue; + + iface->cross_connect_enabled = 0; + iface->cross_connect_in_use = 0; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } + } + + return 0; +} + + +static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + if (!uplink->global->cross_connection) + return; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (iface->ap_iface == NULL) + continue; + if (iface->cross_connect_in_use) + continue; + + iface->cross_connect_in_use = 1; + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + } +} + + +static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) +{ + struct wpa_supplicant *iface; + + for (iface = uplink->global->ifaces; iface; iface = iface->next) { + if (!iface->cross_connect_enabled) + continue; + if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) != + 0) + continue; + if (!iface->cross_connect_in_use) + continue; + + wpa_msg(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); + iface->cross_connect_in_use = 0; + } +} + + +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_INFRA || + wpa_s->cross_connect_disallowed) + wpas_p2p_disable_cross_connect(wpa_s); + else + wpas_p2p_enable_cross_connect(wpa_s); +} + + +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_disable_cross_connect(wpa_s); +} + + +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *iface; + + if (!wpa_s->global->cross_connection) + return; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface == wpa_s) + continue; + if (iface->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) + continue; + if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + continue; + + wpa_s->cross_connect_enabled = 1; + os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname, + sizeof(wpa_s->cross_connect_uplink)); + wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from " + "%s to %s whenever uplink is available", + wpa_s->ifname, wpa_s->cross_connect_uplink); + + if (iface->ap_iface || iface->current_ssid == NULL || + iface->current_ssid->mode != WPAS_MODE_INFRA || + iface->cross_connect_disallowed || + iface->wpa_state != WPA_COMPLETED) + break; + + wpa_s->cross_connect_in_use = 1; + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); + break; + } +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index e1578fca9..11674e0db 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -108,5 +108,8 @@ void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start, int duration); +int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled); +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s); +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s); #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index d062fab1d..af6377783 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -2548,6 +2548,10 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 177fe5bb5..8536b4558 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -36,6 +36,7 @@ #include "common/wpa_ctrl.h" #include "mlme.h" #include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" @@ -1169,6 +1170,21 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, if (res >= 0) wpa_ie_len += res; } + + wpa_s->cross_connect_disallowed = 0; + if (bss) { + struct wpabuf *p2p; + p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p) { + wpa_s->cross_connect_disallowed = + p2p_get_cross_connect_disallowed(p2p); + wpabuf_free(p2p); + wpa_printf(MSG_DEBUG, "P2P: WLAN AP %s cross " + "connection", + wpa_s->cross_connect_disallowed ? + "disallows" : "allows"); + } + } #endif /* CONFIG_P2P */ wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 534ce942e..fb1b955ff 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -214,6 +214,7 @@ struct wpa_global { struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */ int p2p_disabled; + int cross_connection; }; @@ -486,6 +487,27 @@ struct wpa_supplicant { int pending_join_wps_method; unsigned int roc_waiting_drv_freq; int force_long_sd; + + /* + * Whether cross connection is disallowed by the AP to which this + * interface is associated (only valid if there is an association). + */ + int cross_connect_disallowed; + + /* + * Whether this P2P group is configured to use cross connection (only + * valid if this is P2P GO interface). The actual cross connect packet + * forwarding may not be configured depending on the uplink status. + */ + int cross_connect_enabled; + + /* Whether cross connection forwarding is in use at the moment. */ + int cross_connect_in_use; + + /* + * Uplink interface name for cross connection + */ + char cross_connect_uplink[100]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid;