P2P: Add support for cross connection
authorJouni Malinen <jouni.malinen@atheros.com>
Sat, 10 Jul 2010 17:19:34 +0000 (10:19 -0700)
committerJouni Malinen <j@w1.fi>
Thu, 9 Sep 2010 14:17:20 +0000 (07:17 -0700)
If enabled, cross connection allows GO to forward IPv4 packets
using masquerading NAT from the P2P clients in the group to an
uplink WLAN connection. This is disabled by default and can be
enabled with "wpa_cli p2p_set cross_connect 1" on the P2P device
interface.

15 files changed:
src/common/wpa_ctrl.h
src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_go_neg.c
src/p2p/p2p_group.c
src/p2p/p2p_i.h
src/p2p/p2p_parse.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/examples/p2p-action.sh
wpa_supplicant/notify.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/p2p_supplicant.h
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 88412ba..b70cf7a 100644 (file)
@@ -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: <peer address> <PIN> */
 #define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
 /* parameters: <peer address> */
index 9fc6abf..86261ca 100644 (file)
@@ -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(?) */
+}
index e108172..8be8aea 100644 (file)
@@ -1171,6 +1171,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
  * Returns: Pointer to P2P Device Address or %NULL if not included
@@ -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 */
index 4686007..227b47c 100644 (file)
@@ -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,
index dd938d6..17837cb 100644 (file)
@@ -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);
 }
 
index 5c9d0eb..f349bc7 100644 (file)
@@ -365,6 +365,8 @@ struct p2p_data {
        unsigned int ext_listen_interval_usec;
 
        u8 peer_filter[ETH_ALEN];
+
+       int cross_connect;
 };
 
 /**
index 07b9b51..b82848e 100644 (file)
@@ -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;
index 8355757..739b9e9 100644 (file)
@@ -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);
 
index ee645cb..38fd8d6 100755 (executable)
@@ -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
index ac65b4f..c9a1b4b 100644 (file)
@@ -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 */
 }
 
 
index b257654..cbe1c4f 100644 (file)
@@ -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;
+       }
+}
index e1578fc..11674e0 100644 (file)
@@ -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 */
index d062fab..af63777 100644 (file)
@@ -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;
index 177fe5b..8536b45 100644 (file)
@@ -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);
index 534ce94..fb1b955 100644 (file)
@@ -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;