1 ## @defgroup routing Routing 2 ## @brief Abfrage von Routing-Informationen und Einrichtung des Policy-Routings. 3 # Beginn der Doku-Gruppe 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
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
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. 24 echo
"$target" | grep -q -E
'^[0-9]+(\.[0-9]+){3}(/[0-9]+)?$' 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. 34 echo
"$target" | grep -q -E
"^[0-9a-fA-F:]+(/[0-9]+)?$" 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 46 [ -z
"$(get_target_route_interface "$ip
")" ] || echo
"$ip" 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 60 local tos_field=
"${2:-}" 64 [ -n
"$tos_field" ] && route_get_args=
"tos $tos_field" 68 all_addresses=$(query_dns
"$target")
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 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)$" \
86 | sed
's/^.* dev \+\([^ \t]\+\) \+.*$/\1/' 91 # Entferne alle Policy-Routing-Regeln die dem gegebenen Ausdruck entsprechen. 92 # Es erfolgt keine Fehlerausgabe und keine Fehlermeldungen. 93 delete_policy_rule() {
96 while ip -family
"$family" rule del
"$@";
do true;
done 2>/dev/
null 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
109 local network_with_prefix
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 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
131 # ermittle alle physischen Geräte inklusive Bridge-Interfaces, die zu dieser Zone gehören 132 for interface in $(get_zone_interfaces "$zone");
do 137 done | sort | uniq |
while read -r device;
do 138 [ -n
"$device" ] && [
"$device" !=
"none" ] && ip -family
"$family" rule add iif
"$device" "$@" 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
154 local priority=
"$OLSR_POLICY_DEFAULT_PRIORITY" 156 # Sicherstellen, dass die Tabellen existieren und zur olsrd-Konfiguration passen 158 # die Uplink-Tabelle ist unabhaengig von olsr 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 168 # free-Verkehr geht immer in den Tunnel (falls das Paket installiert ist) 169 if [ -n
"${ZONE_FREE:-}" ]; then
171 priority=$((priority + 1))
174 # sehr wichtig - also zuerst: keine vorbeifliegenden Mesh-Pakete umlenken 176 priority=$((priority + 1))
178 priority=$((priority + 1))
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 189 priority=$((priority + 1))
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))
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))
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))
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" 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}' 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
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))
263 echo
"$table_id $table_name" >>
"$RT_FILE" 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. 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.
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
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) }}' 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 302 # wenn kein Parameter uebergeben, dann breche ab 303 [ -z
"$target" ] &&
return 0
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) { 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" 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 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" 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 364 local ip_interface_map
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
")" 375 # Ende der Doku-Gruppe
get_hop_count_and_etx(host)
Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
is_ipv4(target)
Prüfe ob der übergebene Text eine IPv4-Adresse ist.
olsr_sync_routing_tables()
Synchronisiere die olsrd-Routingtabellen-Konfiguration mit den iproute-Routingtabellennummern.
get_olsr_route_count_by_device()
Liefere die Anzahl von olsr-Routen, die auf ein bestimmtes Netzwerk-Interface verweisen.
get_routing_table_id(table_name)
Ermittle die Nummer der namentlich gegebenen Routing-Tabelle.
add_routing_table(table_name)
Erstelle einen neuen Routing-Tabellen-Eintrag.
add_network_policy_rule_by_destination(network, more)
erzeuge Policy-Rules entsprechend der IP-Bereiche eines Netzwerks
set eu grep root::etc shadow exit if command v chpasswd dev null
request_olsrd_txtinfo(request)
Sende eine Anfrage an das txtinfo-Interface von olsrd.
is_ipv6(target)
Prüfe ob der übergebene Text eine IPv6-Adresse ist.
add_zone_policy_rules_by_iif(family, zone, route)
Erzeuge Policy-Rules fuer Quell-Interfaces.
get_traceroute(host)
Liefere einen traceroute zu einem Host zurueck.
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).
get_olsr_neighbours()
Ermittle die direkten olsr-Nachbarn und liefere ihre IPs und interessante Kennzahlen zurück...
filter_routable_addresses()
Filtere aus einer Menge von Ziel-IPs diejenigen heraus, für die eine passende Routing-Regel existiert...
get_device_of_interface()
Ermittle das physische Netzwerk-Gerät, das einem logischen Netzwerk entspricht.
initialize_olsrd_policy_routing()
Policy-Routing-Initialisierung nach dem System-Boot und nach Interface-Hotplug-Ereignissen.
get_target_route_interface(target)
Ermittle das Netzwerkinterface, über den der Verkehr zu einem Ziel laufen würde.
get_olsr_route_count_by_neighbour()
Liefere die Anzahl von olsr-Routen, die auf einen bestimmten Routing-Nachbarn verweisen.