1 ## @defgroup network Netzwerk
2 ## @brief Umgang mit uci-Netzwerk-Interfaces und Firewall-Zonen
3 # Beginn der Doku-Gruppe
10 # diese Domain wird testweise abgefragt, um die Verfügbarkeit des on-DNS zu prüfen
11 DNS_SERVICE_REFERENCE=
"ca.on"
14 # Liefere alle IPs fuer diesen Namen zurueck
16 nslookup
"$1" 2>/dev/
null | sed
'1,/^Name:/d' | awk
'{print $3}' | sort -n
21 nslookup
"$1" 2>/dev/
null | tail -n 1 | awk
'{ printf "%s", $4 }'
25 ## @fn has_opennet_dns()
26 ## @brief Prüfe, ob *.on-Domains aufgelöst werden.
27 ## @returns Der Exitcode ist Null, falls on-DNS verfügbar ist.
28 ## @details Die maximale Laufzeit dieser Funktion ist auf eine Sekunde begrenzt.
30 trap
"error_trap has_opennet_dns '$*'" $GUARD_TRAPS
31 # timeout ist kein shell-builtin - es benoetigt also ein global ausfuehrbares Kommando
32 timeout 1 on-
function query_dns
"$DNS_SERVICE_REFERENCE" | grep -q . &&
return 0
33 trap
"" $GUARD_TRAPS &&
return 1
37 ## @fn get_ping_time()
38 ## @brief Ermittle die Latenz eines Ping-Pakets auf dem Weg zu einem Ziel.
39 ## @param target IP oder DNS-Name des Zielhosts
40 ## @param duration die Dauer der Ping-Kommunikation in Sekunden (falls ungesetzt: 5)
41 ## @returns Ausgabe der mittleren Ping-Zeit in ganzen Sekunden; bei Nichterreichbarkit ist die Ausgabe leer
43 trap
"error_trap get_ping_time '$*'" $GUARD_TRAPS
45 local duration=
"${2:-5}"
48 [ -z
"$ip" ] &&
return 0
49 ping -w
"$duration" -q
"$ip" 2>/dev/
null \
50 | grep
"min/avg/max" \
53 | awk
'{ print int($1 + 0.5); }'
57 # Lege eine Weiterleitungsregel fuer die firewall an (firewall.@forwarding[?]=...)
58 # WICHTIG: anschliessend muss "uci commit firewall" ausgefuehrt werden
59 # Parameter: Quell-Zone und Ziel-Zone
61 trap
"error_trap add_zone_forward '$*'" $GUARD_TRAPS
65 uci_prefix=$(find_first_uci_section firewall forwarding
"src=$source" "dest=$dest")
66 # die Weiterleitungsregel existiert bereits -> Ende
67 [ -n
"$uci_prefix" ] &&
return 0
68 # neue Regel erstellen
69 uci_prefix=
"firewall.$(uci add firewall forwarding)"
70 uci set
"${uci_prefix}.src=$source"
71 uci set
"${uci_prefix}.dest=$dest"
75 # Das Masquerading in die Opennet-Zone soll nur fuer bestimmte Quell-Netze erfolgen.
76 # Diese Funktion wird bei hotplug-Netzwerkaenderungen ausgefuehrt.
77 update_opennet_zone_masquerading() {
78 trap
"error_trap update_opennet_zone_masquerading '$*'" $GUARD_TRAPS
80 local network_with_prefix
82 uci_prefix=$(find_first_uci_section firewall zone
"name=$ZONE_MESH")
83 # Abbruch, falls die Zone fehlt
84 [ -z
"$uci_prefix" ] &&
msg_info "failed to find opennet mesh zone ($ZONE_MESH)" &&
return 0
85 # alle masquerade-Netzwerke entfernen
87 # aktuelle Netzwerke wieder hinzufuegen
88 for network in $(get_zone_interfaces
"$ZONE_LOCAL"; get_zone_interfaces
"$ZONE_WAN");
do
90 [ -z
"$network_with_prefix" ] &&
continue
91 uci_add_list "${uci_prefix}.masq_src" "$network_with_prefix"
93 # leider ist masq_src im Zweifelfall nicht
"leer", sondern enthaelt ein Leerzeichen
94 if uci_get
"${uci_prefix}.masq_src" | grep -q
"[^ \t]"; then
95 # masquerading aktiveren (nur fuer die obigen Quell-Adressen)
96 uci set
"${uci_prefix}.masq=1"
98 # Es gibt keine lokalen Interfaces - also duerfen wir kein Masquerading aktivieren.
99 # Leider interpretiert openwrt ein leeres "masq_src" nicht als "masq fuer niemanden" :(
100 uci set
"${uci_prefix}.masq=0"
101 # das firewall-Skript beschwert sich ueber einen leeren Eintrag
104 apply_changes firewall
108 ## @fn get_current_addresses_of_network()
109 ## @brief Liefere die IP-Adressen eines logischen Interface inkl. Praefix-Laenge (z.B. 172.16.0.1/24).
110 ## @param network logisches Netzwerk-Interface
111 ## @details Es werden sowohl IPv4- als auch IPv6-Adressen zurückgeliefert.
113 trap
"error_trap get_current_addresses_of_network '$*'" $GUARD_TRAPS
116 (set +eu; . /lib/functions/network.sh; network_get_subnets subnets
"$network"; echo
"$subnets")
120 # Liefere die logischen Netzwerk-Schnittstellen einer Zone zurueck.
121 get_zone_interfaces() {
122 trap
"error_trap get_zone_interfaces '$*'" $GUARD_TRAPS
126 uci_prefix=$(find_first_uci_section firewall zone
"name=$zone")
127 # keine Zone -> keine Interfaces
128 [ -z
"$uci_prefix" ] &&
return 0
130 # falls 'network' und 'device' leer sind, dann enthaelt 'name' den Interface-Namen
131 # siehe http://wiki.openwrt.org/doc/uci/firewall#zones
132 [ -z
"$interfaces" ] && [ -z
"$(uci_get "${uci_prefix}.device
")" ] && interfaces=
"$(uci_get "${uci_prefix}.name
")"
137 ## @fn get_zone_raw_devices()
138 ## @brief Ermittle die physischen Netzwerkinterfaces, die direkt einer Firewall-Zone zugeordnet sind.
139 ## @details Hier werden _nicht_ die logischen Interfaces in die physischen aufgeloest, sondern
140 ## es wird lediglich der Inhalt des 'devices'-Eintrags einer Firewall-Zone ausgelesen.
142 trap
"error_trap get_zone_raw_devices '$*'" $GUARD_TRAPS
145 uci_prefix=$(find_first_uci_section
"firewall" "zone" "name=$zone")
146 [ -z
"$uci_prefix" ] &&
msg_debug "Failed to retrieve raw devices of non-existing zone '$zone'" &&
return 0
151 # Ist das gegebene physische Netzwerk-Interface Teil einer Firewall-Zone?
152 is_device_in_zone() {
153 trap
"error_trap is_device_in_zone '$*'" $GUARD_TRAPS
158 for log_interface in $(get_zone_interfaces
"$2");
do
160 [
"$device" =
"$item" ] &&
return 0 ||
true
163 trap
"" $GUARD_TRAPS &&
return 1
167 # Ist das gegebene logische Netzwerk-Interface Teil einer Firewall-Zone?
168 is_interface_in_zone() {
172 for item in $(get_zone_interfaces
"$zone");
do
173 [
"$item" =
"$interface" ] &&
return 0 ||
true
175 trap
"" $GUARD_TRAPS &&
return 1
179 ## @fn get_device_of_interface()
180 ## @brief Ermittle das physische Netzwerk-Gerät, das einem logischen Netzwerk entspricht.
181 ## @details Ein Bridge-Interface wird als Gerät betrachtet und zurückgeliefert (nicht seine Einzelteile).
184 [
"$(uci_get "network.${interface}.type
")" =
"bridge" ] \
185 && echo
"br-$interface" \
190 # Ist das gegebene physische Netzwerk-Interface Teil einer Firewall-Zone?
191 is_device_in_zone() {
192 trap
"error_trap is_device_in_zone '$*'" $GUARD_TRAPS
197 for log_interface in $(get_zone_interfaces
"$2");
do
199 [
"$device" =
"$item" ] &&
return 0 ||
true
202 trap
"" $GUARD_TRAPS &&
return 1
206 ## @fn get_subdevices_of_interface()
207 ## @brief Ermittle die physischen Netzwerk-Geräte (bis auf wifi), die zu einem logischen Netzwerk-Interface gehören.
208 ## @details Im Fall eines Bridge-Interface werden nur die beteiligten Komponenten zurückgeliefert.
209 ## Wifi-Geräte werden nur dann zurückgeliefert, wenn sie Teil einer Bridge sind. Andernfalls sind ihre Namen nicht
211 ## @returns Der oder die Namen der physischen Netzwerk-Geräte oder nichts.
213 trap
"error_trap get_subdevices_of_interface '$*'" $GUARD_TRAPS
217 local current_interface
219 # kabelgebundene Geräte
220 for device in $(uci_get
"network.${interface}.ifname");
do
221 # entferne Alias-Nummerierungen
222 device=$(echo
"$device" | cut -f 1 -
d :)
223 [ -z
"$device" -o
"$device" =
"none" ] &&
continue
226 # wir fügen das Ergebnis der ubus-Abfrage hinzu (unten werden Duplikate entfernt)
227 (set +eu; local ifname; . /lib/functions/network.sh; network_get_physdev ifname
"$interface"; echo
"$ifname"; set -eu)
228 } | tr
' ' '\n' | sort | uniq |
while read device;
do
229 # Falls das Verzeichnis existiert, ist es wohl eine Bridge, deren Bestandteile wir ausgeben.
230 # Ansonsten wird das Device ausgegeben.
231 ls
"/sys/devices/virtual/net/$device/brif/" 2>/dev/
null || echo
"$device"
232 done | sort | uniq | grep -v
"^none$" | grep -v
"^$" ||
true
236 ## @fn add_interface_to_zone()
237 ## @brief Fuege ein logisches Netzwerk-Interface zu einer Firewall-Zone hinzu.
238 ## @details Typischerweise ist diese Funktion nur fuer temporaere Netzwerkschnittstellen geeignet.
243 uci_prefix=$(find_first_uci_section
"firewall" "zone" "name=$zone")
244 [ -z
"$uci_prefix" ] &&
msg_debug "Failed to add interface '$interface' to non-existing zone '$zone'" &&
return 0
250 ## @brief Entferne ein logisches Interface aus einer Firewall-Zone.
255 uci_prefix=$(find_first_uci_section
"firewall" "zone" "name=$zone")
256 [ -z
"$uci_prefix" ] &&
msg_debug "Failed to remove interface '$interface' from non-existing zone '$zone'" && trap
"" $GUARD_TRAPS &&
return 1
257 uci -q del_list
"${uci_prefix}.network=$interface"
262 ## @brief Ermittle die Zone eines physischen Netzwerk-Interfaces.
263 ## @param interface Name eines physischen Netzwerk-Interface (z.B. eth0)
264 ## @details Das Ergebnis ist ein leerer String, falls zu diesem Interface keine Zone existiert
265 ## oder falls es das Interface nicht gibt.
267 trap
"error_trap get_zone_of_device '$*'" $GUARD_TRAPS
274 find_all_uci_sections firewall zone |
while read uci_prefix;
do
275 zone=$(uci_get
"${uci_prefix}.name")
276 for interface in $(get_zone_interfaces
"$zone");
do
277 for current_device in \
280 [
"$current_device" =
"$device" ] && echo
"$device" &&
return 0
285 # keine Zone gefunden
289 ## @fn get_zone_of_interface()
290 ## @brief Ermittle die Zone eines logischen Netzwerk-Interfaces.
291 ## @param interface Name eines logischen Netzwerk-Interface (z.B. eth0)
292 ## @details Das Ergebnis ist ein leerer String, falls zu diesem Interface keine Zone existiert
293 ## oder falls es das Interface nicht gibt.
295 trap
"error_trap get_zone_of_interface '$*'" $GUARD_TRAPS
300 find_all_uci_sections firewall zone |
while read uci_prefix;
do
301 zone=$(uci_get
"${uci_prefix}.name")
302 interfaces=$(get_zone_interfaces
"$zone")
303 is_in_list
"$interface" "$interfaces" && echo -n
"$zone" &&
return 0 ||
true
305 # ein leerer Rueckgabewert gilt als Fehler
310 # Liefere die sortierte Liste der Opennet-Interfaces.
312 # 1. dem Netzwerk ist ein Geraet zugeordnet
313 # 2. Netzwerkname beginnend mit "on_wifi", "on_eth", ...
314 # 3. alphabetische Sortierung der Netzwerknamen
315 get_sorted_opennet_interfaces() {
316 trap
"error_trap get_sorted_opennet_interfaces '$*'" $GUARD_TRAPS
319 # wir vergeben einfach statische Ordnungsnummern:
320 # 10 - konfigurierte Interfaces
321 # 20 - nicht konfigurierte Interfaces
322 # Offsets basierend auf dem Netzwerknamen:
326 for network in $(get_zone_interfaces
"$ZONE_MESH");
do
328 [ -z
"$(get_subdevices_of_interface "$network
")" ] && order=20
329 if [
"${network#on_wifi}" !=
"$network" ]; then
331 elif [
"${network#on_eth}" !=
"$network" ]; then
336 echo
"$order $network"
337 done | sort -n | cut -f 2 -
d " "
341 # Liefere alle vorhandenen logischen Netzwerk-Schnittstellen (lan, wan, ...) zurueck.
342 get_all_network_interfaces() {
344 # Die uci-network-Spezifikation sieht keine anonymen uci-Sektionen fuer Netzwerk-Interfaces vor.
345 # Somit ist es wohl korrekt, auf die Namen als Teil des uci-Pfads zu vertrauen.
346 find_all_uci_sections
"network" "interface" | cut -f 2 -
d . |
while read interface;
do
347 # ignoriere loopback-Interfaces und ungueltige
348 [ -z
"$interface" -o
"$interface" =
"none" -o
"$interface" =
"loopback" ] &&
continue
349 # alle uebrigen sind reale Interfaces
356 ## @fn delete_firewall_zone()
357 ## @brief Lösche eine Firewall-Zone, sowie alle Regeln, die sich auf diese Zone beziehen.
358 ## @param zone Name der Zone
359 ## @attention Anschließend ist ein "apply_changes firewall" erforderlich.
365 uci_prefix=$(find_first_uci_section firewall zone
"name=$zone")
367 for section in
"forwarding" "redirect" "rule";
do
368 for key in
"src" "dest";
do
369 find_all_uci_sections firewall
"$section" "${key}=$zone" |
while read uci_prefix;
do
377 ## @fn is_interface_up()
378 ## @brief Prüfe ob ein logisches Netzwerk-Interface aktiv ist.
379 ## @param interface Zu prüfendes logisches Netzwerk-Interface
380 ## @details Im Fall eines Bridge-Interface wird sowohl der Status der Bridge (muss aktiv sein), als
381 ## auch der Status der Bridge-Teilnehmer (mindestens einer muss aktiv sein) geprüft.
383 trap
"error_trap is_interface_up '$*'" $GUARD_TRAPS
385 # falls es ein uebergeordnetes Bridge-Interface geben sollte, dann muss dies ebenfalls aktiv sein
386 if [
"$(uci_get "network.${interface}.type
")" =
"bridge" ]; then
387 # das Bridge-Interface existiert nicht (d.h. es ist down)
388 [ -z
"$(ip link show dev "br-${interface}
" 2>/dev/null || true)" ] && trap
"" $GUARD_TRAPS &&
return 1
389 # Bridge ist aus? Damit ist das befragte Interface ebenfalls aus ...
390 ip link show dev
"br-${interface}" | grep -q
"[\t ]state DOWN[\ ]" && trap
"" $GUARD_TRAPS &&
return 1
394 ip link show dev
"$device" | grep -q
"[\t ]state UP[\ ]" &&
return 0
397 trap
"" $GUARD_TRAPS &&
return 1
400 # Ende der Doku-Gruppe
uci_delete(uci_path)
Lösche ein UCI-Element.
add_interface_to_zone()
Fuege ein logisches Netzwerk-Interface zu einer Firewall-Zone hinzu.
get_zone_of_device(interface)
Ermittle die Zone eines physischen Netzwerk-Interfaces.
uci_add_list(uci_path, new_item)
Füge einen neuen Wert zu einer UCI-Liste hinzu und achte dabei auf Einmaligkeit.
has_opennet_dns()
Prüfe, ob *.on-Domains aufgelöst werden.
get_zone_raw_devices()
Ermittle die physischen Netzwerkinterfaces, die direkt einer Firewall-Zone zugeordnet sind...
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
is_interface_up(interface)
Prüfe ob ein logisches Netzwerk-Interface aktiv ist.
get_subdevices_of_interface()
Ermittle die physischen Netzwerk-Geräte (bis auf wifi), die zu einem logischen Netzwerk-Interface geh...
get_current_addresses_of_network()
Liefere die IP-Adressen eines logischen Interface inkl. Praefix-Laenge (z.B. 172.16.0.1/24, network).
uci_get_list(uci_path)
Liefere alle einzelenen Elemente einer UCI-Liste zurück.
delete_firewall_zone(zone)
Lösche eine Firewall-Zone, sowie alle Regeln, die sich auf diese Zone beziehen.
filter_routable_addresses()
Filtere aus einer Menge von Ziel-IPs diejenigen heraus, für die eine passende Routing-Regel existiert...
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
set eu on function print_services services log for dir in etc on services d var on services volatile d
set eu grep root::etc shadow exit if which chpasswd dev null
get_device_of_interface()
Ermittle das physische Netzwerk-Gerät, das einem logischen Netzwerk entspricht.
get_ping_time(target, duration)
Ermittle die Latenz eines Ping-Pakets auf dem Weg zu einem Ziel.
get_zone_of_interface(interface)
Ermittle die Zone eines logischen Netzwerk-Interfaces.
del_interface_from_zone()
Entferne ein logisches Interface aus einer Firewall-Zone.