Opennet Firmware
routing.sh
gehe zur Dokumentation dieser Datei
1 ## @defgroup routing Routing
2 ## @brief Abfrage von Routing-Informationen und Einrichtung des Policy-Routings.
3 # Beginn der Doku-Gruppe
4 ## @{
5 
6 ROUTING_TABLE_ON_UPLINK=on-tunnel
7 ROUTING_TABLE_MESH=olsrd
8 ROUTING_TABLE_MESH_DEFAULT=olsrd-default
9 OLSR_POLICY_DEFAULT_PRIORITY=20000
10 RT_FILE=/etc/iproute2/rt_tables
11 RT_START_ID=11
12 # Prioritaets-Offset fuer default-Routing-Tabellen (z.B. "default" und "olsrd-default")
13 # shellcheck disable=SC2034
14 DEFAULT_RULE_PRIO_OFFSET=100
15 OLSR_ROUTE_CACHE_FILE=/tmp/olsr_routes.cache
16 
17 
18 ## @fn is_ipv4()
19 ## @brief Prüfe ob der übergebene Text eine IPv4-Adresse ist
20 ## @param target eine Zeichenkette (wahrscheinlich ein DNS-Name, eine IPv4- oder IPv6-Adresse)
21 ## @details Die IP-Adresse darf mit einem Netzwerkpraefix enden.
22 is_ipv4() {
23  local target="$1"
24  echo "$target" | grep -q -E '^[0-9]+(\.[0-9]+){3}(/[0-9]+)?$'
25 }
26 
27 
28 ## @fn is_ipv6()
29 ## @brief Prüfe ob der übergebene Text eine IPv6-Adresse ist
30 ## @param target eine Zeichenkette (wahrscheinlich ein DNS-Name, eine IPv4- oder IPv6-Adresse)
31 ## @details Achtung: der Test ist recht oberflächlich und kann falsche Positive liefern.
32 is_ipv6() {
33  local target="$1"
34  echo "$target" | grep -q -E "^[0-9a-fA-F:]+(/[0-9]+)?$"
35 }
36 
37 
38 ## @fn filter_routable_addresses()
39 ## @brief Filtere aus einer Menge von Ziel-IPs diejenigen heraus, für die eine passende Routing-Regel existiert.
40 ## @details Lies IP-Addressen zeilenweise via stdin ein und gib alle Adressen aus, die (laut "ip route get") erreichbar sind.
41 ## Dies bedeutet nicht, dass wir mit den Adressen kommunizieren koennen - es geht lediglich um lokale Routing-Regeln.
42 ## @return zeilenweise Ausgabe der route-baren Ziel-IPs:w
44  local ip
45  while read -r ip; do
46  [ -z "$(get_target_route_interface "$ip")" ] || echo "$ip"
47  done
48  return 0
49 }
50 
51 
52 ## @fn get_target_route_interface()
53 ## @brief Ermittle das Netzwerkinterface, über den der Verkehr zu einem Ziel laufen würde.
54 ## @param target Hostname oder IP des Ziel-Hosts
55 ## @tos_field tos Optionale type-of-service-Zahl
56 ## @details Falls erforderlich, findet eine Namensauflösung statt.
57 ## @return Name des physischen Netzwerk-Interface, über den der Verkehr zum Ziel-Host fließen würde
59  local target="$1"
60  local tos_field="${2:-}"
61  local all_addresses
62  local ipaddr
63  local route_get_args=
64  [ -n "$tos_field" ] && route_get_args="tos $tos_field"
65  if is_ipv4 "$target" || is_ipv6 "$target"; then
66  all_addresses=$target
67  else
68  all_addresses=$(query_dns "$target")
69  fi
70  for ipaddr in $all_addresses; do
71  # "failed_policy" wird von ipv6 fuer nicht-zustellbare Adressen zurueckgeliefert
72  # Falls ein Hostname mehrere IP-Adressen ergibt (z.B. ipv4 und ipv6), dann werden beide geprüft.
73  # Die Ergebnis der Interface-Ermittlung für eine IPv6-Adresse bei fehlendem IPv6-Gateway sieht folgendermaßen aus:
74  # root@AP-1-193:/tmp/log/on-services# ip route get 2a01:4f8:140:1222::1:7
75  # 12 2a01:4f8:140:1222::1:7 from :: dev lo src fe80::26a4:3cff:fefd:7649 metric -1 error -1
76  # oder:
77  # root@AP-2-156:~# ip route get 2001:67c:1400:2430::1
78  # prohibit 2001:67c:1400:2430::1 from :: dev lo table unspec proto kernel src fe80::216:3eff:fe34:2aa5 metric 4294967295 error -13
79  # Wir ignorieren also Zeilen, die auf "error -1" oder "error -13" enden.
80  # Fehlermeldungen (ip: RTNETLINK answers: Network is unreachable) werden ebenfalls ignoriert.
81  # shellcheck disable=SC2086
82  ip route get "$ipaddr" $route_get_args 2>/dev/null \
83  | grep -vE "^(failed_policy|prohibit)" \
84  | grep -vE "error -(1|13)$" \
85  | grep " dev " \
86  | sed 's/^.* dev \+\([^ \t]\+\) \+.*$/\1/'
87  done | tail -1
88 }
89 
90 
91 # Entferne alle Policy-Routing-Regeln die dem gegebenen Ausdruck entsprechen.
92 # Es erfolgt keine Fehlerausgabe und keine Fehlermeldungen.
93 delete_policy_rule() {
94  local family="$1"
95  shift
96  while ip -family "$family" rule del "$@"; do true; done 2>/dev/null
97 }
98 
99 
100 ## @fn add_network_policy_rule_by_destination()
101 ## @brief erzeuge Policy-Rules entsprechend der IP-Bereiche eines Netzwerks
102 ## @param network logisches Netzwerkinterface
103 ## @param more weitere Parameter: Policy-Rule-Spezifikation
105  trap 'error_trap add_network_policy_rule_by_destination "$*"' EXIT
106  local family="$1"
107  local network="$2"
108  shift 2
109  local network_with_prefix
110  for network_with_prefix in $(get_current_addresses_of_network "$network"); do
111  is_ipv4 "$network_with_prefix" && [ "$family" != "inet" ] && continue
112  is_ipv6 "$network_with_prefix" && [ "$family" != "inet6" ] && continue
113  ip -family "$family" rule add to "$network_with_prefix" "$@" || true
114  done
115  return 0
116 }
117 
118 
119 ## @fn add_zone_policy_rules_by_iif()
120 ## @brief Erzeuge Policy-Rules fuer Quell-Interfaces
121 ## @param family "inet" oder "inet6"
122 ## @param zone Pakete aus allen Interfaces dieser Zone kommend sollen betroffen sein
123 ## @param route Spezifikation einer Route (siehe 'ip route add ...')
125  trap 'error_trap add_zone_policy_rules "$*"' EXIT
126  local family="$1"
127  local zone="$2"
128  shift 2
129  local interface
130  local device
131  # ermittle alle physischen Geräte inklusive Bridge-Interfaces, die zu dieser Zone gehören
132  for interface in $(get_zone_interfaces "$zone"); do
133  get_device_of_interface "$interface"
134  for device in $(get_subdevices_of_interface "$interface"); do
135  echo "$device"
136  done
137  done | sort | uniq | while read -r device; do
138  [ -n "$device" ] && [ "$device" != "none" ] && ip -family "$family" rule add iif "$device" "$@"
139  true
140  done
141  return 0
142 }
143 
144 
145 ## @fn initialize_olsrd_policy_routing()
146 ## @brief Policy-Routing-Initialisierung nach dem System-Boot und nach Interface-Hotplug-Ereignissen
147 ## @details Folgende Seiteneffekte treten ein:
148 ## * alle Policy-Rules mit Bezug zu den Tabellen olsrd/olsrd-default/main werden gelöscht
149 ## * die neuen Policy-Rules für die obigen Tabellen werden an anderer Stelle erzeugt
150 ## Kurz gesagt: alle bisherigen Policy-Rules sind hinterher kaputt
152  trap 'error_trap initialize_olsrd_policy_routing "$*"' EXIT
153  local iface
154  local priority="$OLSR_POLICY_DEFAULT_PRIORITY"
155 
156  # Sicherstellen, dass die Tabellen existieren und zur olsrd-Konfiguration passen
158  # die Uplink-Tabelle ist unabhaengig von olsr
159  add_routing_table "$ROUTING_TABLE_ON_UPLINK" >/dev/null
160 
161  # alle Eintraege loeschen
162  delete_policy_rule inet table "$ROUTING_TABLE_MESH"
163  delete_policy_rule inet table "$ROUTING_TABLE_MESH_DEFAULT"
164  delete_policy_rule inet table "$ROUTING_TABLE_ON_UPLINK"
165  delete_policy_rule inet table main
166  delete_policy_rule inet table default
167 
168  # free-Verkehr geht immer in den Tunnel (falls das Paket installiert ist)
169  if [ -n "${ZONE_FREE:-}" ]; then
170  add_zone_policy_rules_by_iif inet "$ZONE_FREE" table "$ROUTING_TABLE_ON_UPLINK" prio "$priority"
171  priority=$((priority + 1))
172  fi
173 
174  # sehr wichtig - also zuerst: keine vorbeifliegenden Mesh-Pakete umlenken
175  add_zone_policy_rules_by_iif inet "$ZONE_MESH" table "$ROUTING_TABLE_MESH" prio "$priority"
176  priority=$((priority + 1))
177  add_zone_policy_rules_by_iif inet "$ZONE_MESH" table "$ROUTING_TABLE_MESH_DEFAULT" prio "$priority"
178  priority=$((priority + 1))
179 
180  # Pakete mit passendem Ziel orientieren sich an der main-Tabelle
181  # Alle Ziele ausserhalb der mesh-Zone sind geeignet (z.B. local, free, ...).
182  # Wir wollen dadurch explizit keine potentielle default-Route verwenden.
183  # Aufgrund der "while"-Sub-Shell (mit separatem Variablenraum) belassen wir die Regeln
184  # einfach bei gleicher Prioritaet und erhoehen diese erst anschliessend.
185  for iface in $(get_all_network_interfaces); do
186  is_interface_in_zone "$iface" "$ZONE_MESH" && continue
187  add_network_policy_rule_by_destination inet "$iface" table main prio "$priority"
188  done
189  priority=$((priority + 1))
190 
191  # alle nicht-mesh-Quellen routen auch ins olsr-Netz
192  #TODO: wir sollten nur private Ziel-IP-Bereiche (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) zulassen
193  # spaeter sind konfigurierbar weitere IPs (fuer HNAs oeffentlicher Dienste) moeglich
194  ip rule add table "$ROUTING_TABLE_MESH" prio "$priority"
195  priority=$((priority + 1))
196  ip rule add table "$ROUTING_TABLE_MESH_DEFAULT" prio "$priority"
197  priority=$((priority + 1))
198 
199  # Routen, die nicht den lokalen Netz-Interfaces entsprechen (z.B. default-Routen)
200  ip rule add table main prio "$priority"
201  priority=$((priority + 1))
202 
203  # die default-Table und VPN-Tunnel fungieren fuer alle anderen Pakete als default-GW
204  ip rule add table default prio "$priority"
205  priority=$((priority + 1))
206  ip rule add table "$ROUTING_TABLE_ON_UPLINK" prio "$priority"
207  priority=$((priority + 1))
208 }
209 
210 
211 # Stelle sicher, dass eine sinnvolle routing-Tabellen-Datei existiert.
212 # Dies ist erforderlich, falls kein echtes "ip"-Paket installiert ist (im busybox-Paket ist die Datei nicht enthalten).
213 _prepare_routing_table_file() {
214  [ -e "$RT_FILE" ] && return 0
215  mkdir -p "$(dirname "$RT_FILE")"
216  cat >"$RT_FILE" << EOF
217 # erzeugt von "on-core"
218 #
219 255 local
220 254 main
221 253 default
222 0 unspec
223 #
224 # local
225 #
226 #1 inr.ruhep
227 EOF
228 }
229 
230 
231 ## @fn get_routing_table_id()
232 ## @brief Ermittle die Nummer der namentlich gegebenen Routing-Tabelle.
233 ## @param table_name Name der gesuchten Routing-Tabelle
234 ## @return Routing-Tabellen-ID oder nichts (falls die Tabelle nicht existiert)
236  local table_name="$1"
237  _prepare_routing_table_file
238  # Tabellennummer ausgeben, falls sie vorhanden ist
239  grep '^[0-9]\+[ \t]\+'"$table_name$" "$RT_FILE" | awk '{print $1}'
240  return 0
241 }
242 
243 
244 ## @fn add_routing_table()
245 ## @brief Erstelle einen neuen Routing-Tabellen-Eintrag.
246 ## @param table_name der Name der zu erstellenden Routing-Tabelle
247 ## @details Die Routing-Tabellen-Nummer wird automatisch ermittelt.
248 ## Sollte die Tabelle bereits existieren, dann wird ihre Nummer zurückgeliefert.
249 ## @return die neue Routing-Tabellen-Nummer wird zurückgeliefert
251  trap 'error_trap add_routing_table "$*"' EXIT
252  local table_name="$1"
253  _prepare_routing_table_file
254  local table_id
255  table_id=$(get_routing_table_id "$table_name")
256  # schon vorhanden?
257  [ -n "$table_id" ] && echo "$table_id" && return 0
258  # wir muessen den Eintrag hinzufuegen
259  table_id="$RT_START_ID"
260  while [ -n "$(_get_file_dict_value "$table_id" "$RT_FILE")" ]; do
261  table_id=$((table_id + 1))
262  done
263  echo "$table_id $table_name" >> "$RT_FILE"
264  echo "$table_id"
265 }
266 
267 
268 ## @fn get_hop_count_and_etx()
269 ## @brief Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
270 ## @param host die Ziel-IP
271 ## @returns Der Hop-Count und der ETX-Wert wird mit einem Leerzeichen separiert ausgegeben. Falls keine Route bekannt ist, ist das Ergebnis ein leerer String.
272 ## @details Die Quelle dieser Information ist olsrd. Routen außerhalb von olsrd werden nicht beachtet.
274  local target="$1"
275  local result
276  # kein Ergebnis, falls noch kein Routen-Cache vorliegt (minuetlicher cronjob)
277  [ ! -e "$OLSR_ROUTE_CACHE_FILE" ] && return 0
278  result=$(awk '{ if ($1 == "'"$target"'") { print $3, $4; exit; } }' <"$OLSR_ROUTE_CACHE_FILE")
279  [ -n "$result" ] && echo "$result" && return 0
280  # Überprüfe, ob die IP des Zielhost die eigene IP ist. Dann sollte distance=0 gesetzt werden.
281  if is_ipv4 "$target" || is_ipv6 "$target"; then
282  result=$(ip route get "$target" 2>/dev/null | grep -w "dev lo" || true)
283  [ -n "$result" ] && echo "$result" | grep -vq "^unreachable" && echo "0 0" && return 0
284  true
285  fi
286  # Hole Daten von OLSR2
287  if is_ipv6 "$target" && is_function_available "request_olsrd2_txtinfo"; then
288  request_olsrd2_txtinfo "olsrv2info" "route" \
289  | awk '{ if ($1 == "'"$target"'") { cost=$(NF-1); hops=$NF; print(hops, 1 / cost) }}'
290  fi
291 }
292 
293 
294 ## @fn get_traceroute()
295 ## @brief Liefere einen traceroute zu einem Host zurueck.
296 ## @param host der Ziel-IP
297 ## @return Eine mit Komma getrennte Liste von IPs
298 get_traceroute() {
299  local target="$1"
300  local result
301 
302  # wenn kein Parameter uebergeben, dann breche ab
303  [ -z "$target" ] && return 0
304 
305  # Verarbeitung:
306  # - erste Zeile auslassen (traceroute-Header)
307  # - unbekannte Hops ignorieren ("*" oder "???")
308  # - Einträge durch Kommata separieren
309  # - unterdrücke Fehler (z.B. keine Route zum Host)
310  timeout 20 traceroute -w 1 -q 1 -n "$target" 2>/dev/null | awk '
311  { if ((NR > 1) && ($2 != "*") && ($2 != "???") && $2) {
312  if (NR == 2) {
313  printf($2);
314  } else {
315  printf(",%s", $2);
316  }
317  }}'
318 }
319 
320 
321 # Diese Funktion sollte oft (minuetlich?) aufgerufen werden, um die olsrd-Routing-Informationen abzufragen.
322 # Dies ist noetig, um deadlocks bei parallelem Zugriff auf den single-thread olsrd zu verhindern.
323 # Symptome eines deadlocks: olsrd ist beendet; viele parallele nc-Instanzen; eine davon ist an den txtinfo-Port gebunden.
324 update_olsr_route_cache() {
325  trap 'error_trap update_olsr_route_cache "$*"' EXIT
326  # die temporaere Datei soll verhindern, dass es zwischendurch ein Zeitfenster mit unvollstaendigen Informationen gibt
327  local tmpfile="${OLSR_ROUTE_CACHE_FILE}.new"
328  # Bei der Ausfuehrung via cron wird SIGPIPE eventuell behandelt, auf dass die Ausfuehrung
329  # ohne Erzeugung der Datei abbrechen koennte. Daher ist die &&-Verknuepfung sinnvoll.
330  request_olsrd_txtinfo rou | sed '/^[^0-9]/d; s#/32##' > "$tmpfile" && mv "$tmpfile" "$OLSR_ROUTE_CACHE_FILE"
331  return 0
332 }
333 
334 
335 ## @fn get_olsr_route_count_by_device()
336 ## @brief Liefere die Anzahl von olsr-Routen, die auf ein bestimmtes Netzwerk-Interface verweisen
338  local device_regex="$1"
339  # kein Ergebnis, falls noch kein Routen-Cache vorliegt (minuetlicher cronjob)
340  [ -e "$OLSR_ROUTE_CACHE_FILE" ] || return 0
341  awk '{ print $5 }' "$OLSR_ROUTE_CACHE_FILE" | grep -c "^$device_regex$" || true
342 }
343 
344 
345 ## @fn get_olsr_route_count_by_neighbour()
346 ## @brief Liefere die Anzahl von olsr-Routen, die auf einen bestimmten Routing-Nachbarn verweisen.
348  local neighbour_ip="$1"
349  # kein Ergebnis, falls noch kein Routen-Cache vorliegt (minuetlicher cronjob)
350  [ -e "$OLSR_ROUTE_CACHE_FILE" ] || return 0
351  awk 'BEGIN { count=0; } { if ($2 == "'"$neighbour_ip"'") count++; } END { print count; }' "$OLSR_ROUTE_CACHE_FILE"
352 }
353 
354 
355 ## @fn get_olsr_neighbours()
356 ## @brief Ermittle die direkten olsr-Nachbarn und liefere ihre IPs und interessante Kennzahlen zurück.
357 ## details Ergebnisformat: NEIGHBOUR_IP LINK_QUALITY NEIGHBOUR_LINK_QUALITY ETX ROUTE_COUNT
359  local local_ip
360  local neighbour_ip
361  local lq
362  local nlq
363  local etx
364  local ip_interface_map
365  local interface
366  ip_interface_map=$(request_olsrd_txtinfo "int" | awk '{print($5, $1);}')
367  request_olsrd_txtinfo "lin" | grep "^[0-9]" | awk '{ print $1,$2,$4,$5,$6 }' | sort -n \
368  | while read -r local_ip neighbour_ip lq nlq etx; do
369  interface=$(echo "$ip_interface_map" | grep -wF "$local_ip" | awk '{print $2}')
370  [ -z "$interface" ] && interface="unknown"
371  echo "$neighbour_ip $interface $lq $nlq $etx $(get_olsr_route_count_by_neighbour "$neighbour_ip")"
372  done
373 }
374 
375 # Ende der Doku-Gruppe
376 ## @}
get_hop_count_and_etx(host)
Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
Definition: routing.sh:62
is_ipv4(target)
Prüfe ob der übergebene Text eine IPv4-Adresse ist.
Definition: routing.sh:8
olsr_sync_routing_tables()
Synchronisiere die olsrd-Routingtabellen-Konfiguration mit den iproute-Routingtabellennummern.
Definition: olsr.sh:7
get_olsr_route_count_by_device()
Liefere die Anzahl von olsr-Routen, die auf ein bestimmtes Netzwerk-Interface verweisen.
Definition: routing.sh:70
get_routing_table_id(table_name)
Ermittle die Nummer der namentlich gegebenen Routing-Tabelle.
Definition: routing.sh:49
add_routing_table(table_name)
Erstelle einen neuen Routing-Tabellen-Eintrag.
Definition: routing.sh:56
add_network_policy_rule_by_destination(network, more)
erzeuge Policy-Rules entsprechend der IP-Bereiche eines Netzwerks
Definition: routing.sh:31
set eu grep root::etc shadow exit if command v chpasswd dev null
Definition: on-password:12
request_olsrd_txtinfo(request)
Sende eine Anfrage an das txtinfo-Interface von olsrd.
Definition: olsr.sh:21
is_ipv6(target)
Prüfe ob der übergebene Text eine IPv6-Adresse ist.
Definition: routing.sh:13
add_zone_policy_rules_by_iif(family, zone, route)
Erzeuge Policy-Rules fuer Quell-Interfaces.
Definition: routing.sh:37
get_traceroute(host)
Liefere einen traceroute zu einem Host zurueck.
Definition: routing.sh:67
get_subdevices_of_interface()
Ermittle die physischen Netzwerk-Geräte (bis auf wifi), die zu einem logischen Netzwerk-Interface geh...
Definition: network.sh:42
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
get_olsr_neighbours()
Ermittle die direkten olsr-Nachbarn und liefere ihre IPs und interessante Kennzahlen zurück...
Definition: routing.sh:77
filter_routable_addresses()
Filtere aus einer Menge von Ziel-IPs diejenigen heraus, für die eine passende Routing-Regel existiert...
Definition: routing.sh:19
shift
Definition: core.sh:85
get_device_of_interface()
Ermittle das physische Netzwerk-Gerät, das einem logischen Netzwerk entspricht.
Definition: network.sh:28
initialize_olsrd_policy_routing()
Policy-Routing-Initialisierung nach dem System-Boot und nach Interface-Hotplug-Ereignissen.
Definition: routing.sh:44
done
Definition: core.sh:85
get_target_route_interface(target)
Ermittle das Netzwerkinterface, über den der Verkehr zu einem Ziel laufen würde.
Definition: routing.sh:26
get_olsr_route_count_by_neighbour()
Liefere die Anzahl von olsr-Routen, die auf einen bestimmten Routing-Nachbarn verweisen.
Definition: routing.sh:73