]> git.ipfire.org Git - people/stevee/network.git/blob - functions
network: New package.
[people/stevee/network.git] / functions
1 #!/bin/sh
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2009 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 HOME_DIR=${HOME_DIR-/lib/network}
23 CONFIG_DIR=/etc/network
24 HOOKS_DIR=${HOME_DIR}/hooks
25 LOG_DIR=/var/log/network
26
27 CONNECTIONS_FILE=/var/log/network/connections.db
28
29 CONFIG_ZONES=${CONFIG_DIR}/zones
30 CONFIG_PORTS=${CONFIG_DIR}/ports
31 CONFIG_HOOKS=${CONFIG_DIR}/hooks
32 CONFIG_PPP=${CONFIG_DIR}/ppp
33 CONFIG_UUIDS=${CONFIG_DIR}/uuids
34
35 # Create config directories
36 for dir in ${CONFIG_ZONES} ${CONFIG_PORTS} ${CONFIG_HOOKS} ${CONFIG_PPP} ${CONFIG_UUIDS}; do
37 [ -d "${dir}" ] && continue
38 mkdir -p "${dir}"
39 done
40
41 COMMON_DEVICE=port+
42
43 EXIT_OK=0
44 EXIT_ERROR=1
45 EXIT_CONF_ERROR=2
46
47 VALID_ZONES="blue green orange red grey"
48
49 [ -n "${DEBUG}" ] || DEBUG=
50 [ -n "${VERBOSE}" ] || VERBOSE=
51
52 function is_mac() {
53 [[ $1 =~ ^[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]\:[0-9a-f][0-9a-f]$ ]]
54 }
55
56 function is_uuid() {
57 local string=${1}
58
59 # Length must be 37 characters
60 if [ ${#string} -eq 36 ] \
61 && [ "${string:8:1}" = "-" ] \
62 && [ "${string:13:1}" = "-" ] \
63 && [ "${string:18:1}" = "-" ] \
64 && [ "${string:23:1}" = "-" ]; then
65 return ${EXIT_OK}
66 fi
67 return ${EXIT_ERROR}
68 }
69
70 function get_device_by_mac() {
71 local mac=${1}
72 local device
73
74 for device in /sys/class/net/*; do
75 [ -d "${device}" ] || continue
76 if [ "$(cat $device/address)" = "$mac" ]; then
77 device=${device##*/}
78 # Skip virtual devices
79 if [ -e "/proc/net/vlan/$device" ]; then
80 continue
81 fi
82 # Skip zones
83 if zone_exists ${device}; then
84 continue
85 fi
86 echo ${device}
87 return 0
88 fi
89 done
90 return 1
91 }
92
93 function get_device_by_mac_and_vid() {
94 local mac=$1
95 local vid=$2
96
97 local i
98 local VID
99 local DEVICE
100 if [ -e "/proc/net/vlan/config" ]; then
101 grep '|' /proc/net/vlan/config | sed "s/|//g" | \
102 while read DEVICE VID PARENT; do
103 if [ "${vid}" = "${VID}" ] && [ "$(macify ${PARENT})" = "${mac}" ]; then
104 echo "${DEVICE}"
105 return 0
106 fi
107 done
108 fi
109 return 1
110 }
111
112 function get_device() {
113 if [ ${#@} -gt 1 ]; then
114 get_device_by_mac_and_vid $@
115 else
116 get_device_by_mac $@
117 fi
118 }
119
120 function get_mac_by_device() {
121 local device
122 device=$1
123 if [ -d "/sys/class/net/$device" ]; then
124 cat /sys/class/net/$device/address
125 return 0
126 fi
127 return 1
128 }
129
130 function get_mac() {
131 get_mac_by_device $@
132 }
133
134 function devicify() {
135 local device=${1}
136 local mac
137
138 [ -n "${device}" ] || return 1
139
140 if is_mac ${device}; then
141 mac=${device}
142 device=$(get_device_by_mac ${device})
143 fi
144 if [ -n "${device}" ]; then
145 echo ${device}
146 return 0
147 else
148 echo "devicify: Could not find device of $@" >&2
149 return 1
150 fi
151 }
152
153 function macify() {
154 local input=${1}
155 local mac
156
157 if is_mac ${input}; then
158 mac=${input}
159 else
160 mac=$(get_mac_by_device ${input})
161 fi
162 echo ${mac}
163 }
164
165 function device_exists() {
166 [ -n "${1}" ] || return ${EXIT_ERROR}
167 local device=$(devicify ${1})
168 [ -n "${device}" ] || return ${EXIT_ERROR}
169 ip link show ${device} &>/dev/null
170 }
171
172 function device_is_bonding() {
173 [ -d "/sys/class/net/${1}/bonding" ]
174 }
175
176 function device_is_bonded() {
177 local dev
178 for dev in /sys/class/net/*; do
179 # Skip crappy files
180 [ -d "${dev}" ] || continue
181
182 # Continue if not a bonding device
183 device_is_bonding "${dev##*/}" || continue
184
185 if grep -q "\<${1}\>" ${dev}/bonding/slaves; then
186 return 0
187 fi
188 done
189 return 1
190 }
191
192 function device_is_bridge() {
193 [ -d "/sys/class/net/${1}/bridge" ]
194 }
195
196 function device_is_up() {
197 ip link show $(devicify ${1}) 2>/dev/null | grep -qE "<.*UP.*>"
198 }
199
200 function device_is_vlan() {
201 if [ ! -e "/proc/net/vlan/config" ]; then
202 return 1
203 fi
204 grep -q "^${1}" /proc/net/vlan/config
205 }
206
207 function device_is_ppp() {
208 # XXX need something better
209 [ "${1:0:3}" = "ppp" ]
210 }
211
212 function device_is_loopback() {
213 local device=$(devicify ${1})
214 [ "${device}" = "lo" ]
215 }
216
217 function device_is_real() {
218 local device=${1}
219
220 device_is_loopback ${device} && \
221 return ${EXIT_ERROR}
222
223 device_is_bonding ${device} && \
224 return ${EXIT_ERROR}
225
226 device_is_bridge ${device} && \
227 return ${EXIT_ERROR}
228
229 device_is_ppp ${device} && \
230 return ${EXIT_ERROR}
231
232 device_is_vlan ${device} && \
233 return ${EXIT_ERROR}
234
235 return ${EXIT_OK}
236 }
237
238 function device_type() {
239 local device=$(devicify ${1})
240
241 if device_is_vlan ${device}; then
242 echo "vlan"
243
244 elif device_is_bonding ${device}; then
245 echo "bonding"
246
247 elif device_is_bridge ${device}; then
248 echo "bridge"
249
250 elif device_is_ppp ${device}; then
251 echo "ppp"
252
253 elif device_is_loopback ${device}; then
254 echo "loopback"
255
256 elif device_is_real ${device}; then
257 echo "real"
258
259 else
260 echo "unknown"
261 fi
262 }
263
264 function device_has_vlans() {
265 if [ ! -e "/proc/net/vlan/config" ]; then
266 return 1
267 fi
268 grep -q "${1}$" /proc/net/vlan/config
269 }
270
271 function device_has_carrier() {
272 local device=$(devicify ${1})
273 [ "$(</sys/class/net/${device}/carrier)" = "1" ]
274 }
275
276 function device_get_free() {
277 local destination=${1}
278
279 # Replace + by a valid number
280 if grep -q "+$" <<<${destination}; then
281 local number=0
282 destination=$(sed -e "s/+//" <<<$destination)
283 while [ "${number}" -le "100" ]; do
284 if ! device_exists "${destination}${number}"; then
285 destination="${destination}${number}"
286 break
287 fi
288 number=$(($number + 1))
289 done
290 fi
291 echo "${destination}"
292 }
293
294 function device_rename() {
295 local source=$1
296 local destination=$(device_get_free ${2})
297
298 # Check if devices exists
299 if ! device_exists ${source} || device_exists ${destination}; then
300 return 4
301 fi
302
303 local up
304 if device_is_up ${source}; then
305 ip link set ${source} down
306 up=1
307 fi
308
309 ip link set ${source} name ${destination}
310
311 if [ "${up}" = "1" ]; then
312 ip link set ${destination} up
313 fi
314 }
315
316 function hook_exists() {
317 [ -x "${HOOKS_DIR}/${1}" ]
318 }
319
320 function port_exists() {
321 device_exists $@
322 }
323
324 function port_is_up() {
325 port_exists $@ && device_is_up $@
326 }
327
328 function zone_exists() {
329 [ -e "$CONFIG_ZONES/${1}" ]
330 }
331
332 function zone_is_up() {
333 zone_exists $@ && device_is_up $@
334 }
335
336 function zone_is_forwarding() {
337 local seconds=45
338 local zone=${1}
339
340 local device
341 while [ ${seconds} -gt 0 ]; do
342 for device in /sys/class/net/${zone}/brif/*; do
343 [ -e "${device}/state" ] || continue
344 if [ "$(<${device}/state)" = "3" ]; then
345 return ${EXIT_OK}
346 fi
347 done
348 sleep 1
349 seconds=$((${seconds} - 1))
350 done
351 return ${EXIT_ERROR}
352 }
353
354 function bridge_devices() {
355 local bridge=$1
356 [ -z "${bridge}" ] && return 2
357 brctl show | grep "^${bridge}" | awk '{ print $NF }' | grep -v "^interfaces$"
358 }
359
360 function zone_add_port() {
361 local zone=${1}
362 local port=${2}
363
364 brctl addif ${zone} ${port}
365 }
366
367 function zone_del_port() {
368 local zone=${1}
369 local port=${2}
370
371 brctl delif ${zone} ${port}
372 }
373
374 function zone_list() {
375 local zone
376 for zone in $(find ${CONFIG_ZONES}/* 2>/dev/null); do
377 [ -d "${zone}" ] && echo ${zone}
378 done
379 }
380
381 function zone_is_red() {
382 local zone=${1}
383 [ "${zone#red}" != "${zone}" ]
384 }
385
386 function _run_hooks() {
387 local action
388 local type
389
390 while [ $# -gt 0 ]; do
391 case "${1}" in
392 --type=*)
393 type=${1#--type=}
394 ;;
395 *)
396 action="${1}"
397 shift; break
398 ;;
399 esac
400 shift
401 done
402
403 local dir=${1}; shift
404 local failed
405 local hook
406 local hooks
407
408 if [ -z "${action}" ] || [ -z "${dir}" ]; then
409 echo "Not enough parameters given." >&2
410 return 1
411 fi
412
413 for hook in $(find ${dir}); do
414 # Skip dirs
415 [ -d "${hook}" ] && continue
416
417 (
418 . ${hook}
419 # Skip hooks that are not of the given type
420 if [ -n "${type}" ] && [ "$(hook_type ${HOOK})" != "${type}" ]; then
421 continue
422 fi
423 if [ -n "${HOOK}" ]; then
424 hook_run ${HOOK} --config=${hook} $@ ${action}
425 RET=$?
426 else
427 echo -e "${FAILURE}Unable to process ${hook}. Either"
428 echo -e "${FAILURE}the HOOK variable was not set,"
429 echo -e "${FAILURE}or the specified hook cannot be executed."
430 message=""
431 log_failure_msg
432 fi
433 exit ${RET}
434 ) || failed=1
435 done
436
437 return ${failed}
438 }
439
440 function hooks_run_all() {
441 _run_hooks $@
442 }
443
444 function hooks_run_ports() {
445 _run_hooks --type="port" $@
446 }
447
448 function hooks_run_zones() {
449 _run_hooks --type="zone" $@
450 }
451
452 function hook_type() {
453 local hook=${1}
454 (
455 eval $(${HOOKS_DIR}/${hook} info)
456 echo "${HOOK_TYPE}"
457 )
458 }
459
460 function hook_list() {
461 local type=${1}
462 local hook
463 for hook in ${HOOKS_DIR}/*; do
464 hook=${hook##*/}
465
466 [[ ${hook} =~ helper$ ]] && continue
467
468 if [ -n "${type}" ] && [ "$(hook_type ${hook})" != "${type}" ]; then
469 continue
470 fi
471 echo "${hook}"
472 done
473 }
474
475 function config_get_hook() {
476 local config=${1}
477 if [ ! -e "${config}" ]; then
478 log_failure_msg "Config file \"${config}\" does not exist."
479 return ${EXIT_ERROR}
480 fi
481 ( . ${config}; echo ${HOOK} )
482 }
483
484 function hook_run() {
485 local hook=${1}
486 shift
487
488 if ! hook_exists ${hook}; then
489 log_failure_msg "Hook ${hook} cannot be found or is not executeable."
490 return ${EXIT_ERROR}
491 fi
492 [ -n "${DEBUG}" ] && echo "Running hook: ${hook} $@"
493 DEBUG=${DEBUG} VERBOSE=${VERBOSE} ${HOOKS_DIR}/${hook} $@
494 return $?
495 }
496
497 function hook_run_multiple() {
498 local zone
499 local config
500 local hook
501 local hook_type2
502 local type
503
504 while [ "$#" -gt "0" ]; do
505 case "${1}" in
506 --type=*)
507 type=${1#--type=}
508 ;;
509 *)
510 zone=${1}
511 break
512 ;;
513 esac
514 shift
515 done
516
517 if ! zone_exists ${zone}; then
518 return ${EXIT_ERROR}
519 fi
520
521 for config in $(find ${CONFIG_ZONES}/${zone} 2>/dev/null); do
522 hook=$(config_get_hook ${config})
523 if [ -n "${type}" ]; then
524 hook_type2=$(hook_type ${hook})
525 if [ "${type}" != "${hook_type2}" ]; then
526 continue
527 fi
528 fi
529 hook_run ${hook} $@
530 done
531 }
532
533 function zone_run() {
534 local zone=${1}
535 shift
536
537 if ! zone_exists ${zone}; then
538 log_failure_msg "Zone ${zone} does not exist."
539 exit ${EXIT_ERROR}
540 fi
541 decho "Running zone: ${zone} $@"
542 DEBUG=${DEBUG} VERBOSE=${VERBOSE} ${HOME_DIR}/zone --zone=${zone} $@
543 }
544
545 function zone_valid_name() {
546 local zone=${1}
547 local match
548
549 local i
550 for i in ${VALID_ZONES}; do
551 match="${match}|${i}[0-9]{1,5}"
552 done
553 [[ ${zone} =~ ${match:1:${#match}} ]]
554 }
555
556 function isset() {
557 local key=${1}
558 [ -n "${!key}" ] && return
559 if [[ ${key} =~ port|zone ]]; then
560 echo "ERROR: The --${key} flag is not set." >&2
561 else
562 echo "ERROR: The \"${key}\" variable is not set properly." >&2
563 fi
564 return 1
565 }
566
567 # Test if device is attached to the given bridge
568 function zone_has_device_attached () {
569 local zone=${1}
570 local device=${2}
571
572 [ -d "/sys/class/net/${zone}/brif/${device}" ]
573 }
574
575 function device_has_ipv4() {
576 local device=${1}
577 local ip=${2}
578 ip addr show ${device} | grep inet | fgrep -q ${ip}
579 }
580
581 function check_config() {
582 local failed
583 local i
584
585 for i in $@; do
586 isset ${i} || failed=1
587 done
588 if [ "${failed}" = "1" ]; then
589 echo "Exiting..."
590 exit ${EXIT_ERROR}
591 fi
592 }
593
594 function mac_generate() {
595 local mac="00"
596 while [ "${#mac}" -lt 15 ]; do
597 mac="${mac}:$(cut -c 1-2 /proc/sys/kernel/random/uuid)"
598 done
599 echo "${mac}"
600 }
601
602 function connection() {
603 local action
604
605 local dns
606 local interface
607 local iplocal
608 local ipremote
609 local name
610 local status
611 local weight
612 local zone
613
614 while [ $# -gt 0 ]; do
615 case "${1}" in
616 --up)
617 action="up"
618 ;;
619 --down)
620 action="down"
621 ;;
622 --starting)
623 action="starting"
624 ;;
625 --stopping)
626 action="stopping"
627 ;;
628 --name=*)
629 name=${1#--name=}
630 ;;
631 --zone=*)
632 zone=${1#--zone=}
633 zone_is_red ${zone} || return 0
634 ;;
635 --interface=*)
636 interface=${1#--interface=}
637 ;;
638 --iplocal=*)
639 iplocal=${1#--iplocal=}
640 ;;
641 --ipremote=*)
642 ipremote=${1#--ipremote=}
643 ;;
644 --weight=*)
645 weight=${1#--weight=}
646 ;;
647 --dns=*)
648 dns=${1#--dns=}
649 ;;
650 esac
651 shift
652 done
653
654 if [ ! -e "${CONNECTIONS_FILE}" ]; then
655 sqlite3 -batch ${CONNECTIONS_FILE} <<EOF
656 CREATE TABLE connections(name, zone, interface, iplocal, ipremote, weight, dns, status);
657 EOF
658 fi
659
660 if [ -z "${zone}" ]; then
661 return 2
662 fi
663
664 status=${action}
665
666 sqlite3 -batch ${CONNECTIONS_FILE} <<EOF
667 DELETE FROM connections WHERE zone = '${zone}';
668 INSERT INTO connections(name, zone, interface, iplocal, ipremote, weight, dns, status)
669 VALUES('${name}', '${zone}', '${interface}', '${iplocal}', '${ipremote}', '${weight}', '${dns}', '${status}');
670 EOF
671
672 }
673
674 function uuid() {
675 cat /proc/sys/kernel/random/uuid
676 }