]> git.ipfire.org Git - people/arne_f/network.git/blob - functions.device
network: Improve function that creates virtual devices.
[people/arne_f/network.git] / 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 function devicify() {
23 local device=${1}
24
25 if device_exists ${device}; then
26 echo "${device}"
27 return ${EXIT_OK}
28 fi
29
30 local d
31 for d in $(devices_get_all); do
32 if [ "$(device_get_address ${d})" = "${device}" ]; then
33 echo "${d}"
34 return ${EXIT_OK}
35 fi
36 done
37
38 return ${EXIT_ERROR}
39 }
40
41 function macify() {
42 local device=${1}
43
44 if mac_is_valid ${device}; then
45 echo "${device}"
46 return ${EXIT_OK}
47 fi
48
49 if device_exists ${device}; then
50 device_get_address ${device}
51 return ${EXIT_OK}
52 fi
53
54 return ${EXIT_ERROR}
55 }
56
57 # Check if the device exists
58 function device_exists() {
59 local device=${1}
60
61 # If device name was not found, exit.
62 [ -n "${device}" ] || return ${EXIT_ERROR}
63
64 [ -d "${SYS_CLASS_NET}/${device}" ]
65 }
66
67 # Check if the device is up
68 function device_is_up() {
69 local device=${1}
70
71 device_exists ${device} || return ${EXIT_ERROR}
72
73 ip link show ${device} 2>/dev/null | grep -qE "<.*UP.*>"
74 }
75
76 # Check if the device is a bonding device
77 function device_is_bonding() {
78 [ -d "/sys/class/net/${1}/bonding" ]
79 }
80
81 # Check if the device bonded in a bonding device
82 function device_is_bonded() {
83 local dev
84 for dev in /sys/class/net/*; do
85 # Skip crappy files
86 [ -d "${dev}" ] || continue
87
88 # Continue if not a bonding device
89 device_is_bonding "${dev##*/}" || continue
90
91 if grep -q "\<${1}\>" ${dev}/bonding/slaves; then
92 return 0
93 fi
94 done
95
96 return 1
97 }
98
99 # Check if the device is a bridge
100 function device_is_bridge() {
101 [ -d "/sys/class/net/${1}/bridge" ]
102 }
103
104 # Check if the device is a virtual device
105 function device_is_virtual() {
106 local device=${1}
107
108 [ -e "/proc/net/vlan/${device}" ]
109 }
110
111 # Check if the device has virtual devices
112 function device_has_virtuals() {
113 local device=${1}
114
115 if device_is_virtual ${device}; then
116 return 1
117 fi
118
119 if [ ! -e "/proc/net/vlan/config" ]; then
120 return 1
121 fi
122 grep -q "${1}$" /proc/net/vlan/config
123 }
124
125 function device_is_vlan() { # XXX Compat function
126 log DEBUG "Deprecated function device_is_vlan() was used."
127
128 device_is_virtual $@
129 }
130
131 # Check if the device is a ppp device
132 function device_is_ppp() {
133 local device=${1}
134
135 ip link show ${device} 2>/dev/null | grep -qE "<.*POINTOPOINT.*>"
136 }
137
138 # Check if the device is a loopback device
139 function device_is_loopback() {
140 local device=$(devicify ${1})
141 [ "${device}" = "lo" ]
142 }
143
144 # Check if the device is a physical network interface
145 function device_is_real() {
146 local device=${1}
147
148 device_is_loopback ${device} && \
149 return ${EXIT_ERROR}
150
151 device_is_bonding ${device} && \
152 return ${EXIT_ERROR}
153
154 device_is_bridge ${device} && \
155 return ${EXIT_ERROR}
156
157 device_is_ppp ${device} && \
158 return ${EXIT_ERROR}
159
160 device_is_virtual ${device} && \
161 return ${EXIT_ERROR}
162
163 return ${EXIT_OK}
164 }
165
166 # Get the device type
167 function device_get_type() {
168 local device=$(devicify ${1})
169
170 if device_is_vlan ${device}; then
171 echo "vlan"
172
173 elif device_is_bonding ${device}; then
174 echo "bonding"
175
176 elif device_is_bridge ${device}; then
177 echo "bridge"
178
179 elif device_is_ppp ${device}; then
180 echo "ppp"
181
182 elif device_is_loopback ${device}; then
183 echo "loopback"
184
185 elif device_is_real ${device}; then
186 echo "real"
187
188 else
189 echo "unknown"
190 fi
191 }
192
193 function device_get_address() {
194 local device=${1}
195
196 cat ${SYS_CLASS_NET}/${device}/address 2>/dev/null
197 }
198
199 function device_set_address() {
200 local device=${1}
201 local addr=${2}
202
203 if ! device_exists ${device}; then
204 error "Device '${device}' does not exist."
205 return ${EXIT_ERROR}
206 fi
207
208 log INFO "Setting address of '${device}' to '${addr}' - was $(device_get_address ${device})."
209
210 local up
211 if device_is_up ${device}; then
212 device_set_down ${device}
213 up=1
214 fi
215
216 ip link set ${device} address ${addr}
217 local ret=$?
218
219 if [ "${up}" = "1" ]; then
220 device_set_up ${device}
221 fi
222
223 if [ "${ret}" != "0" ]; then
224 error_log "Could not set address '${addr}' on device '${device}'."
225 fi
226
227 return ${ret}
228 }
229
230 function devices_get_all() {
231 local device
232 for device in ${SYS_CLASS_NET}/*; do
233 echo "$(basename ${device})"
234 done | sort
235 }
236
237 # Check if a device has a cable plugged in
238 function device_has_carrier() {
239 local device=$(devicify ${1})
240 [ "$(<${SYS_CLASS_NET}/${device}/carrier)" = "1" ]
241 }
242
243 # Check if the device is free
244 function device_is_free() {
245 local device=${1}
246
247 device_is_used ${device} && \
248 return ${EXIT_ERROR}
249
250 return ${EXIT_OK}
251 }
252
253 # Check if the device is used
254 function device_is_used() {
255 local device=$(devicify ${1})
256
257 device_has_virtuals ${device} && \
258 return ${EXIT_OK}
259 device_is_bonded ${device} && \
260 return ${EXIT_OK}
261
262 return ${EXIT_ERROR}
263 }
264
265 # XXX to be removed I think
266 function device_get_free() {
267 local destination=${1}
268
269 # Replace + by a valid number
270 if grep -q "+$" <<<${destination}; then
271 local number=0
272 destination=$(sed -e "s/+//" <<<$destination)
273 while [ "${number}" -le "100" ]; do
274 if ! device_exists "${destination}${number}"; then
275 destination="${destination}${number}"
276 break
277 fi
278 number=$(($number + 1))
279 done
280 fi
281 echo "${destination}"
282 }
283
284 function device_rename() {
285 warning_log "Called deprecated function 'device_rename'"
286
287 device_set_name $@
288 }
289
290 function device_hash() {
291 local device=${1}
292
293 macify ${device} | tr -d ':'
294 }
295
296 # Give the device a new name
297 function device_set_name() {
298 local source=$1
299 local destination=$(device_get_free ${2})
300
301 # Check if devices exists
302 if ! device_exists ${source} || device_exists ${destination}; then
303 return 4
304 fi
305
306 local up
307 if device_is_up ${source}; then
308 ip link set ${source} down
309 up=1
310 fi
311
312 ip link set ${source} name ${destination}
313
314 if [ "${up}" = "1" ]; then
315 ip link set ${destination} up
316 fi
317 }
318
319 # Set device up
320 function device_set_up() {
321 local device=$(devicify ${1})
322
323 # Do nothing if device is already up
324 device_is_up ${device} && return ${EXIT_OK}
325
326 log DEBUG "Setting up device $@"
327 ip link set ${device} up
328 }
329
330 # Set device down
331 function device_set_down() {
332 local device=$(devicify ${1})
333
334 # Do nothing if device is not up
335 device_is_up ${device} || return ${EXIT_OK}
336
337 log DEBUG "Tearing down device $@"
338 ip link set ${device} down
339 }
340
341 # Set new address to a device
342 function device_set_mac() {
343 warning_log "Called deprecated function 'device_set_mac'"
344
345 device_set_address $@
346 }
347
348 function device_get_mtu() {
349 local device=${1}
350
351 if ! device_exists ${device}; then
352 error "Device '${device}' does not exist."
353 return ${EXIT_ERROR}
354 fi
355
356 cat ${SYS_CLASS_NET}/${device}/mtu
357 }
358
359 # Set mtu to a device
360 function device_set_mtu() {
361 local device=${1}
362 local mtu=${2}
363
364 if ! device_exists ${device}; then
365 error "Device '${device}' does not exist."
366 return ${EXIT_ERROR}
367 fi
368
369 local oldmtu=$(device_get_mtu ${device})
370
371 if [ "${oldmtu}" = "${mtu}" ]; then
372 # No need to set mtu.
373 return ${EXIT_OK}
374 fi
375
376 log INFO "Setting mtu of '${device}' to '${mtu}' - was ${oldmtu}."
377
378 local up
379 if device_is_up ${device}; then
380 device_set_down ${device}
381 up=1
382 fi
383
384 ip link set ${device} mtu ${mtu}
385 local ret=$?
386
387 if [ "${up}" = "1" ]; then
388 device_set_up ${device}
389 fi
390
391 if [ "${ret}" != "0" ]; then
392 error_log "Could not set mtu '${mtu}' on device '${device}'."
393 fi
394
395 return ${ret}
396 }
397
398 function device_discover() {
399 local device=${1}
400
401 log INFO "Running discovery process on device '${device}'."
402
403 local hook
404 for hook in $(hooks_get_all); do
405 hook_exec ${hook} discover ${device}
406 done
407 }
408
409 function device_create_virtual() {
410 log WARN "Called deprecated function device_create_virtual"
411 device_virtual_create $@
412 }
413
414 function device_virtual_create() {
415 local port=$(devicify ${1})
416 local vid=${2}
417 local mac=${3}
418 local newport=${port}v${vid}
419
420 if [ -z "${mac}" ]; then
421 mac=$(mac_generate)
422 fi
423
424 log INFO "Creating virtual device '${newport}' with address '${mac}'."
425
426 local oldport=$(device_virtual_get_by_parent_and_vid ${port} ${vid})
427
428 if device_exists ${oldport}; then
429 local differences
430
431 if [ "${oldport}" != "${newport}" ]; then
432 differences="${differences} name"
433 fi
434 if [ "$(device_get_address ${oldport})" != "${mac}" ]; then
435 differences="${differences} address"
436 fi
437
438 echo "differences: $differences"
439
440 if [ -n "${differences}" ]; then
441 if device_is_used ${oldport}; then
442 error_log "There was a device '${oldport}' set up with VID '${vid}' and parent '${port}' which is used somewhere else. Cannot go on."
443 return ${EXIT_ERROR}
444 else
445 log DEBUG "There is a device '${oldport}' but it not used, so we grab it to ourselves."
446 fi
447 else
448 log DEBUG "Device '${newport}' already exists and reflects our configuration. Go on."
449
450 device_set_up ${oldport}
451 return ${EXIT_OK}
452 fi
453
454 else
455 log DEBUG "Virtual device '${newport}' does not exist, yet."
456
457 vconfig set_name_type DEV_PLUS_VID_NO_PAD >/dev/null
458 vconfig add ${port} ${vid} >/dev/null
459
460 if [ $? -ne ${EXIT_OK} ]; then
461 error_log "Could not create virtual device '${newport}'."
462 return ${EXIT_ERROR}
463 fi
464
465 oldport=$(device_virtual_get_by_parent_and_vid ${port} ${vid})
466
467 fi
468
469 assert device_exists ${oldport}
470
471 if ! device_exists ${oldport}; then
472 error "Could not determine the created virtual device '${newport}'."
473 return ${EXIT_ERROR}
474 fi
475
476 # The device is expected to be named like ${port}.${vid}
477 # and will be renamed to the virtual schema
478 device_set_name ${oldport} ${newport}
479
480 if [ $? -ne ${EXIT_OK} ]; then
481 error_log "Could not set name of virtual device '${newport}'."
482 return ${EXIT_ERROR}
483 fi
484
485 assert device_exists ${newport}
486
487 # Setting new mac address
488 device_set_address ${newport} ${mac}
489
490 if [ $? -ne ${EXIT_OK} ]; then
491 error_log "Could not set address '${mac}' to virtual device '${newport}'."
492 return ${EXIT_ERROR}
493 fi
494
495 # Bring up the new device
496 device_set_up ${newport}
497
498 return ${EXIT_OK}
499 }
500
501 function device_virtual_remove() {
502 local device=$(devicify ${1})
503
504 log INFO "Removing virtual device '${device}' with address '$(macify ${devive})'."
505
506 device_set_down ${device}
507
508 vconfig rem ${device} >/dev/null
509
510 if [ $? -ne ${EXIT_OK} ]; then
511 error_log "Could not remote virtual device '${newport}'."
512 return ${EXIT_ERROR}
513 fi
514
515 return ${EXIT_OK}
516 }
517
518 function device_virtual_get_by_parent_and_vid() {
519 local parent=${1}
520 local vid=${2}
521
522 local v_port
523 local v_id
524 local v_parent
525
526 fgrep '|' < /proc/net/vlan/config | tr -d '|' | \
527 while read v_port v_id v_parent; do
528 if [ "${v_parent}" = "${parent}" ] && [ "${v_id}" = "${vid}" ]; then
529 echo "${v_port}"
530 return ${EXIT_OK}
531 fi
532 done
533
534 return ${EXIT_ERROR}
535 }
536
537 function device_bonding_create() {
538 local device=${1}
539 local mac=${2}
540
541 [ -z "${mac}" ] && mac=$(mac_generate)
542
543 log INFO "Creating bonding device '${device}' (${mac})."
544
545 echo "+${device}" > /sys/class/net/bonding_masters
546 device_set_mac ${mac}
547 device_set_up ${device}
548 }
549
550 function device_bonding_remove() {
551 local device=$(devicify ${1})
552
553 log INFO "Remove bonding device '${device}'."
554
555 device_set_down ${device}
556 echo "-${device}" > /sys/class/net/bonding_masters
557 }
558
559 function bonding_set_mode() {
560 local device=${1}
561 local mode=${2}
562
563 log INFO "Setting bonding mode on '${device}' '${mode}'."
564
565 echo "${mode}" > /sys/class/net/${device}/bonding/mode
566 }
567
568 function bonding_enslave_device() {
569 local device=$(devicify ${1})
570 local slave=$(devicify ${2})
571 shift 2
572
573 log INFO "Enslaving slave '${slave}' to '${device}'."
574
575 device_set_down ${slave}
576 echo "+${slave}" > /sys/class/net/${device}/bonding/slaves
577 }
578
579 function bridge_attach_device() {
580 local bridge=${1}
581 local device=${2}
582
583 if ! device_exists ${bridge}; then
584 error "Bridge '${bridge}' does not exist."
585 return ${EXIT_ERROR}
586 fi
587
588 if ! device_exists ${device}; then
589 error "Device '${device}' does not exist."
590 return ${EXIT_ERROR}
591 fi
592
593 log INFO "Attaching device '${device}' to bridge '${bridge}'."
594
595 # XXX device_set_up ${device} # Do we need this here?
596
597 brctl addif ${bridge} ${device}
598 }
599
600 function bridge_detach_device() {
601 local bridge=${1}
602 local device=${2}
603
604 if ! device_exists ${bridge}; then
605 error "Bridge '${bridge}' does not exist."
606 return ${EXIT_ERROR}
607 fi
608
609 if ! device_exists ${device}; then
610 error "Device '${device}' does not exist."
611 return ${EXIT_ERROR}
612 fi
613
614 log INFO "Detaching device '${device}' from bridge '${bridge}'."
615
616 brctl delif ${bridge} ${device}
617
618 device_set_down ${device}
619 }
620
621 function bridge_is_forwarding() {
622 local seconds=45
623 local zone=${1}
624
625 bridge_has_carrier ${zone} || return ${EXIT_ERROR}
626
627 local device
628 while [ ${seconds} -gt 0 ]; do
629 for device in ${SYS_CLASS_NET}/${zone}/brif/*; do
630 [ -e "${device}/state" ] || continue
631 if [ "$(<${device}/state)" = "3" ]; then
632 return ${EXIT_OK}
633 fi
634 done
635 sleep 1
636 seconds=$((${seconds} - 1))
637 done
638
639 return ${EXIT_ERROR}
640 }
641
642 function bridge_has_carrier() {
643 local zone=${1}
644
645 local has_carrier=${EXIT_ERROR}
646
647 local device
648 for device in ${SYS_CLASS_NET}/${zone}/brif/*; do
649 device=$(basename ${device})
650 device_exists ${device} || continue
651
652 device_has_carrier ${device} && has_carrier=${EXIT_OK}
653 done
654
655 return ${has_carrier}
656 }
657
658 function device_has_ipv4() {
659 local device=${1}
660 local addr=${2}
661
662 if ! device_exists ${device}; then
663 error "Device '${device}' does not exist."
664 return ${EXIT_ERROR}
665 fi
666
667 ip addr show ${device} | grep -q -e "inet " -e "${addr}"
668 }