diff --git a/packages/ns-api/README.md b/packages/ns-api/README.md index a0265ba4c..6a53ba3e9 100644 --- a/packages/ns-api/README.md +++ b/packages/ns-api/README.md @@ -2847,6 +2847,10 @@ List active DHCPv4 leases: api-cli ns.dhcp list-active-leases ``` +The method reads the dnsmasq lease file configured in +`dhcp.ns_dnsmasq.leasefile` and falls back to `/tmp/dhcp.leases` when the +option is unset. + Response example: ```json { diff --git a/packages/ns-api/files/ns.dhcp b/packages/ns-api/files/ns.dhcp index 7c8218919..4cab83e56 100755 --- a/packages/ns-api/files/ns.dhcp +++ b/packages/ns-api/files/ns.dhcp @@ -10,6 +10,7 @@ import sys import json import ipaddress +import os import subprocess from euci import EUci from nethsec import utils, objects @@ -221,7 +222,11 @@ def list_active_leases(): if 'mac' in ldata and 'ip' in ldata: static_leases.append(ldata['mac'].lower()) - with open("/tmp/dhcp.leases", "r") as fp: + leasefile = u.get('dhcp', 'ns_dnsmasq', 'dhcp', 'leasefile', default='/tmp/dhcp.leases') + if not os.path.exists(leasefile): + return ret + + with open(leasefile, "r") as fp: for line in fp.readlines(): tmp = line.split(" ") hostname = tmp[3] diff --git a/packages/ns-api/files/ns.ha b/packages/ns-api/files/ns.ha index 73df7dfb1..cf18207d9 100755 --- a/packages/ns-api/files/ns.ha +++ b/packages/ns-api/files/ns.ha @@ -83,6 +83,9 @@ def get_device_from_ip(uci, ipaddr): return (n, uci.get('network', n, 'device', default=None)) return (None, None) +def get_dhcp_leasefile(uci): + return uci.get('dhcp', 'ns_dnsmasq', 'dhcp', 'leasefile', default='/tmp/dhcp.leases') + def get_main_vrrp_interface(uci): for section in utils.get_all_by_type(uci, 'keepalived', 'ipaddress'): if uci.get('keepalived', section, 'name', default=None) != 'lan_ha': @@ -444,7 +447,7 @@ def init_local(role, primary_node_ip, backup_node_ip, virtual_ip, lan_interface, '/etc/adblock', '/etc/banip', '/etc/netifyd', - '/tmp/dhcp.leases', + get_dhcp_leasefile(u), '/var/ns-snort', '/tmp/banIP-backup', '/tmp/adblock-Backup' diff --git a/packages/ns-storage/Makefile b/packages/ns-storage/Makefile index 8bd79ac3b..8a8d20db1 100644 --- a/packages/ns-storage/Makefile +++ b/packages/ns-storage/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ns-storage -PKG_VERSION:=1.0.1 +PKG_VERSION:=1.0.2 PKG_RELEASE:=1 PKG_BUILD_DIR:=$(BUILD_DIR)/ns-storage-$(PKG_VERSION) @@ -62,6 +62,7 @@ define Package/ns-storage/install $(INSTALL_BIN) ./files/ns-storage-setup-partition $(1)/usr/libexec $(INSTALL_BIN) ./files/ns-storage-setup-disk $(1)/usr/libexec $(INSTALL_BIN) ./files/ns-storage-has-free-space $(1)/usr/libexec + $(INSTALL_BIN) ./files/ns-storage-dhcp-leases $(1)/usr/libexec $(INSTALL_BIN) ./files/remove-storage $(1)/usr/sbin $(INSTALL_BIN) ./files/rotate-messages $(1)/usr/sbin $(INSTALL_BIN) ./files/sync-data $(1)/usr/sbin diff --git a/packages/ns-storage/README.md b/packages/ns-storage/README.md index ba92773c3..3c47d1908 100644 --- a/packages/ns-storage/README.md +++ b/packages/ns-storage/README.md @@ -10,6 +10,7 @@ Features: - device initialization and mounting - rsyslog configuration +- dnsmasq DHCP lease persistence on mounted storage - logrotate for extra log files - customizable cron job to sync data once a day @@ -66,8 +67,9 @@ add-storage /dev/sda The script will mount the storage at `/mnt/data` and configure the system as follow: -- rsyslog will write logs also inside `/mnt/data/logs/messages` file -- logrotate will rotate `/mnt/data/logs/messages` once a week (see `/etc/logrotate/data.conf` for more info) +- rsyslog will write logs also inside `/mnt/data/log/messages` file +- logrotate will rotate `/mnt/data/log/messages` once a week (see `/etc/logrotate/data.conf` for more info) +- dnsmasq will store DHCP leases inside `/mnt/data/dnsmasq/dhcp.leases`; `/tmp/dhcp.leases` remains available as a compatibility symlink while the storage is mounted ### Storage status alert @@ -91,3 +93,6 @@ To remove the data storage and restore in-memory log retention only, execute: ``` remove-storage ``` + +The `remove-storage` script switches dnsmasq back to `/tmp/dhcp.leases` before +unmounting `/mnt/data`. diff --git a/packages/ns-storage/files/add-storage b/packages/ns-storage/files/add-storage index 885950c73..11851168c 100755 --- a/packages/ns-storage/files/add-storage +++ b/packages/ns-storage/files/add-storage @@ -48,3 +48,6 @@ crontab -l | grep -q '/usr/sbin/sync-data' || echo '40 1 * * * /usr/sbin/sync-da # check for existing OpenVPN RW connection records in /var and merge them into storage if needed /usr/libexec/ns-openvpn/openvpn-merge-connections-db + +# Setup persistent dnsmasq leases file +/usr/libexec/ns-storage-dhcp-leases \ No newline at end of file diff --git a/packages/ns-storage/files/ns-storage-check.init b/packages/ns-storage/files/ns-storage-check.init index 35f8e97c5..50371bc8f 100644 --- a/packages/ns-storage/files/ns-storage-check.init +++ b/packages/ns-storage/files/ns-storage-check.init @@ -26,6 +26,8 @@ storage_check() uci set rsyslog.ns_data.destination="/mnt/data/log/messages" uci commit rsyslog fi + + /usr/libexec/ns-storage-dhcp-leases } boot() diff --git a/packages/ns-storage/files/ns-storage-dhcp-leases b/packages/ns-storage/files/ns-storage-dhcp-leases new file mode 100644 index 000000000..027b6f88a --- /dev/null +++ b/packages/ns-storage/files/ns-storage-dhcp-leases @@ -0,0 +1,49 @@ +#!/bin/sh +# +# Copyright (C) 2026 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-2.0-only +# + +# This script is used to move the dnsmasq leases file between the storage and the tmpfs, +# depending on the storage availability. + +TMP_LEASEFILE=/tmp/dhcp.leases +STORAGE_TARGET=/mnt/data +STORAGE_DIR=${STORAGE_TARGET}/dnsmasq +STORAGE_LEASEFILE=${STORAGE_DIR}/dhcp.leases + +is_storage_mounted() +{ + awk -v target="${STORAGE_TARGET}" '$2 == target {found=1} END {exit !found}' /proc/mounts +} + +get_current_leasefile() +{ + current="$(uci -q get dhcp.ns_dnsmasq.leasefile)" + if [ -n "${current}" ]; then + printf '%s\n' "${current}" + else + printf '%s\n' "${TMP_LEASEFILE}" + fi +} + +current_leasefile="$(get_current_leasefile)" +if is_storage_mounted; then + target_leasefile="${STORAGE_LEASEFILE}" +else + target_leasefile="${TMP_LEASEFILE}" +fi + +if [ "${current_leasefile}" != "${target_leasefile}" ]; then + if [ "${current_leasefile}" = "${STORAGE_LEASEFILE}" ]; then + # From storage to tmp + cp -af "${STORAGE_LEASEFILE}" "${TMP_LEASEFILE}" || : + else + # From tmp to storage + mkdir -p "${STORAGE_DIR}" + cp -af "${TMP_LEASEFILE}" "${STORAGE_LEASEFILE}" || : + fi + uci set dhcp.ns_dnsmasq.leasefile="$target_leasefile" + uci commit dhcp + reload_config +fi \ No newline at end of file diff --git a/packages/ns-storage/files/remove-storage b/packages/ns-storage/files/remove-storage index 0b5ed7648..1dde7d33a 100644 --- a/packages/ns-storage/files/remove-storage +++ b/packages/ns-storage/files/remove-storage @@ -34,3 +34,6 @@ rm -rf /mnt/data if [ "$rom_disk" == "$data_disk" ]; then parted "/dev/${data_disk}" rm 3 fi + +# Restore dnsmasq to /tmp before the storage disappears. +/usr/libexec/ns-storage-dhcp-leases \ No newline at end of file