Opennet Firmware
 Alle Dateien Funktionen Variablen Gruppen Seiten
network.sh
gehe zur Dokumentation dieser Datei
1 ## @defgroup network Netzwerk
2 ## @brie 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 ZONE_TUNNEL=on_vpn
10 ZONE_FREE=free
11 NETWORK_TUNNEL=on_vpn
12 NETWORK_FREE=free
13 
14 
15 # Liefere alle IPs fuer diesen Namen zurueck
16 query_dns() {
17  nslookup "$1" | sed '1,/^Name:/d' | awk '{print $3}' | sort -n
18 }
19 
20 
21 query_dns_reverse() {
22  nslookup "$1" 2>/dev/null | tail -n 1 | awk '{ printf "%s", $4 }'
23 }
24 
25 
26 ## @fn query_srv_record()
27 ## @brief Liefere die SRV Records zu einer Domain zurück.
28 ## @param srv_domain Dienst-Domain (z.B. _mesh-openvpn._udp.opennet-initiative.de)
29 ## @returns Zeilenweise Ausgabe von SRV Records: PRIORITY WEIGHT PORT HOSTNAME
30 ## @details Siehe RFC 2782 für die SRV-Spezifikation. Die Abfrage erfordert dig drill oder unbound-host.
31 query_srv_records() {
32  local domain="$1"
33  # verschiedene DNS-Werkzeuge sind nutzbar: dig, drill oder unbound-host
34  # "djbdns-tools" unterstützt leider nicht das Parsen von srv-Records (siehe "dnsq 33 DOMAIN localhost")
35  # "drill" ist das kleinste Werkzeug
36  if which dig >/dev/null; then
37  dig +short SRV "$domain"
38  elif which drill >/dev/null; then
39  drill "$domain" SRV | grep -v "^;" \
40  | grep "[[:space:]]IN[[:space:]]\+SRV[[:space:]]\+[[:digit:]]\+[[:space:]]\+[[:digit:]]" \
41  | awk '{print $5, $6, $7, $8}'
42  elif which unbound-host >/dev/null; then
43  unbound-host -t SRV "$domain" \
44  | awk '{print $5, $6, $7, $8}'
45  else
46  msg_info "ERROR: Missing advanced DNS resolver for mesh gateway discovery"
47  fi | sed 's/\.$//'
48  # (siehe oben) entferne den abschliessenden Top-Level-Domain-Punkt ("on-i.de." statt "on-i.de")
49 }
50 
51 
52 ## @fn get_ping_time()
53 ## @brief Ermittle die Latenz eines Ping-Pakets auf dem Weg zu einem Ziel.
54 ## @param target IP oder DNS-Name des Zielhosts
55 ## @param duration die Dauer der Ping-Kommunikation in Sekunden (falls ungesetzt: 5)
56 ## @returns Ausgabe der mittleren Ping-Zeit in ganzen Sekunden; bei Nichterreichbarkit ist die Ausgabe leer
57 get_ping_time() {
58  local target="$1"
59  local duration="${2:-5}"
60  local ip=$(query_dns "$target" | filter_routable_addresses | tail -1)
61  [ -z "$ip" ] && return 0
62  ping -w "$duration" -q "$ip" 2>/dev/null \
63  | grep "min/avg/max" \
64  | cut -f 2 -d = \
65  | cut -f 2 -d / \
66  | awk '{ print int($1 + 0.5); }'
67 }
68 
69 
70 # Lege eine Weiterleitungsregel fuer die firewall an (firewall.@forwarding[?]=...)
71 # WICHTIG: anschliessend muss "uci commit firewall" ausgefuehrt werden
72 # Parameter: Quell-Zone und Ziel-Zone
73 add_zone_forward() {
74  trap "error_trap add_zone_forward '$*'" $GUARD_TRAPS
75  local source=$1
76  local dest=$2
77  local uci_prefix=$(find_first_uci_section firewall forwarding "src=$source" "dest=$dest")
78  # die Weiterleitungsregel existiert bereits -> Ende
79  [ -n "$uci_prefix" ] && return 0
80  # neue Regel erstellen
81  uci_prefix="firewall.$(uci add firewall forwarding)"
82  uci set "${uci_prefix}.src=$source"
83  uci set "${uci_prefix}.dest=$dest"
84 }
85 
86 
87 # Das Masquerading in die Opennet-Zone soll nur fuer bestimmte Quell-Netze erfolgen.
88 # Diese Funktion wird bei hotplug-Netzwerkaenderungen ausgefuehrt.
89 update_opennet_zone_masquerading() {
90  trap "error_trap update_opennet_zone_masquerading '$*'" $GUARD_TRAPS
91  local network
92  local networkprefix
93  local uci_prefix=$(find_first_uci_section firewall zone "name=$ZONE_MESH")
94  # Abbruch, falls die Zone fehlt
95  [ -z "$uci_prefix" ] && msg_info "failed to find opennet mesh zone ($ZONE_MESH)" && return 0
96  # alle masquerade-Netzwerke entfernen
97  uci_delete "${uci_prefix}.masq_src"
98  # aktuelle Netzwerke wieder hinzufuegen
99  for network in $(get_zone_interfaces "$ZONE_LOCAL"); do
100  networkprefix=$(get_address_of_network "$network")
101  uci_add_list "${uci_prefix}.masq_src" "$networkprefix"
102  done
103  # leider ist masq_src im Zweifelfall nicht "leer", sondern enthaelt ein Leerzeichen
104  if uci_get "${uci_prefix}.masq_src" | grep -q "[^ \t]"; then
105  # masquerading aktiveren (nur fuer die obigen Quell-Adressen)
106  uci set "${uci_prefix}.masq=1"
107  else
108  # Es gibt keine lokalen Interfaces - also duerfen wir kein Masquerading aktivieren.
109  # Leider interpretiert openwrt ein leeres "masq_src" nicht als "masq fuer niemanden" :(
110  uci set "${uci_prefix}.masq=0"
111  fi
112  apply_changes firewall
113 }
114 
115 
116 # Liefere die IP-Adresse eines logischen Interface inkl. Praefix-Laenge (z.B. 172.16.0.1/24).
117 # Parameter: logisches Netzwerk-Interface
118 get_address_of_network() {
119  trap "error_trap get_address_of_network '$*'" $GUARD_TRAPS
120  local network="$1"
121  local ranges
122  # Kurzzeitig den eventuellen strikten Modus abschalten.
123  # (lib/functions.sh kommt mit dem strikten Modus nicht zurecht)
124  (
125  set +eu
126  . "${IPKG_INSTROOT:-}/lib/functions/network.sh"
127  __network_ifstatus "ranges" "$network" "['ipv4-address'][*]['address','mask']" "/"
128  echo "$ranges"
129  set -eu
130  )
131  return 0
132 }
133 
134 
135 # Liefere die logischen Netzwerk-Schnittstellen einer Zone zurueck.
136 get_zone_interfaces() {
137  trap "error_trap get_zone_interfaces '$*'" $GUARD_TRAPS
138  local zone="$1"
139  local uci_prefix=$(find_first_uci_section firewall zone "name=$zone")
140  # keine Zone -> keine Interfaces
141  [ -z "$uci_prefix" ] && return 0
142  local interfaces=$(uci_get "${uci_prefix}.network")
143  # falls 'network' und 'device' leer sind, dann enthaelt 'name' den Interface-Namen
144  # siehe http://wiki.openwrt.org/doc/uci/firewall#zones
145  [ -z "$interfaces" ] && [ -z "$(uci_get "${uci_prefix}.device")" ] && interfaces="$(uci_get "${uci_prefix}.name")"
146  echo "$interfaces"
147  return 0
148 }
149 
150 
151 # Liefere die physischen Netzwerk-Schnittstellen einer Zone zurueck.
152 get_zone_devices() {
153  trap "error_trap get_zone_devices '$*'" $GUARD_TRAPS
154  local zone="$1"
155  local iface
156  local result
157  for iface in $(get_zone_interfaces "$zone"); do
158  for result in $(uci_get "network.${iface}.ifname"); do
159  echo "$result"
160  done
161  # Namen von Bridge-Interfaces werden explizit vergeben
162  [ "$(uci_get "network.${iface}.type")" = "bridge" ] && echo "br-$iface"
163  true
164  done
165 }
166 
167 
168 # Ist das gegebene physische Netzwer-Interface Teil einer Firewall-Zone?
169 is_device_in_zone() {
170  trap "error_trap is_device_in_zone '$*'" $GUARD_TRAPS
171  local device=$1
172  local zone=$2
173  local item
174  for log_interface in $(get_zone_interfaces "$2"); do
175  for item in $(uci_get "network.${log_interface}.ifname"); do
176  # Entferne den Teil nach Doppelpunkten - fuer Alias-Interfaces
177  [ "$device" = "$(echo "$item" | cut -f 1 -d :)" ] && return 0 || true
178  done
179  done
180  trap "" $GUARD_TRAPS && return 1
181 }
182 
183 
184 # Ist das gegebene logische Netzwerk-Interface Teil einer Firewall-Zone?
185 is_interface_in_zone() {
186  local interface=$1
187  local zone=$2
188  local item
189  for item in $(get_zone_interfaces "$2"); do
190  [ "$item" = "$interface" ] && return 0 || true
191  done
192  trap "" $GUARD_TRAPS && return 1
193 }
194 
195 
196 add_interface_to_zone() {
197  local zone=$1
198  local interface=$2
199  local uci_prefix=$(find_first_uci_section firewall zone "name=$zone")
200  [ -z "$uci_prefix" ] && msg_debug "failed to add interface '$interface' to non-existing zone '$zone'" && trap "" $GUARD_TRAPS && return 1
201  uci_add_list "${uci_prefix}.network" "$interface"
202 }
203 
204 
205 del_interface_from_zone() {
206  local zone=$1
207  local interface=$2
208  local uci_prefix=$(find_first_uci_section firewall zone "name=$zone")
209  [ -z "$uci_prefix" ] && msg_debug "failed to remove interface '$interface' from non-existing zone '$zone'" && trap "" $GUARD_TRAPS && return 1
210  uci del_list "${uci_prefix}.network=$interface"
211 }
212 
213 
214 ## @fn get_zone_of_device()
215 ## @brief Ermittle die Zone eines physischen Netzwerk-Interfaces.
216 ## @param interface Name eines physischen Netzwerk-Interface (z.B. eth0)
217 ## @details Das Ergebnis ist ein leerer String, falls zu diesem Interface keine Zone existiert
218 ## oder falls es das Interface nicht gibt.
219 get_zone_of_device() {
220  trap "error_trap get_zone_of_device '$*'" $GUARD_TRAPS
221  local device="$1"
222  local uci_prefix
223  local devices
224  local zone
225  find_all_uci_sections firewall zone | while read uci_prefix; do
226  zone=$(uci_get "${uci_prefix}.name")
227  devices=$(get_zone_devices "$zone")
228  is_in_list "$device" "$devices" && echo -n "$zone" && return 0 || true
229  done
230  # ein leerer Rueckgabewert gilt als Fehler
231  return 0
232 }
233 
234 
235 ## @fn get_zone_of_interface()
236 ## @brief Ermittle die Zone eines logischen Netzwerk-Interfaces.
237 ## @param interface Name eines logischen Netzwerk-Interface (z.B. eth0)
238 ## @details Das Ergebnis ist ein leerer String, falls zu diesem Interface keine Zone existiert
239 ## oder falls es das Interface nicht gibt.
240 get_zone_of_interface() {
241  trap "error_trap get_zone_of_interface '$*'" $GUARD_TRAPS
242  local interface=$1
243  local uci_prefix
244  local interfaces
245  local zone
246  find_all_uci_sections firewall zone | while read uci_prefix; do
247  zone=$(uci_get "${uci_prefix}.name")
248  interfaces=$(get_zone_interfaces "$zone")
249  is_in_list "$interface" "$interfaces" && echo -n "$zone" && return 0 || true
250  done
251  # ein leerer Rueckgabewert gilt als Fehler
252  return 0
253 }
254 
255 
256 # Liefere die sortierte Liste der Opennet-Interfaces.
257 # Prioritaeten:
258 # 1. dem Netzwerk ist ein Geraet zugeordnet
259 # 2. Netzwerkname beginnend mit "on_wifi", "on_eth", ...
260 # 3. alphabetische Sortierung der Netzwerknamen
261 get_sorted_opennet_interfaces() {
262  trap "error_trap get_sorted_opennet_interfaces '$*'" $GUARD_TRAPS
263  local uci_prefix
264  local order
265  # wir vergeben einfach statische Ordnungsnummern:
266  # 10 - konfigurierte Interfaces
267  # 20 - nicht konfigurierte Interfaces
268  # Offsets basierend auf dem Netzwerknamen:
269  # 1 - on_wifi*
270  # 2 - on_eth*
271  # 3 - alle anderen
272  for network in $(get_zone_interfaces "$ZONE_MESH"); do
273  uci_prefix=network.$network
274  order=10
275  [ "$(uci_get "${uci_prefix}.ifname")" == "none" ] && order=20
276  if [ "${network#on_wifi}" != "$network" ]; then
277  order=$((order+1))
278  elif [ "${network#on_eth}" != "$network" ]; then
279  order=$((order+2))
280  else
281  order=$((order+3))
282  fi
283  echo "$order $network"
284  done | sort -n | cut -f 2 -d " "
285 }
286 
287 
288 # Liefere alle vorhandenen logischen Netzwerk-Schnittstellen (lan, wan, ...) zurueck.
289 get_all_network_interfaces() {
290  local interface
291  uci show network | grep "^network\.[^.]\+=interface$" | cut -f 2 -d . | cut -f 1 -d = | while read interface; do
292  # ignoriere loopback-Interface
293  [ "$interface" = "loopback" ] && continue
294  # alle uebrigen sind reale Interfaces
295  echo "$interface"
296  done
297  return 0
298 }
299 
300 
301 rename_firewall_zone() {
302  trap "error_trap rename_firewall_zone '$*'" $GUARD_TRAPS
303  local old_zone="$1"
304  local new_zone="$2"
305  local setting
306  local uci_prefix
307  local key
308  local old_uci_prefix=$(find_first_uci_section firewall zone "name=$old_zone")
309  # die Zone existiert nicht (mehr)
310  [ -z "$old_uci_prefix" ] && return 0
311  local new_uci_prefix=$(find_first_uci_section firewall zone "name=$new_zone")
312  [ -z "$new_uci_prefix" ] && new_uci_prefix="firewall.$(uci add firewall zone)"
313  uci show "$old_uci_prefix" | cut -f 3- -d . | while read setting; do
314  # die erste Zeile (der Zonen-Typ) ueberspringen
315  [ -z "$setting" ] && continue
316  uci set "${new_uci_prefix}.$setting"
317  done
318  # den Namen ueberschreiben (er wurde oben von der alten Zone uebernommen)
319  uci set "${new_uci_prefix}.name=$new_zone"
320  # aktualisiere alle Forwardings, Redirects und Regeln
321  for section in "forwarding" "redirect" "rule"; do
322  for key in "src" "dest"; do
323  find_all_uci_sections firewall "$section" "${key}=$old_zone" | while read uci_prefix; do
324  uci set "${uci_prefix}.${key}=$new_zone"
325  done
326  done
327  done
328  # fertig - wir loeschen die alte Zone
329  uci_delete "$old_uci_prefix"
330  apply_changes firewall
331 }
332 
333 # Ende der Doku-Gruppe
334 ## @}