Wifi set up on linux, the minimal way

Wifi set up on linux, the minimal way

There more than one ways to set up your wifi on your linux system, and almost all of them would require you to basically install a network manager of some sort, to handle everything for you. But here i am going to show you a mininal way to set up your own wifi network manager.

Dependencies:

  • wpa_supplicant
  • A dhcp client ( e.g. dhclient or dhcpcd )
  • iproute2
  • libnotify ( optional )

wpa_supplicant NetworkManager and many other network management tools uses wpa_supplicant behind the scene to actually establish the (secure) connection. For an insecure network you don't even need that, Just the good old iw is enough. But Here we considering secure networks as well so we would be using wpa_supplicant.

dhcp A dhcp client becomes necessary for establishing and securing an IP address from the Access Point in a dynamic set up, hence we would need that too.

iproute2 iproute2 gives us the ever important tool ip that we can utilise to manage our network interfaces

notify-send to send qute little notifications

Some of this would be present your system .If not, install them.

First let's set up the basics

# your interface name, check it with the ip addr command. For me it's wlo1
Interface="wlo1"

# Your dhcp client, for me it is dhcpcd
dhcpclient="dhcpcd"

# Path to your local wpa_supplicant directory
WPA_SUPPLICANT=$HOME/.config/wpa

#if the directory doesn't exist this one takes care of it.
mkdir -p $WPA_SUPPLICANT
WPA_SUPPLICANT=$WPA_SUPPLICANT/wpa_supplicant

#Make sure the interface is not blocked
sudo rfkill unblock "$(rfkill|grep wlan|cut -d' ' -f2)"

# Check whether the interface down.If so, set it up
[ "$(grep $Interface <(ip link show up ))" ] || sudo ip link set $Interface  up

Check whether required programs are installed

check()
{
  [ $(type $1 > /dev/null) ] || { echo "$1 not installed" && exit 1 ; }
}

check wpa_supplicant
check $dhcpclient
check ip
type notify-send > /dev/null && notify=1

And now we will set up functions that takes care of specific tasks.

First we would configure a help() function that basically outputs basic usage with fine figlet ASCII text of the program name accompanied by a one line description of the program This function would display the options along with a short description of what it means:

help() {

    cat <<END
    $(figlet $(basename $0))

A minimal wifi network manager
____________________________________________________
        Args --------- Meaning
____________________________________________________

   -n|-new ---------- create new connnection/password
   -s|-term --------- Terminate wpa_supplicant & dhcp client
   -h|-help --------- Help
   -v|-verbose ------ Verbose output
END
exit 0
}

Let's write the code to generate/handle new connection:


# Generate new network/password
new_connection() {
   (( don )) || return
    read -r -p "Entere the SSID: " SSID
    __asterisk_prompt_for_passphrase
    read -r -p "Would you like to control wpa_supplicant with its frontend tool wpa_cli?(y/N) " YesNo
    [[ $YesNo =~ [Yy] ]] && echo -e "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=$USER\nmac_addr=1\npreassoc_mac_addr=1\ngas_rand_mac_addr=1" >  "$WPA_SUPPLICANTCONF"



    [[ $SSID ]] && wpa_passphrase "$SSID" "$passphrase" >> "$WPA_SUPPLICANTCONF"
    (( $(pgrep  wpa_supplicant) )) && terminate
    don=1
    unset YesNo
    main

}

In the above function, notice the line echo -e "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=$USER\nmac_addr=1\npreassoc_mac_addr=1\ngas_rand_mac_addr=1". By assigning the ctrl_interface we can control wpa_supplicant via the wpa_cli tool. wpa_supplicant can generate random MAC addresses for your interface and the options mac_addr, preassoc_mac_addr,gas_rand_mac_addr configures it do exactly that. Since we want to be able to control it with wpa_cli and also have it manage random MAC addresses for our interface, we put it in our wpa_supplicant configuration file.

Then The function that manages termination of the connection, it basically uses wpa_cli to kill the network and then kills the dhcp client as well. Use it only when you are not using the dhcp client for networks.

# Teminate connection
terminate() {
    sudo pkill $dhcpclient
    sudo wpa_cli terminate $Interface && (( notify )) && notify-send "" "Wifi disconnected"   
}

wpa_supplicant handles password protected networks, so we need to have a secure way of typing our passwords. We do that with this function that masks your input characters with asterisks. This is an internal function and would called by other functions whenever required.

#generate asterisk for passphrase prompt
__asterisk_prompt_for_passphrase() {
    unset passphrase
    prompt="Enter Passphrase: "
    while IFS= read -r -p "$prompt" -s -n 1 char
    do
        #Enter - accept passphrase
        if [[ $char == $'\0' ]] ; then
            echo
            break
        fi
        #Backspace
        if [[ $char == $'\177' ]]; then
            prompt=$'\b \b'
            passphrase="${passphrase%?}"
        else
            prompt="*"
            passphrase+="$char"
        fi
    done
}

