summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/8021x275
-rwxr-xr-xsrc/lib/auto.action56
-rw-r--r--src/lib/connections/README28
-rw-r--r--src/lib/connections/bond40
-rw-r--r--src/lib/connections/bridge47
-rw-r--r--src/lib/connections/ethernet279
-rw-r--r--src/lib/connections/pppoe77
-rw-r--r--src/lib/connections/tunnel34
-rw-r--r--src/lib/connections/tuntap28
-rw-r--r--src/lib/connections/vlan28
-rw-r--r--src/lib/connections/wireless116
-rw-r--r--src/lib/globals136
-rw-r--r--src/lib/network368
-rw-r--r--src/lib/rfkill61
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:
+