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