The function is the core of our pgogram, the main function.


main() {

    #you may require to mention a driver wrapper with -D nl80211,wext. Refer wpa_supplicant(8)
    [ $( pgrep wpa_supplicant ) ] || { sudo wpa_supplicant -c "$WPA_SUPPLICANTCONF" -i"$Interface" -B && exit_code=$? ; }
    if [[ $exit_code -eq 0 ]];then
        echo "wpa supplicant initialised"
    elif [[ -n $exit_code ]]; then
        echo "wpa supplicant initialisation error: $exit_code"
        exit 1
    fi

    #6. If dhcp client is not running, start it to get a lease:
    if [[ $(pidof $dhcpclient) ]]; then
        sudo $dhcpclient -k $Interface
        sudo $dhcpclient $Interface
    else
    echo "starting $dhcpclient ..."
    echo "scanning for network..."
        sudo $dhcpclient $Interface
    e_status=$?
    if [[ $e_status  -eq 1 ]]; then
        (( notify )) && notify-send "No wifi found" "Check and confirm the network is visible"
        echo "No wifi network found"
        exit 1
    elif [[ $e_status -ne 0 ]]; then
        (( notify )) && notify-send "Wifi problem" "Couldn't initiate connection : $e_status "
        exit $?
    fi
    echo "$dhcpclient started!"
    fi
    echo "  Connection established"
    (( notify )) && notify-send " " "Connection established"                                    
    unset dhcpclient Interface

    if ping google.com -c 1 &>/dev/null; then
        (( notify )) && notify-send "Network is up" "enjoy 🙂"
        return 0
    fi
    (( notify )) && notify-send "😢" "Can't ping google"
}

The necessary functions are all set. Now some logic that that would prompt the user for establishing a new connection if no configured network is detected:

# generate wpa_supplicant.conf with SSID and passphrase if it doesn't already exist:
if ! [ -s "$WPA_SUPPLICANTCONF" ]; then

    read -r -p "It appears no Network is configured. Would you like to configure one(y/N)? " YN
    [[ $YN =~ [nN] || -z $YN ]] &&  exit 0;
    if [[ $YN =~ [yY] ]]; then
        read -r -p "Would you like to control wpa_supplicant with its frontend tool wpa_cli?(y/N) " YesNo
        [[ $YesNo =~ [Yy] ]] && echo -e "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=$USER" >  "$WPA_SUPPLICANTCONF"
        read -r -p "Entere the SSID: " SSID
        __asterisk_prompt_for_passphrase
        wpa_passphrase "$SSID" "$passphrase" >>  "$WPA_SUPPLICANTCONF"
        chmod 600 "$WPA_SUPPLICANTCONF"
        (( $(pgrep  wpa_supplicant) )) && terminate
        unset YN
        main
        don=1
    else
        echo "Wrong choice" >&2
        echo "try again" >&2
        exit 1
    fi
fi

now we use getopt to scan through the options being passed and call the assigned functions accordingly.

OPT=$(getopt -o :hntv --long :help,new,term,verbose -a -n 'wifi_s' -- "$@")
eval set -- "$OPT"
unset OPT

while true;do
    case $1 in
        -h|--help) help ;shift ;;
        -n|--new) new_connection ;shift ;;
        -t|--term) terminate ; shift ;;
        -v|--verbose) set -x;shift ;;
        *) main;break;;
    esac
done

------------------------------------------------------------------------------------

So all combined, the final script looks like this:

 #!/usr/bin/sh

# Author: charlie39( iamkg050392 at gmail dot com)
# Github : https://github.com/charlie39 

# I release this script in the public domain 
# you can do whatever you like with it


# your wifi interface:
Interface="wlo1"

#y our DHCP client:e.g - dhcpcd,dhclient
dhcpclient="dhcpcd"


# path to your wpa_supplicant.conf
WPA_SUPPLICANTCONF=$HOME/.config/wpa/wpa_supplicant.conf
# mkdir -p "$WPA_SUPPLICANTCONF"

# make sure interface is not blocked:
sudo rfkill unblock "$(rfkill|grep wlan|cut -d' ' -f2)"

# initiate the interface:
[ "$(grep $Interface <(ip link show up ))" ] || sudo ip link set $Interface  up

# you can install wireless_tools and get the SSIDs of available networks
# iwlist scan | grep SSID

check() {
    type $1 > /dev/null || { echo "$1 is installed" && exit 1 ; }
}

check wpa_supplicant
check $dhcpclient
check ip
type notify-send >/dev/null && notify=1

