# #
###############################################################################
-device_list() {
- local devices
+declare -A DEVICE_LINK_SPEEDS=(
+ [10BaseT-Half]=0x1
+ [10BaseT-Full]=0x2
+ [100BaseT-Half]=0x4
+ [100BaseT-Full]=0x8
+ [1000BaseT-Half]=0x10
+ [1000BaseT-Full]=0x20
+ [10000BaseT-Full]=0x1000
+)
+device_list() {
# Add all interfaces
- list_append devices $(devices_get_all)
-
- # Add all PHYs
- list_append devices $(phy_list)
+ local device
+ for device in $(list_directory ${SYS_CLASS_NET}); do
+ if device_exists "${device}"; then
+ print "${device}"
+ fi
+ done
- # Add all serial devices
- list_append devices $(serial_list)
+ # List all PHYs
+ phy_list
- # Return a sorted result
- list_sort ${devices}
+ # List all serial devices
+ serial_list
}
# Check if the device exists
# Nothing to do, it device does not exist.
device_exists ${device} || return ${EXIT_OK}
+ # Shut down device before we delete it
+ device_set_down "${device}"
+
# Delete the device.
cmd_quiet ip link delete ${device}
local ret=$?
assert isset idx
local device device_idx
- for device in ${SYS_CLASS_NET}/*; do
- device=$(basename ${device})
- device_exists ${device} || continue
-
+ for device in $(list_directory "${SYS_CLASS_NET}"); do
device_idx=$(device_get_ifindex ${device})
if [ "${device_idx}" = "${idx}" ]; then
print "$(<${path})"
}
-# Check if the device is a batman-adv bridge
-device_is_batman_adv() {
- [ -d "${SYS_CLASS_NET}/${1}/mesh" ]
-}
-
-# Check if the device is a batman-adv slave port
-device_is_batman_adv_slave() {
+device_get_driver() {
local device="${1}"
+ assert isset device
- if [ -d "${SYS_CLASS_NET}/${device}/batman_adv" ]; then
- local status="$(<${SYS_CLASS_NET}/${device}/batman_adv/iface_status)"
-
- case "${status}" in
- "active")
- return ${EXIT_TRUE}
- ;;
- *)
- return ${EXIT_FALSE}
- ;;
- esac
- fi
-
- return ${EXIT_FALSE}
+ get_driver_from_path "${SYS_CLASS_NET}/${device}/device/driver/module"
}
# Check if the device is a bonding device
done < ${PROC_NET_VLAN_CONFIG}
}
+__device_type_matches() {
+ local device="${1}"
+ local type="${2}"
+
+ local _type="$(__device_get_file "${device}" "type")"
+
+ if [ "${type}" = "${_type}" ]; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+}
+
# Check if the device is a ppp device
device_is_ppp() {
- local device=${1}
-
- local type=$(__device_get_file ${device} type)
+ local device="${1}"
+ assert isset device
- [ "${type}" = "512" ] && return ${EXIT_OK} || return ${EXIT_ERROR}
+ __device_type_matches "${device}" 512
}
# Check if the device is a pointopoint device.
[[ ${device} =~ ^dummy[0-9]+$ ]]
}
+device_is_ipsec() {
+ local device="${1}"
+
+ [[ ${device} =~ ^ipsec\- ]]
+}
+
# Check if the device is a wireless device
device_is_wireless() {
local device=${1}
[ -d "${SYS_CLASS_NET}/${device}/phy80211" ]
}
+device_is_vti() {
+ local device="${1}"
+ assert isset device
+
+ __device_type_matches "${device}" 768
+}
+
+device_is_vti6() {
+ local device="${1}"
+ assert isset device
+
+ __device_type_matches "${device}" 769
+}
+
device_get_phy() {
local device="${1}"
}
device_is_phy() {
- phy_exists $@
+ phy_exists "$@"
}
device_is_serial() {
- serial_exists $@
+ serial_exists "$@"
}
# Returns true if a device is a tun device
elif device_is_ppp ${device}; then
echo "ppp"
- elif device_is_batman_adv ${device}; then
- echo "batman-adv"
-
elif device_is_loopback ${device}; then
echo "loopback"
elif device_is_phy ${device}; then
echo "phy"
+ else
+ echo "$(device_tunnel_get_type "${device}")"
+ fi
+}
+
+# This function just checks the types a ip-tunnel device usually have
+# so when we know that the device is an ip-tunnel device we save time
+device_tunnel_get_type() {
+ local device=${1}
+
+ # If the device does not exist (happens on udev remove events),
+ # we do not bother to run all checks.
+ if ! device_exists "${device}"; then
+ echo "unknown"
+
+ elif device_is_vti ${device}; then
+ echo "vti"
+
+ elif device_is_vti6 ${device}; then
+ echo "vti6"
+
else
echo "unknown"
fi
device_get() {
local device
- local devices
-
- for device in ${SYS_CLASS_NET}/*; do
- device=$(basename ${device})
-
+ for device in $(list_directory "${SYS_CLASS_NET}"); do
# bonding_masters is no device
[ "${device}" = "bonding_masters" ] && continue
- devices="${devices} ${device}"
+ echo "${device}"
done
- echo ${devices}
return ${EXIT_OK}
}
-devices_get_all() {
- device_get
-}
-
# Check if a device has a cable plugged in
device_has_carrier() {
local device=${1}
# Check if the device is free
device_is_free() {
- ! device_is_used $@
+ ! device_is_used "$@"
}
# Check if the device is used
fi
}
+device_set_master() {
+ local device="${1}"
+ assert isset device
+
+ local master="${2}"
+ assert isset master
+
+ if ! cmd ip link set "${device}" master "${master}"; then
+ log ERROR "Could not set master ${master} for device ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ return ${EXIT_OK}
+}
+
+device_remove_master() {
+ local device="${1}"
+ assert isset device
+
+ if ! cmd ip link set "${device}" nomaster; then
+ log ERROR "Could not remove master for device ${device}"
+ return ${EXIT_ERROR}
+ fi
+
+ return ${EXIT_OK}
+}
+
# Set device up
device_set_up() {
- local device=${1}
+ assert [ $# -eq 1 ]
- # Silently fail if device was not found
- [ -z "${device}" ] && return ${EXIT_ERROR}
+ local device=${1}
# Do nothing if device is already up
device_is_up ${device} && return ${EXIT_OK}
+ log INFO "Bringing up ${device}"
+
device_set_parent_up ${device}
+ if ! cmd ip link set ${device} up; then
+ return ${EXIT_ERROR}
+ fi
- log DEBUG "Setting up device '${device}'"
+ # Set SMP affinity
+ if interrupt_use_smp_affinity; then
+ device_auto_configure_smp_affinity ${device}
+ fi
- ip link set ${device} up
+ return ${EXIT_OK}
}
device_set_parent_up() {
# Set device down
device_set_down() {
- local device=${1}
- assert isset device
+ assert [ $# -eq 1 ]
+ local device=${1}
local ret=${EXIT_OK}
if device_is_up ${device}; then
- log DEBUG "Tearing down device '${device}'"
+ log INFO "Bringing down ${device}"
- ip link set ${device} down
+ cmd ip link set ${device} down
ret=$?
fi
device_get_mtu() {
local device=${1}
- if ! device_exists ${device}; then
- error "Device '${device}' does not exist."
- return ${EXIT_ERROR}
- fi
+ # Return an error if the device does not exist
+ device_exists ${device} || return ${EXIT_ERROR}
echo $(<${SYS_CLASS_NET}/${device}/mtu)
}
local device=${1}
local mtu=${2}
- if ! device_exists ${device}; then
- error "Device '${device}' does not exist."
- return ${EXIT_ERROR}
- fi
-
- local oldmtu=$(device_get_mtu ${device})
+ assert device_exists ${device}
- if [ "${oldmtu}" = "${mtu}" ]; then
- # No need to set mtu.
- return ${EXIT_OK}
+ # Handle bridges differently
+ if device_is_bridge ${device}; then
+ local port
+ for port in $(bridge_get_members ${device}); do
+ device_set_mtu ${port} ${mtu}
+ done
fi
- log INFO "Setting mtu of '${device}' to '${mtu}' - was ${oldmtu}."
+ log INFO "Setting MTU of ${device} to ${mtu}"
local up
if device_is_up ${device}; then
up=1
fi
- ip link set ${device} mtu ${mtu}
- local ret=$?
+ local ret=${EXIT_OK}
+ if ! cmd ip link set ${device} mtu ${mtu}; then
+ ret=${EXIT_ERROR}
- if [ "${up}" = "1" ]; then
- device_set_up ${device}
+ log ERROR "Could not set MTU ${mtu} on ${device}"
fi
- if [ "${ret}" != "0" ]; then
- error_log "Could not set mtu '${mtu}' on device '${device}'."
+ if [ "${up}" = "1" ]; then
+ device_set_up ${device}
fi
return ${ret}
done
}
+device_identify() {
+ assert [ $# -ge 1 ]
+
+ local device="${1}"
+
+ # Flash for ten seconds by default
+ local seconds="10"
+
+ # Run in background?
+ local background="false"
+
+ local arg
+ while read arg; do
+ case "${arg}" in
+ --background)
+ background="true"
+ ;;
+ --seconds=*)
+ seconds="$(cli_get_val "${arg}")"
+ ;;
+ esac
+ done <<< "$(args "$@")"
+
+ assert isinteger seconds
+
+ if ! device_exists "${device}"; then
+ log ERROR "Cannot identify device ${device}: Does not exist"
+ return ${EXIT_ERROR}
+ fi
+
+ if ! device_is_ethernet "${device}"; then
+ log DEBUG "Cannot identify device ${device}: Not an ethernet device"
+ return ${EXIT_NOT_SUPPORTED}
+ fi
+
+ log DEBUG "Identifying device ${device}"
+
+ local command="ethtool --identify ${device} ${seconds}"
+ local ret=0
+
+ if enabled background; then
+ cmd_background "${command}"
+ else
+ cmd_quiet "${command}"
+ ret=$?
+ fi
+
+ return ${ret}
+}
+
device_has_ip() {
local device=${1}
local addr=${2}
;;
esac
- listmatch ${addr} $(device_get_addresses ${device})
+ list_match ${addr} $(device_get_addresses ${device})
}
device_get_addresses() {
local device=${1}
local file=${2}
- assert isset device
- assert isset file
-
- local path="${SYS_CLASS_NET}/${device}/${file}"
- [ -r "${path}" ] || return ${EXIT_ERROR}
-
- echo "$(<${path})"
+ fread "${SYS_CLASS_NET}/${device}/${file}"
}
__device_set_file() {
local file="${2}"
local value="${3}"
- local path="${SYS_CLASS_NET}/${device}/${file}"
- if [ ! -w "${path}" ]; then
- log DEBUG "Cannot write to file '${file}' (${value})"
- return ${EXIT_ERROR}
- fi
-
- echo "${value}" > "${path}"
+ fappend "${SYS_CLASS_NET}/${device}/${file}" "${value}"
}
device_get_rx_bytes() {
__device_get_file ${device} statistics/tx_errors
}
+device_advertise_link_speeds() {
+ local device="${1}"
+ shift
+
+ assert isset device
+
+ # Advertised modes in hex
+ local advertise=0
+
+ local mode
+ for mode in $@; do
+ local m="${DEVICE_LINK_SPEEDS[${mode}]}"
+ if isset m; then
+ advertise="$(( advertise | m ))"
+ fi
+ done
+
+ # If nothing was selected, we reset and enable everything
+ if [ ${advertise} -eq 0 ]; then
+ advertise=0xffffff
+ fi
+
+ # Enable auto-negotiation
+ cmd_quiet ethtool --change "${device}" autoneg on
+
+ # Set advertised link speeds
+ if ! cmd_quiet ethtool --change "${device}" advertise "0x$(hex "${advertise}")"; then
+ log ERROR "Could not set link modes of ${device}: $@"
+ return ${EXIT_ERROR}
+ fi
+
+ log DEBUG "Set device link modes of ${device} to $@"
+ return ${EXIT_ERROR}
+}
+
device_get_speed() {
local device=${1}
- __device_get_file ${device} speed
+ local speed=$(__device_get_file ${device} speed)
+
+ # Exit for no output (i.e. no link detected)
+ isset speed || return ${EXIT_ERROR}
+
+ # Don't return anything for negative values
+ [ ${speed} -lt 0 ] && return ${EXIT_ERROR}
+
+ print "${speed}"
}
device_get_duplex() {
local device=${1}
- __device_get_file ${device} duplex
+ local duplex=$(__device_get_file ${device} duplex)
+
+ case "${duplex}" in
+ unknown)
+ return ${EXIT_ERROR}
+ ;;
+ *)
+ print "${duplex}"
+ ;;
+ esac
}
device_get_link_string() {
print "${s}"
}
+
+device_auto_configure_smp_affinity() {
+ assert [ $# -eq 1 ]
+
+ local device=${1}
+
+ if lock_acquire "smp-affinity" 60; then
+ device_set_smp_affinity ${device} auto
+
+ lock_release "smp-affinity"
+ fi
+}
+
+device_set_smp_affinity() {
+ assert [ $# -eq 2 ]
+
+ local device=${1}
+ local mode=${2}
+
+ # mode can be auto which will automatically try to find
+ # the least busy processor, or an integer for the desired
+ # processor that should handle this device
+
+ local num_processors=$(system_get_processors)
+
+ if [ "${mode}" = "auto" ]; then
+ local processor=$(interrupt_choose_least_busy_processor)
+ else
+ assert isinteger mode
+ local processor=${mode}
+
+ if [ ${processor} -gt ${num_processors} ]; then
+ log ERROR "Processor ${processor} does not exist"
+ return ${EXIT_ERROR}
+ fi
+ fi
+
+ local interrupts=$(interrupts_for_device ${device})
+ if ! isset interrupts; then
+ log DEBUG "${device} has no interrupts. Not changing SMP affinity"
+ return ${EXIT_OK}
+ fi
+
+ # Set SMP affinity
+ local interrupt
+ for interrupt in ${interrupts}; do
+ interrupt_set_smp_affinity ${interrupt} ${processor}
+ done
+
+ # Find all queues and assign them to the next processor
+ local queue
+ for queue in $(device_get_queues ${device}); do
+ case "${queue}" in
+ # Only handle receive queues
+ rx-*)
+ for interrupt in $(interrupts_for_device_queue ${device} ${queue}); do
+ interrupt_set_smp_affinity ${interrupt} ${processor}
+ done
+
+ device_queue_set_smp_affinity ${device} ${queue} ${processor}
+ ;;
+
+ # Ignore the rest
+ *)
+ continue
+ ;;
+ esac
+
+ # Get the next available processor if in auto mode
+ [ "${mode}" = "auto" ] && processor=$(system_get_next_processor ${processor})
+ done
+
+ return ${EXIT_OK}
+}
+
+device_get_queues() {
+ assert [ $# -eq 1 ]
+
+ local device=${1}
+
+ list_directory "${SYS_CLASS_NET}/${device}/queues"
+}
+
+device_supports_multiqueue() {
+ local device=${1}
+
+ local num_queues=$(device_num_queues ${device})
+
+ if isset num_queues && [ ${num_queues} -gt 2 ]; then
+ return ${EXIT_TRUE}
+ fi
+
+ return ${EXIT_FALSE}
+}
+
+device_num_queues() {
+ local device=${1}
+ local type=${2}
+
+ isset type && assert isoneof type rx tx
+
+ local i=0
+
+ local q
+ for q in $(device_get_queues ${device}); do
+ case "${type},${q}" in
+ rx,rx-*)
+ (( i++ ))
+ ;;
+ tx,tx-*)
+ (( i++ ))
+ ;;
+ *,*)
+ (( i++ ))
+ ;;
+ esac
+ done
+
+ print ${i}
+}
+
+device_queue_get_smp_affinity() {
+ assert [ $# -eq 2 ]
+
+ local device=${1}
+ local queue=${2}
+
+ local path="${SYS_CLASS_NET}/${device}/queues/${queue}"
+
+ case "${queue}" in
+ rx-*)
+ path="${path}/rps_cpus"
+ ;;
+ tx-*)
+ path="${path}/xps_cpus"
+ ;;
+ esac
+ assert [ -r "${path}" ]
+
+ __bitmap_to_processor_ids $(<${path})
+}
+
+device_queue_set_smp_affinity() {
+ assert [ $# -eq 3 ]
+
+ local device=${1}
+ local queue=${2}
+ local processor=${3}
+
+ local path="${SYS_CLASS_NET}/${device}/queues/${queue}/rps_cpus"
+ assert [ -w "${path}" ]
+
+ log DEBUG "Setting SMP affinity of ${device} (${queue}) to processor ${processor}"
+
+ __processor_id_to_bitmap ${processor} > ${path}
+}
+
+# Tries to find a device which has the given IP address assigned
+device_get_by_assigned_ip_address() {
+ local ip=${1}
+
+ assert isset ip
+
+ local device
+
+ # Read the first line of ip addr show to
+ read -r device <<< $(ip addr show to "${ip}")
+
+ # If we did not found a device we return with ${EXIT_ERROR}
+ if ! isset device; then
+ return ${EXIT_ERROR}
+ fi
+
+ # We get something like:
+ # 3: upl0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
+ # and we want upl0 so we take the second word and removing the :
+ device=(${device})
+ device=${device[1]}
+ device=${device%:}
+
+ print "${device}"
+ return ${EXIT_OK}
+}
+
+device_get_by_mac_address() {
+ local mac=${1}
+
+ assert isset mac
+
+ local device
+
+ for device in $(device_list); do
+ if [ "${mac}" = "$(device_get_address ${device})" ]; then
+ print "${device}"
+ return ${EXIT_OK}
+ fi
+ done
+
+ # We could not found a port to the given mac address so we return exit error
+ return ${EXIT_ERROR}
+}