#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2010 Michael Tremer & Christian Schmidt # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### function devicify() { local device=${1} if device_exists ${device}; then echo "${device}" return ${EXIT_OK} fi local d for d in $(devices_get_all); do if [ "$(device_get_address ${d})" = "${device}" ]; then echo "${d}" return ${EXIT_OK} fi done return ${EXIT_ERROR} } function macify() { local device=${1} if mac_is_valid ${device}; then echo "${device}" return ${EXIT_OK} fi if device_exists ${device}; then device_get_address ${device} return ${EXIT_OK} fi return ${EXIT_ERROR} } # Check if the device exists function device_exists() { local device=${1} # If device name was not found, exit. [ -n "${device}" ] || return ${EXIT_ERROR} [ -d "${SYS_CLASS_NET}/${device}" ] } # Check if the device is up function device_is_up() { local device=${1} device_exists ${device} || return ${EXIT_ERROR} ip link show ${device} 2>/dev/null | grep -qE "<.*UP.*>" } # Check if the device is a bonding device function device_is_bonding() { [ -d "/sys/class/net/${1}/bonding" ] } # Check if the device bonded in a bonding device function device_is_bonded() { local dev for dev in /sys/class/net/*; do # Skip crappy files [ -d "${dev}" ] || continue # Continue if not a bonding device device_is_bonding "${dev##*/}" || continue if grep -q "\<${1}\>" ${dev}/bonding/slaves; then return 0 fi done return 1 } # Check if the device is a bridge function device_is_bridge() { [ -d "/sys/class/net/${1}/bridge" ] } function device_is_bridge_attached() { local device=${1} [ -d "${SYS_CLASS_NET}/${device}/brport" ] } # Check if the device is a virtual device function device_is_virtual() { local device=${1} [ -e "/proc/net/vlan/${device}" ] } # Check if the device has virtual devices function device_has_virtuals() { local device=${1} if device_is_virtual ${device}; then return 1 fi if [ ! -e "/proc/net/vlan/config" ]; then return 1 fi grep -q "${1}$" /proc/net/vlan/config } function device_is_vlan() { # XXX Compat function log DEBUG "Deprecated function device_is_vlan() was used." device_is_virtual $@ } # Check if the device is a ppp device function device_is_ppp() { local device=${1} ip link show ${device} 2>/dev/null | grep -qE "<.*POINTOPOINT.*>" } # Check if the device is a loopback device function device_is_loopback() { local device=$(devicify ${1}) [ "${device}" = "lo" ] } # Check if the device is a physical network interface function device_is_real() { local device=${1} device_is_loopback ${device} && \ return ${EXIT_ERROR} device_is_bonding ${device} && \ return ${EXIT_ERROR} device_is_bridge ${device} && \ return ${EXIT_ERROR} device_is_ppp ${device} && \ return ${EXIT_ERROR} device_is_virtual ${device} && \ return ${EXIT_ERROR} return ${EXIT_OK} } # Get the device type function device_get_type() { local device=$(devicify ${1}) if device_is_vlan ${device}; then echo "vlan" elif device_is_bonding ${device}; then echo "bonding" elif device_is_bridge ${device}; then echo "bridge" elif device_is_ppp ${device}; then echo "ppp" elif device_is_loopback ${device}; then echo "loopback" elif device_is_real ${device}; then echo "real" else echo "unknown" fi } function device_get_address() { local device=${1} cat ${SYS_CLASS_NET}/${device}/address 2>/dev/null } function device_set_address() { local device=${1} local addr=${2} if ! device_exists ${device}; then error "Device '${device}' does not exist." return ${EXIT_ERROR} fi log INFO "Setting address of '${device}' to '${addr}' - was $(device_get_address ${device})." local up if device_is_up ${device}; then device_set_down ${device} up=1 fi ip link set ${device} address ${addr} local ret=$? if [ "${up}" = "1" ]; then device_set_up ${device} fi if [ "${ret}" != "0" ]; then error_log "Could not set address '${addr}' on device '${device}'." fi return ${ret} } function devices_get_all() { local device for device in ${SYS_CLASS_NET}/*; do echo "$(basename ${device})" done | sort } # Check if a device has a cable plugged in function device_has_carrier() { local device=$(devicify ${1}) [ "$(<${SYS_CLASS_NET}/${device}/carrier)" = "1" ] } # Check if the device is free function device_is_free() { ! device_is_used $@ } # Check if the device is used function device_is_used() { local device=$(devicify ${1}) device_has_virtuals ${device} && \ return ${EXIT_OK} device_is_bonded ${device} && \ return ${EXIT_OK} device_is_bridge_attached ${device} && \ return ${EXIT_OK} return ${EXIT_ERROR} } # XXX to be removed I think function device_get_free() { local destination=${1} # Replace + by a valid number if grep -q "+$" <<<${destination}; then local number=0 destination=$(sed -e "s/+//" <<<$destination) while [ "${number}" -le "100" ]; do if ! device_exists "${destination}${number}"; then destination="${destination}${number}" break fi number=$(($number + 1)) done fi echo "${destination}" } function device_rename() { warning_log "Called deprecated function 'device_rename'" device_set_name $@ } function device_hash() { local device=${1} # Get mac address of device and remove all colons (:) # that will result in a hash. device=$(macify ${device}) echo "${device//:/}" } # Give the device a new name function device_set_name() { local source=$1 local destination=$(device_get_free ${2}) # Check if devices exists if ! device_exists ${source} || device_exists ${destination}; then return 4 fi local up if device_is_up ${source}; then ip link set ${source} down up=1 fi ip link set ${source} name ${destination} if [ "${up}" = "1" ]; then ip link set ${destination} up fi } # Set device up function device_set_up() { local device=$(devicify ${1}) # Do nothing if device is already up device_is_up ${device} && return ${EXIT_OK} device_set_parent_up ${device} log DEBUG "Setting up device '${device}'" ip link set ${device} up } function device_set_parent_up() { local device=${1} local parent if device_is_virtual ${device}; then parent=$(device_virtual_get_parent ${device}) device_is_up ${parent} && return ${EXIT_OK} log DEBUG "Setting up parent device '${parent}' of '${device}'" device_set_up ${parent} return $? fi return ${EXIT_OK} } # Set device down function device_set_down() { local device=$(devicify ${1}) local ret=${EXIT_OK} if device_is_up ${device}; then log DEBUG "Tearing down device '${device}'" ip link set ${device} down ret=$? fi device_set_parent_down ${device} return ${ret} } function device_set_parent_down() { local device=${1} local parent if device_is_virtual ${device}; then parent=$(device_virtual_get_parent ${device}) device_is_up ${parent} || return ${EXIT_OK} if device_is_free ${parent}; then log DEBUG "Tearing down parent device '${parent}' of '${device}'" device_set_down ${parent} fi fi return ${EXIT_OK} } function device_get_mtu() { local device=${1} if ! device_exists ${device}; then error "Device '${device}' does not exist." return ${EXIT_ERROR} fi echo $(<${SYS_CLASS_NET}/${device}/mtu) } # Set mtu to a device function device_set_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}) if [ "${oldmtu}" = "${mtu}" ]; then # No need to set mtu. return ${EXIT_OK} fi log INFO "Setting mtu of '${device}' to '${mtu}' - was ${oldmtu}." local up if device_is_up ${device}; then device_set_down ${device} up=1 fi ip link set ${device} mtu ${mtu} local ret=$? if [ "${up}" = "1" ]; then device_set_up ${device} fi if [ "${ret}" != "0" ]; then error_log "Could not set mtu '${mtu}' on device '${device}'." fi return ${ret} } function device_discover() { local device=${1} log INFO "Running discovery process on device '${device}'." local hook for hook in $(hooks_get_all); do hook_exec ${hook} discover ${device} done } function device_create_virtual() { log WARN "Called deprecated function device_create_virtual" device_virtual_create $@ } function device_virtual_create() { local port=$(devicify ${1}) local vid=${2} local mac=${3} local newport=${port}v${vid} if [ -z "${mac}" ]; then mac=$(mac_generate) fi log INFO "Creating virtual device '${newport}' with address '${mac}'." local oldport=$(device_virtual_get_by_parent_and_vid ${port} ${vid}) if device_exists ${oldport}; then local differences if [ "${oldport}" != "${newport}" ]; then differences="${differences} name" fi if [ "$(device_get_address ${oldport})" != "${mac}" ]; then differences="${differences} address" fi echo "differences: $differences" if [ -n "${differences}" ]; then if device_is_used ${oldport}; then error_log "There was a device '${oldport}' set up with VID '${vid}' and parent '${port}' which is used somewhere else. Cannot go on." return ${EXIT_ERROR} else log DEBUG "There is a device '${oldport}' but it not used, so we grab it to ourselves." fi else log DEBUG "Device '${newport}' already exists and reflects our configuration. Go on." device_set_up ${oldport} return ${EXIT_OK} fi else log DEBUG "Virtual device '${newport}' does not exist, yet." vconfig set_name_type DEV_PLUS_VID_NO_PAD >/dev/null vconfig add ${port} ${vid} >/dev/null if [ $? -ne ${EXIT_OK} ]; then error_log "Could not create virtual device '${newport}'." return ${EXIT_ERROR} fi oldport=$(device_virtual_get_by_parent_and_vid ${port} ${vid}) fi assert device_exists ${oldport} if ! device_exists ${oldport}; then error "Could not determine the created virtual device '${newport}'." return ${EXIT_ERROR} fi # The device is expected to be named like ${port}.${vid} # and will be renamed to the virtual schema device_set_name ${oldport} ${newport} if [ $? -ne ${EXIT_OK} ]; then error_log "Could not set name of virtual device '${newport}'." return ${EXIT_ERROR} fi assert device_exists ${newport} # Setting new mac address device_set_address ${newport} ${mac} if [ $? -ne ${EXIT_OK} ]; then error_log "Could not set address '${mac}' to virtual device '${newport}'." return ${EXIT_ERROR} fi # Bring up the new device device_set_up ${newport} return ${EXIT_OK} } function device_virtual_remove() { local device=$(devicify ${1}) log INFO "Removing virtual device '${device}' with address '$(macify ${device})'." device_set_down ${device} vconfig rem ${device} >/dev/null if [ $? -ne ${EXIT_OK} ]; then error_log "Could not remote virtual device '${newport}'." return ${EXIT_ERROR} fi return ${EXIT_OK} } function device_virtual_get_parent() { local device=${1} local parent=$(grep "^${device}" < /proc/net/vlan/config | awk '{ print $NF }') if device_exists ${parent}; then echo "${parent}" return ${EXIT_OK} fi return ${EXIT_ERROR} } function device_virtual_get_by_parent_and_vid() { local parent=${1} local vid=${2} local v_port local v_id local v_parent fgrep '|' < /proc/net/vlan/config | tr -d '|' | \ while read v_port v_id v_parent; do if [ "${v_parent}" = "${parent}" ] && [ "${v_id}" = "${vid}" ]; then echo "${v_port}" return ${EXIT_OK} fi done return ${EXIT_ERROR} } function device_bonding_create() { local device=${1} local mac=${2} [ -z "${mac}" ] && mac=$(mac_generate) log INFO "Creating bonding device '${device}' (${mac})." echo "+${device}" > /sys/class/net/bonding_masters device_set_address ${mac} device_set_up ${device} } function device_bonding_remove() { local device=$(devicify ${1}) log INFO "Remove bonding device '${device}'." device_set_down ${device} echo "-${device}" > /sys/class/net/bonding_masters } function bonding_set_mode() { local device=${1} local mode=${2} log INFO "Setting bonding mode on '${device}' '${mode}'." echo "${mode}" > /sys/class/net/${device}/bonding/mode } function bonding_enslave_device() { local device=$(devicify ${1}) local slave=$(devicify ${2}) shift 2 log INFO "Enslaving slave '${slave}' to '${device}'." device_set_down ${slave} echo "+${slave}" > /sys/class/net/${device}/bonding/slaves } function bridge_attach_device() { local bridge=${1} local device=${2} if ! device_exists ${bridge}; then error "Bridge '${bridge}' does not exist." return ${EXIT_ERROR} fi if ! device_exists ${device}; then error "Device '${device}' does not exist." return ${EXIT_ERROR} fi log INFO "Attaching device '${device}' to bridge '${bridge}'." # XXX device_set_up ${device} # Do we need this here? brctl addif ${bridge} ${device} } function bridge_detach_device() { local bridge=${1} local device=${2} if ! device_exists ${bridge}; then error "Bridge '${bridge}' does not exist." return ${EXIT_ERROR} fi if ! device_exists ${device}; then error "Device '${device}' does not exist." return ${EXIT_ERROR} fi log INFO "Detaching device '${device}' from bridge '${bridge}'." brctl delif ${bridge} ${device} device_set_down ${device} } function bridge_is_forwarding() { local seconds=45 local zone=${1} bridge_has_carrier ${zone} || return ${EXIT_ERROR} local device while [ ${seconds} -gt 0 ]; do for device in ${SYS_CLASS_NET}/${zone}/brif/*; do [ -e "${device}/state" ] || continue if [ "$(<${device}/state)" = "3" ]; then return ${EXIT_OK} fi done sleep 1 seconds=$((${seconds} - 1)) done return ${EXIT_ERROR} } function bridge_has_carrier() { local zone=${1} local has_carrier=${EXIT_ERROR} local device for device in ${SYS_CLASS_NET}/${zone}/brif/*; do device=$(basename ${device}) device_exists ${device} || continue device_has_carrier ${device} && has_carrier=${EXIT_OK} done return ${has_carrier} } function device_has_ipv4() { local device=${1} local addr=${2} if ! device_exists ${device}; then error "Device '${device}' does not exist." return ${EXIT_ERROR} fi ip addr show ${device} | grep -q -e "inet " -e "${addr}" }