{% /* * Part of AREDN® -- Used for creating Amateur Radio Emergency Data Networks * Copyright (C) 2024 Tim Wilkinson * See Contributors file for additional contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional Terms: * * Additional use restrictions exist on the AREDN® trademark and logo. * See AREDNLicense.txt for more info. * * Attributions to the AREDN® Project must be retained in the source code. * If importing this code into a new or existing project attribution * to the AREDN® project must be added to the source code. * * You must not misrepresent the origin of the material contained within. * * Modified versions must be modified to attribute to the original source * and be marked in reasonable ways as differentiate it from the original * version */ %} {% if (request.env.REQUEST_METHOD === "PUT") { if ("options" in request.args) { configuration.prepareChanges(); const options = json(request.args.options); const dhcp = configuration.getDHCP(); let f = fs.open(dhcp.reservations, "w"); if (f) { const base = iptoarr(dhcp.start)[3]; for (let i = 0; i < length(options); i++) { const o = options[i]; if (o.reserved) { const ip = iptoarr(o.ip)[3]; if (length(o.name) > 0 && ip >= base && match(o.mac, /^([0-9a-fA-F][0-9a-fA-F]:){5}[0-9a-fA-F][0-9a-fA-F]$/)) { f.write(`${o.mac} ${ip - base + 2} ${o.name}${o.noprop ? " #NOPROP" : ""}\n`); } } } f.close(); } } if ("advtags" in request.args) { configuration.prepareChanges(); const advtags = json(request.args.advtags); const dhcp = configuration.getDHCP(); let f = fs.open(dhcp.dhcptags, "w"); if (f) { for (let i = 0; i < length(advtags); i++) { const t = advtags[i]; f.write(`${t.name} ${t.type} ${t.match}\n`); } f.close(); } } if ("advoptions" in request.args) { configuration.prepareChanges(); const advoptions = json(request.args.advoptions); const dhcp = configuration.getDHCP(); let f = fs.open(dhcp.dhcpoptions, "w"); if (f) { for (let i = 0; i < length(advoptions); i++) { const o = advoptions[i]; f.write(`${o.name} ${o.always ? "force" : "onrequest"} ${o.type} ${o.value}\n`); } f.close(); } } print(_R("changes")); return; } if (request.env.REQUEST_METHOD === "DELETE") { configuration.revertModalChanges(); print(_R("changes")); return; } const dhcp = configuration.getDHCP(); const start = iptoarr(dhcp.start); const end = iptoarr(dhcp.end); const leases = []; const options = []; const advoptions = []; const advtags = []; for (let i = start[3]; i <= end[3]; i++) { push(options, { mac: "", ip: `${start[0]}.${start[1]}.${start[2]}.${i}`, name: "", noprop: false, reserved: false, leased: false }); } let reservations = 0; let active = 0; let f = fs.open(dhcp.reservations); if (f) { for (let l = f.read("line"); length(l); l = f.read("line")) { // mac, last-ip, name, flags const v = match(trim(l), /^([^ ]+) ([^ ]+) ([^ ]+) ?(.*)/); if (v) { const o = options[int(v[2]) - 2]; if (o) { o.mac = v[1]; o.name = v[3]; o.noprop = v[4] == "#NOPROP"; o.reserved = true; reservations++; } } } f.close(); } f = fs.open(dhcp.leases); if (f) { for (let l = f.read("line"); length(l); l = f.read("line")) { // ?, mac, ip, name, ? const v = match(l, /^(.+) (.+) (.+) (.+) (.+)$/); if (v) { const ip = iptoarr(v[3]); const o = options[ip[3] - start[3]]; if (o) { o.leased = true; if (o.mac === "") { o.mac = v[2]; } if (o.name === "") { o.name = v[4]; } active++; } } } f.close(); } f = fs.open(dhcp.dhcptags); if (f) { for (let l = f.read("line"); length(l); l = f.read("line")) { const m = match(replace(l, /\n+$/, ""), /^([^\W_]+)\s(\w+)\s(.+)$/); if (m) { const p = replace(replace(replace(m[3], /"/g, """), //g, ">"); push(advtags, { name: m[1], type: m[2], match: p }); } } f.close(); } f = fs.open(dhcp.dhcpoptions); if (f) { for (let l = f.read("line"); length(l); l = f.read("line")) { const m = match(replace(l, /\n+$/, ""), /^(\S*)\s(force|onrequest)\s(\d+)\s(.+)$/); if (m) { push(advoptions, { name: m[1], always: m[2] === "force", type: int(m[3]), value: m[4] }); } } f.close(); } const dhcpOptionTypes = { "1": "netmask", "2": "time-offset", "3": "router", "6": "dns-server", "7": "log-server", "9": "lpr-server", "13": "boot-file-size", "15": "domain-name", "16": "swap-server", "17": "root-path", "18": "extension-path", "19": "ip-forward-enable", "20": "non-local-source-routing", "21": "policy-filter", "22": "max-datagram-reassembly", "23": "default-ttl", "26": "mtu", "27": "all-subnets-local", "31": "router-discovery", "32": "router-solicitation", "33": "static-route", "34": "trailer-encapsulation", "35": "arp-timeout", "36": "ethernet-encap", "37": "tcp-ttl", "38": "tcp-keepalive", "40": "nis-domain", "41": "nis-server", "42": "ntp-server", "44": "netbios-ns", "45": "netbios-dd", "46": "netbios-nodetype", "47": "netbios-scope", "48": "x-windows-fs", "49": "x-windows-dm", "58": "T1", "59": "T2", "60": "vendor-class", "64": "nis+-domain", "65": "nis+-server", "66": "tftp-server", "67": "bootfile-name", "68": "mobile-ip-home", "69": "smtp-server", "70": "pop3-server", "71": "nntp-server", "74": "irc-server", "77": "user-class", "80": "rapid-commit", "93": "client-arch", "94": "client-interface-id", "97": "client-machine-id", "100": "posix-timezone", "101": "tzdb-timezone", "108": "ipv6-only", "119": "domain-search", "120": "sip-server", "121": "classless-static-route", "125": "vendor-id-encap", "150": "tftp-server-address", "255": "server-ip-address" }; %}
{{_R("dialog-header", "LAN DHCP")}}
Address Reservations
Hostnames with fixed addresses
{{_H("Creates a permenant mapping between a device MAC address and an IP address on the LAN network. The given hostname is available to everyone on the mesh unless the entry is marked as do not propagate")}}
hostname
ip address
mac a‌ddress
do not propagate
{% if (reservations > 0) { for (let i = 0; i < length(options); i++) { const o = options[i]; if (o.reserved) { %}
{% } } } %}

Active Leases
Addresses currently in use
{{_H("The list of active leases currently allocated to LAN devices. Any of these leases can be promoted to a permanent mapping to allow IP Addresses to be fixed to specific devices.")}} {% if (active > 0) { %}
hostname
ip address
mac a‌ddress
{% for (let i = 0; i < length(options); i++) { const o = options[i]; if (o.leased) { %}
{% } } } %}
{{_R("dialog-advanced")}}
{% if (includeAdvanced) { %}
Tags
Tags for advanced options
tag
type
match
{% for (let i = 0; i < length(advtags); i++) { const t = advtags[i]; %}
{% } %}
Options
Advanced options
tag
option
value
always
{% for (let i = 0; i < length(advoptions); i++) { const o = advoptions[i]; %}
{% } %}
{% } %}
{% for (let k in dhcpOptionTypes) { print(``); } %} {{_R("dialog-footer")}}