1 ## @defgroup services Dienste
2 ## @brief Verwaltung von Diensten (z.B. via olsrd-nameservice announciert)
3 # Beginn der Doku-Gruppe
6 VOLATILE_SERVICE_STATUS_DIR=/tmp/on-services-
volatile.d
7 PERSISTENT_SERVICE_STATUS_DIR=/etc/on-services.d
8 # eine grosse Zahl sorgt dafuer, dass neu entdeckte Dienste hinten angehaengt werden
9 DEFAULT_SERVICE_RANK=10000
10 DEFAULT_SERVICE_SORTING=etx
11 # Die folgenden Attribute werden dauerhaft (im Flash) gespeichert. Häufige Änderungen sind also eher unerwünscht.
12 # Gruende fuer ausgefallene/unintuitive Attribute:
13 # uci_dependency: später zu beräumende uci-Einträge wollen wir uns merken
14 # file_dependency: siehe uci_dependency
15 # priority: DNS-entdeckte Dienste enthalten ein "priority"-Attribut, nach einem reboot wieder verfügbar sein sollte
16 # rank/offset: Attribute zur Ermittlung der Dienstreihenfolge
17 # disabled: der Dienst wurde vom Nutzenden an- oder abgewählt
18 # source: die Quelle des Diensts (olsrd/dns/manual) muss erhalten bleiben, um ihn später löschen zu können
19 # Wir beachten den vorherigen Zustand der Variable, damit andere Module (z.B. on-usergw) diese
20 # ebenfalls beeinflussen können.
21 PERSISTENT_SERVICE_ATTRIBUTES=
"${PERSISTENT_SERVICE_ATTRIBUTES:-} service scheme host port protocol path uci_dependency file_dependency priority rank offset disabled source"
23 SERVICES_LOG_BASE=/var/log/on-services
24 # Namenspräfix für weiterzuleitende Dienste
25 RELAYABLE_SERVICE_PREFIX=
"proxy-"
26 UPDATE_TRUSTED_SERVICES_PERIOD_MINUTES=360
27 USER_SERVICES_URL=https:
29 # andere Module fügen eventuell weitere URLs hinzu
30 SERVICES_LIST_URLS=
"${SERVICES_LIST_URLS:-} $USER_SERVICES_URL"
33 ## @fn get_service_name()
34 ## @brief Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
35 ## @details Reihenfolge der Eingabeparameter: SERVICE_TYPE SCHEMA HOST PORT PROTOCOL PATH
43 local name=
"${service}_${scheme}_${host}_${port}_${protocol}"
44 [ -n
"${path#/}" ] && name=
"${name}_${path#/}"
45 echo
"$name" | sed
's/[^A-Za-z0-9_]/_/g'
49 ## @fn notify_service()
50 ## @brief Aktualisiere den Zeitstempel und die Entfernung (etx) eines Dienstes
51 ## @param service z.B. "gw"
52 ## @param scheme z.B. "openvpn"
53 ## @param host z.B. "192.168.2.254"
54 ## @param port z.B. "1600"
55 ## @param path z.B. "/"
56 ## @param protocol z.B. "udp"
57 ## @param details z.B. "via:megumi"
58 ## @returns Der Dienstname wird ausgegeben.
60 trap
'error_trap notify_service "$*"' EXIT
61 # wir erwarten sieben Parameter
62 [
"$#" -eq 7 ] || [
"$#" -eq 8 ]
69 ## @fn notify_services()
70 ## @brief Siehe "notify_service" - jedoch effizienter im Umgang mit einer großen Anzahl von Diensten
71 ## @param source Quelle (z.B. "olsr")
72 ## @returns Alle Dienstnamen werden ausgegeben.
74 trap
'error_trap notify_services "$*"' EXIT
87 while read -r service scheme host port protocol path details;
do
88 service_name=$(
get_service_name "$service" "$scheme" "$host" "$port" "$protocol" "$path")
89 # Diese Attribute duerften sich nicht aendern, aber wir wollen sicherheitshalber lieber kaputten
91 #
"details",
"timestamp" und
"source" sind die flexiblen Werte.
93 "service" "$service" \
97 "protocol" "$protocol" \
99 "details" "$details" \
100 "timestamp" "$(get_uptime_minutes)" \
112 ## @fn is_existing_service()
113 ## @brief Prüfe ob ein Service existiert
114 ## @param service_name der Name des Diensts
115 ## @returns exitcode=0 falls der Dienst existiert
117 local service_name=
"$1"
118 if [ -n
"$service_name" ] && [ -e
"$PERSISTENT_SERVICE_STATUS_DIR/$service_name" ]; then
121 trap
"" EXIT &&
return 1
126 ## @fn _get_local_bias_for_service()
127 ## @brief Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1) - abhängig von der lokalen IP und dem Dienstnamen.
128 ## @param service_name der Name des Diensts für den ein Bias-Wert zu ermitteln ist.
129 ## @details Dadurch können wir beim Sortieren strukturelle Bevorzugungen (z.B. durch alphabetische Sortierung) verhindern.
131 local service_name=
"$1"
132 # lade den Wert aus dem Cache, falls moeglich
135 if [ -z
"$bias_cache" ]; then
136 # Die resultierende host_number darf nicht zu gross sein (z.B. mit Exponentendarstellung),
137 # da andernfalls awk die Berechnung fehlerhaft durchführt.
139 host_number=$(echo
"$service_name$(get_local_bias_number)" | md5sum | sed
's/[^0-9]//g')
140 # Laenge von
'host_number' reduzieren (die Berechnung schlaegt sonst fehl)
141 # Wir fuegen die 1 an den Beginn, um die Interpretation als octal-Zahl zu verhindern (fuehrende Null).
142 bias_cache=$(( 1${host_number:0:6} % LOCAL_BIAS_MODULO))
145 echo -n
"$bias_cache"
149 # Ermittle die Service-Prioritaet eines Dienstes.
150 # Der Wert ist beliebig und nur im Vergleich mit den Prioritaeten der anderen Dienste verwendbar.
151 # Als optionaler zweiter Parameter kann die Sortierung uebergeben werden. Falls diese nicht uebergeben wurde,
152 # wird die aktuell konfigurierte Sortierung benutzt.
153 # Sollte ein Dienst ein "priority"-Attribut tragen, dann wird die uebliche Dienst-Sortierung aufgehoben
154 # und lediglich "priority" (und gegebenenfalls separat "offset") beachtet.
155 get_service_priority() {
156 trap
'error_trap get_service_priority "$*"' EXIT
157 local service_name=
"$1"
158 local sorting=
"${2:-}"
161 # priority wird von nicht-olsr-Clients verwendet (z.B. mesh-Gateways mit oeffentlichen IPs)
164 if [ -n
"$priority" ]; then
165 # dieses Ziel traegt anscheinend keine Routing-Metrik
168 echo
"$((priority + offset))"
170 # wir benoetigen Informationen fuer Ziele mit Routing-Metriken
171 # aus Performance-Gruenden kommt die Sortierung manchmal von aussen
172 [ -z
"$sorting" ] && sorting=$(get_service_sorting)
173 if [
"$sorting" =
"etx" ] || [
"$sorting" =
"hop" ]; then
174 get_distance_with_offset
"$service_name" "$sorting"
175 elif [
"$sorting" =
"manual" ]; then
178 msg_error "Unknown sorting method for services: $sorting"
184 echo
"${base_priority:-$DEFAULT_SERVICE_RANK}" | awk
'{ print $1 * 1000 + '"$service_bias"'; }'
188 get_distance_with_offset() {
189 trap
'error_trap get_distance_with_offset "$*"' EXIT
190 local service_name=
"$1"
191 local sorting=
"${2:-}"
195 # aus Performance-Gruenden wird manchmal das sorting von aussen vorgegeben
196 [ -z
"$sorting" ] && sorting=$(get_service_sorting)
198 [ -z
"$distance" ] &&
return 0
200 [ -z
"$offset" ] && offset=0
201 if [
"$sorting" =
"etx" ]; then
202 base_value=
"$distance"
203 elif [
"$sorting" =
"hop" ]; then
206 msg_debug "get_distance_with_offset: sorting '$sorting' not implemented"
208 [ -n
"$base_value" ] && echo
"$base_value" "$offset" | awk
'{ print $1 + $2 }'
212 set_service_sorting() {
213 trap
'error_trap set_service_sorting "$*"' EXIT
214 local new_sorting=
"$1"
216 old_sorting=$(get_service_sorting)
217 [
"$old_sorting" =
"$new_sorting" ] &&
return 0
218 if [
"$new_sorting" !=
"manual" ] && [
"$new_sorting" !=
"hop" ] && [
"$new_sorting" !=
"etx" ]; then
219 msg_error "Ignoring unknown sorting method: $new_sorting"
220 trap
"" EXIT &&
return 1
222 uci set
"on-core.settings.service_sorting=$new_sorting"
223 apply_changes
"on-core"
228 # Liefere die aktuelle Sortier-Methode.
229 # Falls eine ungueltige Sortier-Methode gesetzt ist, wird diese auf die Standard-Sortierung zurueckgesetzt.
230 # Die Ausgabe dieser Funktion ist also in jedem Fall eine gueltige Sortier-Methode.
231 get_service_sorting() {
232 trap
'error_trap get_service_sorting "$*"' EXIT
234 sorting=$(uci_get
"on-core.settings.service_sorting")
235 if [
"$sorting" =
"manual" ] || [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
236 # zulaessige Sortierung
239 # unbekannte Sortierung: dauerhaft setzen
240 # keine Warnung falls die Sortierung nicht gesetzt wurde
241 [ -n
"$sorting" ] &&
msg_error "coercing unknown sorting method: $sorting -> $DEFAULT_SERVICE_SORTING"
242 uci set
"on-core.settings.service_sorting=$DEFAULT_SERVICE_SORTING"
243 echo -n
"$DEFAULT_SERVICE_SORTING"
249 ## @fn sort_services_by_priority()
250 ## @brief Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste.
251 ## @details Die Prioritätsinformation wird typischerweise für nicht-mesh-verteilte Dienste verwendet (z.B. den mesh-Tunnel).
253 trap
'error_trap sort_services_by_priority "$*"' EXIT
257 sorting=$(get_service_sorting)
258 while read -r service_name;
do
259 priority=$(get_service_priority
"$service_name" "$sorting")
260 # keine Entfernung (nicht erreichbar) -> ganz nach hinten sortieren (schmutzig, aber wohl ausreichend)
261 [ -z
"$priority" ] && priority=999999999999999
262 echo
"$priority" "$service_name"
263 done | sort -n | awk
'{print $2}'
267 ## @fn filter_reachable_services()
268 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
269 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an die Standardausgabe
270 ## weitergeleitet, falls der Dienst erreichbar sind. "Erreichbarkeit" gilt als erreicht, wenn
271 ## der Host via olsr route-bar ist oder wenn er als DNS-entdeckter Dienst eine Priorität hat
272 ## oder wenn er manuell hinzugefügt wurde.
275 while read -r service_name;
do
276 { [ -n
"$(get_service_value "$service_name
" "distance
")" ] \
277 || [ -n
"$(get_service_value "$service_name
" "priority
")" ] \
278 || [
"$(get_service_value "$service_name
" "source
")" =
"manual" ]
279 } && echo
"$service_name"
285 ## @fn filter_enabled_services()
286 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
287 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an
288 ## die Standardausgabe weitergeleitet, falls der Dienst nicht abgewählt wurde.
292 while read -r service_name;
do
294 [ -n
"$disabled" ] && uci_is_true
"$disabled" &&
continue
300 ## @fn pipe_service_attribute()
301 ## @brief Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
302 ## @param key Der Name eines Dienst-Attributs
303 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
304 ## @details Die Dienstenamen werden via Standardeingabe erwartet. Auf der Standardausgabe wird für
305 ## einen Dienst entweder ein Wert oder nichts ausgeliefert. Keine Ausgabe erfolgt, falls der
306 ## Wert des Dienste-Attributs leer ist. Bei der Eingabe von mehreren Diensten werden also
307 ## eventuell weniger Zeilen ausgegeben, als eingelesen wurden.
308 ## Falls der optionale zweite 'default'-Parameter nicht leer ist, dann wird bei einem leeren
309 ## Ergebnis stattdessen dieser Wert ausgegeben. Der 'default'-Parameter sorgt somit dafür, dass
310 ## die Anzahl der eingelesenen Zeilen in jedem Fall mit der Anzahl der ausgegebenen Zeilen
312 ## Die Ausgabe besteht aus dem Service-Namen und dem Attributinhalt (getrennt durch einen Tabulator).
315 local
default=
"${2:-}"
318 while read -r service_name;
do
319 [ -z
"$service_name" ] &&
continue
321 [ -z
"$value" ] &&
value=
"$default"
322 [ -n
"$value" ] && printf
'%s\t%s\n' "$service_name" "$value"
328 ## @fn get_services()
329 ## @param service_type (optional) ein Service-Typ
330 ## @brief Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind.
331 ## Falls kein Typ angegben wird, dann werden alle Dienste ungeachtet ihres Typs ausgegeben.
333 trap
'error_trap get_services "$*"' EXIT
334 local service_type=
"${1:-}"
335 # alle Dienste ausgeben
336 # kein Dienste-Verzeichnis? Keine Ergebnisse ...
337 [ -e
"$PERSISTENT_SERVICE_STATUS_DIR" ] ||
return 0
338 find
"$PERSISTENT_SERVICE_STATUS_DIR" -type f -size +1c -print0 \
339 | xargs -0 -r -n 1 basename \
340 |
if [ -n
"$service_type" ]; then
348 ## @fn filter_services_by_value()
349 ## @param key ein Schlüssel
350 ## @param value ein Wert
351 ## @details Als Parameter kann ein "key/value"-Schluesselpaar angegeben werden.
352 ## Nur diejenigen Dienste, auf die diese Bedingung zutrifft, werden zurueckgeliefert.
357 while read -r service_name;
do
358 [
"$value" !=
"$(get_service_value "$service_name
" "$key
")" ] || echo
"$service_name"
363 ## @fn set_service_value()
364 ## @brief Setzen eines oder mehrerer Werte fuer einen Dienst.
365 ## Je nach Schluesselname wird der Inhalt in die persistente uci- oder
366 # die volatile tmpfs-Datenbank geschrieben.
368 local service_name=
"$1"
373 [ -z
"$service_name" ] \
374 &&
msg_error "No service given for attribute change ($attribute=$value)" \
375 && trap
"" EXIT &&
return 1
376 while [
"$#" -gt 0 ];
do
380 # unverändert? ueberspringen ...
381 [
"$value" =
"$(get_service_value "$service_name
" "$attribute
")" ] &&
continue
382 if echo
"$PERSISTENT_SERVICE_ATTRIBUTES" | grep -q -w
"$attribute"; then
383 dirname=
"$PERSISTENT_SERVICE_STATUS_DIR"
385 dirname=
"$VOLATILE_SERVICE_STATUS_DIR"
387 _set_file_dict_value
"$dirname/$service_name" "$attribute" "$value"
392 ## @fn get_service_value()
393 ## @brief Auslesen eines Werts aus der Service-Datenbank.
394 ## @param key Der Name eines Dienst-Attributs
395 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
396 ## @details Falls das Attribut nicht existiert, wird ein leerer Text zurückgeliefert.
397 ## Es gibt keinen abschließenden Zeilenumbruch.
399 local service_name=
"$1"
401 local
default=
"${3:-}"
404 [ -z
"$service_name" ] \
405 &&
msg_error "No service given for attribute request ('$attribute')" \
406 && trap
"" EXIT &&
return 1
407 value=$(
_get_file_dict_value "$attribute" "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" "$VOLATILE_SERVICE_STATUS_DIR/$service_name")
408 [ -n
"$value" ] && echo -n
"$value" || echo -n
"$default"
413 # Liefere die Suffixe aller Schluessel aus der Service-Attribut-Datenbank,
414 # die mit dem gegebenen Praefix uebereinstimmen.
415 get_service_attributes() {
416 _get_file_dict_keys
"$PERSISTENT_SERVICE_STATUS_DIR/$1" "$VOLATILE_SERVICE_STATUS_DIR/$1"
420 ## @fn print_services()
421 ## @brief menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
422 ## @param service_type (optional) ein Service-Type
423 ## @returns Ausgabe der bekannten Dienste (für Menschen - nicht parsebar)
425 trap
'error_trap print_services "$*"' EXIT
431 for attribute in $(get_service_attributes
"$service_name");
do
432 printf
'\t%s=%s\n' "$attribute" "$(get_service_value "$service_name
" "$attribute
")"
439 # Speichere das angegebene uci-Praefix als eine von einem Service abhaengige Konfiguration.
440 # Dies ist sinnvoll fuer abgeleitete VPN-Konfigurationen oder Portweiterleitungen.
441 # Schnittstelle: siehe _add_service_dependency
442 service_add_uci_dependency() {
443 _add_service_dependency
"uci_dependency" "$@"
447 # Speichere einen Dateinamen als Abhaengigkeit eines Service.
448 # Dies ist sinnvoll fuer Dateien, die nicht mehr gebraucht werden, sobald der Service entfernt wird.
449 # Schnittstelle: siehe _add_service_dependency
450 service_add_file_dependency() {
451 _add_service_dependency
"file_dependency" "$@"
455 # Speichere eine Abhaengigkeit fuer einen Dienst.
456 # Parameter: Service-Name
457 # Parameter: textuelle Darstellung einer Abhaengigkeit (ohne Leerzeichen)
458 _add_service_dependency() {
459 trap
'error_trap _add_service_dependency "$*"' EXIT
460 local dependency=
"$1"
461 local service_name=
"$2"
467 # schon vorhanden -> fertig
468 [
"$dep" =
"$token" ] &&
return 0
471 if [ -z
"$deps" ]; then
480 # Entferne alle mit diesem Service verbundenen Konfigurationen (inkl. Rekonfiguration von firewall, etc.).
481 cleanup_service_dependencies() {
482 trap
'error_trap cleanup_service_dependencies "$*"' EXIT
483 local service_name=
"$1"
491 # uci-Sektionen loeschen
494 # gib die oberste config-Ebene aus - fuer spaeteres "apply_changes"
495 echo
"$dep" | cut -f 1 -
d .
496 done | sort | uniq |
while read -r branch;
do
497 apply_changes
"$branch"
504 trap
'error_trap delete_service "$*"' EXIT
505 local service_name=
"$1"
506 [ -z
"$service_name" ] &&
msg_error "No service given for deletion" && trap
"" EXIT &&
return 1
507 cleanup_service_dependencies
"$service_name"
508 rm -f
"$PERSISTENT_SERVICE_STATUS_DIR/$service_name"
509 rm -f
"$VOLATILE_SERVICE_STATUS_DIR/$service_name"
513 # Durchlaufe alle Dienste und verteile Rangziffern ohne Doppelung an alle Dienste.
514 # Die Dienst-Arten (z.B. DNS und UGW) werden dabei nicht beachtet.
515 # Die Rangziffern sind anschliessend streng monoton aufsteigend - beginnend bei 1.
516 # Falls aktuell die manuelle Sortierung aktiv ist, wird deren Reihenfolge beibehalten.
517 # Ansonsten basiert die Vergabe der Rangziffern auf der Reihenfolge entsprechend der aktuell aktiven Sortierung.
518 _distribute_service_ranks() {
528 ## @fn move_service_up()
529 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben
530 ## @param service_name der zu verschiebende Dienst
531 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
532 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
533 ## * manual: Verschiebung vor den davorplatzierten Dienst desselben Typs
534 ## * etx/hop: Reduzierung des Offsets um eins
535 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
537 trap
'error_trap move_service_up "$*"' EXIT
538 local service_name=
"$1"
542 local current_service
544 sorting=$(get_service_sorting)
545 if [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
546 # reduziere den Offset um eins
548 temp=$(echo
"$temp" | awk
'{ print $1 - 1 }')
550 elif [
"$sorting" =
"manual" ]; then
552 if [
"$current_service" =
"$service_name" ]; then
553 if [ -z
"$prev_service" ]; then
554 # es gibt keinen Dienst oberhalb des zu verschiebenden
557 # wir verschieben den Dienst ueber den davor liegenden
559 # ziehe einen halben Rang ab
560 temp=$(echo
"$temp" | awk
'{ print $1 - 0.5 }')
562 # erneuere die Rang-Vergabe
563 _distribute_service_ranks
568 prev_service=
"$current_service"
571 msg_info "Warning: [move_service_up] for this sorting method is not implemented: $sorting"
576 ## @fn move_service_down()
577 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten
578 ## @param service_name der zu verschiebende Dienst
579 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
580 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
581 ## * manual: Verschiebung hinter den dahinterliegenden Dienst desselben Typs
582 ## * etx/hop: Erhöhung des Offsets um eins
583 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
585 trap
'error_trap move_service_down "$*"' EXIT
586 local service_name=
"$1"
590 local current_service
592 sorting=$(get_service_sorting)
593 if [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
594 # reduziere den Offset um eins
596 temp=$(echo
"$temp" | awk
'{ print $1 + 1 }')
598 elif [
"$sorting" =
"manual" ]; then
600 if [
"$prev_service" =
"$service_name" ]; then
601 # wir verschieben den Dienst hinter den danach liegenden
603 # fuege einen halben Rang hinzu
604 temp=$(echo
"$temp" | awk
'{ print $1 + 0.5 }')
606 # erneuere die Rang-Vergabe
607 _distribute_service_ranks
611 prev_service=
"$current_service"
614 msg_info "Warning: [move_service_down] for this sorting method is not implemented: $sorting"
619 ## @fn move_service_top()
620 ## @brief Verschiebe einen Dienst an die Spitze der Dienst-Sortierung
621 ## @param service_name der zu verschiebende Dienst
622 ## @param service_types ein oder mehrere Dienst-Typen, auf die die Ermittlung der Dienst-Liste begrenzt werden soll (z.B. "gw")
623 ## @details Der Dienst steht anschließend direkt vor dem bisher führenden Dienst der ausgewählten Typen (falls angegeben).
624 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste an die Spitze.
626 trap
'error_trap move_service_top "$*"' EXIT
627 local service_name=
"$1"
638 sorting=$(get_service_sorting)
639 # kein top-Service oder wir sind bereits ganz oben -> Ende
640 if [ -z
"$top_service" ] || [
"$top_service" =
"$service_name" ]; then
642 elif [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
643 top_distance=$(get_distance_with_offset
"$top_service" "$sorting")
644 our_distance=$(get_distance_with_offset
"$service_name" "$sorting")
645 [ -z
"$our_distance" ] &&
msg_info "Failed to move unreachable service ('$service_name') to top" &&
return 0
647 # wir verschieben unseren Offset, auf dass wir knapp ueber
"top" stehen
648 new_offset=$(echo | awk
"{ print $current_offset + int($top_distance - $our_distance) - 1 }")
650 elif [
"$sorting" =
"manual" ]; then
651 # setze den Rang des Dienstes auf den top-Dienst minus 0.5
653 new_rank=$(echo
"$top_rank" | awk
'{ print $1 - 0.5 }')
655 # erneuere die Rang-Vergabe
656 _distribute_service_ranks
658 msg_info "Warning: [move_service_top] for this sorting method is not implemented: $sorting"
663 ## @fn get_service_detail()
664 ## @brief Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
665 ## @param service_name Name eines Diensts
666 ## @param key Name des Schlüssels
667 ## @param default dieser Wert wird zurückgeliefert, falls der Schlüssel nicht gefunden wurde
668 ## @returns den ermittelten Wert aus dem Schlüssel-Wert-Paar
670 local service_name=
"$1"
672 local
default=
"${3:-}"
675 [ -n
"$value" ] && echo -n
"$value" || echo -n
"$default"
680 ## @fn set_service_detail()
681 ## @brief Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
682 ## @param service_name Name eines Diensts
683 ## @param key Name des Schlüssels
684 ## @param value der neue Wert
685 ## @details Ein leerer Wert löscht das Schlüssel-Wert-Paar.
687 local service_name=
"$1"
691 new_details=$(
get_service_value "$service_name" "details" | replace_in_key_value_list
"$key" ":" "$value")
697 # Liefere eine Semikolon-separierte Liste von Service-Eigenschaften zurueck.
698 # Jede Eigenschaft wird folgendermassen ausgedrueckt:
699 # type|source|key[|default]
700 # Dabei sind folgende Inhalte moeglich:
701 # type: Rueckgabetyp (string, number, bool)
702 # source: Quelle der Informationen (value, detail, function, id)
703 # key: Name des Werts, des Details oder der Funktion
704 # default: Standardwert, falls das Ergebnis leer sein sollte
705 # Wahrheitswerte werden als "true" oder "false" zurueckgeliefert. Alle anderen Rueckgabetypen bleiben unveraendert.
706 # Das Ergebnis sieht folgendermassen aus:
707 # SERVICE_NAME;RESULT1;RESULT2;...
708 get_service_as_csv() {
709 local service_name=
"$1"
718 # Abbruch mit Fehler bei unbekanntem Dienst
720 echo -n
"$service_name"
721 for specification in
"$@";
do
722 rtype=$(echo
"$specification" | cut -f 1 -
d "|")
723 source=$(echo
"$specification" | cut -f 2 -
d "|")
724 key=$(echo
"$specification" | cut -f 3 -
d "|")
725 default=$(echo
"$specification" | cut -f 4- -
d "|")
726 # Ermittlung des Funktionsaufrufs
727 if [
"$source" =
"function" ]; then
728 if [
"$rtype" =
"bool" ]; then
729 "$key" "$service_name" &&
value=
"true" ||
value=
"false"
731 value=$(
"$key" "$service_name")
734 if [
"$source" =
"value" ]; then
736 elif [
"$source" =
"detail" ]; then
739 msg_error "Unknown service attribute requested: $specification"
740 echo -n
"${separator}"
743 [ -z
"$value" ] && [ -n
"$default" ] &&
value=
"$default"
744 if [
"$rtype" =
"bool" ]; then
745 # Pruefung auf wahr/falsch
746 value=$(uci_is_true
"$value" && echo
"true" || echo
"false")
749 echo -n
"${separator}${value}"
751 # mit Zeilenumbruch abschliessen
756 ## @fn get_service_log_filename()
757 ## @brief Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu") sind möglich.
758 ## @param service Name eines Dienstes.
759 ## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
760 ## @details Die Funktion stellt sicher, dass das Verzeichnis der ermittelten Log-Datei anschließend existiert.
762 trap
'error_trap get_service_log_filename "$*"' EXIT
763 local service_name=
"$1"
766 local filename=
"$service_name"
767 while [ $# -gt 0 ];
do
768 filename=
"${filename}.$1"
771 full_filename=
"$SERVICES_LOG_BASE/$(get_safe_filename "$filename
").log"
772 mkdir -p
"$(dirname "$full_filename
")"
773 echo -n
"$full_filename"
777 ## @fn get_service_log_content()
778 ## @brief Lies den Inhalt einer Log-Datei für einen Dienst aus.
779 ## @param service Name eines Dienstes.
780 ## @param max_lines maximale Anzahl der auszuliefernden Zeilen (unbegrenzt: 0)
781 ## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
782 ## @see get_service_log_filename
784 trap
'error_trap get_service_log_content "$*"' EXIT
785 local service_name=
"$1"
790 [ -e
"$log_filename" ] ||
return 0
791 if [
"$max_lines" =
"0" ]; then
792 # alle Einträge ausgeben
795 # nur die letzten Einträge ausliefern
801 ## @fn is_service_routed_via_wan()
802 ## @brief Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde.
803 ## @param service_name der Name des Diensts
804 ## @returns Exitcode == 0, falls das Routing über das WAN-Interface verläuft.
806 trap
'error_trap is_service_routed_via_wan "$*"' EXIT
807 local service_name=
"$1"
808 # verwende die übergebene TypeOfService-Angabe oder (falls vorhanden/installiert) den
809 # TOS-Wert, der fuer nicht-Tunnel-Verkehr verwendet wird (typischerweise ist dies die
810 # die Intention des Anfragenden)
811 local tos_field=
"${2:-}"
812 [ -z
"$tos_field" ] && tos_field=
"${TOS_NON_TUNNEL:-}"
814 local outgoing_device
818 if is_device_in_zone
"$outgoing_device" "$ZONE_WAN"; then
819 msg_debug "target '$host' routing through wan device: $outgoing_device"
823 msg_debug "warning: target '$host' is routed via interface '$outgoing_device' (zone '$outgoing_zone') instead of the expected WAN zone ($ZONE_WAN)"
824 trap
"" EXIT &&
return 1
829 _notify_service_success() {
830 local service_name=
"$1"
837 _notify_service_failure() {
838 local service_name=
"$1"
839 local max_fail_attempts=
"$2"
840 # erhoehe den Fehlerzaehler
842 fail_counter=$(( $(
get_service_value "$service_name" "status_fail_counter" "0") + 1))
844 # Pruefe, ob der Fehlerzaehler gross genug ist, um seinen Status auf
"fail" zu setzen.
845 if [
"$fail_counter" -ge
"$max_fail_attempts" ]; then
846 # Die maximale Anzahl von aufeinanderfolgenden fehlgeschlagenen Tests wurde erreicht:
847 # markiere ihn als kaputt.
849 elif uci_is_true
"$(get_service_value "$service_name
" "status
")"; then
850 # Bisher galt der Dienst als funktionsfaehig - wir setzen ihn auf
"neutral" bis
851 # die maximale Anzahl aufeinanderfolgender Fehler erreicht ist.
854 # Der Test gilt wohl schon als fehlerhaft - das kann so bleiben.
861 ## @fn is_trusted_service_list_outdated()
862 ## @brief Ermittle ob mindestens ein Zeitstempel für einen "trusted" Dienst vorhanden ist, der nicht älter
863 ## als die vorgegebene Aktualisierungsperiode ist.
864 ## @returns Wahr, falls kein Diest mit aktuellem Zeitstempel gefunden wurde.
866 trap
'error_trap is_trusted_service_list_outdated "$*"' EXIT
867 local most_recent_timestamp
872 # kein Zeitstempel -> dies gilt als
"veraltet"
873 [ -z
"$most_recent_timestamp" ] &&
return 0
874 # der aktuellste Zeitstempel ist zu alt
875 is_timestamp_older_minutes
"$most_recent_timestamp" "$UPDATE_TRUSTED_SERVICES_PERIOD_MINUTES" &&
return 0
876 trap
"" EXIT &&
return 1
880 retrieve_service_list_url() {
882 if ! http_request
"$url"; then
883 if echo
"$url" | grep -q
'^https://'; then
884 # The URL probably uses a certificate signed by the Opennet CA,
885 # while "on-certificate" is not installed.
886 # Thus we do not emit any warnings.
889 msg_info "Failed to retrieve list of services from $url"
895 ## @fn update_trusted_services_list()
896 ## @brief Hole die vertrauenswürdigen Dienste von signierten Opennet-Quellen.
897 ## @details Diese Dienste führen beispielsweise auf UGW-APs zur Konfiguration von Portweiterleitungen
898 ## ins Internet. Daher sind sie nur aus vertrauenswürdiger Quelle zu akzeptieren (oder manuell).
911 service_list=$(
for url in $SERVICES_LIST_URLS;
do retrieve_service_list_url
"$url";
done)
912 # leeres Ergebnis? Noch keine Internet-Verbindung? Keine Aktualisierung, keine Beraeumung ...
913 [ -z
"$service_list" ] &&
msg_info "No trusted services discovered: skipping update." &&
return
914 echo
"$service_list" | grep -v
"^#" | sed
's/\t\+/\t/g' |
while read -r line;
do
915 service_type=$(echo
"$line" | cut -f 1)
916 # falls der Dienst-Typ mit
"proxy-" beginnt, soll er weitergeleitet werden
917 if [
"${service_type#$RELAYABLE_SERVICE_PREFIX}" =
"$service_type" ]; then
923 # entferne das Praefix
925 scheme=$(echo
"$line" | cut -f 2)
926 host=$(echo
"$line" | cut -f 3)
927 port=$(echo
"$line" | cut -f 4)
928 protocol=$(echo
"$line" | cut -f 5)
929 priority=$(echo
"$line" | cut -f 6)
930 details=$(echo
"$line" | cut -f 7-)
931 service_name=$(
notify_service "trusted" "$service_type" "$scheme" "$host" "$port" "$protocol" "/" "$details")
933 if [ -n
"$is_proxy" ]; then
934 if is_function_available
"pick_local_service_relay_port"; then
935 pick_local_service_relay_port
"$service_name" >/dev/
null
940 # veraltete Dienste entfernen
942 min_timestamp=$(($(get_uptime_minutes) - $(get_on_core_default
"trusted_service_expire_minutes")))
943 # falls die uptime kleiner ist als die Verfallszeit, dann ist ein Test sinnfrei
944 if [
"$min_timestamp" -gt 0 ]; then
947 # Wurde der Service erst vor kurzem aktualisiert? Sonst loeschen ...
948 [
"$timestamp" -ge
"$min_timestamp" ] || delete_service
"$service_name"
951 # aktualisiere DNS- und NTP-Dienste
952 apply_changes on-core
956 ## @fn run_cyclic_service_tests()
957 ## @brief Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist
958 ## @param test_function der Name der zu verwendenden Test-Funktion für einen Dienst (z.B. "verify_vpn_connection")
959 ## @param test_period_minutes Wiederholungsperiode der Dienst-Prüfung
960 ## @param max_fail_attempts Anzahl von Fehlversuchen, bis ein Dienst von "gut" oder "unklar" zu "schlecht" wechselt
961 ## @details Die Diensteanbieter werden in der Reihenfolge ihrer Priorität geprüft.
962 ## Nach dem ersten Durchlauf dieser Funktion sollte typischerweise der nächstgelegene nutzbare Dienst
963 ## als funktionierend markiert sein.
964 ## Falls nach dem Durchlauf aller Dienste keiner positiv getestet wurde (beispielsweise weil alle Zeitstempel zu frisch sind),
965 ## dann wird in jedem Fall der älteste nicht-funktionsfähige Dienst getestet. Dies minimiert die Ausfallzeit im
966 ## Falle einer globalen Nicht-Erreichbarkeit aller Dienstenanbieter ohne auf den Ablauf der Test-Periode warten zu müssen.
967 ## @attention Seiteneffekt: die Zustandsinformationen des getesteten Diensts (Status, Test-Zeitstempel) werden verändert.
969 trap
'error_trap test_openvpn_service_type "$*"' EXIT
970 local test_function=
"$1"
971 local test_period_minutes=
"$2"
972 local max_fail_attempts=
"$3"
981 if [ -z
"$status" ] || is_timestamp_older_minutes
"$timestamp" "$test_period_minutes"; then
982 if "$test_function" "$service_name"; then
983 msg_debug "service $service_name successfully tested"
984 _notify_service_success
"$service_name"
985 # wir sind fertig - keine weiteren Tests
986 # Da "return" aufgrund der Pipe nicht die gesamte Funktion beenden
987 # würde, senden wir stattdessen die Markierung fuer "keine Tests
988 # noetig" und beenden die Schleife.
989 printf
'%s %s\n' "-1" "$service_name"
992 msg_debug "failed to verify $service_name"
993 _notify_service_failure
"$service_name" "$max_fail_attempts"
995 elif uci_is_false
"$status"; then
996 # Junge "kaputte" Dienste sind potentielle Kandidaten fuer einen vorzeitigen Test, falls
997 # ansonsten kein Dienst positiv getestet wurde.
998 echo
"$timestamp $service_name"
1000 # funktionsfaehige "alte" Dienste - es gibt nichts fuer sie zu tun
1001 # Wir sortieren sie nach ganz oben, um bei Existenz eines lauffaehigen Diensts
1002 # keine unnoetigen Tests von nicht-funktionierenden Hosts durchzufuehren.
1003 printf
'%s %s\n' "-1" "$service_name"
1005 done | sort -n |
while read -r timestamp service_name;
do
1006 # Mit dem Zeitstempel "-1" sind funktionierende Dienste markiert. Wir brauchen also keine
1008 [
"$timestamp" =
"-1" ] &&
return 0
1009 # Hier landen wir nur, falls alle defekten Gateways zu jung fuer einen Test waren und
1010 # gleichzeitig kein Dienst erfolgreich getestet wurde bzw. als erfolgreich gilt.
1011 # Dies stellt sicher, dass nach einer kurzen Nicht-Erreichbarkeit aller Gateways (z.B. olsr-Ausfall)
1012 # relativ schnell wieder ein funktionierender Gateway gefunden wird, obwohl alle Test-Zeitstempel noch recht
1014 msg_debug "there is no service to be tested - thus we pick the service with the oldest test timestamp: $service_name"
1015 if "$test_function" "$service_name"; then
1016 _notify_service_success
"$service_name"
1018 _notify_service_failure
"$service_name" "$max_fail_attempts"
1020 # wir wollen nur genau einen Test durchfuehren
1025 # Ende der Doku-Gruppe
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
msg_error(message)
Die Fehlermeldungen werden in die Standard-Fehlerausgabe und ins syslog geschrieben.
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
get_zone_of_device(interface)
Ermittle die Zone eines physischen Netzwerk-Interfaces.
get_hop_count_and_etx(host)
Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
get_target_route_interface(target)
Ermittle das Netzwerkinterface, über den der Verkehr zu einem Ziel laufen würde.
sort_services_by_priority()
Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste.
run_cyclic_service_tests(test_function)
Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist.
get_service_detail(service_name, key, default)
Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts.
get_service_log_content(service, max_lines)
Lies den Inhalt einer Log-Datei für einen Dienst aus.
_get_local_bias_for_service()
Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1, service_name) - abhängig von der l...
pipe_service_attribute(key, default)
Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
move_service_down(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten.
get_service_name()
Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
is_service_routed_via_wan(service_name)
Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde.
is_existing_service(service_name)
Prüfe ob ein Service existiert.
filter_enabled_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
set_service_value()
Setzen eines oder mehrerer Werte fuer einen Dienst. Je nach Schluesselname wird der Inhalt in die per...
is_trusted_service_list_outdated()
Ermittle ob mindestens ein Zeitstempel für einen "trusted" Dienst vorhanden ist, der nicht älter als ...
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird,...
get_service_value(key, default)
Auslesen eines Werts aus der Service-Datenbank.
set_service_detail(service_name, key, value)
Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts.
notify_services(source)
Siehe "notify_service" - jedoch effizienter im Umgang mit einer großen Anzahl von Diensten.
move_service_top(service_name, service_types)
Verschiebe einen Dienst an die Spitze der Dienst-Sortierung.
get_service_log_filename()
Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu",...
notify_service()
Aktualisiere den Zeitstempel und die Entfernung (etx, service, scheme, host, port,...
print_services(service_type)
menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
update_trusted_services_list()
Hole die vertrauenswürdigen Dienste von signierten Opennet-Quellen.
move_service_up(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben.
filter_services_by_value(key, value)
filter_reachable_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
uci_delete(uci_path)
Lösche ein UCI-Element.
set eu on function print_services services log for dir in etc on services d var on services volatile d