5

It is no problem to connect my Android Smartphone with Wi-Fi Direct to the printer at my home and print documents. But how can I use Wi-Fi Direct to connect the phone to a Raspberry Pi without a managed WiFi infrastructure using an access point?

Ingo
  • 40,606
  • 15
  • 76
  • 189

3 Answers3

7

I have tested this with some different smart phones using Android 4.0, Android 6.0, Android 6.0.1 and Android 10, but mainly used the last one. I cannot give all examples so I will only use a FAIRPHONE with Android 10 for the examples. It may be a bit different on your smartphone but it can give you an idea how it should work. On a Raspberry Pi 4B I use Raspberry Pi OS (32-bit) Lite 2020-08-20 based on Debian Buster, updated with sudo apt update && sudo apt full-upgrade && sudo reboot on 2020-09-28. WiFi Direct uses Wi-Fi Protected Setup (WPS) for authentication that knows mainly two modes: Push Button Control (PBC) and Pin Code.

♦ Wi-Fi Direct with a DHCP server on the Group Owner

WiFi Direct is organized in groups and every group has one group owner (GO). Only the group owner is allowed to run a DHCP server because we have to ensure that only one DHCP server is present in the group. So I will set the RasPi to the group owner. To ensure that a device is always negotiated to a group owner we use the option p2p_go_intent=[0..15]. 0 means the RasPi becomes a client, 15 means the RasPi becomes a group owner. 7 means a chance of 50 % to become a group owner.

I prefer to use systemd-networkd because it has all in one and is able to manage dynamically changing interfaces. WiFi Direct groups are represented by virtual wifi interfaces with increasing numbers, e.g. p2p-wlan0-0, p2p-wlan0-1 and so on.

Please do sudo apt install nmap before modifying the network. We need nmap to find the connected smartphone.

Then just follow to Use systemd-networkd for general networking. You can use section "♦ Quick Step". Then come back here.

To configure wpa_supplicant create this file with your settings for country= and device_name=. By specification, the device name should always start with DIRECT-. You can just copy and paste this in one block to your command line beginning with cat and including EOF (delimiter EOF will not get part of the file):

rpi ~$ sudo -Es   # if not already done
rpi ~# cat > /etc/wpa_supplicant/wpa_supplicant-wlan0.conf <<EOF
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=DE
device_name=DIRECT-RasPi1

# If you need to modify the group owner intent, 0-15, the higher
# number indicates preference to become the GO. You can also set
# this on p2p_connect commands.
p2p_go_intent=15

# In order to support 802.11n for the p2p Group Owner
p2p_go_ht40=1

# Device type
#   1-0050F204-1 (Computer / PC)
#   1-0050F204-2 (Computer / Server)
#   5-0050F204-1 (Storage / NAS)
#   6-0050F204-1 (Network Infrastructure / AP)
device_type=6-0050F204-1
driver_param=p2p_device=6
EOF

Enable wpa_supplicant

rpi ~# chmod 600 /etc/wpa_supplicant/wpa_supplicant-wlan0.conf
rpi ~# systemctl disable wpa_supplicant.service
rpi ~# systemctl enable wpa_supplicant@wlan0.service
rpi ~# rfkill unblock wlan

To manage devices with wpa_cli we have to specify the control interface wpa_cli -i <ctrl-interface>. In wpa_supplicant.conf we have defined with ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev where to find the control interfaces. Here the control interface is p2p-dev-wlan0:

rpi ~$ ls -1 /var/run/wpa_supplicant/
p2p-dev-wlan0
wlan0

Because the group owner is working as DHCP server, we have to give its interface a static ip address and enable the DHCP server. Create this file:

rpi ~# cat > /etc/systemd/network/12-p2p-wlan0.network <<EOF
[Match]
Name=p2p-wlan0-*
[Network]
Address=192.168.4.1/24
DHCPServer=yes
EOF

Reboot.

Connect with mandatory method, using pin code asked by the smartphone

The Wi-Fi Direct specification defines that the pin code connection has to be supported by all compliant devices as default. So we will first use this method to be on the save side. Please follow the steps exactly in this order to have the handshake correct. If in doubt, start again from the beginning (see section ♦ Troubleshooting).

On the RasPi I start finding devices:

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_find

Now I go to the WiFi Direct page on my smartphone
Settings -> Network & Internet -> Wi-Fi -> Wi-Fi preferences -> Advanced -> Wi-Fi Direct. Tick the three-point in the upper right corner and select Search for devices if not already Searching... and tick the DIRECT-RasPi1:

Wi-Fi Direct       Wi-Fi Direct

