#!/bin/sh

# Copyright (c) 2016, Justin D Holcomb All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Exits if tap is locked by a guest
__exit_if_tap_locked() {
	local _tap="$1"
	__get_pid_locking_tap $_tap
	if [ -n "$_PID_locking_tap" ]; then
		__gvset_guest_name_by_pid $_PID_locking_tap
		__fault_detected_exit "$_tap belongs to $_GUEST_name_by_pid which is still running, stop the guest before making network changes."
	fi
}

# Get bridge members on system
__get_bridge_members_on_system() {
	local _bridge="$1"

	ifconfig $_bridge > /dev/null 2>&1
	if [ "$?" = 0 ]; then
		_BRIDGE_members_on_system=$( ifconfig $_bridge | grep member | cut -d' ' -f2 )
	else
		_BRIDGE_members_on_system=""
	fi
}

# Get outside interface for bridge
__get_bridge_phy_iface_chyves() {
	local _bridge="$1"
	_BRIDGE_phy_iface_chyves=$( __return_property_value_from_config_file "global" "${_bridge}_phy_attach" )

	if [ -z "$_BRIDGE_phy_iface_chyves" ]; then
		__fault_detected_exit "Outside interface not configured for $_bridge. Please set to 'private' if no vlan or physical interface is going to be attached."
	fi
}

# Get a list of bridges on chyves
__get_bridge_list_on_chyves() {
	local _var1=$( grep -o -E '^bridge[0-9]{1,5}' /chyves/$_PRIMARY_POOL/.config/global.cfg | sort | uniq )
	_BRIDGE_list_on_chyves="$_var1"
}

# Get a tap's bridge membership as set in .config as a chyves properties
__get_parent_bridge_for_tap_chyves() {
	local _tap="$1"
	local _flag="$2"
	_PARENT_bridge_for_tap_chyves=$( grep -E "bridge[0-9]{1,5}_tap_members=.*$_tap$|bridge[0-9]{1,5}_tap_members=.*$_tap,.*" /chyves/$_PRIMARY_POOL/.config/global.cfg | cut -d'_' -f1 )

	if [ -z "$_PARENT_bridge_for_tap_chyves" ]; then
		if [ -z "$_flag" ]; then
			__fault_detected_exit "$_tap not found to be configured in chyves for a bridge interface. Please add to a bridge: 'chyves network bridge{n} join $_tap'"
		fi
	fi
}

# Get a list of interfaces on system
__get_iface_list_on_system() {
	local _type="$1"    # vlan|bridge|tap|<kernel-drv>|$null
	local _omit='^lo$'  # | separated list

	if [ -z "$_type" ]; then
		_IFACE_list_on_system=$( ifconfig -l | tr ' ' '\n' | grep -v -E "$_omit" )
	else
		_IFACE_list_on_system=$( ifconfig -l | tr ' ' '\n' | grep -v -E "$_omit" | grep "$_type" )
	fi
}

# Get a tap's guest membership
__get_parent_guest_for_tap_chyves() {
	local _tap="$1"

	# Search each guest config for tap
	for _guest in `echo "$_GUEST_NAMES_ACTIVE" | tr ',' ' '`
	do
		_GUEST_name="$_guest"
		__gvset_guest_pool
		if [ -n "$( grep -E "^net_ifaces=.*$_tap$|^net_ifaces=.*$_tap,.*" /chyves/$_GUEST_pool/guests/$_GUEST_name/.config/.cfg )" ]; then
			if [ -z "$_PARENT_guest_for_tap_chyves" ]; then
				_PARENT_guest_for_tap_chyves="$_guest"
			else
				_PARENT_guest_for_tap_chyves="$_PARENT_guest_for_tap_chyves,$_guest"
			fi
		fi
	done

	# Exits if no parent is found.
	if [ -z "$_PARENT_guest_for_tap_chyves" ]; then
		__log 1 "$_tap not found to be configured in chyves for a guest."
	fi

	__log 4 "\$_PARENT_guest_for_tap_chyves: $_PARENT_guest_for_tap_chyves"
}

# Get PID on tap
__get_pid_locking_tap() {

	if [ -z "$( ifconfig -l | tr ' ' '\n' | grep -w -E "^$1\$" )" ]; then
		return
	fi

	local _tap="$1"
	_PID_locking_tap=$( ifconfig $_tap | grep "Opened by PID" | cut -d' ' -f4 )
}

