|
|
|
@@ -545,6 +545,30 @@ def render_config(
|
|
|
|
|
if not int(row["is_standard"] or 0)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Das Verzeichnis: Code, Name und Typ der eigenen Optionen, die dieser
|
|
|
|
|
# Server tatsaechlich sendet. Es wird nicht gespeichert, sondern aus dem
|
|
|
|
|
# Katalog abgeleitet -- gespeichert waere es eine zweite Wahrheit.
|
|
|
|
|
belegte_codes = {int(row["code"]) for row in option_rows}
|
|
|
|
|
gesendete_ids = set(global_values) | {
|
|
|
|
|
option_id for werte in device_values.values() for option_id in werte
|
|
|
|
|
}
|
|
|
|
|
verzeichnis = " ".join(
|
|
|
|
|
f"{int(row['code'])}:{row['name']}:{row['type']}"
|
|
|
|
|
for row in option_rows
|
|
|
|
|
if not int(row["is_standard"] or 0) and int(row["id"]) in gesendete_ids
|
|
|
|
|
)
|
|
|
|
|
if verzeichnis and KATALOG_CODE not in belegte_codes:
|
|
|
|
|
custom_defs.append(
|
|
|
|
|
{"name": KATALOG_NAME, "code": KATALOG_CODE, "type": "string", "space": "dhcp4"}
|
|
|
|
|
)
|
|
|
|
|
global_option_data.append({"name": KATALOG_NAME, "data": verzeichnis})
|
|
|
|
|
elif verzeichnis:
|
|
|
|
|
warnings.append(
|
|
|
|
|
f"Code {KATALOG_CODE} ist von einer eigenen Option belegt | das Verzeichnis für "
|
|
|
|
|
"die Clients wird nicht mitgesendet. Neue Optionen sind dort dann nur unter "
|
|
|
|
|
"ihrer Nummer zu sehen, bis die Konfiguration erneut verteilt wird."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
config = {
|
|
|
|
|
"Dhcp4": {
|
|
|
|
|
"interfaces-config": {"interfaces": sorted(interfaces)},
|
|
|
|
@@ -729,6 +753,26 @@ def read_leases(sysops: SysOps, *, path: str = LEASE_FILE) -> list[Lease]:
|
|
|
|
|
return sorted(latest.values(), key=lambda entry: tuple(int(p) for p in entry.address.split(".") if p.isdigit()))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#: Der Bereich, den DHCP fuer eigene Optionen vorsieht (RFC 2132: "site
|
|
|
|
|
#: specific"). **Nur** hier hinein gehoeren eigene Codes, und zwar nicht aus
|
|
|
|
|
#: Ordnungsliebe: systemd-networkd hebt eine unbekannte Option ausschliesslich
|
|
|
|
|
#: dann auf, wenn ihr Code in diesem Bereich liegt. Alles darunter wirft es
|
|
|
|
|
#: weg, ohne dass irgendwo etwas danebengeht.
|
|
|
|
|
PRIVATBEREICH = range(224, 255)
|
|
|
|
|
|
|
|
|
|
#: Code und Name des Verzeichnisses, das der Server ueber sich selbst mitsendet.
|
|
|
|
|
#:
|
|
|
|
|
#: **Warum es das gibt.** Auf der Leitung reisen nur Code, Laenge und Bytes --
|
|
|
|
|
#: kein Name, kein Typ. Ein Client kann darum nicht wissen, dass 225 "url"
|
|
|
|
|
#: heisst und Text enthaelt. Bisher bekam er die Zuordnung beim Verteilen
|
|
|
|
|
#: mit; kam spaeter eine Option dazu, musste man erneut verteilen. Also sendet
|
|
|
|
|
#: der Server sein Verzeichnis einfach mit: "225:url:string 226:monitor:...".
|
|
|
|
|
#: Der Uebersetzer auf dem Client liest es bei jeder Lease neu -- eine neue
|
|
|
|
|
#: Option ist damit sofort unter ihrem Namen lesbar, ohne den Client
|
|
|
|
|
#: anzufassen.
|
|
|
|
|
KATALOG_CODE = 224
|
|
|
|
|
KATALOG_NAME = "tesm-katalog"
|
|
|
|
|
|
|
|
|
|
#: Optionen, die jeder DHCP-Client von sich aus erfragt. Sie in die
|
|
|
|
|
#: Client-Konfiguration zu schreiben waere Ballast -- und wer Ballast liest,
|
|
|
|
|
#: liest irgendwann gar nicht mehr.
|
|
|
|
@@ -838,22 +882,71 @@ cat /run/tesm-dhcp-options.env 2>/dev/null || true
|
|
|
|
|
#: Bytes. Dass 225 "url" heisst und eine Zeichenkette ist, steht allein im
|
|
|
|
|
#: Katalog dieses Servers -- systemd kann es nicht wissen und legt deshalb
|
|
|
|
|
#: Hex unter der Nummer ab. Diese Zuordnung reist mit.
|
|
|
|
|
UEBERSETZER = """#!/bin/sh
|
|
|
|
|
UEBERSETZER = r"""#!/bin/sh
|
|
|
|
|
# Von TESM erzeugt. Schreibt die eigenen DHCP-Optionen im Klartext nach
|
|
|
|
|
# /run/tesm-dhcp-options.env -- eine Zeile "name=wert" je Option.
|
|
|
|
|
#
|
|
|
|
|
# Uebersetzt wird **alles**, was in der Lease steht, nicht eine feste Liste:
|
|
|
|
|
# Namen und Typen kommen aus dem Verzeichnis, das der Server als Option
|
|
|
|
|
# @KATALOG@ mitsendet. Eine neue Option ist damit sofort unter ihrem Namen
|
|
|
|
|
# lesbar, ohne dass hier etwas geaendert werden muesste. Fehlt das
|
|
|
|
|
# Verzeichnis, gilt die Zuordnung vom Tag des Verteilens; fehlt auch die,
|
|
|
|
|
# heisst die Option nach ihrer Nummer.
|
|
|
|
|
set -e
|
|
|
|
|
ziel=/run/tesm-dhcp-options.env
|
|
|
|
|
ersatz="@PAARE@"
|
|
|
|
|
|
|
|
|
|
schnittstelle=$(ip -o -4 route show default 2>/dev/null | awk '{print $5; exit}')
|
|
|
|
|
[ -n "$schnittstelle" ] || exit 0
|
|
|
|
|
lease=/run/systemd/netif/leases/$(cat /sys/class/net/"$schnittstelle"/ifindex 2>/dev/null)
|
|
|
|
|
[ -f "$lease" ] || exit 0
|
|
|
|
|
|
|
|
|
|
text() { /usr/bin/printf "$(echo "$1" | sed 's/../@BS@x&/g')"; }
|
|
|
|
|
|
|
|
|
|
entziffere() {
|
|
|
|
|
# $1 = Hexfolge, $2 = Typ. Ohne den Typ waere jede Adresse Buchstabensalat:
|
|
|
|
|
# eine IPv4-Adresse reist als vier rohe Bytes, nicht als "192.168.0.1".
|
|
|
|
|
case "$2" in
|
|
|
|
|
ipv4-address)
|
|
|
|
|
set -- $(echo "$1" | sed 's/../& /g')
|
|
|
|
|
[ $# -ge 4 ] || return 0
|
|
|
|
|
printf '%d.%d.%d.%d' "0x$1" "0x$2" "0x$3" "0x$4"
|
|
|
|
|
;;
|
|
|
|
|
uint8|uint16|uint32)
|
|
|
|
|
printf '%d' "0x$1"
|
|
|
|
|
;;
|
|
|
|
|
boolean)
|
|
|
|
|
case "$1" in 00) printf 'nein' ;; *) printf 'ja' ;; esac
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
text "$1"
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nachschlagen() {
|
|
|
|
|
# Erst im mitgesendeten Verzeichnis, dann in der Zuordnung vom Verteilen.
|
|
|
|
|
for eintrag in $verzeichnis $ersatz; do
|
|
|
|
|
case "$eintrag" in
|
|
|
|
|
"$1":*)
|
|
|
|
|
rest=${eintrag#*:}
|
|
|
|
|
echo "${rest%%:*} ${rest#*:}"
|
|
|
|
|
return 0
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
echo "option_$1 string"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verzeichnis=$(text "$(sed -n 's/^OPTION_@KATALOG@=//p' "$lease" | head -n1)")
|
|
|
|
|
|
|
|
|
|
zwischen=$(mktemp)
|
|
|
|
|
for paar in @PAARE@; do
|
|
|
|
|
code=${paar%%:*}
|
|
|
|
|
name=${paar#*:}
|
|
|
|
|
roh=$(sed -n "s/^OPTION_$code=//p" "$lease" | head -n1)
|
|
|
|
|
[ -n "$roh" ] || continue
|
|
|
|
|
wert=$(/usr/bin/printf "$(echo "$roh" | sed 's/../@BS@x&/g')")
|
|
|
|
|
sed -n 's/^OPTION_\([0-9]*\)=\(.*\)$/\1 \2/p' "$lease" | while read -r code hex; do
|
|
|
|
|
[ "$code" = "@KATALOG@" ] && continue
|
|
|
|
|
angabe=$(nachschlagen "$code")
|
|
|
|
|
name=${angabe%% *}
|
|
|
|
|
typ=${angabe#* }
|
|
|
|
|
wert=$(entziffere "$hex" "$typ")
|
|
|
|
|
echo "$name=$wert" >> "$zwischen"
|
|
|
|
|
done
|
|
|
|
|
mv -f "$zwischen" "$ziel"
|
|
|
|
@@ -935,10 +1028,19 @@ def client_konfiguration(conn: sqlite3.Connection, stil: str) -> dict[str, Any]:
|
|
|
|
|
"#",
|
|
|
|
|
"# Die Werte stehen danach in /run/systemd/netif/leases/<ifindex>",
|
|
|
|
|
"# als OPTION_<code>= -- hexkodiert, weil systemd fremde Optionen nicht",
|
|
|
|
|
"# deutet.",
|
|
|
|
|
"# deutet. Den Klartext schreibt der mitgelieferte Uebersetzer nach",
|
|
|
|
|
"# /run/tesm-dhcp-options.env.",
|
|
|
|
|
"#",
|
|
|
|
|
"# Angefordert wird der ganze Bereich 224-254, damit eine spaeter",
|
|
|
|
|
"# hinzugefuegte Option ohne Zutun ankommt. Eigene Optionen ausserhalb",
|
|
|
|
|
"# dieses Bereichs hebt systemd-networkd nicht auf.",
|
|
|
|
|
"[DHCPv4]",
|
|
|
|
|
]
|
|
|
|
|
zeilen.append("RequestOptions=" + " ".join(str(code) for code in codes))
|
|
|
|
|
# Angefordert wird der **ganze** eigene Bereich, nicht nur das, was
|
|
|
|
|
# heute gesendet wird: sonst muesste man nach jeder neuen Option zu
|
|
|
|
|
# jedem Client. Ein Client bekommt trotzdem nur, was der Server hat.
|
|
|
|
|
angefragt = sorted(set(PRIVATBEREICH) | set(codes))
|
|
|
|
|
zeilen.append("RequestOptions=" + " ".join(str(code) for code in angefragt))
|
|
|
|
|
nachher = "networkctl reload && networkctl reconfigure <Schnittstelle>"
|
|
|
|
|
elif stil == "dhcpcd":
|
|
|
|
|
zeilen = [
|
|
|
|
@@ -1045,8 +1147,14 @@ def _uebernahme_skript(stil: str, inhalt: str, benannt: list[dict[str, Any]] | N
|
|
|
|
|
benannt = benannt or []
|
|
|
|
|
|
|
|
|
|
if stil == "networkd":
|
|
|
|
|
paare = " ".join(f"{eintrag['code']}:{eintrag['name']}" for eintrag in benannt)
|
|
|
|
|
uebersetzer = UEBERSETZER.replace("@PAARE@", paare).replace("@BS@", "\\\\") # sed gibt aus zwei Zeichen eines aus
|
|
|
|
|
paare = " ".join(
|
|
|
|
|
f"{eintrag['code']}:{eintrag['name']}:{eintrag['type']}" for eintrag in benannt
|
|
|
|
|
)
|
|
|
|
|
uebersetzer = (
|
|
|
|
|
UEBERSETZER.replace("@PAARE@", paare)
|
|
|
|
|
.replace("@KATALOG@", str(KATALOG_CODE))
|
|
|
|
|
.replace("@BS@", "\\\\") # sed gibt aus zwei Zeichen eines aus
|
|
|
|
|
)
|
|
|
|
|
return (
|
|
|
|
|
NETWORKD_SKRIPT.replace("@FRACHT@", fracht)
|
|
|
|
|
.replace("@UEBERSETZER@", _b64(uebersetzer))
|
|
|
|
|