You have two minutes time to response. Now I can look with wpa_cli -i p2p-dev-wlan0 p2p_peers what mac addresses of found devices are available. Then I have to show the details for every mac address with wpa_cli -i p2p-dev-wlan0 p2p_peer <MAC-ADDR> what name it has to find my smartphone. I do it with this one liner, for example:

rpi ~$ for i in $( wpa_cli -i p2p-dev-wlan0 p2p_peers ); do echo -n "$i "; wpa_cli -i p2p-dev-wlan0 p2p_peer $i | grep device_name=; done

86:cf:bf:8e:f1:06 device_name=Ingos Smartphone
32:cd:a7:f2:ee:5c device_name=DIRECT-1KC48x Series

and find 86:cf:bf:8e:f1:06 for Ingos Smartphone. Now I only work with this mac address (p2p_dev_addr) and connect to it. Note that we use pin auth (pin authentication). This will return a pin on the console:

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_connect 86:cf:bf:8e:f1:06 pin auth
87160055rpi ~$

On the smartphone just type in the given pin (here it's 87160055). The status will then change to Connected:

Wi-Fi Direct

Go now to section Test and finish the network connection

Connect with push button method (recommended)

If you have checked that the mandatory connection method with pin entry works, then you can try to use the push button connection (pbc). With it you do not need to fiddle with a pin.

The problem is that we have to tell the smartphone to use pbc. But I haven't found a place to set it on the phone. So we must tell it from the RasPi. This is done with the command set config_methods. This configuration is send to the phone when finding is active, so we have to set it before starting p2p_find:

rpi ~$ wpa_cli -i p2p-dev-wlan0 set config_methods virtual_push_button
rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_find

Now I go to the WiFi Direct page on my smartphone
Settings -> Network & Internet -> Wi-Fi -> Wi-Fi preferences -> Advanced -> Wi-Fi Direct. Tick the three-point in the upper right corner and select Search for devices if not already Searching... and tick the DIRECT-RasPi1:

Wi-Fi Direct       Wi-Fi Direct

Now I can look with wpa_cli -i p2p-dev-wlan0 p2p_peers what mac addresses of found devices are available. Then I have to show the details for every mac address with wpa_cli -i p2p-dev-wlan0 p2p_peer <MAC-ADDR> what name it has to find my smartphone. I do it with this one liner, for example:

rpi ~$ for i in $( wpa_cli -i p2p-dev-wlan0 p2p_peers ); do echo -n "$i "; wpa_cli -i p2p-dev-wlan0 p2p_peer $i | grep device_name=; done

86:cf:bf:8e:f1:06 device_name=Ingos Smartphone
32:cd:a7:f2:ee:5c device_name=DIRECT-1KC48x Series

and find 86:cf:bf:8e:f1:06 for Ingos Smartphone. Now I only work with this mac address (p2p_dev_addr) and connect to it. Note that we use pbc.

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_connect 86:cf:bf:8e:f1:06 pbc
OK

On the smartphone the status will then change to Connected:

Wi-Fi Direct

Go now to section Test and finish the network connection

Connect with the pin code method, with pin set by the smartphone

For the completeness I will show also this connection method. If you have checked that the mandatory connection method with pin entry, asked by the phone works, then you can try to use this method with setting the pin.

The problem is that we have to tell the smartphone to use pin auth. But I haven't found a place to set it on the phone. So we must tell it from the RasPi. This is done with the command set config_methods. This configuration is send to the phone when finding is active, so we have to set it before starting p2p_find:

rpi ~$ wpa_cli -i p2p-dev-wlan0 set config_methods virtual_display
rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_find

Now I go to the WiFi Direct page on my smartphone
Settings -> Network & Internet -> Wi-Fi -> Wi-Fi preferences -> Advanced -> Wi-Fi Direct. Tick the three-point in the upper right corner and select Search for devices if not already Searching... and tick the DIRECT-RasPi1. You are presented a pin:

Wi-Fi Direct       Wi-Fi Direct

Now I can look with wpa_cli -i p2p-dev-wlan0 p2p_peers what mac addresses of found devices are available. Then I have to show the details for every mac address with wpa_cli -i p2p-dev-wlan0 p2p_peer <MAC-ADDR> what name it has to find my smartphone. I do it with this one liner, for example:

rpi ~$ for i in $( wpa_cli -i p2p-dev-wlan0 p2p_peers ); do echo -n "$i "; wpa_cli -i p2p-dev-wlan0 p2p_peer $i | grep device_name=; done

86:cf:bf:8e:f1:06 device_name=Ingos Smartphone
32:cd:a7:f2:ee:5c device_name=DIRECT-1KC48x Series

and find 86:cf:bf:8e:f1:06 for Ingos Smartphone. Now I only work with this mac address (p2p_dev_addr) and connect to it. Note that we use the pin presented by the smartphone (here 69307386):

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_connect 86:cf:bf:8e:f1:06 69307386
OK

On the smartphone click OK to the Invitation sent and the status will change to Connected:

Wi-Fi Direct

Test and finish the network connection

To test if the smartphone is connected wireless we scan for its ip address. Because we have just created a new group we should find only one ip address in addition to the RasPis one. I use nmap to scan for the ip address of the smartphone and ping it:

rpi ~$ nmap -sn 192.168.4.0/24
Starting Nmap 7.70 ( https://nmap.org ) at 2020-09-28 19:46 BST
Nmap scan report for raspberrypi (192.168.4.1)
Host is up (0.0022s latency).
Nmap scan report for 192.168.4.76
Host is up (0.020s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 3.08 seconds

rpi ~$ ping -c3 192.168.4.76
PING 192.168.4.76 (192.168.4.76) 56(84) bytes of data.
64 bytes from 192.168.4.76: icmp_seq=1 ttl=64 time=62.8 ms
64 bytes from 192.168.4.76: icmp_seq=2 ttl=64 time=63.3 ms
64 bytes from 192.168.4.76: icmp_seq=3 ttl=64 time=60.10 ms

--- 192.168.4.76 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 4ms
rtt min/avg/max/mdev = 60.962/62.365/63.297/1.030 ms

To finish the connection we need the group identifier. You can find it with:

rpi ~$ ip -br link | grep -Po 'p2p-wlan0-\d+'
p2p-wlan0-12

And with this we finish the connection with p2p_group_remove:

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_group_remove p2p-wlan0-12

Or with a one-liner:

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_group_remove $(ip -br link | grep -Po 'p2p-wlan0-\d+')

Make connection method persistent

If you have decided what connection method you want to use, you can set it persistent in wpa_supplicants configuration. Just add this to /etc/wpa_supplicant/wpa_supplicant-wlan0.conf, but only for pbc and for pin setting by the smartphone. You do not need it for the mandatory pin entry on the smartphone.

## Config Methods
## List of the supported configuration methods
## Available methods: usba ethernet label display ext_nfc_token int_nfc_token
##       nfc_interface push_button keypad virtual_display physical_display
##       virtual_push_button physical_push_button
## For WSC 1.0:
##config_methods=label display push_button keypad
## For WSC 2.0:
##config_methods=label virtual_display virtual_push_button keypad
# uncomment nothing for mandatory setting (pin entry on the smartphone)
# uncomment next line for pbc:
#config_methods=virtual_push_button
# uncomment next line for pin setting by the smartphone:
#config_methods=virtual_display

♦ Troubleshooting

Start connection from beginning

Wi-Fi Direct is a protocol with a complex handshake. If the order of the handshake is broken it is a good idea to start it again from the beginning by resetting all settings:

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_flush
rpi ~$ wpa_cli -i p2p-dev-wlan0 set config_methods
rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_group_remove $(ip -br link | grep -Po 'p2p-wlan0-\d+')

On the smartphone cancel a pending invitation, wait several seconds, exit the Wi-Fi Direct configuration dialog, wait several seconds and select it again.

Other issues

If there is something wrong then from the wpa_cli commands you only get the message FAIL. This doesn't help much. To get more detailed information you can start wpa_supplicant running in the foreground. For this I use screen so I can run wpa_supplicant in one window and do the commands in a second window. First you have to stop the background service to start wpa_supplicant in foreground:

rpi ~$ sudo systemctl stop wpa_supplicant@wlan0.service
rpi ~$ sudo /sbin/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wlan0.conf -Dnl80211 -iwlan0
Successfully initialized wpa_supplicant

For a successful connection using push button you will find then:

P3P-DEVICE-FOUND fa:b4:6a:07:16:1d p2p_dev_addr=fa:b4:6a:07:96:1d pri_dev_type=3-0050F204-1 name='DIRECT-1D-HP ENVY 5000 series' config_methods=0x0 dev_capab=0x25 group_capab=0x81 vendor_elems=1 new=1
P2P-PROV-DISC-PBC-REQ 86:cf:bf:8e:f1:06 p2p_dev_addr=86:cf:bf:8e:f1:06 pri_dev_type=10-0050F204-5 name='Ingos Smartphone' config_methods=0x188 dev_capab=0x25 group_capab=0x0
P2P-DEVICE-FOUND 86:cf:bf:8e:f1:06 p2p_dev_addr=86:cf:bf:8e:f1:06 pri_dev_type=10-0050F204-5 name='Ingos Smartphone' config_methods=0x80 dev_capab=0x25 group_capab=0x2a new=0
P2P-GO-NEG-REQUEST 86:cf:bf:8e:f1:06 dev_passwd_id=4 go_intent=6

P2P-FIND-STOPPED
P2P-GO-NEG-SUCCESS role=GO freq=5240 ht40=1 peer_dev=86:cf:bf:8e:f1:06 peer_iface=86:cf:bf:8e:f1:06 wps_method=PBC
p2p-wlan0-2: interface state UNINITIALIZED->HT_SCAN
p2p-wlan0-2: Could not connect to kernel driver
Using interface p2p-wlan0-2 with hwaddr 22:34:00:d6:29:4a and ssid "DIRECT-AP"
p2p-wlan0-2: interface state HT_SCAN->ENABLED
p2p-wlan0-2: AP-ENABLED
p2p-wlan0-2: CTRL-EVENT-CONNECTED - Connection to 22:34:00:d6:29:4a completed [id=0 id_str=]
p2p-wlan0-2: WPS-PBC-ACTIVE
p2p-wlan0-2: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0
p2p-wlan0-2: CTRL-EVENT-EAP-STARTED 86:cf:bf:8e:f1:06
p2p-wlan0-2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1
p2p-wlan0-2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=14122 method=254
p2p-wlan0-2: WPS-REG-SUCCESS 86:cf:bf:8e:f1:06 e0507343-6f62-5734-bec1-38cbda98d73f
P2P-GROUP-FORMATION-SUCCESS
P2P-GROUP-STARTED p2p-wlan0-2 GO ssid="DIRECT-AP" freq=5240 go_dev_addr=22:34:00:d6:a9:4a
p2p-wlan0-2: WPS-PBC-DISABLE
p2p-wlan0-2: WPS-SUCCESS
p2p-wlan0-2: CTRL-EVENT-EAP-FAILURE 86:cf:bf:8e:f1:06
p2p-wlan0-2: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0
p2p-wlan0-2: AP-STA-CONNECTED 86:cf:bf:8e:f1:06 p2p_dev_addr=86:cf:bf:8e:f1:06
AP-STA-CONNECTED 86:cf:bf:8e:f1:06 p2p_dev_addr=86:cf:bf:8e:f1:06

With line:

P2P-GROUP-STARTED p2p-wlan0-2 GO ssid="DIRECT-AP" freq=5240 go_dev_addr=22:34:00:d6:a9:4a

You can see that the group uses frequency 5240 MHz. If the smartphone does not support the 5 GHz band the connection will FAIL, of course. Then you must specify to connect with a frequency from the 2.4 GHz band, e.g. with freq=2412 (channel 1):

rpi ~$ wpa_cli -i p2p-dev-wlan0 p2p_connect 9a:0c:82:ba:7a:aa pbc freq=2412

Be sure that you do not have Invited an other Available device. You can invite only one device. Inviting your smartphone then, will fail without a message.


references:
(1) OMAP Wireless Connectivity NLCP WiFi Direct Configuration Scripts
(2) White Paper - Wi-Fi Direct
(3) Draft WiFi P2P Technical Specification.pdf
(4) Wi-Fi Simple Configuration Protocol and Usability Best Practices for the Wi-Fi ProtectedSetup™Program
(4) wpa_supplicant and Wi-Fi P2P
(5) wpa_supplicant and Wi-Fi Protected Setup (WPS)
(6) Linux Wireless - P2P howto

Ingo
  • 40,606
  • 15
  • 76
  • 189
  • How to debug "Failed to obtain IP address" on Android side after connecting successfully to Group Owner in AP mode following `wpa_cli p2p_group_add persistent=x` ? – Jortstek Nov 13 '21 at 01:16
1

This answer is an update to the Ingo’s excellent description. Here we would like to use the default networking system of Raspbian (assuming to have previously configured it) rather than replacing it with systemd-networkd. Additional notes on the Wi-Fi Direct workflow are added to the end.

The standard networking mode to configure TCP/IP in Raspbian/Raspberry Pi OS is mentioned in the related documentation. At the time of writing, dhcpcd is used (dhcpcd5 package). This mode replaced the former Debian Networking (ifupdown package), where the configuration was done in /etc/network/interfaces. The documentation also describes how to setup a Wi-Fi client/Wi-Fi station via wpa_supplicant by configuring /etc/wpa_supplicant/wpa_supplicant.conf to connect the Raspberry Pi to an existing wireless LAN on its default wlan0 interface; alternatively, it describes how to set up a routed wireless access point on the same wlan0 interface (instead of the Wi-Fi station). As DNS forwarder and DHCP Server, it uses dnsmasq. It also uses DNS Multicast (libnss-mdns) and Zeroconf/Apple Bonjour (avahi-daemon). We will keep all these modules and will configure dnsmasq as DHCP Server for the Wi-Fi Direct sessions. We will not use systemd-resolved.

Wi-Fi Direct (formerly named Wi-Fi Peer-to-Peer, or P2P) allows two devices to connect directly to each other, without the need for a traditional Wireless Access Point (AP). The role of the access point is replaced by the so-called Group Owner, typically negotiated during the connection setup.

An advantage of Wi-Fi Direct with Android is that it can coexist with a traditional Wi-Fi connection as well as with a cellular connection: this means that an Android smartphone can be connected to a mobile network, or to a Wi-Fi AP with internet access (which always takes priority to the mobile network for its internal Android routing configuration) and at the same time connect to the Raspberry Pi via Wi-Fi Direct, without losing the routing to the mobile network or AP (because, differently from the standard Wi-Fi connection, Wi-Fi Direct does not interfere with the mobile routing). Apple iOS devices do not support Wi-Fi Direct.

We want Raspberry Pi to always be a Wi-Fi Direct Group Owner (GO) acting in access point mode. This corresponds to a P2P-GO wireless device feature. The Group Owner features a DHCP server function, providing addresses to the Wi-Fi Direct clients connecting to the group.

Notice that if a classic Wireless Access Point is configured (AP), Wi-Fi Direct might not work, depending on the device driver and on the hardware. The iw list command has a section named valid interface combinations where every line contains alternative combinations. With the Broadcom BCM2711 SoC included in a Raspberry Pi 4 B, I get the following:

        valid interface combinations:
                 * #{ managed } <= 1, #{ P2P-device } <= 1, #{ P2P-client, P2P-GO } <= 1,
                   total <= 3, #channels <= 2
                 * #{ managed } <= 1, #{ AP } <= 1, #{ P2P-client } <= 1, #{ P2P-device } <= 1,
                   total <= 4, #channels <= 1
        Device supports scan flush.
        Device supports randomizing MAC-addr in sched scans.
        Supported extended features:
                * [ 4WAY_HANDSHAKE_STA_PSK ]: 4-way handshake with PSK in station mode

This means that with this device, the setup of a P2P-GO is not compatible with a concurrent configuration of an AP on the same board. A second (USB) wireless network board (with AP features) would be needed to have both running.

If an AP service is active on the board where P2P-GO has to be used, run the following to temporarily free resources (and drop all AP active sessions by consequence):

iw dev
# read the interface name related to the "type AP" section. E.g., uap0
sudo /sbin/iw dev uap0 del

While generally the traditional AP should be disabled before starting P2P, Wi-Fi client (STA) and P2P can usually run concurrently. Correspondently, if a P2P group is created, a traditional AP interface cannot be started until the group is deleted.

Groups related to wlan0 are generally named p2p-wlan0-0, p2p-wlan0-1, p2p-wlan0-2, …; the number of concurrent groups depends on the hw and on the device driver; with the internal wireless function of a Raspberry Pi 4, only one single GO can be set-up. A previously existing group has to be removed before creating a new one. New groups will have progressive naming rather than reusing names of deleted groups.

Commands like iw dev, iwconfig, ip -br link, ls /var/run/wpa_supplicant, wpa_cli interface can list active groups (that for wlan0 are starting with p2p-wlan0-...). The interface command can be used within a wpa_cli workflow. iw dev is also able to report the interface type ("type P2P-GO").

By monitoring a P2P setup through journalctl, errors like Failed to create interface p2p-wlan0-0: -16 (Device or resource busy) mean that a group already exists or that an AP interface is active while a new group is being created. Similar errors occur with hostapd when trying to start an AP while a GO group locks the device driver.

The capability to activate a Wi-Fi Direct GO role is performed by the interaction of wpa_supplicant and systemd-networkd. Both would need an appropriate configuration. dnsmasq can intervene in case the internal DHCP server function of systemd-networkd is not configured. Same as avahi-daemon, if active.

To automate the management of P2P sessions in order for an Android smartphone to be able to connect and disconnect sessions with a host (e.g., Raspberry Pi) in AP mode using the available configuration methods (e.g., keypad, virtual_push_button, …), wpa_supplicant needs an external session manager (external application). hostapd is not able to govern the Wi-Fi Direct AP mode. The external session manager can either interact with wpa_supplicant through API or via wpa_cli client. At the time of writing, there is no best practice (or well-known software) for performing this; to test the process, we will use the wpa_cli client in interactive mode, sending commands one by one, by hand.

Specifically:

  • wpa_supplicant manages p2p negotiation, interacts with the device driver to create a P2P-GO group interface (or remove it), establishes the P2P session.
  • systemd-networkd dynamically sets the IP v4 address of a newly created group; if a DHCP server function is configured, it provides a DHCP address to the connected device (STA/client station); in order to perform the IP configuration of groups, it checks files in /etc/systemd/network/ directory matching the name of the created group interface. Notice that ifupdown is not able to perform this, even if it allows pattern-based hotplug configurations; also, dhcpcd is not able to set a static IP to these virtual interfaces with progressive names (and, in both cases, related manuals do not provide clear configuration examples).
  • dnsmasq can be used to provide a more enhanced set of DHCP server functions rather than the basic features available with systemd-networkd; dnsmasq only intervenes to give an address to the clients while activating the session, after the group is created (and it is not impacted during group creation).

dhcpcd (the default a DHCP client) is not able to manage the Wi-Fi Direct P2P group interfaces and must be disabled for P2P by adding the following on top of /etc/dhcpcd.conf configuration file:

sudo vi /etc/dhcpcd.conf # add the following to the top of the file:

# Disable processing P2P GO interfaces
denyinterfaces p2p-wlan0-*

In a default Raspberry configuration where wlan0 is active, wpa_supplicant is silently activated by a default dhcpcd hook on wlan0 (provided that nohook wpa_supplicant was not added for that interface).

To verify this hook, check files included in the /lib/dhcpcd/dhcpcd-hooks directory: one of this is 10-wpa_supplicant, which is responsible for activating wpa_supplicant on the new interface. It is automatically run when the new interface is discovered by dhcpcd.

The wpa_supplicant configuration file in use for the system (/etc/wpa_supplicant/wpa_supplicant.conf by default) has to be edited by adding the P2P settings:

sudo vi /etc/wpa_supplicant/wpa_supplicant.conf
...
# Wi-Fi Direct AP name:
device_name=DIRECT-RasPi1
# If you need to modify the group owner intent, 0-15, the higher
# number indicates preference to become the GO. You can also set
# this on p2p_connect commands.
p2p_go_intent=15

# In order to support 802.11n for the p2p Group Owner
p2p_go_ht40=1

# Device type
#   1-0050F204-1 (Computer / PC)
#   1-0050F204-2 (Computer / Server)
#   5-0050F204-1 (Storage / NAS)
#   6-0050F204-1 (Network Infrastructure / AP)
device_type=6-0050F204-1
driver_param=p2p_device=6
...

Configure wlan0 P2P GO (group owner) address and systemd-networkd DHCP server:

sudo -Es
cat > /etc/systemd/network/12-p2p-wlan0.network <<\EOF
[Match]
Name=p2p-wlan0-*
[Network]
#LLMNR=no
#MulticastDNS=yes
#IPMasquerade=yes
Address=192.168.4.1/24
# Comment out the following lines to disable the internal DHCP Server function and use, e.g., dnsmasq
DHCPServer=yes
[DHCPServer]
#DNS=84.200.69.80 1.1.1.1
EOF

Alternatively, only configure the wlan0 P2P GO (group owner) address and use an external DHCP server with more features than the very basic ones available with systemd-networkd.service:

cat > /etc/systemd/network/12-p2p-wlan0.network <<\EOF
[Match]
Name=p2p-wlan0-*
[Network]
#LLMNR=no
#MulticastDNS=yes
#IPMasquerade=yes
Address=192.168.4.1/24
EOF

As external DHCP server, we use dnsmasq Install it with the following command:

sudo apt-get install -y dnsmasq

Add the following lines to /etc/dnsmasq.conf:

interface=p2p-wlan*
no-dhcp-interface=lo,wlan0
domain-needed
bogus-priv
dhcp-range=192.168.4.50,192.168.4.199,12h
dhcp-option=3,192.168.50.1

If an AP is also used with, e.g., its uap0 interface, the following can be added (noticing that the service might not be concurrently active for the previously explained reasons):

interface=uap0,p2p-wlan*
no-dhcp-interface=lo,wlan0
domain-needed
bogus-priv
server=8.8.8.8
dhcp-range=192.168.50.50,192.168.50.199,12h
dhcp-range=192.168.4.50,192.168.4.199,12h

systemd-networkd must be enabled and started:

systemctl enable systemd-networkd.service
systemctl start systemd-networkd.service

The configuration is finished. You must reboot before testing.

To test the GO group creation, run journalctl -f in a window; in another widow, run wpa_cli (interactive mode), then enter p2p_group_add. If the group is successfully created, it has to be removed with p2p_group_remove p2p-wlan0-0 (use the appropriate group name following the logs of p2p_group_add).

A connection which remains active only for few seconds and then always drops is a typical effect of a missing DHCPv4 service and this can be inspected through journalctl. Revising the DHCP server configuration might fix such problem.

The command set config_methods virtual_push_button or set config_methods keypad differentiates the mode used by p2p_find to announce the Raspberry Pi device to the wireless network. Before changing the method, p2p_stop_find must be issued. Anyway, clients might keep the announced config method cached for a while.

In pbc mode, where no pop-up password is asked, the Raspberry cannot use the Android MAC address to recognize the device, because it changes at every device reboot. The device name might be used anyway (but it is fully open to everybody).

Basic workflow in pbc mode (virtual push button):

  • run wpa_cli and send set config_methods virtual_push_button (OK is returned to each command as default answer in case of missing output); verify that a p2p_find is not active before sending the config_methods command; otherwise, in case CTRL-EVENT-SCAN-STARTED events are received, issue a p2p_stop_find command before and wait for P2P-FIND-STOPPED / P2P-DEVICE-LOST.
  • use the interface command to verify that no p2p-wlan0- group is available; in case, remove it with p2p_group_remove p2p-wlan0-<number> (p2p-dev-wlan0 and wlan0 are not GO groups). The WiFi name is not yet visible to the client
  • send p2p_find (the WiFi name will become visible to the clients and marked as “available”). CTRL-EVENT-SCAN-STARTED events are received.
  • the client selects the connection; in consequence of the previous server setup (virtual_push_button), it will use PCB mode, so the state will go to “invited” without password pop-up)
  • wpa_cli returns events P2P-PROV-DISC-PBC-REQ <addr>, P2P-GO-NEG-REQUEST <addr>, P2P-DEVICE-FOUND
  • here a control on the name of the client can be possibly done before authorizing the session.
  • send p2p_connect <addr> pbc; notice that it is not strictly necessary to wait for the client connection to invoke p2p_connect: it might be issued just after p2p_find, but the client can deny the connection depending on its state, returning FAIL; notice also that the p2p_connect will be interrupted by any other sent command, like p2p_find: in order to avoid interrupting the connection with another command, AP-STA-CONNECTED should be waited within a congruent timeout period.
  • the received events are P2P-FIND-STOPPED, P2P-GO-NEG-SUCCESS, CTRL-EVENT-SCAN-RESULTS, P2P-GROUP-FORMATION-SUCCESS, P2P-GROUP-STARTED (followed by the group name, which is useful to be registered in order to allow deleting it on client disconnection), AP-STA-CONNECTED. The client sets the “connected” state after receiving an IP address.
  • if the client disconnects, the AP-STA-DISCONNECTED event is received. Alternatively, the group can be removed in order to produce the disconnection of the client.
  • upon AP-STA-DISCONNECTED event, it is useful to send p2p_group_remove <group> so that a new group can be created. Then, send again p2p_find to announce again the server to the network.

Basic workflow in fixed password mode (keypad):

  • send set config_methods keypad
  • send p2p_find
  • received events P2P-PROV-DISC-SHOW-PIN, P2P-GO-NEG-REQUEST, P2P-DEVICE-FOUND`
  • the client pops up the password message; enter an 8-digit password
  • send p2p_connect <addr> <8-digit password> display
  • received events P2P-FIND-STOPPED, P2P-GO-NEG-SUCCESS, CTRL-EVENT-SCAN-RESULTS, P2P-GROUP-FORMATION-SUCCESS, P2P-GROUP-STARTED (followed by the group name, which is useful to be registered in order to allow deleting it on client disconnection), AP-STA-CONNECTED. The client sets the “connected” state after receiving an IP address.
  • if the client disconnects, the AP-STA-DISCONNECTED event is received. Alternatively, the group can be removed in order to produce the disconnection of the client.
  • upon AP-STA-DISCONNECTED event, it is useful to send p2p_group_remove <group> so that a new group can be created. Then, send again p2p_find to announce again the server to the network.

If p2p_connect fails, first check the existence of EBUSY errors 16 in journalctl, meaning that a group or an AP had already been allocated; then verify that the client accepted the request.

To start a Wi-Fi Direct connection with Android and connect a Raspberry Pi AP, tap Settings > Wi-Fi > Advanced settings > Wi-Fi Direct and wait for the Raspberry Pi peer device to appear.

Currently, the only API available to interface wpa_supplicant on P2P (Wi-Fi Direct) sessions seems to be the UNIX socket, or alternatively by directly screenscraping the wpa_cli client via bidirectional pipe. The Unix socket named interface is typically /run/wpa_supplicant/wlan0, as usually defined in the wpa_supplicant configuration file /etc/wpa_supplicant/wpa_supplicant.conf: ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev, where netdev is a Unix group that is generally associated to the pi user available in a Raspberry Pi.

wpa_supplicant also allows the dbus interface when wpa_supplicant is run with the -u option; anyway, with the current wpa_supplicant version (v2.8-devel), the internal P2P objects do not seem to be registered to the dbus interface, so a Python request like

python3 -c 'import dbus;\
dbus.Interface(dbus.SystemBus().get_object("fi.w1.wpa_supplicant1",\
"/fi/w1/wpa_supplicant1"), "fi.w1.wpa_supplicant1")\
.GetInterface("p2p-dev-wlan0")'

fails with message dbus.exceptions.DBusException: fi.w1.wpa_supplicant1.InterfaceUnknown: wpa_supplicant knows nothing about this interface.. This is because wpa_supplicant does not expose p2p-dev-wlan0 to dbus. It means that the old Python test examples included in wpa_supplicant sources, which exploited dbus, are not usable. Notice also that if p2p-dev-wlan0 in the above Python command is changed to wlan0 (which is unrelated to P2P anyway), the command returns with no errors.

ircama
  • 128
  • 1
  • 6
  • Thanks for the update :-) in particular the comparison with traditional methods (Debian ifupdown and dhcpcd). I have noticed the problems (the reason why I use systemd-networkd) but not seen with this clarity. – Ingo Dec 01 '20 at 19:53
  • @Ingo: thanks a lot for your first post! I'm planning to further update mine as soon as I have enough reputation scores to also include additional links. By having some more time there would be the opportunity to complement the description with some notes on persistent groups.Using systemd-networkd is clean and linear, but the DHCP features are quite limited; it is anyway possible to exploit dnsmasq with systemd-networkd if DHCPServer=yes [DHCPServer] are not used. – ircama Dec 02 '20 at 00:04
  • Yes, the built-in features of systemd-networkd are limited but they serve 80% of the use cases. Because we have mostly to do with beginners here on Raspberry Pi I like to keep it as simple as possible. You only have to add some lines to the `.network` files. About persistent groups you may have a look at [p2p_group_add setting own password and ssid](https://raspberrypi.stackexchange.com/a/94693/79866). About reputation, you need some more Q&A that I can upvote. – Ingo Dec 02 '20 at 10:23
  • @Ingo I have now the possibility to add links to my description and I did it, thanks. Your [note on the usage of persistent groups](https://raspberrypi.stackexchange.com/questions/94667/p2p-group-add-setting-own-password-and-ssid/94693#94693) has good insights, not clearly written elsewhere. – ircama Dec 02 '20 at 18:54
0

I think I have a much simpler solution which works with an out-of-the-box Raspberry Pi OS install. If there are problems here I'm happy to be corrected, but this script seems to work.

Before the first run:

  1. Modify /etc/wpa_supplicant/wpa_supplicant.conf to set device_name to whatever you want it to be.
  2. Do apt install -y dnsmasq.
  3. Reboot (may have to perform steps 1 and 3 again - see note at bottom)

Then, on Pi startup, do:

# Setup Wifi
wpa_cli p2p_listen                  # gets everything moving
sleep 5
wpa_cli p2p_group_add persistent    # creates p2p-wlan-0
sleep 5

# Setup networking on the new group (pick whatever private IP you wish)
ifconfig p2p-wlan0-0 YOUR.LOCAL.IP.ADDR metmask PUT.YOUR.NETMASK.HERE

# Setup DHCP
killall dnsmasq
dnsmasq -I p2p-wlan0-0 -p0 -z -FIP.ADDR.RANGE.START,IP.ADDR.RANGE.END

# Continually push the button for pushbutton-connect
while [[ true ]]; do
  wpa_cli wps_pbc
  sleep 5
done

That's it! no crazy configurations or anything. It just works. Some of this was voodoo magic, so there could be things that I got wrong, or not needed, or should be done a better way, but this seemed to allow for a simpler install with a simple script. Just be sure to set your IP address to what you want it, and set the IP address range for DHCP.

One other note - sometimes I've had to re-edit /etc/wpa_supplicant/wpa_supplicant.conf. This usually only happens after the first time I edit it, but I don't know why. Also, after you do this edit, you should reboot, I believe. Again, make sure your edit "took" after reboot.

johnnyb
  • 101
  • 1