# Get a list of taps on a bridge in chyves properties
__get_tap_list_for_bridge_on_chyves() {
	local _bridge="$1"
	_TAP_list_for_bridge_on_chyves=$( __return_property_value_from_config_file "global" "${_bridge}_tap_members" | tr ',' '\n' )
}

# Used to associate a bridge with a physical or vlan interface interface
__network_attach() {
	local _device="$1"
	local _dev2="$2"

	__verify_valid_system_iface "$_device" -c
	__write_property_value_to_config_file "global" "${_device}_phy_attach" "$_dev2"
	__network_add_dev_to_bridge "$_device" "$_dev2"
}

# Add tap/vale interface to guest
# Part of this function is duplicated in __clone_guest
__network_add() {
	local _bridge _dev2 _dev2_type _guest _guest_net_config
	local _dev2="$1"
	local _bridge="$2"
	local _guest="$3"      # Optional, used for clone to set property for non-$_GUEST_name

	# Re-populate network type for $_dev2
	__verify_valid_iface_format "$_dev2"
	local _dev2_type="$_IFACE_type"

	# tap specific tasks
	if [ "$_dev2_type" = "tap" ]; then

		# If bridge parameter not supplied use value from defaults
		[ -z "$_bridge" ] && local _bridge="$( __return_property_value_from_config_file "defaults" "bridge" )"

		# Join tap to bridge
		__network_bridge_join "$_bridge" "$_dev2"
	fi

	# Get net config for guest
	local _guest_net_config=$( __return_property_value_from_config_file "guest" "net_ifaces" )

	# Set variable for new net configuration
	if [ "$_guest_net_config" = "-" ] || [ "$_guest_net_config" = "" ]; then
		local _guest_net_config="$_dev2"
	else
		local _guest_net_config="$_guest_net_config,$_dev2"
	fi

	# Set net string as chyves property
	__write_property_value_to_config_file "guest" "net_ifaces" "$_guest_net_config"
}

# Remove tap/vale interface from guest
__network_remove() {
	local _bridge _dev2 _dev2_type _guest_net_config
	local _dev2="$1"

	# Re-populate network type for $_dev2
	__verify_valid_iface_format "$_dev2"
	local _dev2_type="$_IFACE_type"

	# tap specific tasks
	if [ "$_dev2_type" = "tap" ]; then

		# Verify tap is in use.
		__verify_tap_not_in_use "$_dev2" -inverse-exit

		# Set bridge to remove tap from. This GV was set by a func call in __verify_tap_not_in_use
		local _bridge="$_PARENT_bridge_for_tap_chyves"

		# Unjoin the tap from the bridge in chyves
		[ "$_NETWORK_DESIGN_MODE" = "auto" ] && __network_bridge_unjoin "$_bridge" "$_dev2"
	fi

	__network_remove_guest_property_backend "$_dev2"
}

# Remove tap/vale interface from guest configuration
__network_remove_guest_property_backend() {
	local _device_to_remove="$1"

	# Get config for guest
	local _guest_net_config=$( __return_property_value_from_config_file "guest" "net_ifaces" )

	# Set variable for new net configuration
	# Expand string from comma separated, remove intended device, compress string back to comma separated, remove trailing commas.
	local _guest_net_config=$( echo "${_guest_net_config}" | tr ',' '\n' | grep -v -E "^$_device_to_remove$" | tr '\n' ',' | sed -e 's/,$//g' )

	# A dash is preferred rather than $null
	[ -z "$_guest_net_config" ] && local _guest_net_config="-"

	# Set net string as chyves property
	__write_property_value_to_config_file "guest" "net_ifaces" "$_guest_net_config"
}