help() {

    cat <<END
    $(figlet $(basename $0))

A minimal wifi network manager
____________________________________________________
        Args --------- Meaning
____________________________________________________

   -n|-new ---------- create new connnection/password
   -s|-term --------- Terminate wpa_supplicant & dhcp client
   -h|-help --------- Help
   -v|-verbose ------ Verbose output
END
exit 0
}

# Generate new network/password 
new_connection() {
   (( don )) || return
    read -r -p "Entere the SSID: " SSID
    __asterisk_prompt_for_passphrase
    read -r -p "Would you like to control wpa_supplicant with its frontend tool wpa_cli?(y/N) " YesNo
    [[ $YesNo =~ [Yy] ]] && echo -e "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=$USER" >  "$WPA_SUPPLICANTCONF"
    [[ $SSID ]] && wpa_passphrase "$SSID" "$passphrase" >> "$WPA_SUPPLICANTCONF"
    (( $(pgrep  wpa_supplicant) )) && terminate
    don=1
    unset YesNo
    main

}



# Teminate connection
terminate() {
    sudo pkill -KILL $dhcpclient
    sudo wpa_cli terminate $Interface && (( notify )) && notify-send "" "$Interface is disconnected"
    exit 
}

#generate asterisk for passphrase prompt
__asterisk_prompt_for_passphrase() {
    unset passphrase
    prompt="Enter Passphrase: "
    while IFS= read -r -p "$prompt" -s -n 1 char
    do
        #Enter - accept passphrase
        if [[ $char == $'\0' ]] ; then
            echo
            break
        fi
        #Backspace
        if [[ $char == $'\177' ]]; then
            prompt=$'\b \b'
            passphrase="${passphrase%?}"
        else
            prompt="*"
            passphrase+="$char"
        fi
    done
}

main() {

    #you may require to mention a driver wrapper with -D nl80211,wext. Refer wpa_supplicant(8)
    [ $( pgrep wpa_supplicant ) ] || { sudo wpa_supplicant -c "$WPA_SUPPLICANTCONF" -i"$Interface" -B && exit_code=$? ; }
    if [[ $exit_code -eq 0 ]];then
        echo "wpa supplicant initialised"
    elif [[ -n $exit_code ]]; then
        echo "wpa supplicant initialisation error: $exit_code"
        exit 1
    fi

    #6. If dhcp client is not running, start it to get a lease:
    if [[ $(pidof $dhcpclient) ]]; then
        sudo $dhcpclient -k $Interface
        sudo $dhcpclient $Interface
    else
    echo "starting $dhcpclient ..."
    echo "scanning for network..."
        sudo $dhcpclient $Interface
    e_status=$?
    if [[ $e_status  -eq 1 ]]; then
        (( notify )) && notify-send "No wifi found" "Check and confirm the network is visible"
        echo "No wifi network found"
        exit 1
    elif [[ $e_status -ne 0 ]]; then
        (( notify )) && notify-send "Wifi problem" "Couldn't initiate connection : $e_status "
        exit $?
    fi
    echo "$dhcpclient started!"
    fi
    echo "  Connection established"
    (( notify )) && notify-send " " "Connection established"                                    
    unset dhcpclient Interface

    if ping google.com -c 1 &>/dev/null; then
        (( notify )) && notify-send "Network is up" "enjoy 🙂"
        return 0
    fi
    (( notify )) && notify-send "😢" "Can't ping google"
}

# generate wpa_supplicant.conf with SSID and passphrase if it doesn't already exist:
if ! [ -s "$WPA_SUPPLICANTCONF" ]; then

    read -r -p "It appears no Network is configured. Would you like to configure one(y/N)? " YN
    [[ $YN =~ [nN] || -z $YN ]] &&  exit 0;
    if [[ $YN =~ [yY] ]]; then
        read -r -p "Would you like to control wpa_supplicant with its frontend tool wpa_cli?(y/N) " YesNo
        [[ $YesNo =~ [Yy] ]] && echo -e "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=$USER" >  "$WPA_SUPPLICANTCONF"
        read -r -p "Entere the SSID: " SSID
        __asterisk_prompt_for_passphrase
        wpa_passphrase "$SSID" "$passphrase" >>  "$WPA_SUPPLICANTCONF"
        chmod 600 "$WPA_SUPPLICANTCONF"
        (( $(pgrep  wpa_supplicant) )) && terminate
        unset YN
        main
        don=1
    else
        echo "Wrong choice" >&2
        echo "try again" >&2
        exit 1
    fi
fi


OPT=$(getopt -o :hntv --long :help,new,term,verbose -a -n 'wifi_s' -- "$@")
eval set -- "$OPT"
unset OPT

while true;do
    case $1 in
        -h|--help) help $1 ;shift ;;
        -n|--new) new_connection ;shift ;;
        -t|--term) terminate ; shift ;;
        -v|--verbose) set -x;shift ;;
        *) main;break;;
    esac
done

The complete script is on my github gist for easier copy pasting.