diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/8021x | 275 | ||||
-rwxr-xr-x | src/lib/auto.action | 56 | ||||
-rw-r--r-- | src/lib/connections/README | 28 | ||||
-rw-r--r-- | src/lib/connections/bond | 40 | ||||
-rw-r--r-- | src/lib/connections/bridge | 47 | ||||
-rw-r--r-- | src/lib/connections/ethernet | 279 | ||||
-rw-r--r-- | src/lib/connections/pppoe | 77 | ||||
-rw-r--r-- | src/lib/connections/tunnel | 34 | ||||
-rw-r--r-- | src/lib/connections/tuntap | 28 | ||||
-rw-r--r-- | src/lib/connections/vlan | 28 | ||||
-rw-r--r-- | src/lib/connections/wireless | 116 | ||||
-rw-r--r-- | src/lib/globals | 136 | ||||
-rw-r--r-- | src/lib/network | 368 | ||||
-rw-r--r-- | src/lib/rfkill | 61 |
14 files changed, 1573 insertions, 0 deletions
diff --git a/src/lib/8021x b/src/lib/8021x new file mode 100644 index 0000000..d2ddfe4 --- /dev/null +++ b/src/lib/8021x @@ -0,0 +1,275 @@ +# Usage: wpa_call $interface $call ... +# Wrapper around wpa_cli to deal with supplicant configurations that set a +# non-standard control path. +wpa_call() +{ + local args=("-i" "$1") + shift + + if [[ -n "$WPA_CTRL_DIR" ]]; then + args+=("-p" "$WPA_CTRL_DIR") + elif [[ -n "$WPA_CONF" ]] && grep -q "^[[:space:]]*ctrl_interface=" "$WPA_CONF"; then + WPA_CTRL_DIR=$(grep -m 1 "^[[:space:]]*ctrl_interface=" "$WPA_CONF") + WPA_CTRL_DIR=${WPA_CTRL_DIR#*ctrl_interface=} + if [[ "$WPA_CTRL_DIR" == DIR=* ]]; then + WPA_CTRL_DIR=${WPA_CTRL_DIR:4} + WPA_CTRL_DIR=${WPA_CTRL_DIR%% GROUP=*} + fi + args+=("-p" "$WPA_CTRL_DIR") + fi + report_debug wpa_cli "${args[@]}" "$@" + wpa_cli "${args[@]}" "$@" +} + +# Uses wpa_supplicant to check for association to a network +# wpa_check interface [timeout] +wpa_check() +{ + local timeout=0 INTERFACE="$1" TIMEOUT="${2:-15}" CONDITION="${3:-COMPLETED}" + # CONDITION is required as wired connections are ready at ASSOCIATED not COMPLETED FS#20150 + + while (( timeout < TIMEOUT )); do + ( # Sometimes wpa_supplicant isn't ready so silence errors for 2s only to avoid hiding real errors + if (( timeout < 2 )); then + eval $(wpa_call "$INTERFACE" status 2> /dev/null | grep -F "wpa_state=") + else + eval $(wpa_call "$INTERFACE" status | grep -F "wpa_state=") + fi + [[ "$wpa_state" = "$CONDITION" ]] + ) && return 0 + sleep 1 + (( ++timeout )) + done + echo "$wpa_state" + # wpa_cli -i "$INTERFACE" terminate >/dev/null 2>&1 # callers sometimes called stop_wpa, which does more but seems redundant + # termination should either be handled properly here, or by callers + stop_wpa "$INTERFACE" + return 1 +} + +start_wpa() +{ + local INTERFACE="$1" WPA_CONF="$2" WPA_DRIVER="$3" + shift 3 + local WPA_OPTS="$@" PIDFILE="/run/wpa_supplicant_${INTERFACE}.pid" + + if [[ -n "$WPA_CONF" ]]; then + WPA_CONF="-c$WPA_CONF" + else + WPA_CTRL_DIR="/run/wpa_supplicant" + WPA_CONF="-C$WPA_CTRL_DIR" + fi + + wpa_supplicant -B -P "$PIDFILE" -i "$INTERFACE" -D "$WPA_DRIVER" "$WPA_CONF" $WPA_OPTS + + # wait up to one second for the pid file to appear + timeout_wait 1 '[[ -f "$PIDFILE" ]]'; + return $? +} + +stop_wpa() +{ + local INTERFACE="$1" + # we need this as long as wpa_cli has a different default than netcfg + [[ -z "$WPA_CTRL_DIR" && -z "$WPA_CONF" ]] && WPA_CTRL_DIR="/run/wpa_supplicant" + + # done if wpa_supplicant is already terminated for this interface + [[ -e "$WPA_CTRL_DIR/$INTERFACE" ]] || return + + wpa_call "$INTERFACE" terminate > /dev/null + + # wait up to one second for the pid file to be removed + timeout_wait 1 '[[ ! -f "/run/wpa_supplicant_${INTERFACE}.pid" ]]' || \ + kill "$(< "/run/wpa_supplicant_${INTERFACE}.pid")" &> /dev/null & +} + +wpa_reconfigure() { + wpa_call "$1" reconfigure > /dev/null + return $? +} + +wpa_check_current_essid() { + # usage: wpa_check_current_essid $interface $essid + # check that wpa_supplicant is connected to the right essid + local INTERFACE=$1 ESSID=$2 status + status=$(wpa_call "$INTERFACE" status | grep "^ssid=") + if (( $? == 0 )) && [[ "$status" == "ssid=$ESSID" ]]; then + return 0 + else + return 1 + fi +} + +wpa_find_essid() { + # usage: wpa_find_essid $INTERFACE $ESSID + # look for existence of a given essid. Assumes wpa_supplicant is + # running + result=$(wpa_supplicant_scan_and_find "$1" 5 "$2") + ret=$? + echo $result + report_debug wpa_find_essid "\"$result\"" + return $ret +} + +wpa_find_ap() { + # usage: wpa_find_essid $INTERFACE $ESSID + # look for existence of a given essid. Assumes wpa_supplicant is + # running + bssid=${2,,} # set to lowercase + result=$(wpa_supplicant_scan_and_find "$1" 1 "$bssid") + ret=$? + echo $result + report_debug wpa_find_ap "\"$result\"" + return $ret +} + +wpa_supplicant_scan_and_find() { + #usage: wpa_supplicant_scan_and_find $INTERFACE $FIELD $ITEM + # field = 1 for bssid, 5 for essid + # item = string to lookup + local INTERFACE="$1" FIELD="$2" ITEM="$3" RETRIES=5 scan_ok=0 try + for ((try=0; try < $RETRIES; try++)); do + local found + wpa_call "$INTERFACE" scan > /dev/null + sleep 2 + found=$(wpa_call "$INTERFACE" scan_results | tail -n+2 | cut -f "$FIELD" | grep -F -x -m 1 -- "$ITEM") + (( $? == 0 )) && scan_ok=1 + + # ITEM has been found, echo it + if [[ -n "$found" ]]; then + echo "$found" + return 0 + fi + done + if (( $scan_ok != 1 )); then + report_debug wpa_supplicant_scan_and_find "unable to retrieve scan results" + fi + return 1 +} + +wpa_supplicant_scan() { + local INTERFACE="$1" fields="$2" spawned_wpa=0 essids + # temp file used, as keeping ESSID's with spaces in their name in arrays + # is hard, obscure and kinda nasty. This is simpler and clearer. + + [[ -z "$INTERFACE" ]] && return 1 + essids=$(mktemp --tmpdir essid.XXXXXXXX) + + if [[ "$(wpa_call "$INTERFACE" ping 2> /dev/null)" != "PONG" ]]; then + start_wpa "$INTERFACE" "" "${WPA_DRIVER:-nl80211,wext}" || return 1 + spawned_wpa=1 + fi + + wpa_call "$INTERFACE" scan > /dev/null + # Wait at least 3 seconds for scan results + sleep 3 + # Sometimes, that is not enough (FS#29946) + timeout_wait 7 '! wpa_call "$INTERFACE" status | grep -F -q "wpa_state=SCANNING"' + wpa_call "$INTERFACE" scan_results | + tail -n+2 | + sort -rn -k3 | + sort -u -k5 | + sort -rn -k3 | + cut -f"$fields" > "$essids" + + # Fields are tab delimited + # Remove extraneous output from wpa_cli + # Sort by strength + # Remove duplicates + # Re-sort by strength as the removal disorders the list + # Cut to the AP/essid fields only + + (( $spawned_wpa == 1 )) && stop_wpa "$INTERFACE" + + # File of 0 length, ie. no ssid's. + if [[ ! -s "$essids" ]]; then + rm -f "$essids" + return 1 + fi + + echo "$essids" + return 0 +} + +# Requires already loaded profile +make_wpa_config_file() { + local WPA_CONFD="$STATE_DIR/wpa.$1" + + # make empty tmp dir with correct permissions, rename it + check_make_state_dir + mkdir -p /run/wpa_supplicant + rm -rf "$WPA_CONFD" + mv -f "$(mktemp -d --tmpdir=$STATE_DIR)" "$WPA_CONFD" || return 1 + echo "ctrl_interface=/run/wpa_supplicant" >> "$WPA_CONFD/wpa.conf" + echo "ctrl_interface_group=${WPA_GROUP:-wheel}" >> "$WPA_CONFD/wpa.conf" + [[ $WPA_COUNTRY ]] && echo "country=$WPA_COUNTRY" >> "$WPA_CONFD/wpa.conf" + [[ -n "$ADHOC" ]] && echo "ap_scan=2" >> "$WPA_CONFD/wpa.conf" + echo "$WPA_CONFD/wpa.conf" +} + +# Requires already loaded profile +make_wpa_config() { + case $SECURITY in + none|wep|wpa) + case "${ESSID_TYPE:-ascii}" in + ascii) + echo "ssid=\"$ESSID\"" + ;; + hex) + # Hex ESSID is written unquoted and in lowercase (FS#24333) + echo "ssid=${ESSID,,}" + ;; + *) + report_fail "ESSID_TYPE must be set to 'ascii' or 'hex'." + return 1 + ;; + esac + if [[ -n "$AP" ]]; then + echo "bssid=${AP,,}" + fi + [[ -n "$ADHOC" ]] && echo "mode=1" + ;; + wpa-configsection) + echo "$CONFIGSECTION" + ;; + *) + return 1 + ;; + esac + + # Key management + case $SECURITY in + none) + echo "key_mgmt=NONE" + ;; + wep) + echo "key_mgmt=NONE" + echo "wep_tx_keyidx=0" + if [[ ${KEY:0:2} == "s:" ]]; then # TODO: does wpa_supplicant handle this as expected? + echo "wep_key0=\"${KEY:2}\"" + else + echo "wep_key0=$KEY" + fi + ;; + wpa) + echo "proto=RSN WPA" + if [[ "${#KEY}" -eq 64 ]]; then + echo "psk=$KEY" + else + echo "psk=\"$KEY\"" + fi + ;; + esac + + # Hidden SSID + if checkyesno ${HIDDEN:-no}; then + echo "scan_ssid=1" + fi + + # Priority group for the network + if [[ -n "$PRIORITY" ]]; then + echo "priority=$PRIORITY" + fi +} + +# vim: ft=sh ts=4 et sw=4 tw=0: diff --git a/src/lib/auto.action b/src/lib/auto.action new file mode 100755 index 0000000..1f29364 --- /dev/null +++ b/src/lib/auto.action @@ -0,0 +1,56 @@ +#! /bin/bash + +interface="$1" +ssid="$2" +profile="$3" +action="$4" + +. /usr/lib/network/network +[[ "$profile" ]] && load_profile "$profile" + +case $action in + CONNECT) + if [[ -z $profile ]]; then + # Load interface specific config + [[ -f "$IFACE_DIR/$interface" ]] && source "$IFACE_DIR/$interface" + dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" -K $DHCP_OPTIONS "$interface" + exit $? + fi + if ! DHCPCD_INTERNAL_OPTIONS="-K" $CONN_DIR/ethernet up "$profile"; then + exit 1 # what to do if fail? + fi + + set_profile up "$profile" + + if ! ( eval $POST_UP ); then # JP: sandbox the eval + # failing POST_UP will take interface down + "$CONN_DIR/$ethernet" down "$profile" + exit 1 + fi + ;; + DISCONNECT) + if [[ -z $profile ]]; then + dhcpcd -k "$interface" + exit $? + fi + if ! ( eval $PRE_DOWN ); then # JP: sandbox the eval + exit 1 + fi + if ! "$CONN_DIR/ethernet" down "$profile"; then + exit 1 + fi + if ! ( eval $POST_DOWN ); then # JP: sandbox the eval + exit 1 + fi + set_profile down "$profile" + ;; + LOST|REESTABLISHED) + # Not handled. + exit 0 + ;; + *) + # ??? + exit 1 + ;; +esac + diff --git a/src/lib/connections/README b/src/lib/connections/README new file mode 100644 index 0000000..38ef11a --- /dev/null +++ b/src/lib/connections/README @@ -0,0 +1,28 @@ +Support for connection types is implemented by connection files in + + /usr/lib/network/connections/ + +The file name determines the name of the connection type, so support +for the aviancarrier connection type will be provided by the file: + + /usr/lib/network/connections/aviancarrier + +Files that implement support for a connection type should NOT be +executable. Such files should contain valid Bash code, among which two +functions, namely <connection_type>_up and <connection_type>_down. For +the aviancarrier file this would be: + + aviancarrier_up + aviancarrier_down + +These functions are responsible for bringing the network up and down, +respectively. When the functions are called, three bash files are +already sourced, so all functions and variables in those files are +available. The readily sourced files are: + + /usr/lib/network/network + /usr/lib/network/globals + /etc/network.d/<profile> + +Here, <profile> is the profile file specifying the desired network +configuration. diff --git a/src/lib/connections/bond b/src/lib/connections/bond new file mode 100644 index 0000000..bc5aa95 --- /dev/null +++ b/src/lib/connections/bond @@ -0,0 +1,40 @@ +#! /bin/bash +. /usr/lib/network/network +IFENSLAVE="/sbin/ifenslave" + +bond_up() { + load_profile "$1" + + if [[ -e "/sys/class/net/$INTERFACE" ]]; then + report_fail "Interface $INTERFACE already exists." + exit 1 + else + ip link add dev $INTERFACE type bond + fi + bring_interface up "$INTERFACE" + + for slave in "${SLAVE_INTERFACES[@]}"; do + bring_interface up "$slave" + $IFENSLAVE $INTERFACE $slave + done + + "$CONN_DIR/ethernet" up "$1" + return 0 +} + +bond_down() { + load_profile "$1" + + for slave in "${SLAVE_INTERFACES[@]}"; do + $IFENSLAVE $INTERFACE -d $slave + done + + "$CONN_DIR/ethernet" down "$1" + ip link delete "$INTERFACE" + return 0 +} + +bond_$1 "$2" +exit $? + +# vim: set ts=4 et sw=4: diff --git a/src/lib/connections/bridge b/src/lib/connections/bridge new file mode 100644 index 0000000..6b3ab67 --- /dev/null +++ b/src/lib/connections/bridge @@ -0,0 +1,47 @@ +#! /bin/bash +. /usr/lib/network/network +BRCTL="/usr/sbin/brctl" + +bridge_up() { + local bridge_interface + load_profile "$1" + + if [[ -e "/sys/class/net/$INTERFACE" ]]; then + if [[ ! -d "/sys/class/net/$INTERFACE/brif" ]]; then + report_fail "Interface $INTERFACE already exists and is not a bridge." + exit 1 + fi + else + $BRCTL addbr "$INTERFACE" + fi + + for bridge_client in $BRIDGE_INTERFACES; do + ip link set "$bridge_client" promisc on up + ip addr flush dev "$bridge_client" + $BRCTL addif "$INTERFACE" "$bridge_client" + done + # Set options + [[ "$FWD_DELAY" ]] && $BRCTL setfd "$INTERFACE" "$FWD_DELAY" + [[ "$MAX_AGE" ]] && $BRCTL setmaxage "$INTERFACE" "$MAX_AGE" + + "$CONN_DIR/ethernet" up "$1" + return 0 +} + +bridge_down() { + local bridge_interface + load_profile "$1" + + for bridge_client in $BRIDGE_INTERFACES; do + ip link set "$bridge_client" promisc off down + $BRCTL delif "$INTERFACE" "$bridge_client" + done + + "$CONN_DIR/ethernet" down "$1" + $BRCTL delbr "$INTERFACE" + return 0 +} + +bridge_$1 "$2" +exit $? +# vim: set ts=4 et sw=4: diff --git a/src/lib/connections/ethernet b/src/lib/connections/ethernet new file mode 100644 index 0000000..487adf8 --- /dev/null +++ b/src/lib/connections/ethernet @@ -0,0 +1,279 @@ +#! /bin/bash +# Source file for the 'ethernet' connection +# ethernet_up $profile +# ethernet_down $profile +# ethernet_status + +. /usr/lib/network/network + +report_iproute() +{ + report_fail "$*" + bring_interface down "$INTERFACE" + exit 1 +} + +ethernet_up() { + load_profile "$1" + SYSCTL_INTERFACE="${INTERFACE/.//}" + + if ! is_interface "$INTERFACE"; then + report_iproute "Interface $INTERFACE does not exist" + fi + + # Disable IPv6 before bringing the interface up to prevent SLAAC + if [[ "$IP6" == "no" ]]; then + sysctl -q -w "net.ipv6.conf.$SYSCTL_INTERFACE.disable_ipv6=1" + fi + + report_debug ethernet_up bring_interface up "$INTERFACE" + bring_interface up "$INTERFACE" + + if ! checkyesno "${SKIPNOCARRIER:-no}"; then + # Some cards are plain slow to come up. Don't fail immediately. + if ! timeout_wait "${CARRIER_TIMEOUT:-5}" '(( $(< "/sys/class/net/$INTERFACE/carrier") ))'; then + report_iproute "No connection" + fi + fi + + + if checkyesno "${AUTH8021X:-no}"; then + . "$SUBR_DIR/8021x" + [[ -z "$WPA_CONF" ]] && WPA_CONF="/etc/wpa_supplicant.conf" + [[ -z "$WPA_DRIVER" ]] && WPA_DRIVER="wired" + + report_debug ethernet_up start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_DRIVER" "$WPA_OPTS" + if ! start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_DRIVER" "$WPA_OPTS"; then + report_fail "wpa_supplicant did not start, possible configuration error" + return 1 + fi + + if ! wpa_check "$INTERFACE" "$TIMEOUT" "ASSOCIATED"; then + bring_interface down "$INTERFACE" + report_fail "WPA Authentication/Association Failed" + return 1 + fi + fi + + if [[ -z "$IP" && -z "$IP6" ]]; then + report_iproute "At least one of IP or IP6 should be specified" + return 1 + fi + + case "$IP" in + dhcp) + if checkyesno "${DHCLIENT:-no}"; then + rm -r "/run/dhclient-${INTERFACE}.pid" >/dev/null 2>&1 + report_debug ethernet_up dhclient -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf "/run/dhclient-$INTERFACE.pid" "$INTERFACE" + if ! dhclient -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf "/run/dhclient-${INTERFACE}.pid" ${DHCLIENT_OPTIONS} "$INTERFACE"; then + report_fail "DHCP IP lease attempt failed." + stop_80211x + return 1 + fi + else + # Clear remaining pid files. + rm -f "/run/dhcpcd-$INTERFACE".{pid,cache} >/dev/null 2>&1 + # If using own dns, tell dhcpcd to NOT replace resolv.conf + [[ -n "$DNS" ]] && DHCP_OPTIONS="-C resolv.conf $DHCP_OPTIONS" + # Start dhcpcd + report_debug ethernet_up dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCPCD_INTERNAL_OPTIONS $DHCP_OPTIONS "$INTERFACE" + dhcpcd -qL -t "${DHCP_TIMEOUT:-10}" $DHCPCD_INTERNAL_OPTIONS $DHCP_OPTIONS "$INTERFACE" 2>&1 | report_debug "$(cat)" + if [[ "$PIPESTATUS" -ne 0 ]]; then + report_fail "DHCP IP lease attempt failed." + stop_80211x + return 1 + fi + fi + ;; + static) + if [[ -n "$ADDR" ]]; then + [[ -z $NETMASK ]] && NETMASK=24 + report_debug ethernet_up ip addr add "$ADDR/$NETMASK" brd + dev "$INTERFACE" + if ! ip addr add "$ADDR/$NETMASK" brd + dev "$INTERFACE"; then + report_iproute "Could not configure interface" + fi + fi + if [[ -n "$GATEWAY" ]]; then + report_debug ethernet_up ip route add default via "$GATEWAY" dev "$INTERFACE" + if ! ip route add default via "$GATEWAY" dev "$INTERFACE"; then + report_iproute "Adding gateway $GATEWAY failed" + fi + fi + ;; + ""|no) + ;; + *) + report_iproute "IP must be either 'dhcp', 'static' or 'no'" + ;; + esac + + if [[ -n "$IP" && -n "$ROUTES" ]]; then + for route in "${ROUTES[@]}"; do + report_debug ethernet_up ip route add $route dev "$INTERFACE" + if ! ip route add $route dev "$INTERFACE"; then + report_iproute "Adding route '$route' failed" + fi + done + fi + + # Load ipv6 module if necessary (FS#25530) + case "$IP6" in + dhcp*|stateless|static) + [[ -d "/proc/sys/net/ipv6" ]] || modprobe ipv6 + ;; + no) + [[ -d "/proc/sys/net/ipv6" ]] && sysctl -q -w "net.ipv6.conf.$SYSCTL_INTERFACE.accept_ra=0" + ;; + "") # undefined IP6 does not prevent RA's from being received -> nop + ;; + *) + report_iproute "IP6 must be 'dhcp', 'dhcp-noaddr', 'stateless', 'static' or 'no'" + ;; + esac + + case "$IP6" in + dhcp*) + if ! type dhclient &>/dev/null; then + report_fail "You need to install dhclient to use DHCPv6." + stop_80211x + return 1 + fi + sysctl -q -w "net.ipv6.conf.$SYSCTL_INTERFACE.accept_ra=1" + if [[ "$IP6" == "dhcp-noaddr" ]]; then + DHCLIENT6_OPTIONS="-S ${DHCLIENT6_OPTIONS}" + fi + _DHCLIENT_PIDFILE="/run/dhclient6-${INTERFACE}.pid" + rm -r ${_DHCLIENT_PIDFILE} &>/dev/null + report_debug ethernet_up dhclient -6 -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf ${_DHCLIENT_PIDFILE} "$INTERFACE" + if ! dhclient -6 -q -e TIMEOUT="${DHCP_TIMEOUT:-10}" -pf ${_DHCLIENT_PIDFILE} ${DHCLIENT6_OPTIONS} "$INTERFACE"; then + report_fail "DHCPv6 IP lease attempt failed." + stop_80211x + return 1 + fi + ;; + stateless) + sysctl -q -w "net.ipv6.conf.$SYSCTL_INTERFACE.accept_ra=1" + ;; + static) + sysctl -q -w "net.ipv6.conf.$SYSCTL_INTERFACE.accept_ra=0" + if [[ -n "$ADDR6" ]]; then + for addr in "${ADDR6[@]}"; do + report_debug ethernet_up ip -6 addr add $addr dev "$INTERFACE" + if ! ip -6 addr add $addr dev "$INTERFACE"; then + report_iproute "Could not add address '$addr' to interface" + fi + done + fi + ;; + esac + + if [[ -n "$IP6" ]]; then + # Wait for DAD to finish (FS#28887) + report_debug ethernet_up ip -6 addr show dev "$INTERFACE" tentative + if ! timeout_wait "${DAD_TIMEOUT:-3}" '[[ -z "$(ip -6 addr show dev "$INTERFACE" tentative)" ]]'; then + report_iproute "Duplicate Address Detection is taking too long" + fi + + # Add static IPv6 routes + if [[ -n "$ROUTES6" ]]; then + for route in "${ROUTES6[@]}"; do + report_debug ethernet_up ip -6 route add $route dev "$INTERFACE" + if ! ip -6 route add $route dev "$INTERFACE"; then + report_iproute "Adding route '$route' failed" + fi + done + fi + + # Set a custom gateway after waiting for DAD to finish + if [[ "$IP6" == "static" && -n "$GATEWAY6" ]]; then + report_debug ethernet_up ip -6 route replace default via "$GATEWAY6" dev "$INTERFACE" + if ! ip -6 route replace default via "$GATEWAY6" dev "$INTERFACE"; then + report_iproute "Adding gateway $GATEWAY6 failed" + fi + fi + fi + + if [[ -n "$IPCFG" ]]; then + for line in "${IPCFG[@]}"; do + report_debug ethernet_up ip "$line" + if ! ip $line; then + report_iproute "Could not configure interface ($line)." + fi + done + fi + + # Set hostname + if [[ -n "$HOSTNAME" ]]; then + report_debug ethernet_up hostname "$HOSTNAME" + if ! echo "$HOSTNAME" >/proc/sys/kernel/hostname; then + report_iproute "Cannot set hostname to $HOSTNAME" + fi + fi + + # Generate a new resolv.conf + if [[ -n "$DNS" ]]; then + : >/etc/resolv.conf + [[ -n "$DOMAIN" ]] && echo "domain $DOMAIN" >>/etc/resolv.conf + [[ -n "$SEARCH" ]] && echo "search $SEARCH" >>/etc/resolv.conf + for dns in "${DNS[@]}"; do + echo "nameserver $dns" >>/etc/resolv.conf + done + for dnsoption in "${DNS_OPTIONS[@]}"; do + echo "options $dnsoption" >>/etc/resolv.conf + done + fi + + return 0 +} + +ethernet_down() { + load_profile "$1" + + if [[ "$IP" == "dhcp" ]]; then + if checkyesno "${DHCLIENT:-no}"; then + if [[ -f "/run/dhclient-$INTERFACE.pid" ]]; then + report_debug ethernet_down dhclient -q -x "$INTERFACE" -pf "/run/dhclient-$INTERFACE.pid" + dhclient -q -x "$INTERFACE" -pf "/run/dhclient-$INTERFACE.pid" &>/dev/null + #dhclient -q -r "$INTERFACE" &>/dev/null + fi + else + if [[ -f "/run/dhcpcd-$INTERFACE.pid" ]]; then + report_debug ethernet_down dhcpcd -qk "$INTERFACE" + dhcpcd -qk "$INTERFACE" &>/dev/null + fi + fi + fi + if [[ "$IP6" == dhcp* ]]; then + if [[ -f "/run/dhclient6-$INTERFACE.pid" ]]; then + report_debug ethernet_down dhclient -6 -q -x "$INTERFACE" -pf "/run/dhclient6-$INTERFACE.pid" + dhclient -6 -q -x "$INTERFACE" -pf "/run/dhclient6-$INTERFACE.pid" &>/dev/null + report_debug ethernet_down /bin/kill $(< /run/dhclient6-$INTERFACE.pid) + /bin/kill $(< /run/dhclient6-$INTERFACE.pid) &>/dev/null + fi + fi + + stop_80211x + + if [[ "$CONNECTION" == "wireless" ]]; then + report_debug ethernet_down bring_interface flush "$INTERFACE" + bring_interface flush "$INTERFACE" + else + report_debug ethernet_down bring_interface down "$INTERFACE" + bring_interface down "$INTERFACE" + fi + return 0 +} + +# Stop wpa_supplicant if neccessary +stop_80211x() { + if checkyesno "${AUTH8021X:-no}"; then + . "$SUBR_DIR/8021x" + [[ -z "$WPA_CONF" ]] && WPA_CONF="/etc/wpa_supplicant.conf" + report_debug ethernet_down stop_wpa "$INTERFACE" + stop_wpa "$INTERFACE" + fi +} + +ethernet_$1 "$2" +exit $? +# vim: set ts=4 et sw=4: diff --git a/src/lib/connections/pppoe b/src/lib/connections/pppoe new file mode 100644 index 0000000..17fe42d --- /dev/null +++ b/src/lib/connections/pppoe @@ -0,0 +1,77 @@ +#! /bin/bash +. /usr/lib/network/network + +_quotestring() { + echo "\"${1/\"/\\\"}\"" +} + +pppoe_up() { + local cfg + load_profile "$1" + + mkdir -p "$STATE_DIR/pppoe.${INTERFACE}.$1/" + chmod 700 "$STATE_DIR/pppoe.${INTERFACE}.$1/" + cfg="$STATE_DIR/pppoe.${INTERFACE}.$1/options" + : > "${cfg}" + chmod 600 "${cfg}" + + echo "plugin rp-pppoe.so" >> "${cfg}" + echo "nic-${INTERFACE}" >> "${cfg}" + echo "noauth" >> "${cfg}" + if checkyesno ${DEFAULTROUTE:-1}; then + echo "defaultroute" >> "${cfg}" + else + echo "nodefaultroute" >> "${cfg}" + fi + if checkyesno ${USEPEERDNS:-1}; then + echo "usepeerdns" >> "${cfg}" + fi + echo "linkname $(_quotestring "$1")" >> "${cfg}" + echo "maxfail 5" >> "${cfg}" + echo "updetach" >> "${cfg}" + if [[ ${CONNECTION_MODE} == demand ]]; then + echo "demand" >> "${cfg}" + echo "idle ${IDLE_TIMEOUT}" >> "${cfg}" + else + echo "persist" >> "${cfg}" + fi + echo "user $(_quotestring "${USER}")" >> "${cfg}" + echo "password $(_quotestring "${PASSWORD}")" >> "${cfg}" + [[ -n ${LCP_ECHO_INTERVAL} ]] && echo "lcp-echo-interval ${LCP_ECHO_INTERVAL}" >> "${cfg}" + [[ -n ${LCP_ECHO_FAILURE} ]] && echo "lcp-echo-failure ${LCP_ECHO_FAILURE}" >> "${cfg}" + [[ -n ${PPPOE_SERVICE} ]] && echo "rp_pppoe_service $(_quotestring "${PPPOE_SERVICE}")" >> "${cfg}" + [[ -n ${PPPOE_AC} ]] && echo "rp_pppoe_ac $(_quotestring "${PPPOE_AC}")" >> "${cfg}" + [[ -n ${PPPOE_SESSION} ]] && echo "rp_pppoe_sess $(_quotestring "${PPPOE_SESSION}")" >> "${cfg}" + [[ -n ${PPPOE_MAC} ]] && echo "pppoe-mac $(_quotestring "${PPPOE_MAC}")" >> "${cfg}" + [[ ${PPPOE_IP6} == yes ]] && echo "+ipv6" >> "${cfg}" + + /sbin/ip link set dev "${INTERFACE}" up + /usr/sbin/pppd file "${cfg}" + + if [[ $? -ne 0 ]]; then + rm "${cfg}" + rmdir "$STATE_DIR/pppoe.${INTERFACE}.$1/" + report_fail "Couldn't make pppd connection." + return 1 + fi +} + +pppoe_down() { + load_profile "$1" + local cfg + cfg="$STATE_DIR/pppoe.${INTERFACE}.$1/options" + PIDFILE="/var/run/ppp-$1.pid" + + if [[ -e $PIDFILE ]]; then + read PID < "$PIDFILE" + [[ "$PID" ]] && kill "$PID" + fi + + rm "${cfg}" + rmdir "$STATE_DIR/pppoe.${INTERFACE}.$1/" +} + +pppoe_$1 "$2" +exit $? + +# vim: ft=sh ts=4 et sw=4: diff --git a/src/lib/connections/tunnel b/src/lib/connections/tunnel new file mode 100644 index 0000000..6cefc5c --- /dev/null +++ b/src/lib/connections/tunnel @@ -0,0 +1,34 @@ +#! /bin/bash +. /usr/lib/network/network + +tunnel_up() { + load_profile "$1" + + if [[ -e "/sys/class/net/$INTERFACE" ]]; then + report_fail "Interface $INTERFACE already exists." + exit 1 + else + ip tunnel add "$INTERFACE" mode "$MODE" remote "$REMOTE" + fi + + if [[ -n "$LOCAL" ]]; then + ip tunnel change "$INTERFACE" local "$LOCAL" + fi + + "$CONN_DIR/ethernet" up "$1" + return 0 +} + +tunnel_down() { + load_profile "$1" + + "$CONN_DIR/ethernet" down "$1" + ip tunnel del "$INTERFACE" + + return 0 +} + +tunnel_$1 "$2" +exit $? + +# vim: set ts=4 et sw=4: diff --git a/src/lib/connections/tuntap b/src/lib/connections/tuntap new file mode 100644 index 0000000..6985c8c --- /dev/null +++ b/src/lib/connections/tuntap @@ -0,0 +1,28 @@ +#! /bin/bash +. /usr/lib/network/network + +tuntap_up() { + load_profile "$1" + + if [[ -e /sys/class/net/$INTERFACE ]]; then + report_fail "Interface $INTERFACE already exists." + exit 1 + else + ip tuntap add dev "$INTERFACE" mode "$MODE" \ + user "$USER" group "$GROUP" + fi + IP=${IP-no} "$CONN_DIR/ethernet" up "$1" + return 0 +} + +tuntap_down() { + load_profile "$1" + + "$CONN_DIR/ethernet" down "$1" + ip tuntap del dev "$INTERFACE" mode "$MODE" + return 0 +} + +tuntap_$1 "$2" +exit $? +# vim: set ts=4 et sw=4 tw=0: diff --git a/src/lib/connections/vlan b/src/lib/connections/vlan new file mode 100644 index 0000000..75c7fa9 --- /dev/null +++ b/src/lib/connections/vlan @@ -0,0 +1,28 @@ +#! /bin/bash +. /usr/lib/network/network + +vlan_up() { + load_profile "$1" + + if [[ -e "/sys/class/net/$INTERFACE" ]]; then + report_fail "Interface $INTERFACE already exists." + exit 1 + else + bring_interface up "$VLAN_PHYS_DEV" + ip link add link "$VLAN_PHYS_DEV" name "$INTERFACE" type vlan id "$VLAN_ID" + fi + "$CONN_DIR/ethernet" up "$1" + return 0 +} + +vlan_down() { + load_profile "$1" + + "$CONN_DIR/ethernet" down "$1" + ip link delete "$INTERFACE" + return 0 +} + +vlan_$1 "$2" +exit $? +# vim: set ts=4 et sw=4: diff --git a/src/lib/connections/wireless b/src/lib/connections/wireless new file mode 100644 index 0000000..135bec7 --- /dev/null +++ b/src/lib/connections/wireless @@ -0,0 +1,116 @@ +#! /bin/bash +. /usr/lib/network/network +. "$SUBR_DIR/8021x" +. "$SUBR_DIR/rfkill" + +wireless_up() { + PROFILE="$1" + load_profile "$PROFILE" + + # Default settings + SECURITY=${SECURITY:-none} + WPA_DRIVER=${WPA_DRIVER:-nl80211,wext} + + enable_rf $INTERFACE $RFKILL $RFKILL_NAME || return 1 + + # Check if interface exists + is_interface "$INTERFACE" || { report_fail "interface $INTERFACE does not exist"; return 1; } + + # Kill any lingering wpa_supplicants. + stop_wpa "$INTERFACE" &> /dev/null + + # Start wpa_supplicant + if [[ "$SECURITY" = "wpa-config" ]]; then + WPA_CONF="${WPA_CONF:-/etc/wpa_supplicant.conf}" + else + WPA_CONF=$(make_wpa_config_file $INTERFACE) + fi + report_debug wireless_up start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_DRIVER" "$WPA_OPTS" + if ! start_wpa "$INTERFACE" "$WPA_CONF" "$WPA_DRIVER" "$WPA_OPTS"; then + report_fail "wpa_supplicant did not start, possible configuration error" + return 1 + fi + + # Scan for network's existence first + if checkyesno "${SCAN:-no}"; then + report_debug wireless_up scanning + local OLDESSID="$ESSID" + if [[ -n "$AP" ]]; then + BSSID=$(wpa_find_ap "$INTERFACE" "$AP") + else + ESSID=$(wpa_find_essid "$INTERFACE" "$ESSID") + fi + if [[ $? -gt 0 ]]; then + report_fail "Wireless network \"$OLDESSID\" not present." + report_debug wireless_up stop_wpa "$INTERFACE" + stop_wpa "$INTERFACE" + return 1 + fi + fi + + # Build configuration file + case "$SECURITY" in + wpa-config) + ;; + none|wep|wpa|wpa-configsection) + printf "%s\n" "network={" "$(make_wpa_config)" "}" >> "$WPA_CONF" + report_debug wireless_up "Configuration generated at $WPA_CONF" + report_debug wireless_up wpa_reconfigure "$INTERFACE" + if ! wpa_reconfigure "$INTERFACE"; then + report_fail "WPA configuration failed!" + stop_wpa "$INTERFACE" + return 1 + fi + ;; + *) + report_fail "Invalid SECURITY setting: $SECURITY" + ;; + esac + + # Bring interface up after starting wpa_supplicant + # This is important since cards such as iwl3945 do not support + # mode switching when they are already up. + report_debug wireless_up ifup + bring_interface up "$INTERFACE" || return 1 + + report_debug wireless_up wpa_check + if ! wpa_check "$INTERFACE" "$TIMEOUT"; then + report_fail "WPA Authentication/Association Failed" + return 1 + fi + + if ! "$CONN_DIR/ethernet" up "$PROFILE"; then + wireless_down "$PROFILE" YES + return 1 + fi +} + +# wireless_down PROFILE [ LEAVE ifconfig up? default no ] +wireless_down() { + local PROFILE="$1" + load_profile "$PROFILE" + + "$CONN_DIR/ethernet" down "$PROFILE" + + # The config file can contain a non-standard control socket path + if [[ "$SECURITY" = "wpa-config" ]]; then + WPA_CONF="${WPA_CONF:-/etc/wpa_supplicant.conf}" + fi + report_debug wireless_down stop_wpa "$INTERFACE" + stop_wpa "$INTERFACE" + rm -rf "$STATE_DIR/wpa.$INTERFACE" + + bring_interface down "$INTERFACE" + + # Handle wireless kill switches + # Any reason why a hardware switch should be considered on interface down? + if [[ "$RFKILL" == "soft" ]]; then + set_rf_state "$INTERFACE" disabled $RFKILL_NAME || return 1 + fi +} + +wireless_$1 "$2" "$3" +exit $? + +# vim: ft=sh ts=4 et sw=4 tw=0: + diff --git a/src/lib/globals b/src/lib/globals new file mode 100644 index 0000000..491f9d6 --- /dev/null +++ b/src/lib/globals @@ -0,0 +1,136 @@ +# /usr/lib/networks/globals +# +# All +x files in /usr/lib/network/hooks will be sourced when this file is. +# Hook files can override any of the utility functions defined here for custom +# behavior (such as logging error messages to syslog). This lets us keep netcfg +# simple but gives it the flexibility for users to make modular use of it to do +# more complex things + + +### Globals +PROFILE_DIR="/etc/network.d" +IFACE_DIR="$PROFILE_DIR/interfaces" +SUBR_DIR="/usr/lib/network" +HOOKS_DIR="$SUBR_DIR/hooks" +CONN_DIR="$SUBR_DIR/connections" +STATE_DIR="/run/network" + + + +### Logging/Error reporting + +function report_err { + echo "$*" +} + +function report_notice { + echo "$*" +} + +function report_debug { + checkyesno "$NETCFG_DEBUG" && echo "DEBUG: $*" >&2 +} + +function report_try { + # This needs -n and a trailing space. + echo -n ":: $* " + REPORT_TRYING=1 +} + +function report_fail { + if [[ -n "$*" ]]; then + if (( REPORT_TRYING )); then + echo "- $* [fail]" + REPORT_TRYING= + else + echo "$*" + fi + elif (( REPORT_TRYING )); then + echo "[fail]" + REPORT_TRYING= + fi +} + +function report_success { + if [[ -n "$*" ]]; then + # This needs -n and a trailing space. + echo -n "- $* " + fi + echo "[done]" + REPORT_TRYING= +} + +### For calling scripts only; don't use in library functions +function exit_stderr { echo "$*" >&2; exit 1; } +function exit_err { report_err "$*"; exit 1; } +function exit_fail { report_fail "$*"; exit 1; } + + +### From FreeBSD's /etc/rc.subr +## +# checkyesno var +# Test $1 variable, and warn if not set to YES or NO. +# Return 0 if it's "yes" (et al), nonzero otherwise. +# To default to yes, do: "checkyesno ${VAR:-yes}". +# +function checkyesno() { + local _value="$1" + #debug "checkyesno: $1 is set to $_value." + case "$_value" in + + # "yes", "true", "on", or "1" + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + return 0 + ;; + + # "no", "false", "off", or "0" + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) + return 1 + ;; + *) + #warn "\$${1} is not set properly - see rc.conf(5)." + return 1 + ;; + esac +} + +## Check if variable is a member of an array +# $1: the variable to find +# $2...: the array elements +function inarray() { + local item search="$1" + shift + for item in "$@"; do + if [[ "$item" == "$search" ]]; then + return 0 + fi + done + return 1 +} + +## Waits until a statement succeeds or a timeout occurs +# $1: timeout in seconds +# $2...: condition command +function timeout_wait() { + local timeout="$1" + (( timeout *= 10 )) + shift + while ! eval "$*"; do + (( timeout-- > 0 )) || return 1 + sleep 0.1 + done + return 0 +} + + +### Load all +x files in $HOOKS_DIR +function load_hooks() { + local hook + for hook in $(find -L "$HOOKS_DIR/" -maxdepth 1 -type f -executable | sort -u); do + source "$hook" + done +} + +load_hooks + +# vim: ft=sh ts=4 et sw=4: diff --git a/src/lib/network b/src/lib/network new file mode 100644 index 0000000..1aa384c --- /dev/null +++ b/src/lib/network @@ -0,0 +1,368 @@ +### Globals +. /usr/lib/network/globals +# will load hooks + +## Loads a profile. +# $1: profile name +load_profile() +{ + unset ROUTES + [[ -z "$1" ]] && return 1 + if [[ ! -f "$PROFILE_DIR/$1" ]]; then + report_err "Profile \"$1\" does not exist" + return 1 + fi + report_debug "Loading profile $1" + INTERFACE=$(. "$PROFILE_DIR/$1"; echo "$INTERFACE") + report_debug "Configuring interface $INTERFACE" + if [[ -z "$INTERFACE" ]]; then + report_err "Profile missing an interface to configure" + return 1 + fi + if [[ -f "$IFACE_DIR/$INTERFACE" ]]; then + report_debug "Interface level configuration enabled: $IFACE_DIR/$INTERFACE" + . "$IFACE_DIR/$INTERFACE" + fi + . "$PROFILE_DIR/$1" # we want profile settings to override, so need to source profile again + if [[ ! -f "$CONN_DIR/$CONNECTION" ]]; then + report_err "$CONNECTION is not a valid connection, check spelling or look at examples" + return 1 + fi +} + +################## +# Profile up/down +################## + +## Take all registered profiles down. +all_down() +{ + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + profile_down "$prof" + done +} + +## Create the state dir and sub directories if they don't already exist. +check_make_state_dir() +{ + [[ ! -d "$STATE_DIR" ]] && mkdir -p "$STATE_DIR"/{interfaces,profiles} + for d in interfaces profiles suspend; do + [[ ! -d "$STATE_DIR/$d" ]] && mkdir "$STATE_DIR/$d" + done +} + +## Save the list of running profiles and take them down if needed +# $1: interface name or "all" +# $2: take associated profiles down (optional, default="yes") +interface_suspend() +{ + report_debug interface_suspend "$@" + + check_make_state_dir + find "$STATE_DIR/profiles/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell + INTERFACE=$(. "$STATE_DIR/profiles/$prof"; echo "$INTERFACE") + if [[ "$1" == all || "$1" == "$INTERFACE" ]]; then + report_notice "suspending interface $INTERFACE with profile $prof" + cp "$STATE_DIR/profiles/$prof" "$STATE_DIR/suspend/" + if checkyesno "${2:-yes}"; then + profile_down "$prof" + fi + fi + done +} + +## Save the list of all running profiles and take them down +all_suspend() { + interface_suspend all +} + +## Restore saved profiles (for resume purposes). +# $@: a list of interfaces not to resume (e.g., because they're disabled) +all_resume() +{ + report_debug all_resume "$@" + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell + INTERFACE=$(. "$STATE_DIR/suspend/$prof"; echo "$INTERFACE") + if [[ $# -eq 0 || ! " $* " =~ " $INTERFACE " ]]; then + report_notice "resuming interface $INTERFACE with profile $prof" + profile_up "$prof" + rm -f "$STATE_DIR/suspend/$prof" # if profile_up succeeds, it will have already removed this + fi + done +} + +## Puts up a profile. +# $1: the profile name +profile_up() +{ + ( + # Keep inside subshell so that options from one profile don't cross to others + # exit 1 used in a subshell is effectively exiting a new process + check_make_state_dir + + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PRE_UP/POST_DOWN etc hooks + + load_profile "$PROFILE" || exit 1 + + if check_profile "$PROFILE"; then + report_err "$PROFILE already connected" + exit 1 + fi + + # EXCLUSIVE, network.d/profile: Individual profile is mutually exclusive + if checkyesno "$EXCLUSIVE"; then + all_down + fi + + report_try "$PROFILE up" + + status=$(check_iface "$INTERFACE") + report_debug "status reported to profile_up as: $status" + case "$status" in + external) + report_fail "Interface $INTERFACE externally controlled" + exit 1 + ;; + disabled) + report_fail "Interface $INTERFACE is disabled" + exit 1 + ;; + "") + ;; + *) + if checkyesno "$CHECK"; then + report_fail "Interface $INTERFACE already in use" + exit 1 + + # not necessary to sandbox this call or reload PROFILE afterwards + elif ! interface_down "$INTERFACE"; then + report_fail + exit 1 + fi + ;; + esac + + if ! ( eval $PRE_UP ); then # JP: sandbox the eval so variables don't bleed into current function + report_debug profile_up "PRE_UP failed" + report_fail + exit 1 + fi + + if ! "$CONN_DIR/$CONNECTION" up "$PROFILE"; then + report_debug profile_up "connect failed" + report_fail + # "$CONN_DIR/$CONNECTION" down "$PROFILE" # JP: should we do this to make sure? + exit 1 + fi + + if ! ( eval $POST_UP ); then # JP: sandbox the eval + report_debug profile_up "POST_UP failed" + report_fail + # failing POST_UP will take interface down + "$CONN_DIR/$CONNECTION" down "$PROFILE" + exit 1 + fi + + set_profile up "$PROFILE" + unset EXCLUSIVE + + # Successfully running a new profile; erase any suspended profiles on this interface + local iface="$INTERFACE" + find "$STATE_DIR/suspend/" -maxdepth 1 -type f -printf '%f\n' \ + | while read prof; do + # the pipe to "while read" will create a subshell + INTERFACE=$(. "$STATE_DIR/suspend/$prof"; echo "$INTERFACE") + if [[ "$iface" == "$INTERFACE" ]]; then + rm "$STATE_DIR/suspend/$prof" + fi + done + + report_success + ); return $? +} + +## Puts a profile down. +# $1: the profile name +profile_down() +{ + ( + check_make_state_dir + + local status PROFILE="$1" # save PROFILE in a variable so that it's available to PRE_UP/POST_DOWN etc hooks + + load_profile "$PROFILE" || exit 1 + + status=$(check_iface "$INTERFACE") + report_debug "status reported to profile_down as: $status" + + if [[ "$status" != "$PROFILE" ]]; then + # if interface not available to be controlled by netcfg, then + # any profiles should have been removed by check_iface + # else we get here if another profile is running + report_err "Profile not connected" + exit 1 + fi + + report_try "$PROFILE down" + if [[ "$(check_iface "$INTERFACE")" == "external" ]]; then + report_fail "$interface was connected by another application" + exit 1 + fi + + if ! ( eval $PRE_DOWN ); then # JP: sandbox the eval + report_debug profile_down "PRE_DOWN failed" + # true # JP: did we want failing PRE_DOWN to leave the profile active? + report_fail + exit 1 + fi + + if ! "$CONN_DIR/$CONNECTION" down "$PROFILE"; then + report_debug profile_up "disconnect failed" + report_fail + exit 1 + fi + + if ! ( eval $POST_DOWN ); then # JP: sandbox the eval + report_debug profile_down "POST_DOWN failed" + report_fail + exit 1 + fi + + set_profile down "$PROFILE" + report_success + ); return $? +} + +# interface_down interface +# take interface down +# +interface_down() +{ + local profile=$(check_iface "$1") + case "$profile" in + ""|disabled) return 0 ;; + external) return 1 ;; + *) profile_down "$profile" ;; + esac +} + +# interface_reconnect interface +# reconnects the profile active on interface +interface_reconnect() +{ + local profile=$(check_iface "$1") + case "$profile" in + ""|disabled|external) + return 1 + ;; + *) + profile_down "$profile" + profile_up "$profile" + ;; + esac +} + +## +# check_iface interface +# Return 0 if interface unavailable (in use by a profile or externally, or disabled) +# Return 1 if interface down and available to be used +# +check_iface() { + if [[ -f "$STATE_DIR/interfaces/$1" ]]; then + cat "$STATE_DIR/interfaces/$1" + return 0 + else + return 1 + fi +} + +# list_profiles +# Outputs a list of all profiles +list_profiles() { + # JP: follow aliases with -L, also skip profiles that start with '.' or end with '~' or '.conf' (so profile.conf can be the wpa.conf file for profile) + find -L "$PROFILE_DIR/" -maxdepth 1 -type f -not -name '*~' -not -name '*.conf' -not -name '.*' -printf "%f\n" +} + +# check_profile profile +# Return 0 if profile registered as being up +# Return 1 if profile not registered +# +check_profile() { + [[ -f "$STATE_DIR/profiles/$1" && ! -f "$STATE_DIR/suspend/$1" ]] && return 0 + return 1 +} + +### Status setting functions +## +# set_profile up/down profile +# Set profile state, either up or down +# +set_profile() { + local INTERFACE + if [[ "$1" == "up" ]]; then + INTERFACE=$(. "$PROFILE_DIR/$2"; echo "$INTERFACE") + cp "$PROFILE_DIR/$2" "$STATE_DIR/profiles/" + set_iface up "$INTERFACE" "$2" + elif [[ "$1" == "down" && -f "$STATE_DIR/profiles/$2" ]]; then # JP: skip if profile not already up + INTERFACE=$(. "$STATE_DIR/profiles/$2"; echo "$INTERFACE") + rm "$STATE_DIR/profiles/$2" + set_iface down "$INTERFACE" "$2" + fi +} + +# set_iface up/down interface [profile] +# Set interface status to up/down +# optionally link it to a profile. +# +set_iface() { + local PROFILE="${3:-external}" + if [[ "$1" == "up" ]]; then + echo "$PROFILE" > "$STATE_DIR/interfaces/$2" + elif [[ "$1" == "down" ]]; then + rm -f "$STATE_DIR/interfaces/$2" # JP: add -f so we don't complain if the interface isn't up + fi +} + +is_interface() { + local INTERFACE="$1" + if [[ ! -e "/sys/class/net/$INTERFACE" ]]; then + if ! echo "$INTERFACE" | grep -F -q ":"; then + return 1 + fi + fi + return 0 +} + +interface_is_up() { + local flags + read flags < "/sys/class/net/$1/flags" + # IFF_UP is defined as 0x1 in linux/if.h + (( flags & 0x1 )) +} + +## Changes a network interface state. +# $1: up, flush, or down. +# $2: the interface name +bring_interface() +{ + local INTERFACE="$2" + case "$1" in + up) + ip link set dev "$INTERFACE" up &>/dev/null + timeout_wait "${UP_TIMEOUT:-5}" 'interface_is_up "$INTERFACE"' || return 1 + ;; + flush|down) + ip addr flush dev "$INTERFACE" &>/dev/null + ;;& + down) + ip link set dev "$INTERFACE" down &>/dev/null + ;; + esac +} + +# vim: ft=sh ts=4 et sw=4: diff --git a/src/lib/rfkill b/src/lib/rfkill new file mode 100644 index 0000000..1832dc1 --- /dev/null +++ b/src/lib/rfkill @@ -0,0 +1,61 @@ +set_rf_state() { + local INTERFACE="$1" state="$2" RFKILL_NAME="$3" + + if [[ "$RFKILL" == "hard" ]]; then + report_fail "Cannot set state on hardware rfkill switch" + return 1 + fi + local path=$(get_rf_path "$INTERFACE" "$RFKILL_NAME") || return 1 + case "$state" in + enabled) + echo 0 > "$path/soft" + ;; + disabled) + echo 1 > "$path/soft" + ;; + esac +} + +get_rf_path() { + local INTERFACE="$1" RFKILL_NAME="$2" path + + if [[ -n "$RFKILL_NAME" ]]; then + for path in /sys/class/rfkill/*; do + if [[ "$(< "$path/name")" == "$RFKILL_NAME" ]]; then + echo "$path" + return 0 + fi + done + report_fail "no rfkill switch with name $RFKILL_NAME" + else + path=$(find -L "/sys/class/net/$INTERFACE/" -maxdepth 2 -type d -name "rfkill*" 2> /dev/null | head -n 1) + if [[ -n "$path" ]]; then + echo "$path" + return 0 + fi + report_fail "no rfkill switch available on interface $INTERFACE" + fi + return 1 +} + +enable_rf() { + local INTERFACE="$1" RFKILL="$2" RFKILL_NAME="$3" path hard soft + + # Enable rfkill if necessary, or fail if it is hardware + if [[ -n "$RFKILL" ]]; then + path=$(get_rf_path "$INTERFACE" "$RFKILL_NAME") || return 1 + read hard < "$path/hard" + read soft < "$path/soft" + + if (( hard )); then + report_fail "radio is disabled on $INTERFACE" + return 1 + elif (( soft )); then + set_rf_state "$INTERFACE" enabled "$RFKILL_NAME" || return 1 + timeout_wait 1 "(( ! \$(< \"$path/soft\") ))" + fi + fi +} + +# vim: ft=sh ts=4 et sw=4: + |