# Join a tap device to a bridge membership
__network_bridge_join() {
	local _device _dev2 _dev2_type _guest_net_config
	local _device="$1"
	local _dev2="$2"
	local _bridge_config_membership=$( __return_property_value_from_config_file "global" "${_device}_tap_members" )

	# Re-populate network type for $_dev2
	__verify_valid_iface_format "$_dev2"
	local _dev2_type="$_IFACE_type"

	# Do not allow tap to be join if used elsewhere
	[ "$_dev2_type" = "tap" ] && __verify_tap_not_in_use $_dev2

	if [ "$_bridge_config_membership" = "-" ] || [ "$_bridge_config_membership" = "" ]; then
		__write_property_value_to_config_file "global" "${_device}_tap_members" "$_dev2"
		__verify_valid_system_iface $_dev2 -c
		__network_add_dev_to_bridge $_device $_dev2
	elif [ -n "$( echo "$_bridge_config_membership" | grep -w "$_dev2" )" ]; then
		__fault_detected_exit "$_dev2 is already a member of $_device"
	else
		local _bridge_config_membership=$( echo "${_bridge_config_membership},$_dev2" )
		__write_property_value_to_config_file "global" "${_device}_tap_members" "$_bridge_config_membership"
		__verify_valid_system_iface $_dev2 -c
		__network_add_dev_to_bridge $_device $_dev2
	fi
}

# Migrates chyve devices on a bridge to another bridge and sets properties as necessary
__network_bridge_migrate() {
	local _current_bridge _future_bridge _future_bridge_type _guest_net_config
	local _current_bridge="$1"
	local _future_bridge="$2"
	local _bridge_config_membership=$( __return_property_value_from_config_file "global" "${_current_bridge}_tap_members" )
	local _bridge_phy=$( __return_property_value_from_config_file "global" "${_current_bridge}_phy_attach" )
	local _future_bridge_config_membership=$( __return_property_value_from_config_file "global" "${_future_bridge}_tap_members" )

	__log 1 "Migrating $_current_bridge to $_future_bridge."

	[ "$_future_bridge_config_membership" != "" ] && __log 1 "Merging $_current_bridge devices with device present on $_future_bridge ($_future_bridge_config_membership)"

	if [ "$_bridge_config_membership" != "" ]; then
		__log 2 "Removing each tap from $_current_bridge ($_bridge_config_membership)"
		for _tap_to_remove in `echo $_bridge_config_membership | tr ',' '\n'`
		do
			__exit_if_tap_locked $_tap_to_remove
			__network_remove_dev_from_bridge "$_current_bridge" $_tap_to_remove
		done
	fi

	if [ "$_bridge_phy" = "private" ] || [ "$_bridge_phy" = "-" ] || [ "$_bridge_phy" = "" ]; then
	else
		__log 2 "Removing $_bridge_phy from $_current_bridge"
		__network_remove_dev_from_bridge "$_current_bridge" $_bridge_phy
	fi

	__log 2 "Setting taps from $_current_bridge to $_future_bridge"
	__write_property_value_to_config_file "global" "${_future_bridge}_tap_members" "$_bridge_config_membership"
	[ "$_GDP_parameters_loaded" != 1 ] && __load_guest_default_parameters

	# If the bridge name is the same as the .defaults one, update it too.
	if [ "$_GDP_bridge" = "$_current_bridge" ]; then
		__log 2 "Updating default bridge for newly created guests in guest defaults."
		__write_property_value_to_config_file "defaults" "bridge" "$_future_bridge"
		__load_guest_default_parameters
	fi

	for _tap_to_add in `echo "$_bridge_config_membership,$_future_bridge_config_membership" | tr ',' '\n' | grep -E [:space:]`
	do
		__network_add_dev_to_bridge $_future_bridge $_tap_to_add
	done

	if [ -n "$_bridge_phy" ]; then
		__log 2 "Adding $_bridge_phy to $_future_bridge"
		__network_add_dev_to_bridge "$_future_bridge" $_bridge_phy
		__write_property_value_to_config_file "global" "${_future_bridge}_phy_attach" "$_bridge_phy"
	fi

	__log 2 "Deleting chyves property '${_current_bridge}_phy_attach'"
	zfs inherit chyves:${_current_bridge}_phy_attach $_PRIMARY_POOL/chyves/.config

	__log 2 "Deleting chyves property '${_current_bridge}_tap_members'"
	zfs inherit chyves:${_current_bridge}_tap_members $_PRIMARY_POOL/chyves/.config

	__get_bridge_members_on_system $_current_bridge
	if [ -z "$_BRIDGE_members_on_system" ]; then
		__log 2 "Destroying $_current_bridge as no members on device."
		ifconfig $_current_bridge destroy
	fi

	__log 1 "Done migrating $_current_bridge to $_future_bridge."
}

