2 ###############################################################################
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2010 Michael Tremer & Christian Schmidt #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
22 declare -A DEVICE_LINK_SPEEDS
=(
29 [10000BaseT-Full
]=0x1000
35 for device
in $
(list_directory
${SYS_CLASS_NET}); do
36 if device_exists
"${device}"; then
44 # List all serial devices
48 # Check if the device exists
52 # If device name was not found, exit.
53 [ -n "${device}" ] ||
return ${EXIT_ERROR}
55 # Check for a normal network device.
56 [ -d "${SYS_CLASS_NET}/${device}" ] && return ${EXIT_OK}
58 # If the check above did not find a result,
60 phy_exists "${device}" && return ${EXIT_OK}
62 # If the check above did not find a result,
63 # we check for serial devices.
64 serial_exists ${device}
67 device_matches_pattern() {
74 pattern="^
${pattern//N/[[:digit:]]+}$
"
76 [[ ${device} =~ ${pattern} ]] \
77 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
84 # Nothing to do, it device does not exist.
85 device_exists ${device} || return ${EXIT_OK}
87 # Shut down device before we delete it
88 device_set_down "${device}"
91 cmd_quiet ip link delete ${device}
94 if [ ${ret} -ne ${EXIT_OK} ]; then
95 log ERROR "device
: Could not delete device
'${device}': ${ret}"
106 local flags=$(__device_get_file ${device} flags)
108 if [[ "$
(( ${flags} & ${flag} ))" -eq 0 ]]; then
115 # Check if the device is up
119 device_exists ${device} || return ${EXIT_ERROR}
121 device_has_flag ${device} 0x1
124 device_ifindex_to_name() {
128 local device device_idx
129 for device in $(list_directory "${SYS_CLASS_NET}"); do
130 device_idx=$(device_get_ifindex ${device})
132 if [ "${device_idx}" = "${idx}" ]; then
141 device_get_ifindex() {
145 local path="${SYS_CLASS_NET}/${1}/ifindex
"
147 # Check if file can be read.
148 [ -r "${path}" ] || return ${EXIT_ERROR}
153 device_get_driver() {
157 get_driver_from_path "${SYS_CLASS_NET}/${device}/device
/driver
/module
"
160 # Check if the device is a bonding device
161 device_is_bonding() {
162 [ -d "/sys
/class
/net
/${1}/bonding
" ]
165 # Check if the device bonded in a bonding device
169 [ -d "${SYS_CLASS_NET}/${device}/bonding_slave
" ]
172 # Check if the device is a bridge
174 [ -d "/sys
/class
/net
/${1}/bridge
" ]
177 device_is_bridge_attached() {
179 [ -d "${SYS_CLASS_NET}/${device}/brport
" ]
182 device_is_wireless_monitor() {
186 device_is_wireless "${device}" && \
187 device_matches_pattern "${device}" "${PORT_PATTERN_WIRELESS_MONITOR}"
190 device_is_wireless_adhoc() {
194 device_is_wireless "${device}" && \
195 device_matches_pattern "${device}" "${PORT_PATTERN_WIRELESS_ADHOC}"
198 device_get_bridge() {
202 # Check if device is attached to a bridge.
203 device_is_bridge_attached ${device} || return ${EXIT_ERROR}
205 local ifindex_path="${SYS_CLASS_NET}/${device}/brport
/bridge
/ifindex
"
206 [ -r "${ifindex_path}" ] || return ${EXIT_ERROR}
208 local ifindex=$(<${ifindex_path})
211 device_ifindex_to_name ${ifindex}
214 # Check if the device is a vlan device
219 [ -e "${PROC_NET_VLAN}/${device}" ]
222 # Check if the device has vlan devices
227 if device_is_vlan ${device}; then
231 local vlans=$(device_get_vlans ${device})
232 [ -n "${vlans}" ] && return ${EXIT_OK} || return ${EXIT_ERROR}
239 # If no 8021q module has been loaded into the kernel,
240 # we cannot do anything.
241 [ -r "${PROC_NET_VLAN_CONFIG}" ] ||
return ${EXIT_OK}
243 local dev spacer1 id spacer2 parent
244 while read dev spacer1 id spacer2 parent
; do
245 [ "${parent}" = "${device}" ] ||
continue
248 done < ${PROC_NET_VLAN_CONFIG}
251 __device_type_matches
() {
255 local _type
="$(__device_get_file "${device}" "type")"
257 if [ "${type}" = "${_type}" ]; then
264 # Check if the device is a ppp device
269 __device_type_matches
"${device}" 512
272 # Check if the device is a pointopoint device.
276 device_has_flag
${device} 0x10
279 # Check if the device is a loopback device
280 device_is_loopback
() {
283 [ "${device}" = "lo" ]
286 # Check if the device is a dummy device
287 # This is the worst possible check, but all I could come up with
291 [[ ${device} =~ ^dummy
[0-9]+$
]]
294 # Check if the device is a wireless device
295 device_is_wireless
() {
298 [ -d "${SYS_CLASS_NET}/${device}/phy80211" ]
305 __device_type_matches
"${device}" 778
312 __device_type_matches
"${device}" 823
319 __device_type_matches
"${device}" 768
326 __device_type_matches
"${device}" 769
332 if device_is_wireless
"${device}"; then
333 print
"$(<${SYS_CLASS_NET}/${device}/phy80211/name)"
348 # Returns true if a device is a tun device
352 [ -e "${SYS_CLASS_NET}/${device}/tun_flags" ]
355 # Check if the device is a physical network interface
356 device_is_ethernet
() {
359 device_is_ethernet_compatible
"${device}" || \
362 device_is_loopback
${device} && \
365 device_is_bonding
${device} && \
368 device_is_bridge
${device} && \
371 device_is_ppp
${device} && \
374 device_is_vlan
${device} && \
377 device_is_dummy
${device} && \
380 device_is_tun
${device} && \
386 # Get the device type
390 # If the device does not exist (happens on udev remove events),
391 # we do not bother to run all checks.
392 if ! device_exists
"${device}"; then
395 elif device_is_vlan
${device}; then
398 elif device_is_bonding
${device}; then
401 elif device_is_bridge
${device}; then
404 elif device_is_ppp
${device}; then
407 elif device_is_loopback
${device}; then
410 elif device_is_wireless_adhoc
${device}; then
411 echo "wireless-adhoc"
413 elif device_is_wireless
${device}; then
416 elif device_is_dummy
${device}; then
419 elif device_is_tun
${device}; then
422 elif device_is_ethernet
${device}; then
425 elif device_is_serial
${device}; then
428 elif device_is_phy
${device}; then
432 echo "$(device_tunnel_get_type "${device}")"
436 # This function just checks the types a ip-tunnel device usually have
437 # so when we know that the device is an ip-tunnel device we save time
438 device_tunnel_get_type
() {
441 # If the device does not exist (happens on udev remove events),
442 # we do not bother to run all checks.
443 if ! device_exists
"${device}"; then
446 elif device_is_vti
${device}; then
449 elif device_is_vti6
${device}; then
457 device_is_ethernet_compatible
() {
460 # /sys/class/net/*/type must equal 1 for ethernet compatible devices
461 local type="$(__device_get_file "${device}" "type")"
462 [[ "${type}" = "1" ]]
465 device_get_status
() {
469 local status
=${STATUS_DOWN}
471 if device_is_up
${device}; then
474 if ! device_has_carrier
${device}; then
475 status
=${STATUS_NOCARRIER}
482 device_get_address
() {
485 cat ${SYS_CLASS_NET}/${device}/address
2>/dev
/null
488 device_set_address
() {
494 if ! device_exists
"${device}"; then
495 error
"Device '${device}' does not exist."
499 # Do nothing if the address has not changed
500 local old_addr
="$(device_get_address "${device}")"
501 if [ -n "${old_addr}" -a "${addr}" = "${old_addr}" ]; then
505 log DEBUG
"Setting address of '${device}' from '${old_addr}' to '${addr}'"
508 if device_is_up
"${device}"; then
509 device_set_down
"${device}"
513 ip link
set "${device}" address
"${addr}"
516 if [ "${up}" = "1" ]; then
517 device_set_up
"${device}"
520 if [ "${ret}" != "0" ]; then
521 error_log
"Could not set address '${addr}' on device '${device}'"
529 for device
in $
(list_directory
"${SYS_CLASS_NET}"); do
530 # bonding_masters is no device
531 [ "${device}" = "bonding_masters" ] && continue
539 # Check if a device has a cable plugged in
540 device_has_carrier
() {
544 local carrier
=$
(__device_get_file
${device} carrier
)
545 [ "${carrier}" = "1" ]
548 device_is_promisc
() {
551 device_has_flag
${device} 0x200
554 device_set_promisc
() {
558 assert device_exists
${device}
560 assert isoneof state on off
562 ip link
set ${device} promisc
${state}
565 # Check if the device is free
567 ! device_is_used
"$@"
570 # Check if the device is used
574 device_has_vlans
${device} && \
576 device_is_bonded
${device} && \
578 device_is_bridge_attached
${device} && \
584 # Give the device a new name
587 local destination
=${2}
589 # Check if devices exists
590 if ! device_exists
${source} || device_exists
${destination}; then
595 if device_is_up
${source}; then
596 ip link
set ${source} down
600 ip link
set ${source} name
${destination}
602 if [ "${up}" = "1" ]; then
603 ip link
set ${destination} up
607 device_set_master
() {
614 if ! cmd ip link
set "${device}" master
"${master}"; then
615 log ERROR
"Could not set master ${master} for device ${device}"
622 device_remove_master
() {
626 if ! cmd ip link
set "${device}" nomaster
; then
627 log ERROR
"Could not remove master for device ${device}"
640 # Do nothing if device is already up
641 device_is_up
${device} && return ${EXIT_OK}
643 log INFO
"Bringing up ${device}"
645 device_set_parent_up
${device}
646 if ! cmd ip link
set ${device} up
; then
651 if interrupt_use_smp_affinity
; then
652 device_auto_configure_smp_affinity
${device}
658 device_set_parent_up
() {
662 if device_is_vlan
${device}; then
663 parent
=$
(vlan_get_parent
${device})
665 device_is_up
${parent} && return ${EXIT_OK}
667 log DEBUG
"Setting up parent device '${parent}' of '${device}'"
669 device_set_up
${parent}
683 if device_is_up
${device}; then
684 log INFO
"Bringing down ${device}"
686 cmd ip link
set ${device} down
690 device_set_parent_down
${device}
695 device_set_parent_down
() {
699 if device_is_vlan
${device}; then
700 parent
=$
(vlan_get_parent
${device})
702 device_is_up
${parent} ||
return ${EXIT_OK}
704 if device_is_free
${parent}; then
705 log DEBUG
"Tearing down parent device '${parent}' of '${device}'"
707 device_set_down
${parent}
717 # Return an error if the device does not exist
718 device_exists
${device} ||
return ${EXIT_ERROR}
720 echo $
(<${SYS_CLASS_NET}/${device}/mtu
)
723 # Set mtu to a device
728 assert device_exists
${device}
730 # Handle bridges differently
731 if device_is_bridge
${device}; then
733 for port
in $
(bridge_get_members
${device}); do
734 device_set_mtu
${port} ${mtu}
738 log INFO
"Setting MTU of ${device} to ${mtu}"
741 if device_is_up
${device}; then
742 device_set_down
${device}
747 if ! cmd ip link
set ${device} mtu
${mtu}; then
750 log ERROR
"Could not set MTU ${mtu} on ${device}"
753 if [ "${up}" = "1" ]; then
754 device_set_up
${device}
760 device_adjust_mtu
() {
764 local other_device
="${2}"
766 local mtu
="$(device_get_mtu "${other_device}")"
767 device_set_mtu
"${device}" "${mtu}"
773 log INFO
"Running discovery process on device '${device}'."
776 for hook
in $
(hook_zone_get_all
); do
777 hook_zone_exec
${hook} discover
${device}
786 # Flash for ten seconds by default
790 local background
="false"
799 seconds
="$(cli_get_val "${arg}")"
802 done <<< "$(args "$@
")"
804 assert isinteger seconds
806 if ! device_exists
"${device}"; then
807 log ERROR
"Cannot identify device ${device}: Does not exist"
811 if ! device_is_ethernet
"${device}"; then
812 log DEBUG
"Cannot identify device ${device}: Not an ethernet device"
813 return ${EXIT_NOT_SUPPORTED}
816 log DEBUG
"Identifying device ${device}"
818 local command="ethtool --identify ${device} ${seconds}"
821 if enabled background
; then
822 cmd_background
"${command}"
824 cmd_quiet
"${command}"
836 assert device_exists
${device}
838 # IPv6 addresses must be fully imploded
839 local protocol
=$
(ip_detect_protocol
${addr})
840 case "${protocol}" in
842 addr
=$
(ipv6_format
"${addr}")
846 list_match
${addr} $
(device_get_addresses
${device})
849 device_get_addresses
() {
852 assert device_exists
${device}
857 ip addr show
${device} | \
858 while read prot addr line
; do
859 [ "${prot:0:4}" = "inet" ] && echo "${addr}"
863 __device_get_file
() {
867 fread
"${SYS_CLASS_NET}/${device}/${file}"
870 __device_set_file
() {
877 fappend
"${SYS_CLASS_NET}/${device}/${file}" "${value}"
880 device_get_rx_bytes
() {
883 __device_get_file
${device} statistics
/rx_bytes
886 device_get_tx_bytes
() {
889 __device_get_file
${device} statistics
/tx_bytes
892 device_get_rx_packets
() {
895 __device_get_file
${device} statistics
/rx_packets
898 device_get_tx_packets
() {
901 __device_get_file
${device} statistics
/tx_packets
904 device_get_rx_errors
() {
907 __device_get_file
${device} statistics
/rx_errors
910 device_get_tx_errors
() {
913 __device_get_file
${device} statistics
/tx_errors
916 device_advertise_link_speeds
() {
922 # Advertised modes in hex
927 local m
="${DEVICE_LINK_SPEEDS[${mode}]}"
929 advertise
="$(( advertise | m ))"
933 # If nothing was selected, we reset and enable everything
934 if [ ${advertise} -eq 0 ]; then
938 # Enable auto-negotiation
939 cmd_quiet ethtool
--change "${device}" autoneg on
941 # Set advertised link speeds
942 if ! cmd_quiet ethtool
--change "${device}" advertise
"0x$(hex "${advertise}")"; then
943 log ERROR
"Could not set link modes of ${device}: $@"
947 log DEBUG
"Set device link modes of ${device} to $@"
954 local speed
=$
(__device_get_file
${device} speed
)
956 # Exit for no output (i.e. no link detected)
957 isset speed ||
return ${EXIT_ERROR}
959 # Don't return anything for negative values
960 [ ${speed} -lt 0 ] && return ${EXIT_ERROR}
965 device_get_duplex
() {
968 local duplex
=$
(__device_get_file
${device} duplex
)
980 device_get_link_string
() {
986 local speed
="$(device_get_speed "${device}")"
988 list_append s
"${speed} MBit/s"
991 local duplex
="$(device_get_duplex "${device}")"
992 if isset duplex
; then
993 list_append s
"${duplex} duplex"
999 device_auto_configure_smp_affinity
() {
1004 if lock_acquire
"smp-affinity" 60; then
1005 device_set_smp_affinity
${device} auto
1007 lock_release
"smp-affinity"
1011 device_set_smp_affinity
() {
1017 # mode can be auto which will automatically try to find
1018 # the least busy processor, or an integer for the desired
1019 # processor that should handle this device
1021 local num_processors
=$
(system_get_processors
)
1023 if [ "${mode}" = "auto" ]; then
1024 local processor
=$
(interrupt_choose_least_busy_processor
)
1026 assert isinteger mode
1027 local processor
=${mode}
1029 if [ ${processor} -gt ${num_processors} ]; then
1030 log ERROR
"Processor ${processor} does not exist"
1031 return ${EXIT_ERROR}
1035 local interrupts
=$
(interrupts_for_device
${device})
1036 if ! isset interrupts
; then
1037 log DEBUG
"${device} has no interrupts. Not changing SMP affinity"
1043 for interrupt
in ${interrupts}; do
1044 interrupt_set_smp_affinity
${interrupt} ${processor}
1047 # Find all queues and assign them to the next processor
1049 for queue
in $
(device_get_queues
${device}); do
1051 # Only handle receive queues
1053 for interrupt
in $
(interrupts_for_device_queue
${device} ${queue}); do
1054 interrupt_set_smp_affinity
${interrupt} ${processor}
1057 device_queue_set_smp_affinity
${device} ${queue} ${processor}
1066 # Get the next available processor if in auto mode
1067 [ "${mode}" = "auto" ] && processor
=$
(system_get_next_processor
${processor})
1073 device_get_queues
() {
1078 list_directory
"${SYS_CLASS_NET}/${device}/queues"
1081 device_supports_multiqueue
() {
1084 local num_queues
=$
(device_num_queues
${device})
1086 if isset num_queues
&& [ ${num_queues} -gt 2 ]; then
1090 return ${EXIT_FALSE}
1093 device_num_queues
() {
1097 isset
type && assert isoneof
type rx tx
1102 for q
in $
(device_get_queues
${device}); do
1103 case "${type},${q}" in
1119 device_queue_get_smp_affinity
() {
1125 local path
="${SYS_CLASS_NET}/${device}/queues/${queue}"
1129 path
="${path}/rps_cpus"
1132 path
="${path}/xps_cpus"
1135 assert
[ -r "${path}" ]
1137 __bitmap_to_processor_ids $
(<${path})
1140 device_queue_set_smp_affinity
() {
1145 local processor
=${3}
1147 local path
="${SYS_CLASS_NET}/${device}/queues/${queue}/rps_cpus"
1148 assert
[ -w "${path}" ]
1150 log DEBUG
"Setting SMP affinity of ${device} (${queue}) to processor ${processor}"
1152 __processor_id_to_bitmap
${processor} > ${path}
1155 # Tries to find a device which has the given IP address assigned
1156 device_get_by_assigned_ip_address
() {
1163 # Read the first line of ip addr show to
1164 read -r device
<<< $
(ip addr show to
"${ip}")
1166 # If we did not found a device we return with ${EXIT_ERROR}
1167 if ! isset device
; then
1168 return ${EXIT_ERROR}
1171 # We get something like:
1172 # 3: upl0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
1173 # and we want upl0 so we take the second word and removing the :
1182 device_get_by_mac_address
() {
1189 for device
in $
(device_list
); do
1190 if [ "${mac}" = "$(device_get_address ${device})" ]; then
1196 # We could not found a port to the given mac address so we return exit error
1197 return ${EXIT_ERROR}