diff --git a/Makefile b/Makefile index 147ec78..5a44d07 100644 --- a/Makefile +++ b/Makefile @@ -108,7 +108,8 @@ EXECUTOR_SCRIPTS_OPT ?= \ gre \ wireguard \ ethtool \ - batman + batman \ + wifi EXECUTOR_SCRIPTS ?= ${EXECUTOR_SCRIPTS_CORE} ${EXECUTOR_SCRIPTS_OPT} @@ -163,6 +164,7 @@ MANPAGES_5 = \ doc/interfaces-ppp.5 \ doc/interfaces-vrf.5 \ doc/interfaces-vxlan.5 \ + doc/interfaces-wifi.5 \ doc/interfaces-wireguard.5 MANPAGES_7 = \ diff --git a/doc/interfaces-wifi.scd b/doc/interfaces-wifi.scd new file mode 100644 index 0000000..a169723 --- /dev/null +++ b/doc/interfaces-wifi.scd @@ -0,0 +1,63 @@ +interfaces-wifi(5) + +# NAME + +*interfaces-wifi* - WiFi vocabulary for the interfaces(5) file format + +# DESCRIPTION + +Wi-Fi (the IEEE 802.11 family of protocols) is a commonly used wireless +networking standard. The following options allow for configuration of +Wi-Fi client interfaces. + +WPA-secured networks are managed using *wpa_supplicant*(8), while insecure +networks are managed directly with *iwconfig*(8). + +# WIFI-RELATED OPTIONS + +*wifi-config-path* _path_ + Denotes the absolute _path_ to a *wpa_supplicant* configuration file. + If no path is given, _/run/wpa_supplicant..conf_ will be + used for a temporary configuration file. This option may not be used + with other configuration options. + +*wifi-ssid* _ssid_ + The SSID the Wi-Fi client should connect to. + +*wifi-psk* _psk_ + The passphrase for connecting to the Wi-Fi network. If unset, the + client will connect without WPA2 encryption. + +# EXAMPLES + +A typical setup may involve connecting to a home and work network. To +achieve this, we can define a pair of virtual interfaces called *wifi-home* +and *wifi-work*, which connect to their respective wifi networks: + +``` +iface wifi-home + use dhcp + wifi-ssid HomeNetwork + wifi-psk ExamplePassphrase + +iface wifi-work + use dhcp + wifi-config-path /etc/network/wpa-work.conf +``` + +The virtual interfaces can be used with *ifup* and *ifdown*: + +``` +# ifup wlan0=wifi-home +# ifdown wlan0 +# ifup wlan0=wifi-work +``` + +# SEE ALSO + +*iwconfig*(8)++ +*wpa_supplicant*(8) + +# AUTHORS + +Ariadne Conill diff --git a/doc/interfaces.scd b/doc/interfaces.scd index 15c43e6..d1131ca 100644 --- a/doc/interfaces.scd +++ b/doc/interfaces.scd @@ -178,6 +178,13 @@ most common executors are: The interface is a Virtual Extensible LAN (VXLAN) tunnel endpoint. +*wifi* + The interface is a Wi-Fi (IEEE 802.11) client interface. + Configuration of the WiFi client interface requires the + *wireless-tools* package to be installed. + The *wpa_supplicant* package must also be installed to + connect to hotspots using WPA-based security. + *wireguard* The interface is a Wireguard VPN tunnel endpoint. diff --git a/executor-scripts/linux/wifi b/executor-scripts/linux/wifi new file mode 100755 index 0000000..93243f1 --- /dev/null +++ b/executor-scripts/linux/wifi @@ -0,0 +1,119 @@ +#!/bin/sh +# Copyright (c) 2020 Ariadne Conill +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# This software is provided 'as is' and without any warranty, express or +# implied. In no event shall the authors be liable for any damages arising +# from the use of this software. +# +# Manage wifi connections using wpa_supplicant. +# +# Vocabulary: +# wifi-ssid - The SSID name to connect to. +# wifi-psk - The pre-shared key to use. +# wifi-config - A path to a wpa_supplicant config file, for special configs. +# +# If wifi-config is not set, wifi-ssid and wifi-psk are required, and a config +# will be generated as /run/wpa_supplicant.$IFACE.conf. +# +# The wpa_supplicant PID is stored in /run/wpa_supplicant.$IFACE.pid. + +die() { + printf "ERROR: %s\n" "$1" >&2 + exit 1 +} + +[ -z "$IFACE" ] && die "IFACE not set" +[ -z "$PHASE" ] && die "PHASE not set" +PIDFILE="/run/wpa_supplicant.$IFACE.pid" + +# Do not allow mixing wifi-config-path and wifi-ssid/wifi-psk. +[ -n "$IF_WIFI_CONFIG_PATH" -a -n "$IF_WIFI_SSID" ] && die "wifi-config-path cannot be used with wifi-ssid" +[ -n "$IF_WIFI_CONFIG_PATH" -a -n "$IF_WIFI_PSK" ] && die "wifi-config-path cannot be used with wifi-psk" + +# Set IF_WIFI_CONFIG_PATH to the default path if not already set. +WIFI_CONFIG_PATH="$IF_WIFI_CONFIG_PATH" +[ -z "$WIFI_CONFIG_PATH" ] && WIFI_CONFIG_PATH="/run/wpa_supplicant.$IFACE.conf" + +# Supplicant options. +WPA_SUPPLICANT_OPTS="-qq -B -i$IFACE -c$WIFI_CONFIG_PATH -P$PIDFILE" + +# Given $IF_WIFI_SSID and $IF_WIFI_PSK, generate a config file at $WIFI_CONFIG_PATH. +generate_config() { + [ -z "$IF_WIFI_SSID" ] && die "wifi-ssid not set" + [ -z "$IF_WIFI_PSK" ] && die "wifi-psk not set" + + # We use a pipeline here to avoid leaking PSK into the process name. + (echo $IF_WIFI_PSK | /sbin/wpa_passphrase $IF_WIFI_SSID) >$WIFI_CONFIG_PATH + + [ ! -e "$WIFI_CONFIG_PATH" ] && die "failed to write temporary config: $WIFI_CONFIG_PATH" +} + +# Should we use the supplicant? +use_supplicant() { + [ -n "$IF_WIFI_CONFIG_PATH" ] && return 0 + [ -n "$IF_WIFI_PSK" ] && return 0 + + return 1 +} + +# Either start a supplicant process for $IFACE, or use iwconfig to trigger an +# association attempt. +start() { + if use_supplicant; then + # If there is no config file located at $WIFI_CONFIG_PATH, generate one. + [ ! -e "$WIFI_CONFIG_PATH" ] && generate_config + + /sbin/wpa_supplicant $WPA_SUPPLICANT_OPTS + else + /usr/sbin/iwconfig $IFACE essid -- "$IF_WIFI_SSID" ap any + fi +} + +# Stop wpa_supplicant safely +stop_wpa_supplicant() { + # Remove generated config file + [ -z "$IF_WIFI_CONFIG_PATH" ] && rm -- "$WIFI_CONFIG_PATH" + + # If there is no PIDFILE, there is nothing we can do + [ ! -d "$PIDFILE" ] && return + + pid=$(cat "$PIDFILE") + rm -- "$PIDFILE" + + # If there is no process with this PID running, we're done here + if [ ! -d "/proc/$pid/" ]; then + return + fi + + # Verify that the name of the running process matches wpa_supplicant + progname_path=$(readlink -n "/proc/$pid/exe") + progname=$(basename "$progname_path") + if [ "$progname" = "wpa_supplicant" ]; then + kill -9 $pid 2>/dev/null + fi +} + +# Either stop the supplicant process for $IFACE, or use iwconfig to dissociate +# from the current SSID. +stop() { + if use_supplicant; then + stop_wpa_supplicant + else + /usr/sbin/iwconfig $IFACE essid any + fi +} + +[ -z "$VERBOSE" ] || set -x + +case "$PHASE" in +pre-up) + start + ;; +post-down) + stop + ;; +esac