# Unjoin a tap device from a bridge membership
__network_bridge_unjoin() {
	local _bridge _bridge_config_membership _tap
	local _bridge=$1
	local _tap="$2"
	local _bridge_config_membership=$( __return_property_value_from_config_file "global" "${_bridge}_tap_members" )

	# Do not allow tap to be moved while guest is running
	__exit_if_tap_locked $_tap

	# Exit if tap not configured on bridge
	__verify_iface_on_bridge "$_bridge" "$_tap"

	# Expand string from comma separated, remove intended device, compress string back to comma separated.
	local _bridge_config_membership=$( echo "${_bridge_config_membership}" | tr ',' '\n' | grep -v $_tap | grep -v -E "^$" | tr '\n' ',' | sed -e 's/,$//g' )

	# Populate an empty $_bridge_config_membership with "-" as this is the empty indicator
	[ -z "$_bridge_config_membership" ] && local _bridge_config_membership="-"

	# Set chyves property
	__write_property_value_to_config_file "global" "${_bridge}_tap_members" "$_bridge_config_membership"

	# Remove
	__network_remove_dev_from_bridge $_bridge $_tap
}

# Adds network device to bridge
__network_add_dev_to_bridge() {
	local _bridge="$1"
	local _dev="$2"
	__verify_valid_system_iface "$_bridge" -c
	__get_bridge_members_on_system $_bridge

	local _bridge_conf=$_BRIDGE_members_on_system

	if [ -z "$( echo "$_bridge_conf" | grep -w "$_dev" )" ]; then
		__log 1 "Adding '$_dev' as a member of '$_bridge'..." -n

		# Handling when MTU on bridge is not 1500
		_bridge_mtu="$( ifconfig $_bridge | grep -o -E 'mtu [0-9]{1,}' | grep -o -E '[0-9]{1,}$' )"
		if [ "$_bridge_mtu" != 1500 ]; then
			__log 1 "Setting MTU on $_dev to $_bridge_mtu"
			ifconfig "$_dev" mtu "$_bridge_mtu"
		fi

		ifconfig "$_bridge" addm "$_dev"
		if [ "$?" != 0 ]; then
			__fault_detected_exit "'$_dev' can not be added to '$_bridge' membership."
		else
			__log 1 " done."
		fi
		ifconfig "$_bridge" up
		ifconfig "$_dev" up
	elif [ -n "$( echo "$_bridge_conf" | grep -w "$_dev" )" ]; then
		__log 1 "'$_dev' already a member of '$_bridge'"
	fi
}

# Adds outside interface to bridge with error handling
__network_add_phy_to_bridge() {
	local _bridge="$1"
	__get_bridge_phy_iface_chyves $_bridge
	local _outside_iface=$_BRIDGE_phy_iface_chyves
	__verify_valid_system_iface $_outside_iface

	# Join the outside interface to the bridge if not a private bridge
	__get_bridge_members_on_system $_bridge
	if [ "$_outside_iface" != "private" ]; then

		# Make a first attempt to get the outside interface connected to bridge
		if [ -z "$( echo "$_BRIDGE_members_on_system" | grep -w "$_outside_iface" )" ]; then
			__network_add_dev_to_bridge $_bridge $_outside_iface

			# Poll system again
			__get_bridge_members_on_system $_bridge

			# Make second attempt
			if [ -z "$( echo "$_BRIDGE_members_on_system" | grep -w "$_outside_iface" )" ]; then
				__log 1 "Could not join $_outside_iface to $_bridge there is no outside network connectivity."
			fi

		# Already member of bridge
		else
			__log 1 "$_outside_iface already member of $_bridge"
		fi
	else
		__log 1 "$_bridge is set as a private bridge. No outside interfaces connected."
	fi
}

