Opennet Firmware
 Alle Dateien Funktionen Variablen Gruppen Seiten
network.sh
gehe zur Dokumentation dieser Datei
1 ## @defgroup network Netzwerk
2 ## @brief Umgang mit uci-Netzwerk-Interfaces und Firewall-Zonen
3 # Beginn der Doku-Gruppe
4 ## @{
5 
6 ZONE_LOCAL=lan
7 ZONE_WAN=wan
8 ZONE_MESH=on_mesh
9 NETWORK_LOCAL=lan
10 # diese Domain wird testweise abgefragt, um die Verfügbarkeit des on-DNS zu prüfen
11 DNS_SERVICE_REFERENCE="ca.on"
12 
13 
14 # Liefere alle IPs fuer diesen Namen zurueck
15 query_dns() {
16  nslookup "$1" 2>/dev/null | sed '1,/^Name:/d' | awk '{print $3}' | sort -n
17 }
18 
19 
20 query_dns_reverse() {
21  nslookup "$1" 2>/dev/null | tail -n 1 | awk '{ printf "%s", $4 }'
22 }
23 
24 
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
34 }
35 
36 
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
44  local target="$1"
45  local duration="${2:-5}"
46  local ip
47  ip=$(query_dns "$target" | filter_routable_addresses | tail -1)
48  [ -z "$ip" ] && return 0
49  ping -w "$duration" -q "$ip" 2>/dev/null \
50  | grep "min/avg/max" \
51  | cut -f 2 -d = \
52  | cut -f 2 -d / \
53  | awk '{ print int($1 + 0.5); }'
54 }
55 
56 
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
60 add_zone_forward() {
61  trap "error_trap add_zone_forward '$*'" $GUARD_TRAPS
62  local source="$1"
63  local dest="$2"
64  local uci_prefix
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"
72 }
73 
74 
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
79  local network
80  local network_with_prefix
81  local uci_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
86  uci_delete "${uci_prefix}.masq_src"
87  # aktuelle Netzwerke wieder hinzufuegen
88  for network in $(get_zone_interfaces "$ZONE_LOCAL"; get_zone_interfaces "$ZONE_WAN"); do
89  network_with_prefix=$(get_current_addresses_of_network "$network")
90  [ -z "$network_with_prefix" ] && continue
91  uci_add_list "${uci_prefix}.masq_src" "$network_with_prefix"
92  done
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"
97  else
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
102  uci_delete "${uci_prefix}.masq_src"
103  fi
104  apply_changes firewall
105 }
106 
107 
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
114  local network="$1"
115  local subnets=
116  (set +eu; . /lib/functions/network.sh; network_get_subnets subnets "$network"; echo "$subnets")
117 }
118 
119 
120 # Liefere die logischen Netzwerk-Schnittstellen einer Zone zurueck.
121 get_zone_interfaces() {
122  trap "error_trap get_zone_interfaces '$*'" $GUARD_TRAPS
123  local zone="$1"
124  local uci_prefix
125  local interfaces
126  uci_prefix=$(find_first_uci_section firewall zone "name=$zone")
127  # keine Zone -> keine Interfaces
128  [ -z "$uci_prefix" ] && return 0
129  interfaces=$(uci_get_list "${uci_prefix}.network")
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")"
133  echo "$interfaces"
134 }
135 
136 
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
143  local zone="$1"
144  local uci_prefix
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
147  uci_get_list "${uci_prefix}.device"
148 }
149 
150 
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
154  local device="$1"
155  local zone="$2"
156  local log_interface
157  local item
158  for log_interface in $(get_zone_interfaces "$2"); do
159  for item in $(get_subdevices_of_interface "$log_interface"); do
160  [ "$device" = "$item" ] && return 0 || true
161  done
162  done
163  trap "" $GUARD_TRAPS && return 1
164 }
165 
166 
167 # Ist das gegebene logische Netzwerk-Interface Teil einer Firewall-Zone?
168 is_interface_in_zone() {
169  local interface="$1"
170  local zone="$2"
171  local item
172  for item in $(get_zone_interfaces "$zone"); do
173  [ "$item" = "$interface" ] && return 0 || true
174  done
175  trap "" $GUARD_TRAPS && return 1
176 }
177 
178 
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).
183  local interface="$1"
184  [ "$(uci_get "network.${interface}.type")" = "bridge" ] \
185  && echo "br-$interface" \
186  || get_subdevices_of_interface "$interface"
187 }
188 
189 
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
193  local device="$1"
194  local zone="$2"
195  local log_interface
196  local item
197  for log_interface in $(get_zone_interfaces "$2"); do
198  for item in $(get_subdevices_of_interface "$log_interface"); do
199  [ "$device" = "$item" ] && return 0 || true
200  done
201  done
202  trap "" $GUARD_TRAPS && return 1
203 }
204 
205 
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
210 ## ermittelbar.
211 ## @returns Der oder die Namen der physischen Netzwerk-Geräte oder nichts.
213  trap "error_trap get_subdevices_of_interface '$*'" $GUARD_TRAPS
214  local interface="$1"
215  local device
216  local uci_prefix
217  local current_interface
218  {
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
224  echo "$device"
225  done
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
233 }
234 
235 
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.
240  local zone="$1"
241  local interface="$2"
242  local uci_prefix
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
245  uci_add_list "${uci_prefix}.network" "$interface"
246 }
247 
248 
250 ## @brief Entferne ein logisches Interface aus einer Firewall-Zone.
252  local zone="$1"
253  local interface="$2"
254  local uci_prefix
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"
258 }
259 
260 
261 ## @fn get_zone_of_device()
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
268  local device="$1"
269  local uci_prefix
270  local devices
271  local zone
272  local interface
273  local current_device
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 \
278  $(get_device_of_interface "$interface") \
279  $(get_subdevices_of_interface "$interface"); do
280  [ "$current_device" = "$device" ] && echo "$device" && return 0
281  true
282  done
283  done
284  done
285  # keine Zone gefunden
286 }
287 
288 
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
296  local interface="$1"
297  local uci_prefix
298  local interfaces
299  local zone
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
304  done
305  # ein leerer Rueckgabewert gilt als Fehler
306  return 0
307 }
308 
309 
310 # Liefere die sortierte Liste der Opennet-Interfaces.
311 # Prioritaeten:
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
317  local order
318  local network
319  # wir vergeben einfach statische Ordnungsnummern:
320  # 10 - konfigurierte Interfaces
321  # 20 - nicht konfigurierte Interfaces
322  # Offsets basierend auf dem Netzwerknamen:
323  # 1 - on_wifi*
324  # 2 - on_eth*
325  # 3 - alle anderen
326  for network in $(get_zone_interfaces "$ZONE_MESH"); do
327  order=10
328  [ -z "$(get_subdevices_of_interface "$network")" ] && order=20
329  if [ "${network#on_wifi}" != "$network" ]; then
330  order=$((order+1))
331  elif [ "${network#on_eth}" != "$network" ]; then
332  order=$((order+2))
333  else
334  order=$((order+3))
335  fi
336  echo "$order $network"
337  done | sort -n | cut -f 2 -d " "
338 }
339 
340 
341 # Liefere alle vorhandenen logischen Netzwerk-Schnittstellen (lan, wan, ...) zurueck.
342 get_all_network_interfaces() {
343  local interface
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
350  echo "$interface"
351  done | sort | uniq
352  return 0
353 }
354 
355 
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.
361  local zone="$1"
362  local section
363  local key
364  local uci_prefix
365  uci_prefix=$(find_first_uci_section firewall zone "name=$zone")
366  uci_delete "$uci_prefix"
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
370  uci_delete "$uci_prefix"
371  done
372  done
373  done
374 }
375 
376 
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.
382 is_interface_up() {
383  trap "error_trap is_interface_up '$*'" $GUARD_TRAPS
384  local interface="$1"
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
391  fi
392  local device
393  for device in $(get_subdevices_of_interface "$interface"); do
394  ip link show dev "$device" | grep -q "[\t ]state UP[\ ]" && return 0
395  true
396  done
397  trap "" $GUARD_TRAPS && return 1
398 }
399 
400 # Ende der Doku-Gruppe
401 ## @}
uci_delete(uci_path)
Lösche ein UCI-Element.
Definition: uci.sh:38
add_interface_to_zone()
Fuege ein logisches Netzwerk-Interface zu einer Firewall-Zone hinzu.
Definition: network.sh:39
get_zone_of_device(interface)
Ermittle die Zone eines physischen Netzwerk-Interfaces.
Definition: network.sh:48
uci_add_list(uci_path, new_item)
Füge einen neuen Wert zu einer UCI-Liste hinzu und achte dabei auf Einmaligkeit.
Definition: uci.sh:10
local key
Definition: core.sh:81
has_opennet_dns()
Prüfe, ob *.on-Domains aufgelöst werden.
Definition: network.sh:8
get_zone_raw_devices()
Ermittle die physischen Netzwerkinterfaces, die direkt einer Firewall-Zone zugeordnet sind...
Definition: network.sh:24
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
Definition: core.sh:15
is_interface_up(interface)
Prüfe ob ein logisches Netzwerk-Interface aktiv ist.
Definition: network.sh:65
get_subdevices_of_interface()
Ermittle die physischen Netzwerk-Geräte (bis auf wifi), die zu einem logischen Netzwerk-Interface geh...
Definition: network.sh:35
get_current_addresses_of_network()
Liefere die IP-Adressen eines logischen Interface inkl. Praefix-Laenge (z.B. 172.16.0.1/24, network).
Definition: network.sh:19
uci_get_list(uci_path)
Liefere alle einzelenen Elemente einer UCI-Liste zurück.
Definition: uci.sh:15
delete_firewall_zone(zone)
Lösche eine Firewall-Zone, sowie alle Regeln, die sich auf diese Zone beziehen.
Definition: network.sh:59
filter_routable_addresses()
Filtere aus einer Menge von Ziel-IPs diejenigen heraus, für die eine passende Routing-Regel existiert...
Definition: routing.sh:19
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
Definition: core.sh:9
set eu on function print_services services log for dir in etc on services d var on services volatile d
Definition: services:13
set eu grep root::etc shadow exit if which chpasswd dev null
Definition: on-password:12
get_device_of_interface()
Ermittle das physische Netzwerk-Gerät, das einem logischen Netzwerk entspricht.
Definition: network.sh:28
get_ping_time(target, duration)
Ermittle die Latenz eines Ping-Pakets auf dem Weg zu einem Ziel.
Definition: network.sh:14
get_zone_of_interface(interface)
Ermittle die Zone eines logischen Netzwerk-Interfaces.
Definition: network.sh:54
done
Definition: core.sh:81
del_interface_from_zone()
Entferne ein logisches Interface aus einer Firewall-Zone.
Definition: network.sh:42