aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-08-05 21:08:05 +0200
committerHimbeer <himbeer@disroot.org>2024-08-05 21:08:05 +0200
commit8a610131b32b44e9f4acc99e46a4f453a6c00e10 (patch)
tree0058efc75e5c1ac77535b74aed168b5ff062f802
parent51ca2c8568fa4a239950d0f547f1c6eb49146e0d (diff)
Add WAN DHCPv6 information and control (DUID setting + rebind)
-rw-r--r--src-tauri/Cargo.lock2
-rw-r--r--src-tauri/Cargo.toml2
-rw-r--r--src-tauri/src/main.rs358
-rw-r--r--src/styles.css4
-rw-r--r--src/wan.html113
-rw-r--r--src/wan.js117
6 files changed, 592 insertions, 4 deletions
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index aab5a57..e2638a0 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -2496,6 +2496,8 @@ dependencies = [
name = "rsdsl_manager"
version = "0.0.0"
dependencies = [
+ "chrono",
+ "hex",
"reqwest",
"serde",
"serde_json",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index f555043..933089c 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -15,6 +15,8 @@ tauri = { version = "1", features = [ "dialog-message", "dialog-ask", "shell-ope
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12.5", features = ["json"] }
+chrono = { version = "0.4.38", default-features = false, features = ["alloc", "clock"] }
+hex = "0.4.3"
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 77b3a8f..b555123 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,11 +1,13 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-use std::net::{Ipv4Addr, Ipv6Addr};
+use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
use std::sync::Mutex;
+use std::time::{Duration, SystemTime};
use tauri::State;
+use chrono::{DateTime, Local};
use reqwest::{Client, Response};
use reqwest::{StatusCode, Url};
use serde::{Deserialize, Serialize};
@@ -104,6 +106,170 @@ struct Ipv6Connection {
raddr: Ipv6Addr,
}
+#[derive(Debug, Serialize)]
+struct Dhcpv6Status {
+ timestamp: String,
+ srvaddr: String,
+ srvid: String,
+ t1: String,
+ t2: String,
+ prefix: String,
+ wanaddr: String,
+ preflft: String,
+ validlft: String,
+ dns1: String,
+ dns2: String,
+ aftr: String,
+}
+
+impl Dhcpv6Status {
+ fn no_lease() -> Self {
+ Self::with_all(String::from(
+ "✖ Keine Lease vorhanden (erster Systemstart oder Stromausfall?)",
+ ))
+ }
+
+ fn with_all(message: String) -> Self {
+ Self {
+ timestamp: message.clone(),
+ srvaddr: message.clone(),
+ srvid: message.clone(),
+ t1: message.clone(),
+ t2: message.clone(),
+ prefix: message.clone(),
+ wanaddr: message.clone(),
+ preflft: message.clone(),
+ validlft: message.clone(),
+ dns1: message.clone(),
+ dns2: message.clone(),
+ aftr: message,
+ }
+ }
+}
+
+impl From<Dhcpv6Lease> for Dhcpv6Status {
+ fn from(lease: Dhcpv6Lease) -> Self {
+ let validity = if lease.is_valid() { "✅" } else { "❌" };
+
+ Self {
+ timestamp: format!(
+ "{} {}",
+ validity,
+ DateTime::<Local>::from(lease.timestamp).format("%d.%m.%Y %H:%M:%S UTC%Z")
+ ),
+ srvaddr: if lease.server
+ == SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0))
+ {
+ String::from("ff02::1:2 (Alle DHCPv6-Server, da der Server keine spezifische Adresse angegeben hat)")
+ } else {
+ format!("{}", lease.server)
+ },
+ srvid: hex::encode(lease.server_id),
+ t1: if lease.t1 == 0 {
+ String::from("Sofort")
+ } else if lease.t1 == u32::MAX {
+ String::from("Nie")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.t1.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "Alle {} Sekunden ({} Sekunden verbleibend)",
+ lease.t1, remaining_secs
+ )
+ },
+ t2: if lease.t2 == 0 {
+ String::from("Sofort")
+ } else if lease.t2 == u32::MAX {
+ String::from("Nie")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.t2.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "Alle {} Sekunden ({} Sekunden verbleibend)",
+ lease.t2, remaining_secs
+ )
+ },
+ prefix: format!("{}/{}", lease.prefix, lease.len),
+ wanaddr: format!("{}1/64", lease.prefix),
+ preflft: if lease.preflft == 0 {
+ String::from("⚠ Niemals für neue Verbindungen verwenden")
+ } else if lease.preflft == u32::MAX {
+ String::from("Unendlich")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.preflft.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "{} Sekunden ({} Sekunden verbleibend)",
+ lease.preflft, remaining_secs
+ )
+ },
+ validlft: if lease.validlft == 0 {
+ String::from("⚠ Internetanbieter verlangte manuell sofortigen Verfall")
+ } else if lease.validlft == u32::MAX {
+ String::from("Unendlich")
+ } else {
+ let remaining_secs = std::cmp::max(
+ (Duration::from_secs(lease.validlft.into())
+ - lease.timestamp.elapsed().unwrap_or(Duration::ZERO))
+ .as_secs(),
+ 0,
+ );
+ format!(
+ "{} Sekunden ({} Sekunden verbleibend)",
+ lease.validlft, remaining_secs
+ )
+ },
+ dns1: format!("{}", lease.dns1),
+ dns2: format!("{}", lease.dns2),
+ aftr: match lease.aftr {
+ Some(aftr) => format!("🟢 Aktiviert | Tunnel-Endpunkt (AFTR): {}", aftr),
+ None => String::from("⚪ Deaktiviert"),
+ },
+ }
+ }
+}
+
+#[derive(Debug, Deserialize)]
+struct Dhcpv6Lease {
+ timestamp: std::time::SystemTime,
+ server: SocketAddr,
+ server_id: Vec<u8>,
+ t1: u32,
+ t2: u32,
+ prefix: Ipv6Addr,
+ len: u8,
+ preflft: u32,
+ validlft: u32,
+ dns1: Ipv6Addr,
+ dns2: Ipv6Addr,
+ aftr: Option<String>,
+}
+
+#[derive(Debug, Serialize)]
+struct Duid {
+ duid: String,
+ status_text: String,
+}
+
+impl Dhcpv6Lease {
+ fn is_valid(&self) -> bool {
+ let expiry = self.timestamp + Duration::from_secs(self.validlft.into());
+ SystemTime::now() < expiry
+ }
+}
+
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
async fn connect(
@@ -421,6 +587,191 @@ async fn handle_connection_status_response(response: Response) -> ConnectionStat
}
}
+#[tauri::command]
+async fn dhcpv6_status(state: State<'_, Mutex<Session>>) -> Result<Dhcpv6Status, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(Dhcpv6Status::with_all(String::from(
+ "❗ Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ )))
+ }
+ };
+
+ let response = client
+ .get(instance.url.join("/data/read").unwrap())
+ .query(&[("path", "/data/dhcp6.lease")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_dhcpv6_status_response(response).await,
+ Err(e) => Dhcpv6Status::with_all(format!("❗ Abfrage fehlgeschlagen: {}", e)),
+ })
+}
+
+async fn handle_dhcpv6_status_response(response: Response) -> Dhcpv6Status {
+ let status = response.status();
+ if status.is_success() {
+ match response.json::<Dhcpv6Lease>().await {
+ Ok(lease) => Dhcpv6Status::from(lease),
+ Err(e) => Dhcpv6Status::with_all(format!("❗ Fehlerhafte Leasedatei. Fehler: {}", e)),
+ }
+ } else if status == StatusCode::UNAUTHORIZED {
+ Dhcpv6Status::with_all(String::from(
+ "❗ Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!",
+ ))
+ } else if status == StatusCode::NOT_FOUND {
+ Dhcpv6Status::no_lease()
+ } else if status.is_client_error() {
+ Dhcpv6Status::with_all(format!("❗ Clientseitiger Fehler: {}", status))
+ } else if status.is_server_error() {
+ Dhcpv6Status::with_all(format!("❗ Serverseitiger Fehler: {}", status))
+ } else {
+ Dhcpv6Status::with_all(format!("❗ Unerwarteter Statuscode: {}", status))
+ }
+}
+
+#[tauri::command]
+async fn load_duid(state: State<'_, Mutex<Session>>) -> Result<Duid, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(Duid {
+ duid: String::new(),
+ status_text: String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ),
+ })
+ }
+ };
+
+ let response = client
+ .get(instance.url.join("/data/read").unwrap())
+ .query(&[("path", "/data/dhcp6.duid")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_load_duid_response(response).await,
+ Err(e) => Duid {
+ duid: String::new(),
+ status_text: format!("Abruf des aktuellen Client-DUID fehlgeschlagen: {}", e),
+ },
+ })
+}
+
+async fn handle_load_duid_response(response: Response) -> Duid {
+ let status = response.status();
+ if status.is_success() {
+ let bytes = match response.bytes().await {
+ Ok(bytes) => bytes,
+ Err(e) => {
+ return Duid {
+ duid: String::new(),
+ status_text: format!(
+ "Keine Rohdaten vom Server erhalten, bitte Neustart durchführen. Fehler: {}",
+ e
+ ),
+ }
+ }
+ };
+
+ Duid {
+ duid: hex::encode(bytes),
+ status_text: String::new(),
+ }
+ } else if status == StatusCode::UNAUTHORIZED {
+ Duid {
+ duid: String::new(),
+ status_text: String::from(
+ "Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!",
+ ),
+ }
+ } else if status == StatusCode::NOT_FOUND {
+ Duid{
+ duid:String::new(),
+ status_text:String::from("Kein Client-DUID gespeichert (erster Systemstart oder Stromausfall?), wird bei Bedarf zufällig generiert und gespeichert"),
+ }
+ } else if status.is_client_error() {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Clientseitiger Fehler: {}", status),
+ }
+ } else if status.is_server_error() {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Serverseitiger Fehler: {}", status),
+ }
+ } else {
+ Duid {
+ duid: String::new(),
+ status_text: format!("Unerwarteter Statuscode: {}", status),
+ }
+ }
+}
+
+#[tauri::command]
+async fn change_duid(duid: String, state: State<'_, Mutex<Session>>) -> Result<String, ()> {
+ let (client, instance) = {
+ let state = state.lock().unwrap();
+ (state.client.clone(), state.instance.clone())
+ };
+ let instance = match instance {
+ Some(instance) => instance,
+ None => {
+ return Ok(String::from(
+ "Keine Instanz ausgewählt, bitte melden Sie sich neu an!",
+ ))
+ }
+ };
+
+ let bytes = match hex::decode(&duid) {
+ Ok(bytes) => bytes,
+ Err(e) => {
+ return Ok(format!(
+ "Eingabe ist keine gültige Hexadezimalsequenz: {}",
+ e
+ ))
+ }
+ };
+
+ let response = client
+ .post(instance.url.join("/data/write").unwrap())
+ .query(&[("path", "/data/dhcp6.duid")])
+ .basic_auth("rustkrazy", Some(&instance.password))
+ .body(bytes)
+ .send();
+
+ Ok(match response.await {
+ Ok(response) => handle_change_duid_response(response),
+ Err(e) => format!("Änderung fehlgeschlagen: {}", e),
+ })
+}
+
+fn handle_change_duid_response(response: Response) -> String {
+ let status = response.status();
+ if status.is_success() {
+ String::from("Änderung erfolgreich")
+ } else if status == StatusCode::UNAUTHORIZED {
+ String::from("Ungültiges Verwaltungspasswort, bitte melden Sie sich neu an!")
+ } else if status.is_client_error() {
+ format!("Clientseitiger Fehler: {}", status)
+ } else if status.is_server_error() {
+ format!("Serverseitiger Fehler: {}", status)
+ } else {
+ format!("Unerwarteter Statuscode: {}", status)
+ }
+}
+
fn main() {
tauri::Builder::default()
.manage(Mutex::new(Session {
@@ -436,7 +787,10 @@ fn main() {
load_wan_credentials,
change_wan_credentials,
kill,
- connection_status
+ connection_status,
+ dhcpv6_status,
+ load_duid,
+ change_duid
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
diff --git a/src/styles.css b/src/styles.css
index f0cae8d..fe1e0e2 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -98,6 +98,10 @@ input {
width: 30em;
}
+.row label {
+ padding-right: 0.5em;
+}
+
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
diff --git a/src/wan.html b/src/wan.html
index b8b533b..fdfc943 100644
--- a/src/wan.html
+++ b/src/wan.html
@@ -116,6 +116,119 @@
<p id="credentials-status">Warte auf Initialisierung...</p>
</fieldset>
</form>
+
+ <br />
+
+ <form id="dhcpv6-form">
+ <fieldset>
+ <legend>DHCPv6-Client</legend>
+
+ <div class="row">
+ <label for="dhcpv6-timestamp" form="dhcpv6-form">Letzte Gültigkeitsbestätigung:</label>
+ <output id="dhcpv6-timestamp">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-srvaddr" form="dhcpv6-form">Serveradresse für weitere Anfragen:</label>
+ <output id="dhcpv6-srvaddr">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-srvid" form="dhcpv6-form">Server-DUID (Hexadezimal):</label>
+ <output id="dhcpv6-srvid">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-t1" form="dhcpv6-form">Verlängerung (Renew):</label>
+ <output id="dhcpv6-t1">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-t2" form="dhcpv6-form">Verlängerung mit Serversuche (Rebind):</label>
+ <output id="dhcpv6-t2">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-prefix" form="dhcpv6-form">Präfix:</label>
+ <output id="dhcpv6-prefix">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-wanaddr" form="dhcpv6-form">WAN-Adresse (aus Präfix abgeleitet):</label>
+ <output id="dhcpv6-wanaddr">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-preflft" form="dhcpv6-form">Verwendungsdauer:</label>
+ <output id="dhcpv6-preflft">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-validlft" form="dhcpv6-form">Gültigkeitsdauer:</label>
+ <output id="dhcpv6-validlft">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-dns1" form="dhcpv6-form">Primärer DNS-Server (nur für DS-Lite AFTR):</label>
+ <output id="dhcpv6-dns1">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-dns2" form="dhcpv6-form">Sekundärer DNS-Server (nur für DS-Lite AFTR):</label>
+ <output id="dhcpv6-dns2">❓ Abfrage...</output>
+ </div>
+
+ <div class="row">
+ <label for="dhcpv6-aftr" form="dhcpv6-form">DS-Lite:</label>
+ <output id="dhcpv6-aftr">❓ Abfrage...</output>
+ </div>
+
+ <br />
+
+ <div class="row">
+ <button id="dhcpv6-kill">🔄 Verlängerung mit Serversuche</button>
+ </div>
+
+ <p>Information: Nur bei einem grünen Haken bei "Letzte
+ Gültigkeitsbestätigung" ist das Präfix gültig und eine Verbindung mit
+ dem IPv6-Internet hergestellt (die reine Einwahl, s. oben, genügt
+ nicht). Bei abgelaufener Verwendungsdauer sollen laut Standard keine
+ neuen Verbindungen mehr aufgebaut werden. Diese Implementation
+ ignoriert diese Begrenzung, um im (anbieterseitigen
+ Software-)Störungsfall eine höhere Verfügbarkeit zu erreichen. Eine
+ Verlängerung mit Serversuche startet den DHCPv6-Client vollständig
+ neu und dauert ca. 30 Sekunden, sollte die Internetverbindung aber
+ nicht beeinträchtigen. Sie kann Abhilfe schaffen, wenn die
+ IPv6-Einwahl gelingt, aber keine Verbindung zum IPv6-Internet besteht
+ oder in dieser Übersicht das Präfix als ungültig angezeigt wird.</p>
+
+ <label for="dhcpv6-duid" form="dhcpv6-form">Client-DUID (Hexadezimal):</label>
+ <div class="row">
+ <input id="dhcpv6-duid" />
+ </div>
+
+ <br />
+
+ <div class="row">
+ <button id="dhcpv6-submit" type="submit">Client-DUID ändern</button>
+ </div>
+
+ <p id="dhcpv6-status">Warte auf Initialisierung...</p>
+
+ <p>Information: Der Client-DUID (<b>D</b>HCP <b>U</b>nique
+ <b>ID</b>entifier) ist ein eindeutiger Wert, den der Internetanbieter
+ für die IPv6-Präfixvergabe verwenden kann. Dieser Wert sollte
+ konstant sein, um das Präfix konstant zu halten und somit weniger oft
+ die Einträge des dynamisches DNS (für Serververfügbarkeit)
+ aktualisieren zu müssen. Die eingesetzte Implementation nutzt den
+ DUID-UUID-Untertyp, der hier eingegebene Wert bezieht sich aber auf
+ den vollständigen DUID, nicht nur auf den UUID-Bestandteil. Er wird
+ zufällig generiert, falls noch kein Wert gespeichert ist oder der
+ gespeicherte Wert ungültig ist. Falls nötig kann der DUID hier
+ eingesehen und verändert werden. Einige Internetanbieter schreiben
+ die Nutzung eines bestimmten DUIDs vor.</p>
+ </fieldset>
+ </form>
</div>
</body>
</html>
diff --git a/src/wan.js b/src/wan.js
index 0605ee7..675ef18 100644
--- a/src/wan.js
+++ b/src/wan.js
@@ -10,6 +10,23 @@ let credentialsPasswordEl;
let credentialsSubmitEl;
let credentialsStatusEl;
+let dhcpv6TimestampEl;
+let dhcpv6SrvAddrEl;
+let dhcpv6SrvIdEl;
+let dhcpv6T1El;
+let dhcpv6T2El;
+let dhcpv6PrefixEl;
+let dhcpv6WanAddrEl;
+let dhcpv6PrefLftEl;
+let dhcpv6ValidLftEl;
+let dhcpv6Dns1El;
+let dhcpv6Dns2El;
+let dhcpv6AftrEl;
+
+let dhcpv6DuidEl;
+let dhcpv6SubmitEl;
+let dhcpv6StatusEl;
+
async function refreshConnectionStatus() {
const connectionStatus = await invoke("connection_status", {});
@@ -104,13 +121,77 @@ async function changeCredentials() {
}
}
-window.addEventListener("DOMContentLoaded", () => {
- refreshConnectionStatus();
+async function refreshDhcpv6Status() {
+ const dhcpv6Status = await invoke("dhcpv6_status", {});
+
+ dhcpv6TimestampEl.innerText = dhcpv6Status.timestamp;
+ dhcpv6SrvAddrEl.innerText = dhcpv6Status.srvaddr;
+ dhcpv6SrvIdEl.innerText = dhcpv6Status.srvid;
+ dhcpv6T1El.innerText = dhcpv6Status.t1;
+ dhcpv6T2El.innerText = dhcpv6Status.t2;
+ dhcpv6PrefixEl.innerText = dhcpv6Status.prefix;
+ dhcpv6WanAddrEl.innerText = dhcpv6Status.wanaddr;
+ dhcpv6PrefLftEl.innerText = dhcpv6Status.preflft;
+ dhcpv6ValidLftEl.innerText = dhcpv6Status.validlft;
+ dhcpv6Dns1El.innerText = dhcpv6Status.dns1;
+ dhcpv6Dns2El.innerText = dhcpv6Status.dns2;
+ dhcpv6AftrEl.innerText = dhcpv6Status.aftr;
+}
+
+async function loadDuid() {
+ dhcpv6StatusEl.innerText = "Lade aktuellen Client-DUID...";
+ document.body.style.cursor = "progress";
+
+ const currentDuid = await invoke("load_duid", {});
+
+ dhcpv6DuidEl.value = currentDuid.duid;
+ dhcpv6StatusEl.innerText = currentDuid.status_text;
+ document.body.style.cursor = "default";
+}
+
+async function changeDuid() {
+ dhcpv6DuidEl.disabled = true;
+ dhcpv6SubmitEl.disabled = true;
+ dhcpv6StatusEl.innerText = "Änderungsanfrage...";
+ document.body.style.cursor = "progress";
+
+ const statusText = await invoke("change_duid", { duid: dhcpv6DuidEl.value });
+
+ dhcpv6DuidEl.disabled = false;
+ dhcpv6SubmitEl.disabled = false;
+ dhcpv6StatusEl.innerText = statusText;
+ document.body.style.cursor = "default";
+
+ if (statusText === "Änderung erfolgreich") {
+ const apply = await ask("Zum Übernehmen des neuen Client-DUID muss der DHCPv6-Client neu gestartet werden. Dies dauert ca. 30 Sekunden, sollte die Internetverbindung aber nicht unterbrechen. Dabei wird eine Verlängerung mit Serversuche durchgeführt. Möchten Sie den DHCPv6-Client jetzt neu starten?", {
+ kind: "info",
+ title: "DHCPv6-Client-Neustart erforderlich",
+ });
+
+ if (apply) {
+ await killDhcpv6();
+ }
+ }
+}
+
+async function killDhcpv6() {
+ const error = await invoke("kill", { process: "rsdsl_dhcp6", signal: "term" });
+ if (error !== "") {
+ await message("Befehl konnte nicht erteilt werden: " + error, {
+ kind: "error",
+ title: "DHCPv6-Client-Neustart nicht erfolgt",
+ });
+ }
+}
+
+window.addEventListener("DOMContentLoaded", () => {
connectionStatusEl = document.querySelector("#connection-status");
connectionIpv4El = document.querySelector("#connection-ipv4");
connectionIpv6El = document.querySelector("#connection-ipv6");
+ refreshConnectionStatus();
+
document.querySelector("#connection-warm-reconnect").addEventListener("click", (e) => {
e.preventDefault();
warmReconnect();
@@ -140,6 +221,38 @@ window.addEventListener("DOMContentLoaded", () => {
});
loadCredentials();
+
+ dhcpv6TimestampEl = document.querySelector("#dhcpv6-timestamp");
+ dhcpv6SrvAddrEl = document.querySelector("#dhcpv6-srvaddr");
+ dhcpv6SrvIdEl = document.querySelector("#dhcpv6-srvid");
+ dhcpv6T1El = document.querySelector("#dhcpv6-t1");
+ dhcpv6T2El = document.querySelector("#dhcpv6-t2");
+ dhcpv6PrefixEl = document.querySelector("#dhcpv6-prefix");
+ dhcpv6WanAddrEl = document.querySelector("#dhcpv6-wanaddr");
+ dhcpv6PrefLftEl = document.querySelector("#dhcpv6-preflft");
+ dhcpv6ValidLftEl = document.querySelector("#dhcpv6-validlft");
+ dhcpv6Dns1El = document.querySelector("#dhcpv6-dns1");
+ dhcpv6Dns2El = document.querySelector("#dhcpv6-dns2");
+ dhcpv6AftrEl = document.querySelector("#dhcpv6-aftr");
+
+ refreshDhcpv6Status();
+
+ document.querySelector("#dhcpv6-kill").addEventListener("click", (e) => {
+ e.preventDefault();
+ killDhcpv6();
+ });
+
+ dhcpv6DuidEl = document.querySelector("#dhcpv6-duid");
+ dhcpv6SubmitEl = document.querySelector("#dhcpv6-submit");
+ dhcpv6StatusEl = document.querySelector("#dhcpv6-status");
+
+ document.querySelector("#dhcpv6-form").addEventListener("submit", (e) => {
+ e.preventDefault();
+ changeDuid();
+ });
+
+ loadDuid();
});
setInterval(refreshConnectionStatus, 3000);
+setInterval(refreshDhcpv6Status, 3000);