2

I have a Python/Flask web app on a Raspberry Pi that calls the following bash script (connect_to_wifi) to connect to WiFi:

sudo killall wpa_supplicant
sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
sudo dhclient wlan0

Basically, a user enters their WiFi credentials, which are saved in wpa_supplicant.conf, and then this script is run. It works great...however, if they mistype their credentials, dhclient hangs forever before failing.

What I want to do is detect if the credentials are correct before proceeding with dhclient. I know that I can check the output of the wpa_supplicant command for a 4-Way Handshake failure if creds are wrong, but when I call this script from my Python app via:

p = Popen(['connect_to_wifi'], stdout=PIPE, bufsize=1)
with p.stdout:
    for line in iter(p.stdout.readline, b''):
        print line,
p.wait()

None of the output from the sudo wpa_supplicant -i... line is being captured. All I want to do is end immediately if I detect a handshake failure, but I'm having trouble capturing the output in Python.

Ryan Bobrowski
  • 2,641
  • 3
  • 31
  • 52
  • There does exist a wpa_cli tool that can connect to a running wpa_supplicant. You might be able to script that or figure out what protocol it uses and speak that natively. – ComputerDruid Mar 29 '18 at 00:13

2 Answers2

3

John Bullard's answer was really clean and solid, but I was having trouble getting it to work consistently because of the comment I made on it:

There is a brief period where iw wlan0 link says it is connected even if invalid credentials are entered in wpa_supplicant.conf. I'm assuming it connects, then verifies creds, and if they're wrong, disconnects. So this script doesn't actually work, at least not every time.

What I ended up doing, with help from that answer, is using the -f flag for wpa_supplicant and writing the output of wpa_supplicant to a file. The while loop then greps for the connected status, in which case it will call dhclient. If it doesn't connect, it will either time out or result in a 4-way handshake failed (if the latter, the script will end earlier).

#!/bin/bash

sudo ip addr flush dev wlan0
sudo killall wpa_supplicant
sudo truncate -s 0 wifi_connection_status.txt
sudo wpa_supplicant -B -i wlan0 -f wifi_connection_status.txt -c /etc/wpa_supplicant/wpa_supplicant.conf

declare -i i=0
declare -i timeout=15
while [ $i -le $timeout ]; do
    if grep -iq 'CTRL-EVENT-CONNECTED' wifi_connection_status.txt; then
        sudo dhclient wlan0
        exit 2
    elif grep -iq '4-Way Handshake failed' wifi_connection_status.txt; then
        exit 2
    fi

    (( i++ ))
    sleep 1
done
Ryan Bobrowski
  • 2,641
  • 3
  • 31
  • 52
2

It doesn't address your Python issue, but (assuming that you have the ability to modify the BASH script) you could put a loop in the BASH script to check the output of iw wlan0 link and wait for it to either return a success message or a timeout counter is reached. It will return "Not Connected" until the connection is established.

Do that just before you call dhclient and you should be fairly certain of a valid connection on which to run.

Something along the lines of:

sudo killall wpa_supplicant
sudo wpa_supplicant -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf

declare -i waitcount=0;
while sudo iw wlan0 link | grep -iq "Not connected"; do
    ((waitcount+=1))
    if (( waitcount > 30 ))
    then
       echo;
       echo "Timeout while waiting for successful association and authentication."
       exit 2;
    fi
    sleep 1s;
    echo -n ".";
done

sudo dhclient wlan0
  • questions: what is the 2 for after the exit command? Also, why is there an echo;? is that to create a blank new line? – Ryan Bobrowski Apr 10 '18 at 15:27
  • also, encountered a problem. There is a brief period where `iw wlan0 link` says it is connected even if invalid credentials are entered in wpa_supplicant.conf. I'm assuming it connects, then verifies creds, and if they're wrong, disconnects. So this script doesn't actually work, at least not every time. – Ryan Bobrowski Apr 10 '18 at 15:53
  • 1
    Sorry...that snippet of code is from a larger set of scripts and some of those echos (echoes?) and exit codes probably seem weird out-of-context. The "exit 2" command just exits with a specific error code (2 in this case) to the calling script so that it knows exactly what failed. And (as you surmised) the standalone echo is just to make sure that the error message shows up on a different line than the progress dots. – John Bullard Apr 12 '18 at 22:17