# Removes network device from bridge
__network_remove_dev_from_bridge() {
	local _bridge="$1"
	local _dev="$2"
	__verify_valid_system_iface $_bridge -n
	__get_bridge_members_on_system $_bridge
	local _bridge_conf=$( ifconfig "$_bridge" )

	if [ -n "$( echo "$_BRIDGE_members_on_system" | grep -w "$_dev" )" ]; then
		__log 1 "Removing '$_dev' as a member of '$_bridge'... " -n
		ifconfig "$_bridge" deletem "$_dev"
		if [ "$?" != 0 ]; then
			__log 1 "Failed to remove '$_dev' from '$_bridge' membership on host."
		fi
		__log 1 " done."
	else
		__log 1 "'$_dev' was not a member of '$_bridge'."
	fi
}

# Makes a bridge private
__network_private() {
	local _bridge="$1"
	__get_bridge_members_on_system "$_bridge"

	# Remove any physical or vlan interfaces from
	for _phy_on_bridge in `echo "$_BRIDGE_members_on_system" | grep -E "$_FREEBSD_NET_DRIVERS_GREP_STRING|^${vlan_iface_base_name}[0-9]?[0-9]?$" | tr '\n' ' '`
	do
		__log 2 "Removing $_phy_on_bridge from $_bridge"
		__network_remove_dev_from_bridge "$_bridge" "$_phy_on_bridge"
	done

	__write_property_value_to_config_file "global" "${_bridge}_phy_attach" "private"
}

# Verifies guest has iface configured for it.
__verify_iface_on_bridge() {
	local _guest_net_config _iface_in_question
	local _bridge_in_question="$1"
	local _iface_in_question="$2"
	local _bridge_config_membership=$( __return_property_value_from_config_file "global" "${_bridge_in_question}_tap_members" )

	if [ "$_bridge_config_membership" = "-" ]; then
		__fault_detected_exit "No members of $_bridge configured for chyves."
	elif [ -z "$( echo "$_bridge_config_membership" | grep "$_iface_in_question" )" ]; then
		__fault_detected_exit "$_iface_in_question not configured on $_bridge_in_question."
	fi
}

# Verifies guest has iface configured for it.
__verify_iface_on_guest() {
	local _guest_net_config _iface_in_question
	local _iface_in_question="$1"

	if [ -z "$_GP_net_ifaces" ]; then
		local _guest_net_config=$( __return_property_value_from_config_file "guest" "net_ifaces" )
		__log 4 "\$_GP_net_ifaces was not populated. You really should not be seeing this."
	else
		local _guest_net_config="$_GP_net_ifaces"
	fi

	if [ -z "$( echo "$_guest_net_config" | grep "$_iface_in_question" )" ]; then
		__fault_detected_exit "$_GUEST_name not associated with $_iface_in_question."
	fi
}

# Verifies tap is not in use on system
__verify_tap_not_in_use() {
	local _tap_in_question _flag
	local _tap_in_question="$1"
	local _flag="$2"  # Valid flag = "-inverse-exit" effectively makes this __verify_tap_in_use

	__get_pid_locking_tap $1
	if [ -n "$_PID_locking_tap" ]; then
		__gvset_guest_name_by_pid $_PID_locking_tap
		if [ "$_flag" != "-no-exit" ]; then
			return 2
		else
			__fault_detected_exit "Tap already in use by $_GUEST_name_by_pid"
		fi
	fi

	__get_parent_bridge_for_tap_chyves $_tap_in_question -n
	if [ -n "$_PARENT_bridge_for_tap_chyves" ]; then
		if [ "$_flag" != "-no-exit" ]; then
			return 2
		elif [ "$_flag" != "-inverse-exit" ]; then
			__fault_detected_exit "'$_tap_in_question' already in use by $_PARENT_bridge_for_tap_chyves"
		fi
	else
		if [ "$_flag" = "-inverse-exit" ]; then
			__fault_detected_exit "Tap '$_tap_in_question' does not have parent device."
		fi
	fi
}

# Check if VALE might be possible on system
__verify_vale_system_compat() {
	[ "$_OS_VERSION_DATE" -lt "1003000" ] && __fault_detected_exit "VALE requires at least version FreeBSD 10.3 RELEASE and a patched bhyve binary."
	[ ! -e /dev/netmap ] && __fault_detected_exit "VALE requires the 'device netmap' be built into the kernel. Missing /dev/netmap."
	__verify_binary_available vale-ctl
	_VALE_system_compat=1
	__log 1 "Using VALE interface, can not verify if bhyve binary is patched. If interface has 'no carrier' then the bhyve binary has not been patch and no traffic can pass on interface."
	__log 1 "Keep in mind VALE support in chyves is experimental and not widely tested. All feedback is appreciated."
}

