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