{% /* * 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(); } %}
{{_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]; %}
{% } %}
{% } %}
{{_R("dialog-footer")}}