# Verifies interface is in valid format for type.
__verify_valid_iface_format() {
	local _int="$1"

	# Verify/Load console kernel module (if_bridge).
	if [ "$_AUTO_LOAD_KERNEL_MODS" = "yes" ]; then
		__verify_kernel_module_loaded if_bridge -l      # This checks and loads the missing nmdm module.
	else
		__verify_kernel_module_loaded if_bridge         # This only checks but does not load nmdm module.
	fi

	# Figure out interface type and assign to a global variable: bridge|tap|<vlan-prefix>|<physical>
	if [ -n "$( echo "$_int" | grep -E "^bridge[0-9]{1,5}$" )" ]; then
		_IFACE_type="bridge"
		_IFACE_drv="$_IFACE_type"
	elif [ -n "$( echo "$_int" | grep -E "^tap[0-9]{1,5}$" )" ]; then
		_IFACE_type="tap"
		_IFACE_drv="$_IFACE_type"
	elif [ -n "$( echo "$_int" | grep -E "^$_VLAN_IFACE_BASE_NAME[0-9]{1,5}$" )" ]; then
		_IFACE_type="vlan"
		_IFACE_drv="$_VLAN_IFACE_BASE_NAME"
	elif [ -n "$( echo "$_int" | grep -E "^vale[0-9a-zA-Z]{1,9}(:[a-zA-Z]{1}|:[0-9]{1,4}?)?$" )" ]; then
		_IFACE_type="vale"
		return
	elif [ -n "$( echo "$_int" | grep -E "$_FREEBSD_NET_DRIVERS_GREP_STRING" )" ]; then
		_IFACE_type="physical"
		_IFACE_drv="$( echo "$_int" | grep -o -E '[a-z]+' )"
	elif [ -n "$( echo "$_int" | grep "private" )" ]; then
		_IFACE_type="private"
		return
	else
		__fault_detected_exit "Invalid interface given: '$_int'."
	fi

	# Checks that only the prefix contains letters
	[ "$( echo "$_int" | grep -c -o -E '[a-z]+' )" -gt 1 ] && __fault_detected_exit "Invalid interface: '$_int'. Only numbers can be used after the interface type."

	# Gets the interface number
	local _int_num="$( echo "$_int" | sed s/$_IFACE_drv//g )"

	# Checks for leading zeros. Only the first device and any vlan interface can have leading zeros.
	[ -n "$( echo "$_int_num" | grep -E '^0{1,}' | grep -v -E '^0$' )" ] && [ "$_IFACE_drv" != "$_VLAN_IFACE_BASE_NAME" ] && __fault_detected_exit "Invalid interface number used: $_int_num. Leading zeros not permitted except for VLANs and the first device (Eg: 'bridge0')."

	# Checks that interface number is less than 32768.
	[ "$_int_num" -gt 32768 ] && __fault_detected_exit "Invalid interface number used: '$_int_num'. Must be under 32769."
}

# Verifies interface exists on system
__verify_valid_system_iface() {
	local _int="$1"
	local _flags="$2"   # Optional
	__verify_valid_iface_format "$_int"
	# $null   Verifies interface is valid, exits if not.
	# -c      [create] Creates interface if it does not exist
	# -n      [no-exit] Does not exit.

	# Do not try to verify "private" or vale interfaces as valid.
	[ "$_int" = "private" ] || [ "$_IFACE_type" = "vale" ] && return

	# The end user configured their network devices
	[ "$_NETWORK_DESIGN_MODE" = "system" ] && return

	ifconfig "$_int" > /dev/null 2>&1

	if [ "$?" = 1 ]; then

		if [ -z "$_flags" ]; then
			__fault_detected_exit "Interface does not exist on system: $_int"
		elif [ "$_flags" = "-c" ]; then
			__log 1 "$_int does not exist on system... " -n
			__log 1 "creating interface... " -n
			ifconfig "$_int" create descr "chyves-$_IFACE_type"
			__log 1 "done."
		elif [ "$_flags" = "-n" ]; then
			__log 1 "$_int does not exist on system."
		fi
	fi
}
