summaryrefslogtreecommitdiffstats
path: root/static/systemd-network.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/systemd-network.js')
-rw-r--r--static/systemd-network.js416
1 files changed, 244 insertions, 172 deletions
diff --git a/static/systemd-network.js b/static/systemd-network.js
index 69020f1..16c079c 100644
--- a/static/systemd-network.js
+++ b/static/systemd-network.js
@@ -1,21 +1,23 @@
/* jshint esversion: 2024, module: true */
import {
- BooleanYesNo,
- ClientIdentifier,
- DHCPMode,
- DNSSECOptions,
- DuplexMode,
+ SectionName,
FieldType,
+ DHCPMode,
IPForward,
IPv6PrivacyExtensions,
LLMNROptions,
MulticastDNS,
- PortType,
+ DNSSECOptions,
+ UseDomains,
+ ClientIdentifier,
RouteScope,
RouteType,
- UseDomains,
WakeOnLAN,
+ PortType,
+ DuplexMode,
+ SLAACOptions,
+ IPv6SendRAOptions,
} from "./network-types.js";
/**
@@ -99,6 +101,17 @@ class BaseField {
return true;
}
+ // Handle boolean conversion for yes/no strings
+ if (this.#type === FieldType.BOOLEAN && typeof this.#value === "string") {
+ const normalized = this.#value.toLowerCase();
+ return (
+ normalized === "yes" ||
+ normalized === "no" ||
+ normalized === "true" ||
+ normalized === "false"
+ );
+ }
+
if (this.#options.pattern && typeof this.#value === "string") {
return this.#options.pattern.test(this.#value);
}
@@ -115,7 +128,60 @@ class BaseField {
* @returns {string}
*/
toString() {
- return this.#value !== null ? String(this.#value) : "";
+ if (this.#value === null || this.#value === undefined) {
+ return "";
+ }
+
+ // Handle boolean conversion
+ if (this.#type === FieldType.BOOLEAN) {
+ if (typeof this.#value === "boolean") {
+ return this.#value ? "yes" : "no";
+ }
+ if (typeof this.#value === "string") {
+ const normalized = this.#value.toLowerCase();
+ if (normalized === "true" || normalized === "false") {
+ return normalized === "true" ? "yes" : "no";
+ }
+ return normalized; // already yes/no
+ }
+ }
+
+ return String(this.#value);
+ }
+
+ /**
+ * Get boolean value
+ * @returns {boolean|null}
+ */
+ getBooleanValue() {
+ if (this.#type !== FieldType.BOOLEAN) {
+ return null;
+ }
+
+ if (this.#value === null || this.#value === undefined) {
+ return null;
+ }
+
+ if (typeof this.#value === "boolean") {
+ return this.#value;
+ }
+
+ if (typeof this.#value === "string") {
+ const normalized = this.#value.toLowerCase();
+ return normalized === "yes" || normalized === "true";
+ }
+
+ return Boolean(this.#value);
+ }
+
+ /**
+ * Set boolean value
+ * @param {boolean} boolValue
+ */
+ setBooleanValue(boolValue) {
+ if (this.#type === FieldType.BOOLEAN) {
+ this.#value = boolValue;
+ }
}
/**
@@ -166,6 +232,16 @@ class IPv6Address extends BaseField {
}
/**
+ * Boolean type with yes/no values
+ * @class BooleanField
+ */
+class BooleanField extends BaseField {
+ constructor(value = null) {
+ super(value, FieldType.BOOLEAN, "Boolean (yes/no)");
+ }
+}
+
+/**
* Port type
* @class Port
*/
@@ -250,7 +326,7 @@ class LinkSection {
this.Duplex = new BaseField(null, FieldType.STRING, "Duplex mode", {
enum: Object.values(DuplexMode),
});
- this.AutoNegotiation = new BooleanYesNo();
+ this.AutoNegotiation = new BooleanField();
this.WakeOnLan = new BaseField(null, FieldType.STRING, "Wake-on-LAN", {
enum: Object.values(WakeOnLAN),
});
@@ -262,8 +338,8 @@ class LinkSection {
FieldType.STRINGS,
"Advertised features",
);
- this.RxFlowControl = new BooleanYesNo();
- this.TxFlowControl = new BooleanYesNo();
+ this.RxFlowControl = new BooleanField();
+ this.TxFlowControl = new BooleanField();
}
}
@@ -281,7 +357,7 @@ class NetworkSection {
this.DHCP = new BaseField(null, FieldType.DHCP_MODE, "DHCP client", {
enum: Object.values(DHCPMode),
});
- this.DHCPServer = new BooleanYesNo();
+ this.DHCPServer = new BooleanField();
this.DNS = new BaseField(null, FieldType.IP_ADDRESSES, "DNS servers");
this.NTP = new BaseField(null, FieldType.IP_ADDRESSES, "NTP servers");
this.IPForward = new BaseField(
@@ -300,7 +376,7 @@ class NetworkSection {
enum: Object.values(IPv6PrivacyExtensions),
},
);
- this.IPv6AcceptRA = new BooleanYesNo();
+ this.IPv6AcceptRA = new BooleanField();
this.LLMNR = new BaseField(null, FieldType.LLMNR, "LLMNR support", {
enum: Object.values(LLMNROptions),
});
@@ -311,15 +387,16 @@ class NetworkSection {
enum: Object.values(DNSSECOptions),
});
this.Domains = new BaseField(null, FieldType.STRINGS, "DNS search domains");
- this.ConfigureWithoutCarrier = new BooleanYesNo();
- this.IgnoreCarrierLoss = new BooleanYesNo();
+ this.ConfigureWithoutCarrier = new BooleanField();
+ this.IgnoreCarrierLoss = new BooleanField();
this.KeepConfiguration = new BaseField(
null,
FieldType.NUMBER,
"Keep configuration time in seconds",
);
- this.LLDP = new BooleanYesNo();
- this.EmitLLDP = new BooleanYesNo();
+ this.LLDP = new BooleanField();
+ this.EmitLLDP = new BooleanField();
+ this.IPv6SendRA = new BooleanField();
}
}
@@ -329,10 +406,10 @@ class NetworkSection {
*/
class DHCPSection {
constructor() {
- this.UseDNS = new BooleanYesNo();
- this.UseNTP = new BooleanYesNo();
- this.UseMTU = new BooleanYesNo();
- this.UseHostname = new BooleanYesNo();
+ this.UseDNS = new BooleanField();
+ this.UseNTP = new BooleanField();
+ this.UseMTU = new BooleanField();
+ this.UseHostname = new BooleanField();
this.UseDomains = new BaseField(
null,
FieldType.USE_DOMAINS,
@@ -354,8 +431,8 @@ class DHCPSection {
FieldType.NUMBER,
"Route metric for DHCP routes",
);
- this.UseRoutes = new BooleanYesNo();
- this.SendRelease = new BooleanYesNo();
+ this.UseRoutes = new BooleanField();
+ this.SendRelease = new BooleanField();
}
}
@@ -373,10 +450,10 @@ class DHCPv4Section {
enum: Object.values(ClientIdentifier),
},
);
- this.UseDNS = new BooleanYesNo();
- this.UseNTP = new BooleanYesNo();
- this.UseMTU = new BooleanYesNo();
- this.UseHostname = new BooleanYesNo();
+ this.UseDNS = new BooleanField();
+ this.UseNTP = new BooleanField();
+ this.UseMTU = new BooleanField();
+ this.UseHostname = new BooleanField();
this.UseDomains = new BaseField(
null,
FieldType.USE_DOMAINS,
@@ -385,7 +462,7 @@ class DHCPv4Section {
enum: Object.values(UseDomains),
},
);
- this.SendRelease = new BooleanYesNo();
+ this.SendRelease = new BooleanField();
}
}
@@ -395,9 +472,9 @@ class DHCPv4Section {
*/
class DHCPv6Section {
constructor() {
- this.UseDNS = new BooleanYesNo();
- this.UseNTP = new BooleanYesNo();
- this.UseHostname = new BooleanYesNo();
+ this.UseDNS = new BooleanField();
+ this.UseNTP = new BooleanField();
+ this.UseHostname = new BooleanField();
this.UseDomains = new BaseField(
null,
FieldType.USE_DOMAINS,
@@ -406,8 +483,8 @@ class DHCPv6Section {
enum: Object.values(UseDomains),
},
);
- this.WithoutRA = new BooleanYesNo();
- this.UseAddress = new BooleanYesNo();
+ this.WithoutRA = new BooleanField();
+ this.UseAddress = new BooleanField();
}
}
@@ -417,7 +494,7 @@ class DHCPv6Section {
*/
class IPv6AcceptRASection {
constructor() {
- this.UseDNS = new BooleanYesNo();
+ this.UseDNS = new BooleanField();
this.UseDomains = new BaseField(
null,
FieldType.USE_DOMAINS,
@@ -426,9 +503,9 @@ class IPv6AcceptRASection {
enum: Object.values(UseDomains),
},
);
- this.UseAutonomousPrefix = new BooleanYesNo();
- this.UseOnLinkPrefix = new BooleanYesNo();
- this.UseRoutePrefix = new BooleanYesNo();
+ this.UseAutonomousPrefix = new BooleanField();
+ this.UseOnLinkPrefix = new BooleanField();
+ this.UseRoutePrefix = new BooleanField();
this.RouteMetric = new BaseField(
null,
FieldType.NUMBER,
@@ -438,12 +515,54 @@ class IPv6AcceptRASection {
}
/**
+ * [IPv6SendRA] section configuration
+ * @class IPv6SendRASection
+ */
+class IPv6SendRASection {
+ constructor() {
+ this.RouterLifetimeSec = new BaseField(
+ null,
+ FieldType.NUMBER,
+ "Router lifetime in seconds",
+ );
+ this.EmitDNS = new BooleanField();
+ this.DNS = new BaseField(
+ null,
+ FieldType.IP_ADDRESSES,
+ "DNS servers to advertise",
+ );
+ this.EmitDomains = new BooleanField();
+ this.Domains = new BaseField(
+ null,
+ FieldType.STRINGS,
+ "Domains to advertise",
+ );
+ this.EmitNTP = new BooleanField();
+ this.NTP = new BaseField(
+ null,
+ FieldType.IP_ADDRESSES,
+ "NTP servers to advertise",
+ );
+ this.RouterPreference = new BaseField(
+ null,
+ FieldType.STRING,
+ "Router preference",
+ {
+ enum: ["high", "medium", "low"],
+ },
+ );
+ this.Managed = new BooleanField();
+ this.OtherInformation = new BooleanField();
+ }
+}
+
+/**
* [SLAAC] section configuration
* @class SLAACSection
*/
class SLAACSection {
constructor() {
- this.UseDNS = new BooleanYesNo();
+ this.UseDNS = new BooleanField();
this.UseDomains = new BaseField(
null,
FieldType.USE_DOMAINS,
@@ -452,17 +571,17 @@ class SLAACSection {
enum: Object.values(UseDomains),
},
);
- this.UseAddress = new BooleanYesNo();
+ this.UseAddress = new BooleanField();
this.RouteMetric = new BaseField(
null,
FieldType.NUMBER,
"Route metric for SLAAC routes",
);
- this.Critical = new BooleanYesNo();
- this.PreferTemporaryAddress = new BooleanYesNo();
- this.UseAutonomousPrefix = new BooleanYesNo();
- this.UseOnLinkPrefix = new BooleanYesNo();
- this.UseRoutePrefix = new BooleanYesNo();
+ this.Critical = new BooleanField();
+ this.PreferTemporaryAddress = new BooleanField();
+ this.UseAutonomousPrefix = new BooleanField();
+ this.UseOnLinkPrefix = new BooleanField();
+ this.UseRoutePrefix = new BooleanField();
}
}
@@ -497,7 +616,7 @@ class AddressSection {
class RouteSection {
constructor() {
this.Gateway = new BaseField(null, FieldType.IP_ADDRESS, "Gateway address");
- this.GatewayOnLink = new BooleanYesNo();
+ this.GatewayOnLink = new BooleanField();
this.Destination = new BaseField(
null,
FieldType.IP_PREFIX,
@@ -543,6 +662,7 @@ class NetworkConfiguration {
#dhcpv4;
#dhcpv6;
#ipv6AcceptRA;
+ #ipv6SendRA;
#slaac;
#address;
#route;
@@ -555,87 +675,44 @@ class NetworkConfiguration {
this.#dhcpv4 = new DHCPv4Section();
this.#dhcpv6 = new DHCPv6Section();
this.#ipv6AcceptRA = new IPv6AcceptRASection();
+ this.#ipv6SendRA = new IPv6SendRASection();
this.#slaac = new SLAACSection();
this.#address = [];
this.#route = [];
}
- /**
- * Get Match section
- * @returns {MatchSection}
- */
+ // ... getter methods for all sections ...
+
get Match() {
return this.#match;
}
-
- /**
- * Get Link section
- * @returns {LinkSection}
- */
get Link() {
return this.#link;
}
-
- /**
- * Get Network section
- * @returns {NetworkSection}
- */
get Network() {
return this.#network;
}
-
- /**
- * Get DHCP section
- * @returns {DHCPSection}
- */
get DHCP() {
return this.#dhcp;
}
-
- /**
- * Get DHCPv4 section
- * @returns {DHCPv4Section}
- */
get DHCPv4() {
return this.#dhcpv4;
}
-
- /**
- * Get DHCPv6 section
- * @returns {DHCPv6Section}
- */
get DHCPv6() {
return this.#dhcpv6;
}
-
- /**
- * Get IPv6AcceptRA section
- * @returns {IPv6AcceptRASection}
- */
get IPv6AcceptRA() {
return this.#ipv6AcceptRA;
}
-
- /**
- * Get SLAAC section
- * @returns {SLAACSection}
- */
+ get IPv6SendRA() {
+ return this.#ipv6SendRA;
+ }
get SLAAC() {
return this.#slaac;
}
-
- /**
- * Get Address sections
- * @returns {Array<AddressSection>}
- */
get Address() {
return [...this.#address];
}
-
- /**
- * Get Route sections
- * @returns {Array<RouteSection>}
- */
get Route() {
return [...this.#route];
}
@@ -653,18 +730,13 @@ class NetworkConfiguration {
DHCPv4: this.#getSectionSchema(this.#dhcpv4),
DHCPv6: this.#getSectionSchema(this.#dhcpv6),
IPv6AcceptRA: this.#getSectionSchema(this.#ipv6AcceptRA),
+ IPv6SendRA: this.#getSectionSchema(this.#ipv6SendRA),
SLAAC: this.#getSectionSchema(this.#slaac),
Address: this.#getArraySectionSchema(AddressSection, "Address"),
Route: this.#getArraySectionSchema(RouteSection, "Route"),
};
}
- /**
- * Get schema for a single section
- * @private
- * @param {Object} section - Section instance
- * @returns {Object} Section schema
- */
#getSectionSchema(section) {
const schema = {};
for (const [key, field] of Object.entries(section)) {
@@ -678,13 +750,6 @@ class NetworkConfiguration {
return schema;
}
- /**
- * Get schema for array sections (Address, Route)
- * @private
- * @param {Class} SectionClass - Section class
- * @param {string} sectionName - Section name
- * @returns {Object} Array section schema
- */
#getArraySectionSchema(SectionClass, sectionName) {
const template = new SectionClass();
return {
@@ -713,15 +778,16 @@ class NetworkConfiguration {
if (!trimmed || trimmed.startsWith("#")) continue;
// Section header
- const sectionMatch = trimmed.match(/^\[(\w+)\]$/);
+ const sectionMatch = trimmed.match(/^\[(\w+)\]$/i);
if (sectionMatch) {
- currentSection = sectionMatch[1].toLowerCase();
+ const sectionName = sectionMatch[1].toLowerCase();
+ currentSection = config.#getSectionEnum(sectionName);
// Start new array sections
- if (currentSection === "address") {
+ if (currentSection === SectionName.ADDRESS) {
currentAddress = new AddressSection();
config.#address.push(currentAddress);
- } else if (currentSection === "route") {
+ } else if (currentSection === SectionName.ROUTE) {
currentRoute = new RouteSection();
config.#route.push(currentRoute);
}
@@ -729,7 +795,7 @@ class NetworkConfiguration {
}
// Key-value pair
- const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
+ const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/i);
if (kvMatch && currentSection) {
const key = kvMatch[1];
const value = kvMatch[2];
@@ -748,9 +814,33 @@ class NetworkConfiguration {
}
/**
+ * Get section enum from string
+ * @private
+ * @param {string} sectionName - Section name as string
+ * @returns {Symbol} Section enum
+ */
+ #getSectionEnum(sectionName) {
+ const sectionMap = {
+ match: SectionName.MATCH,
+ link: SectionName.LINK,
+ network: SectionName.NETWORK,
+ dhcp: SectionName.DHCP,
+ dhcpv4: SectionName.DHCPV4,
+ dhcpv6: SectionName.DHCPV6,
+ ipv6acceptra: SectionName.IPV6_ACCEPT_RA,
+ ipv6sendra: SectionName.IPV6_SEND_RA,
+ slaac: SectionName.SLAAC,
+ address: SectionName.ADDRESS,
+ route: SectionName.ROUTE,
+ };
+
+ return sectionMap[sectionName] || null;
+ }
+
+ /**
* Set configuration value
* @private
- * @param {string} section - Section name
+ * @param {Symbol} section - Section enum
* @param {string} key - Key name
* @param {string} value - Value
* @param {AddressSection} currentAddress - Current address section
@@ -758,36 +848,39 @@ class NetworkConfiguration {
*/
#setValue(section, key, value, currentAddress, currentRoute) {
switch (section) {
- case "match":
+ case SectionName.MATCH:
this.#setMatchValue(key, value);
break;
- case "link":
+ case SectionName.LINK:
this.#setLinkValue(key, value);
break;
- case "network":
+ case SectionName.NETWORK:
this.#setNetworkValue(key, value);
break;
- case "dhcp":
+ case SectionName.DHCP:
this.#setDHCPValue(key, value);
break;
- case "dhcpv4":
+ case SectionName.DHCPV4:
this.#setDHCPv4Value(key, value);
break;
- case "dhcpv6":
+ case SectionName.DHCPV6:
this.#setDHCPv6Value(key, value);
break;
- case "ipv6acceptra":
+ case SectionName.IPV6_ACCEPT_RA:
this.#setIPv6AcceptRAValue(key, value);
break;
- case "slaac":
+ case SectionName.IPV6_SEND_RA:
+ this.#setIPv6SendRAValue(key, value);
+ break;
+ case SectionName.SLAAC:
this.#setSLAACValue(key, value);
break;
- case "address":
+ case SectionName.ADDRESS:
if (currentAddress) {
this.#setAddressValue(currentAddress, key, value);
}
break;
- case "route":
+ case SectionName.ROUTE:
if (currentRoute) {
this.#setRouteValue(currentRoute, key, value);
}
@@ -837,6 +930,12 @@ class NetworkConfiguration {
}
}
+ #setIPv6SendRAValue(key, value) {
+ if (this.#ipv6SendRA[key] !== undefined) {
+ this.#ipv6SendRA[key].value = value;
+ }
+ }
+
#setSLAACValue(key, value) {
if (this.#slaac[key] !== undefined) {
this.#slaac[key].value = value;
@@ -862,53 +961,25 @@ class NetworkConfiguration {
toSystemdConfiguration() {
const sections = [];
- // [Match] section
- if (this.#hasSectionValues(this.#match)) {
- sections.push("[Match]");
- sections.push(...this.#formatSection(this.#match));
- }
-
- // [Link] section
- if (this.#hasSectionValues(this.#link)) {
- sections.push("[Link]");
- sections.push(...this.#formatSection(this.#link));
- }
-
- // [Network] section
- if (this.#hasSectionValues(this.#network)) {
- sections.push("[Network]");
- sections.push(...this.#formatSection(this.#network));
- }
-
- // [DHCP] section
- if (this.#hasSectionValues(this.#dhcp)) {
- sections.push("[DHCP]");
- sections.push(...this.#formatSection(this.#dhcp));
- }
-
- // [DHCPv4] section
- if (this.#hasSectionValues(this.#dhcpv4)) {
- sections.push("[DHCPv4]");
- sections.push(...this.#formatSection(this.#dhcpv4));
- }
-
- // [DHCPv6] section
- if (this.#hasSectionValues(this.#dhcpv6)) {
- sections.push("[DHCPv6]");
- sections.push(...this.#formatSection(this.#dhcpv6));
- }
-
- // [IPv6AcceptRA] section
- if (this.#hasSectionValues(this.#ipv6AcceptRA)) {
- sections.push("[IPv6AcceptRA]");
- sections.push(...this.#formatSection(this.#ipv6AcceptRA));
- }
-
- // [SLAAC] section
- if (this.#hasSectionValues(this.#slaac)) {
- sections.push("[SLAAC]");
- sections.push(...this.#formatSection(this.#slaac));
- }
+ // Add all sections in order
+ const sectionOrder = [
+ { name: "Match", section: this.#match },
+ { name: "Link", section: this.#link },
+ { name: "Network", section: this.#network },
+ { name: "DHCP", section: this.#dhcp },
+ { name: "DHCPv4", section: this.#dhcpv4 },
+ { name: "DHCPv6", section: this.#dhcpv6 },
+ { name: "IPv6AcceptRA", section: this.#ipv6AcceptRA },
+ { name: "IPv6SendRA", section: this.#ipv6SendRA },
+ { name: "SLAAC", section: this.#slaac },
+ ];
+
+ sectionOrder.forEach(({ name, section }) => {
+ if (this.#hasSectionValues(section)) {
+ sections.push(`[${name}]`);
+ sections.push(...this.#formatSection(section));
+ }
+ });
// [Address] sections
this.#address.forEach((addr) => {
@@ -926,7 +997,7 @@ class NetworkConfiguration {
}
});
- return sections.length > 0 ? `${sections.join("\n")}\n` : "";
+ return sections.length > 0 ? sections.join("\n") + "\n" : "";
}
#hasSectionValues(section) {
@@ -950,7 +1021,7 @@ export {
MACAddress,
IPv4Address,
IPv6Address,
- BooleanYesNo,
+ BooleanField,
Port,
MTU,
MatchSection,
@@ -960,6 +1031,7 @@ export {
DHCPv4Section,
DHCPv6Section,
IPv6AcceptRASection,
+ IPv6SendRASection,
SLAACSection,
AddressSection,
RouteSection,