device: Refactor check for device type
[people/ms/network.git] / src / functions / functions.device
1 #!/bin/bash
2 ###############################################################################
3 #                                                                             #
4 # IPFire.org - A linux based firewall                                         #
5 # Copyright (C) 2010  Michael Tremer & Christian Schmidt                      #
6 #                                                                             #
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.                                         #
11 #                                                                             #
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.                                #
16 #                                                                             #
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/>.       #
19 #                                                                             #
20 ###############################################################################
21
22 declare -A DEVICE_LINK_SPEEDS=(
23         [10BaseT-Half]=0x1
24         [10BaseT-Full]=0x2
25         [100BaseT-Half]=0x4
26         [100BaseT-Full]=0x8
27         [1000BaseT-Half]=0x10
28         [1000BaseT-Full]=0x20
29         [10000BaseT-Full]=0x1000
30 )
31
32 device_list() {
33         # Add all interfaces
34         local device
35         for device in $(list_directory ${SYS_CLASS_NET}); do
36                 if device_exists "${device}"; then
37                         print "${device}"
38                 fi
39         done
40
41         # List all PHYs
42         phy_list
43
44         # List all serial devices
45         serial_list
46 }
47
48 # Check if the device exists
49 device_exists() {
50         local device=${1}
51
52         # If device name was not found, exit.
53         [ -n "${device}" ] || return ${EXIT_ERROR}
54
55         # Check for a normal network device.
56         [ -d "${SYS_CLASS_NET}/${device}" ] && return ${EXIT_OK}
57
58         # If the check above did not find a result,
59         # we check for PHYs.
60         phy_exists "${device}" && return ${EXIT_OK}
61
62         # If the check above did not find a result,
63         # we check for serial devices.
64         serial_exists ${device}
65 }
66
67 device_matches_pattern() {
68         local device="${1}"
69         assert isset device
70
71         local pattern="${2}"
72         assert isset pattern
73
74         pattern="^${pattern//N/[[:digit:]]+}$"
75
76         [[ ${device} =~ ${pattern} ]] \
77                 && return ${EXIT_TRUE} || return ${EXIT_FALSE}
78 }
79
80 device_delete() {
81         local device=${1}
82         assert isset device
83
84         # Nothing to do, it device does not exist.
85         device_exists ${device} || return ${EXIT_OK}
86
87         # Shut down device before we delete it
88         device_set_down "${device}"
89
90         # Delete the device.
91         cmd_quiet ip link delete ${device}
92         local ret=$?
93
94         if [ ${ret} -ne ${EXIT_OK} ]; then
95                 log ERROR "device: Could not delete device '${device}': ${ret}"
96                 return ${EXIT_ERROR}
97         fi
98
99         return ${ret}
100 }
101
102 device_has_flag() {
103         local device=${1}
104         local flag=${2}
105
106         local flags=$(__device_get_file ${device} flags)
107
108         if [[ "$(( ${flags} & ${flag} ))" -eq 0 ]]; then
109                 return ${EXIT_FALSE}
110         else
111                 return ${EXIT_TRUE}
112         fi
113 }
114
115 # Check if the device is up
116 device_is_up() {
117         local device=${1}
118
119         device_exists ${device} || return ${EXIT_ERROR}
120
121         device_has_flag ${device} 0x1
122 }
123
124 device_ifindex_to_name() {
125         local idx=${1}
126         assert isset idx
127
128         local device device_idx
129         for device in $(list_directory "${SYS_CLASS_NET}"); do
130                 device_idx=$(device_get_ifindex ${device})
131
132                 if [ "${device_idx}" = "${idx}" ]; then
133                         print "${device}"
134                         return ${EXIT_OK}
135                 fi
136         done
137
138         return ${EXIT_ERROR}
139 }
140
141 device_get_ifindex() {
142         local device=${1}
143         assert isset device
144
145         local path="${SYS_CLASS_NET}/${1}/ifindex"
146
147         # Check if file can be read.
148         [ -r "${path}" ] || return ${EXIT_ERROR}
149
150         print "$(<${path})"
151 }
152
153 device_get_driver() {
154         local device="${1}"
155         assert isset device
156
157         get_driver_from_path "${SYS_CLASS_NET}/${device}/device/driver/module"
158 }
159
160 # Check if the device is a bonding device
161 device_is_bonding() {
162         [ -d "/sys/class/net/${1}/bonding" ]
163 }
164
165 # Check if the device bonded in a bonding device
166 device_is_bonded() {
167         local device=${1}
168
169         [ -d "${SYS_CLASS_NET}/${device}/bonding_slave" ]
170 }
171
172 # Check if the device is a bridge
173 device_is_bridge() {
174         [ -d "/sys/class/net/${1}/bridge" ]
175 }
176
177 device_is_bridge_attached() {
178         local device=${1}
179         [ -d "${SYS_CLASS_NET}/${device}/brport" ]
180 }
181
182 device_is_wireless_monitor() {
183         local device="${1}"
184         assert isset device
185
186         device_is_wireless "${device}" && \
187                 device_matches_pattern "${device}" "${PORT_PATTERN_WIRELESS_MONITOR}"
188 }
189
190 device_is_wireless_adhoc() {
191         local device="${1}"
192         assert isset device
193
194         device_is_wireless "${device}" && \
195                 device_matches_pattern "${device}" "${PORT_PATTERN_WIRELESS_ADHOC}"
196 }
197
198 device_get_bridge() {
199         local device=${1}
200         assert isset device
201
202         # Check if device is attached to a bridge.
203         device_is_bridge_attached ${device} || return ${EXIT_ERROR}
204
205         local ifindex_path="${SYS_CLASS_NET}/${device}/brport/bridge/ifindex"
206         [ -r "${ifindex_path}" ] || return ${EXIT_ERROR}
207
208         local ifindex=$(<${ifindex_path})
209         assert isset ifindex
210
211         device_ifindex_to_name ${ifindex}
212 }
213
214 # Check if the device is a vlan device
215 device_is_vlan() {
216         local device=${1}
217         assert isset device
218
219         [ -e "${PROC_NET_VLAN}/${device}" ]
220 }
221
222 # Check if the device has vlan devices
223 device_has_vlans() {
224         local device=${1}
225         assert isset device
226
227         if device_is_vlan ${device}; then
228                 return ${EXIT_FALSE}
229         fi
230
231         local vlans=$(device_get_vlans ${device})
232         [ -n "${vlans}" ] && return ${EXIT_OK} || return ${EXIT_ERROR}
233 }
234
235 device_get_vlans() {
236         local device=${1}
237         assert isset device
238
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}
242
243         local dev spacer1 id spacer2 parent
244         while read dev spacer1 id spacer2 parent; do
245                 [ "${parent}" = "${device}" ] || continue
246
247                 print "${dev}"
248         done < ${PROC_NET_VLAN_CONFIG}
249 }
250
251 __device_type_matches() {
252         local device="${1}"
253         local type="${2}"
254
255         local _type="$(__device_get_file "${device}" "type")"
256
257         if [ "${type}" = "${_type}" ]; then
258                 return ${EXIT_TRUE}
259         fi
260
261         return ${EXIT_FALSE}
262 }
263
264 # Check if the device is a ppp device
265 device_is_ppp() {
266         local device="${1}"
267         assert isset device
268
269         __device_type_matches "${device}" 512
270 }
271
272 # Check if the device is a pointopoint device.
273 device_is_ptp() {
274         local device=${1}
275
276         device_has_flag ${device} 0x10
277 }
278
279 # Check if the device is a loopback device
280 device_is_loopback() {
281         local device=${1}
282
283         [ "${device}" = "lo" ]
284 }
285
286 # Check if the device is a dummy device
287 #  This is the worst possible check, but all I could come up with
288 device_is_dummy() {
289         local device="${1}"
290
291         [[ ${device} =~ ^dummy[0-9]+$ ]]
292 }
293
294 device_is_ipsec() {
295         local device="${1}"
296
297         [[ ${device} =~ ^ipsec\- ]]
298 }
299
300 # Check if the device is a wireless device
301 device_is_wireless() {
302         local device=${1}
303
304         [ -d "${SYS_CLASS_NET}/${device}/phy80211" ]
305 }
306
307 device_is_vti() {
308         local device="${1}"
309         assert isset device
310
311         __device_type_matches "${device}" 768
312 }
313
314 device_is_vti6() {
315         local device="${1}"
316         assert isset device
317
318         __device_type_matches "${device}" 769
319 }
320
321 device_get_phy() {
322         local device="${1}"
323
324         if device_is_wireless "${device}"; then
325                 print "$(<${SYS_CLASS_NET}/${device}/phy80211/name)"
326                 return ${EXIT_OK}
327         fi
328
329         return ${EXIT_ERROR}
330 }
331
332 device_is_phy() {
333         phy_exists "$@"
334 }
335
336 device_is_serial() {
337         serial_exists "$@"
338 }
339
340 # Returns true if a device is a tun device
341 device_is_tun() {
342         local device="${1}"
343
344         [ -e "${SYS_CLASS_NET}/${device}/tun_flags" ]
345 }
346
347 # Check if the device is a physical network interface
348 device_is_ethernet() {
349         local device=${1}
350
351         device_is_ethernet_compatible "${device}" || \
352                 return ${EXIT_ERROR}
353
354         device_is_loopback ${device} && \
355                 return ${EXIT_ERROR}
356
357         device_is_bonding ${device} &&  \
358                 return ${EXIT_ERROR}
359
360         device_is_bridge ${device} && \
361                 return ${EXIT_ERROR}
362
363         device_is_ppp ${device} && \
364                 return ${EXIT_ERROR}
365
366         device_is_vlan ${device} && \
367                 return ${EXIT_ERROR}
368
369         device_is_dummy ${device} && \
370                 return ${EXIT_ERROR}
371
372         device_is_tun ${device} && \
373                 return ${EXIT_ERROR}
374
375         return ${EXIT_OK}
376 }
377
378 # Get the device type
379 device_get_type() {
380         local device=${1}
381
382         # If the device does not exist (happens on udev remove events),
383         # we do not bother to run all checks.
384         if ! device_exists "${device}"; then
385                 echo "unknown"
386
387         elif device_is_vlan ${device}; then
388                 echo "vlan"
389
390         elif device_is_bonding ${device}; then
391                 echo "bonding"
392
393         elif device_is_bridge ${device}; then
394                 echo "bridge"
395
396         elif device_is_ppp ${device}; then
397                 echo "ppp"
398
399         elif device_is_loopback ${device}; then
400                 echo "loopback"
401
402         elif device_is_wireless_adhoc ${device}; then
403                 echo "wireless-adhoc"
404
405         elif device_is_wireless ${device}; then
406                 echo "wireless"
407
408         elif device_is_dummy ${device}; then
409                 echo "dummy"
410
411         elif device_is_tun ${device}; then
412                 echo "tun"
413
414         elif device_is_ethernet ${device}; then
415                 echo "ethernet"
416
417         elif device_is_serial ${device}; then
418                 echo "serial"
419
420         elif device_is_phy ${device}; then
421                 echo "phy"
422
423         else
424                 echo "$(device_tunnel_get_type "${device}")"
425         fi
426 }
427
428 # This function just checks the types a ip-tunnel device usually have
429 # so when we know that the device is an ip-tunnel device we save time
430 device_tunnel_get_type() {
431         local device=${1}
432
433         # If the device does not exist (happens on udev remove events),
434         # we do not bother to run all checks.
435         if ! device_exists "${device}"; then
436                 echo "unknown"
437
438         elif device_is_vti ${device}; then
439                 echo "vti"
440
441         elif device_is_vti6 ${device}; then
442                 echo "vti6"
443
444         else
445                 echo "unknown"
446         fi
447 }
448
449 device_is_ethernet_compatible() {
450         local device="${1}"
451
452         # /sys/class/net/*/type must equal 1 for ethernet compatible devices
453         local type="$(__device_get_file "${device}" "type")"
454         [[ "${type}" = "1" ]]
455 }
456
457 device_get_status() {
458         local device=${1}
459         assert isset device
460
461         local status=${STATUS_DOWN}
462
463         if device_is_up ${device}; then
464                 status=${STATUS_UP}
465
466                 if ! device_has_carrier ${device}; then
467                         status=${STATUS_NOCARRIER}
468                 fi
469         fi
470
471         echo "${status}"
472 }
473
474 device_get_address() {
475         local device=${1}
476
477         cat ${SYS_CLASS_NET}/${device}/address 2>/dev/null
478 }
479
480 device_set_address() {
481         assert [ $# -eq 2 ]
482
483         local device="${1}"
484         local addr="${2}"
485         
486         if ! device_exists "${device}"; then
487                 error "Device '${device}' does not exist."
488                 return ${EXIT_ERROR}
489         fi
490
491         # Do nothing if the address has not changed
492         local old_addr="$(device_get_address "${device}")"
493         if [ -n "${old_addr}" -a "${addr}" = "${old_addr}" ]; then
494                 return ${EXIT_OK}
495         fi
496
497         log DEBUG "Setting address of '${device}' from '${old_addr}' to '${addr}'"
498
499         local up
500         if device_is_up "${device}"; then
501                 device_set_down "${device}"
502                 up=1
503         fi
504
505         ip link set "${device}" address "${addr}"
506         local ret=$?
507
508         if [ "${up}" = "1" ]; then
509                 device_set_up "${device}"
510         fi
511
512         if [ "${ret}" != "0" ]; then
513                 error_log "Could not set address '${addr}' on device '${device}'"
514         fi
515
516         return ${ret}
517 }
518
519 device_get() {
520         local device
521         for device in $(list_directory "${SYS_CLASS_NET}"); do
522                 # bonding_masters is no device
523                 [ "${device}" = "bonding_masters" ] && continue
524
525                 echo "${device}"
526         done
527
528         return ${EXIT_OK}
529 }
530
531 # Check if a device has a cable plugged in
532 device_has_carrier() {
533         local device=${1}
534         assert isset device
535
536         local carrier=$(__device_get_file ${device} carrier)
537         [ "${carrier}" = "1" ]
538 }
539
540 device_is_promisc() {
541         local device=${1}
542
543         device_has_flag ${device} 0x200
544 }
545
546 device_set_promisc() {
547         local device=${1}
548         local state=${2}
549
550         assert device_exists ${device}
551         assert isset state
552         assert isoneof state on off
553
554         ip link set ${device} promisc ${state}
555 }
556
557 # Check if the device is free
558 device_is_free() {
559         ! device_is_used "$@"
560 }
561
562 # Check if the device is used
563 device_is_used() {
564         local device=${1}
565
566         device_has_vlans ${device} && \
567                 return ${EXIT_OK}
568         device_is_bonded ${device} && \
569                 return ${EXIT_OK}
570         device_is_bridge_attached ${device} && \
571                 return ${EXIT_OK}
572
573         return ${EXIT_ERROR}
574 }
575
576 # Give the device a new name
577 device_set_name() {
578         local source=$1
579         local destination=${2}
580
581         # Check if devices exists
582         if ! device_exists ${source} || device_exists ${destination}; then
583                 return 4
584         fi
585
586         local up
587         if device_is_up ${source}; then
588                 ip link set ${source} down
589                 up=1
590         fi
591
592         ip link set ${source} name ${destination}
593
594         if [ "${up}" = "1" ]; then
595                 ip link set ${destination} up
596         fi
597 }
598
599 device_set_master() {
600         local device="${1}"
601         assert isset device
602
603         local master="${2}"
604         assert isset master
605
606         if ! cmd ip link set "${device}" master "${master}"; then
607                 log ERROR "Could not set master ${master} for device ${device}"
608                 return ${EXIT_ERROR}
609         fi
610
611         return ${EXIT_OK}
612 }
613
614 device_remove_master() {
615         local device="${1}"
616         assert isset device
617
618         if ! cmd ip link set "${device}" nomaster; then
619                 log ERROR "Could not remove master for device ${device}"
620                 return ${EXIT_ERROR}
621         fi
622
623         return ${EXIT_OK}
624 }
625
626 # Set device up
627 device_set_up() {
628         assert [ $# -eq 1 ]
629
630         local device=${1}
631
632         # Do nothing if device is already up
633         device_is_up ${device} && return ${EXIT_OK}
634
635         log INFO "Bringing up ${device}"
636
637         device_set_parent_up ${device}
638         if ! cmd ip link set ${device} up; then
639                 return ${EXIT_ERROR}
640         fi
641
642         # Set SMP affinity
643         if interrupt_use_smp_affinity; then
644                 device_auto_configure_smp_affinity ${device}
645         fi
646
647         return ${EXIT_OK}
648 }
649
650 device_set_parent_up() {
651         local device=${1}
652         local parent
653
654         if device_is_vlan ${device}; then
655                 parent=$(vlan_get_parent ${device})
656
657                 device_is_up ${parent} && return ${EXIT_OK}
658
659                 log DEBUG "Setting up parent device '${parent}' of '${device}'"
660
661                 device_set_up ${parent}
662                 return $?
663         fi
664
665         return ${EXIT_OK}
666 }
667
668 # Set device down
669 device_set_down() {
670         assert [ $# -eq 1 ]
671
672         local device=${1}
673         local ret=${EXIT_OK}
674
675         if device_is_up ${device}; then
676                 log INFO "Bringing down ${device}"
677
678                 cmd ip link set ${device} down
679                 ret=$?
680         fi
681
682         device_set_parent_down ${device}
683
684         return ${ret}
685 }
686
687 device_set_parent_down() {
688         local device=${1}
689         local parent
690
691         if device_is_vlan ${device}; then
692                 parent=$(vlan_get_parent ${device})
693
694                 device_is_up ${parent} || return ${EXIT_OK}
695
696                 if device_is_free ${parent}; then
697                         log DEBUG "Tearing down parent device '${parent}' of '${device}'"
698
699                         device_set_down ${parent}
700                 fi
701         fi
702         
703         return ${EXIT_OK}
704 }
705
706 device_get_mtu() {
707         local device=${1}
708
709         # Return an error if the device does not exist
710         device_exists ${device} || return ${EXIT_ERROR}
711
712         echo $(<${SYS_CLASS_NET}/${device}/mtu)
713 }
714
715 # Set mtu to a device
716 device_set_mtu() {
717         local device=${1}
718         local mtu=${2}
719
720         assert device_exists ${device}
721
722         # Handle bridges differently
723         if device_is_bridge ${device}; then
724                 local port
725                 for port in $(bridge_get_members ${device}); do
726                         device_set_mtu ${port} ${mtu}
727                 done
728         fi
729
730         log INFO "Setting MTU of ${device} to ${mtu}"
731
732         local up
733         if device_is_up ${device}; then
734                 device_set_down ${device}
735                 up=1
736         fi
737
738         local ret=${EXIT_OK}
739         if ! cmd ip link set ${device} mtu ${mtu}; then
740                 ret=${EXIT_ERROR}
741
742                 log ERROR "Could not set MTU ${mtu} on ${device}"
743         fi
744
745         if [ "${up}" = "1" ]; then
746                 device_set_up ${device}
747         fi
748
749         return ${ret}
750 }
751
752 device_adjust_mtu() {
753         assert [ $# -eq 2 ]
754
755         local device="${1}"
756         local other_device="${2}"
757
758         local mtu="$(device_get_mtu "${other_device}")"
759         device_set_mtu "${device}" "${mtu}"
760 }
761
762 device_discover() {
763         local device=${1}
764
765         log INFO "Running discovery process on device '${device}'."
766
767         local hook
768         for hook in $(hook_zone_get_all); do
769                 hook_zone_exec ${hook} discover ${device}
770         done
771 }
772
773 device_identify() {
774         assert [ $# -ge 1 ]
775
776         local device="${1}"
777
778         # Flash for ten seconds by default
779         local seconds="10"
780
781         # Run in background?
782         local background="false"
783
784         local arg
785         while read arg; do
786                 case "${arg}" in
787                         --background)
788                                 background="true"
789                                 ;;
790                         --seconds=*)
791                                 seconds="$(cli_get_val "${arg}")"
792                                 ;;
793                 esac
794         done <<< "$(args "$@")"
795
796         assert isinteger seconds
797
798         if ! device_exists "${device}"; then
799                 log ERROR "Cannot identify device ${device}: Does not exist"
800                 return ${EXIT_ERROR}
801         fi
802
803         if ! device_is_ethernet "${device}"; then
804                 log DEBUG "Cannot identify device ${device}: Not an ethernet device"
805                 return ${EXIT_NOT_SUPPORTED}
806         fi
807
808         log DEBUG "Identifying device ${device}"
809
810         local command="ethtool --identify ${device} ${seconds}"
811         local ret=0
812
813         if enabled background; then
814                 cmd_background "${command}"
815         else
816                 cmd_quiet "${command}"
817                 ret=$?
818         fi
819
820         return ${ret}
821 }
822
823 device_has_ip() {
824         local device=${1}
825         local addr=${2}
826
827         assert isset addr
828         assert device_exists ${device}
829
830         # IPv6 addresses must be fully imploded
831         local protocol=$(ip_detect_protocol ${addr})
832         case "${protocol}" in
833                 ipv6)
834                         addr=$(ipv6_format "${addr}")
835                         ;;
836         esac
837
838         list_match ${addr} $(device_get_addresses ${device})
839 }
840
841 device_get_addresses() {
842         local device=${1}
843
844         assert device_exists ${device}
845
846         local prot
847         local addr
848         local line
849         ip addr show ${device} | \
850                 while read prot addr line; do
851                         [ "${prot:0:4}" = "inet" ] && echo "${addr}"
852                 done
853 }
854
855 __device_get_file() {
856         local device=${1}
857         local file=${2}
858
859         fread "${SYS_CLASS_NET}/${device}/${file}"
860 }
861
862 __device_set_file() {
863         assert [ $# -eq 3 ]
864
865         local device="${1}"
866         local file="${2}"
867         local value="${3}"
868
869         fappend "${SYS_CLASS_NET}/${device}/${file}" "${value}"
870 }
871
872 device_get_rx_bytes() {
873         local device=${1}
874
875         __device_get_file ${device} statistics/rx_bytes
876 }
877
878 device_get_tx_bytes() {
879         local device=${1}
880
881         __device_get_file ${device} statistics/tx_bytes
882 }
883
884 device_get_rx_packets() {
885         local device=${1}
886
887         __device_get_file ${device} statistics/rx_packets
888 }
889
890 device_get_tx_packets() {
891         local device=${1}
892
893         __device_get_file ${device} statistics/tx_packets
894 }
895
896 device_get_rx_errors() {
897         local device=${1}
898
899         __device_get_file ${device} statistics/rx_errors
900 }
901
902 device_get_tx_errors() {
903         local device=${1}
904
905         __device_get_file ${device} statistics/tx_errors
906 }
907
908 device_advertise_link_speeds() {
909         local device="${1}"
910         shift
911
912         assert isset device
913
914         # Advertised modes in hex
915         local advertise=0
916
917         local mode
918         for mode in $@; do
919                 local m="${DEVICE_LINK_SPEEDS[${mode}]}"
920                 if isset m; then
921                         advertise="$(( advertise | m ))"
922                 fi
923         done
924
925         # If nothing was selected, we reset and enable everything
926         if [ ${advertise} -eq 0 ]; then
927                 advertise=0xffffff
928         fi
929
930         # Enable auto-negotiation
931         cmd_quiet ethtool --change "${device}" autoneg on
932
933         # Set advertised link speeds
934         if ! cmd_quiet ethtool --change "${device}" advertise "0x$(hex "${advertise}")"; then
935                 log ERROR "Could not set link modes of ${device}: $@"
936                 return ${EXIT_ERROR}
937         fi
938
939         log DEBUG "Set device link modes of ${device} to $@"
940         return ${EXIT_ERROR}
941 }
942
943 device_get_speed() {
944         local device=${1}
945
946         local speed=$(__device_get_file ${device} speed)
947
948         # Exit for no output (i.e. no link detected)
949         isset speed || return ${EXIT_ERROR}
950
951         # Don't return anything for negative values
952         [ ${speed} -lt 0 ] && return ${EXIT_ERROR}
953
954         print "${speed}"
955 }
956
957 device_get_duplex() {
958         local device=${1}
959
960         local duplex=$(__device_get_file ${device} duplex)
961
962         case "${duplex}" in
963                 unknown)
964                         return ${EXIT_ERROR}
965                         ;;
966                 *)
967                         print "${duplex}"
968                         ;;
969         esac
970 }
971
972 device_get_link_string() {
973         local device="${1}"
974         assert isset device
975
976         local s
977
978         local speed="$(device_get_speed "${device}")"
979         if isset speed; then
980                 list_append s "${speed} MBit/s"
981         fi
982
983         local duplex="$(device_get_duplex "${device}")"
984         if isset duplex; then
985                 list_append s "${duplex} duplex"
986         fi
987
988         print "${s}"
989 }
990
991 device_auto_configure_smp_affinity() {
992         assert [ $# -eq 1 ]
993
994         local device=${1}
995
996         if lock_acquire "smp-affinity" 60; then
997                 device_set_smp_affinity ${device} auto
998
999                 lock_release "smp-affinity"
1000         fi
1001 }
1002
1003 device_set_smp_affinity() {
1004         assert [ $# -eq 2 ]
1005
1006         local device=${1}
1007         local mode=${2}
1008
1009         # mode can be auto which will automatically try to find
1010         # the least busy processor, or an integer for the desired
1011         # processor that should handle this device
1012
1013         local num_processors=$(system_get_processors)
1014
1015         if [ "${mode}" = "auto" ]; then
1016                 local processor=$(interrupt_choose_least_busy_processor)
1017         else
1018                 assert isinteger mode
1019                 local processor=${mode}
1020
1021                 if [ ${processor} -gt ${num_processors} ]; then
1022                         log ERROR "Processor ${processor} does not exist"
1023                         return ${EXIT_ERROR}
1024                 fi
1025         fi
1026
1027         local interrupts=$(interrupts_for_device ${device})
1028         if ! isset interrupts; then
1029                 log DEBUG "${device} has no interrupts. Not changing SMP affinity"
1030                 return ${EXIT_OK}
1031         fi
1032
1033         # Set SMP affinity
1034         local interrupt
1035         for interrupt in ${interrupts}; do
1036                 interrupt_set_smp_affinity ${interrupt} ${processor}
1037         done
1038
1039         # Find all queues and assign them to the next processor
1040         local queue
1041         for queue in $(device_get_queues ${device}); do
1042                 case "${queue}" in
1043                         # Only handle receive queues
1044                         rx-*)
1045                                 for interrupt in $(interrupts_for_device_queue ${device} ${queue}); do
1046                                         interrupt_set_smp_affinity ${interrupt} ${processor}
1047                                 done
1048
1049                                 device_queue_set_smp_affinity ${device} ${queue} ${processor}
1050                                 ;;
1051
1052                         # Ignore the rest
1053                         *)
1054                                 continue
1055                                 ;;
1056                 esac
1057
1058                 # Get the next available processor if in auto mode
1059                 [ "${mode}" = "auto" ] && processor=$(system_get_next_processor ${processor})
1060         done
1061
1062         return ${EXIT_OK}
1063 }
1064
1065 device_get_queues() {
1066         assert [ $# -eq 1 ]
1067
1068         local device=${1}
1069
1070         list_directory "${SYS_CLASS_NET}/${device}/queues"
1071 }
1072
1073 device_supports_multiqueue() {
1074         local device=${1}
1075
1076         local num_queues=$(device_num_queues ${device})
1077
1078         if isset num_queues && [ ${num_queues} -gt 2 ]; then
1079                 return ${EXIT_TRUE}
1080         fi
1081
1082         return ${EXIT_FALSE}
1083 }
1084
1085 device_num_queues() {
1086         local device=${1}
1087         local type=${2}
1088
1089         isset type && assert isoneof type rx tx
1090
1091         local i=0
1092
1093         local q
1094         for q in $(device_get_queues ${device}); do
1095                 case "${type},${q}" in
1096                         rx,rx-*)
1097                                 (( i++ ))
1098                                 ;;
1099                         tx,tx-*)
1100                                 (( i++ ))
1101                                 ;;
1102                         *,*)
1103                                 (( i++ ))
1104                                 ;;
1105                 esac
1106         done
1107
1108         print ${i}
1109 }
1110
1111 device_queue_get_smp_affinity() {
1112         assert [ $# -eq 2 ]
1113
1114         local device=${1}
1115         local queue=${2}
1116
1117         local path="${SYS_CLASS_NET}/${device}/queues/${queue}"
1118
1119         case "${queue}" in
1120                 rx-*)
1121                         path="${path}/rps_cpus"
1122                         ;;
1123                 tx-*)
1124                         path="${path}/xps_cpus"
1125                         ;;
1126         esac
1127         assert [ -r "${path}" ]
1128
1129         __bitmap_to_processor_ids $(<${path})
1130 }
1131
1132 device_queue_set_smp_affinity() {
1133         assert [ $# -eq 3 ]
1134
1135         local device=${1}
1136         local queue=${2}
1137         local processor=${3}
1138
1139         local path="${SYS_CLASS_NET}/${device}/queues/${queue}/rps_cpus"
1140         assert [ -w "${path}" ]
1141
1142         log DEBUG "Setting SMP affinity of ${device} (${queue}) to processor ${processor}"
1143
1144         __processor_id_to_bitmap ${processor} > ${path}
1145 }
1146
1147 # Tries to find a device which has the given IP address assigned
1148 device_get_by_assigned_ip_address() {
1149         local ip=${1}
1150
1151         assert isset ip
1152
1153         local device
1154
1155         # Read the first line of ip addr show to
1156         read -r device <<< $(ip addr show to "${ip}")
1157
1158         # If we did not found a device we return with ${EXIT_ERROR}
1159         if ! isset device; then
1160                 return ${EXIT_ERROR}
1161         fi
1162
1163         # We get something like:
1164         # 3: upl0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
1165         # and we want upl0 so we take the second word and removing the :
1166         device=(${device})
1167         device=${device[1]}
1168         device=${device%:}
1169
1170         print "${device}"
1171         return ${EXIT_OK}
1172 }
1173
1174 device_get_by_mac_address() {
1175         local mac=${1}
1176
1177         assert isset mac
1178
1179         local device
1180
1181         for device in $(device_list); do
1182                 if [ "${mac}" = "$(device_get_address ${device})" ]; then
1183                         print "${device}"
1184                         return ${EXIT_OK}
1185                 fi
1186         done
1187
1188         # We could not found a port to the given mac address so we return exit error
1189         return ${EXIT_ERROR}
1190 }