summaryrefslogtreecommitdiffstats
path: root/src/lib/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/network')
-rw-r--r--src/lib/network368
1 files changed, 368 insertions, 0 deletions
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: