@@ -5,7 +5,7 @@
name = network
version = 010
-release = 5
+release = 6
maintainer = Michael Tremer <michael.tremer@ipfire.org>
groups = Base Networking/Tools
@@ -24,9 +24,12 @@ source_dl = https://source.ipfire.org/releases/network/
build
requires
+ asciidoc
autoconf
automake
docbook-xsl
+ json-c-devel
+ libcap-devel
libnl3-devel
libxslt
systemd-devel
new file mode 100644
@@ -0,0 +1,26 @@
+From af91a344198a1f3c47dc18905870818a0758d427 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 21:55:51 +0100
+Subject: [PATCH 001/304] Bump version to 011
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 9baab31..08e9089 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -21,7 +21,7 @@
+ AC_PREREQ([2.64])
+
+ AC_INIT([network],
+- [010],
++ [011],
+ [info@ipfire.org],
+ [network],
+ [http://www.ipfire.org/])
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,144 @@
+From b99bbd83b94d380bd07dcace8fb0e95b76b01e9f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:13:22 +0200
+Subject: [PATCH 002/304] bridge: Check input and return useful errors
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/bridge | 80 +++++++++++++++++++++++++++++++++---------
+ 1 file changed, 63 insertions(+), 17 deletions(-)
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 38b2b5f..838a513 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -23,13 +23,12 @@
+
+ HOOK_MANPAGE="network-zone-bridge"
+
+-HOOK_SETTINGS="HOOK STP STP_FORWARD_DELAY STP_HELLO STP_MAXAGE"
+-HOOK_SETTINGS="${HOOK_SETTINGS} STP_PRIORITY MAC MTU"
++HOOK_SETTINGS="HOOK ADDRESS STP STP_FORWARD_DELAY STP_HELLO STP_MAXAGE"
++HOOK_SETTINGS="${HOOK_SETTINGS} STP_PRIORITY MTU"
+
+ HOOK_PORT_SETTINGS="COST PRIORITY"
+
+ # Default values
+-MAC=""
+ MTU=1500
+ STP="on"
+ STP_FORWARD_DELAY=0
+@@ -38,7 +37,9 @@ STP_MAXAGE=20
+ STP_PRIORITY=512
+
+ hook_check_settings() {
+- assert ismac MAC
++ assert ismac ADDRESS
++
++ # Spanning Tree Protocol
+ assert isbool STP
+ assert isinteger STP_HELLO
+ assert isinteger STP_FORWARD_DELAY
+@@ -49,33 +50,78 @@ hook_check_settings() {
+ hook_parse_cmdline() {
+ while [ $# -gt 0 ]; do
+ case "${1}" in
++ --address=*)
++ ADDRESS="$(cli_get_val "${1}")"
++
++ if ! mac_is_valid "${ADDRESS}"; then
++ error "Invalid MAC address: ${ADDRESS}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
++
++ --mtu=*)
++ MTU="$(cli_get_val "${1}")"
++
++ if ! mtu_is_valid "ethernet" "${MTU}"; then
++ error "Invalid MTU: ${MTU}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
++
+ --stp=*)
+- STP=${1#--stp=}
++ STP="$(cli_get_val "${1}")"
++
++ if enabled STP; then
++ STP="on"
++ elif disabled STP; then
++ STP="off"
++ else
++ error "Invalid value for STP: ${STP}"
++ return ${EXIT_ERROR}
++ fi
+ ;;
++
+ --stp-hello=*)
+- STP_HELLO=${1#--stp-hello=}
++ STP_HELLO="$(cli_get_val "${1}")"
++
++ if ! isinteger STP_HELLO; then
++ error "Invalid STP hello time: ${STP_HELLO}"
++ return ${EXIT_ERROR}
++ fi
+ ;;
++
+ --stp-forward-delay=*)
+- STP_FORWARD_DELAY=${1#--stp-forward-delay=}
++ STP_FORWARD_DELAY="$(cli_get_val "${1}")"
++
++ if ! isinteger STP_FORWARD_DELAY; then
++ error "Invalid STP forwarding delay: ${STP_FORWARD_DELAY}"
++ return ${EXIT_ERROR}
++ fi
+ ;;
++
+ --stp-priority=*)
+- STP_PRIORITY=${1#--stp-priority=}
+- ;;
+- --mtu=*)
+- MTU=${1#--mtu=}
+- ;;
+- --mac=*)
+- MAC=${1#--mac=}
++ STP_PRIORITY="$(cli_get_val "${1}")"
++
++ if ! isinteger STP_PRIORITY; then
++ error "Invalid STP priority: ${STP_PRIORITY}"
++ return ${EXIT_ERROR}
++ fi
+ ;;
++
+ *)
+- warning "Ignoring unknown option '${1}'"
++ error "Unknown argument: ${1}"
++ return ${EXIT_ERROR}
+ ;;
+ esac
+ shift
+ done
+
+ # Generate a random MAC address if the user passed no one
+- isset MAC || MAC="$(mac_generate)"
++ if isset ADDRESS; then
++ ADDRESS="$(mac_generate)"
++ fi
++
++ return ${EXIT_OK}
+ }
+
+ hook_up() {
+@@ -87,7 +133,7 @@ hook_up() {
+ # Create the bridge if it does not already exist.
+ if ! device_exists "${zone}"; then
+ bridge_create "${zone}" \
+- --address="${MAC}" \
++ --address="${ADDRESS}" \
+ --mtu="${MTU}"
+ fi
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From d95e2fdc65aeeca72ef326102f26727199b27b95 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:15:26 +0200
+Subject: [PATCH 003/304] bridge: Fix assertion for MTU
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/bridge | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 838a513..d610814 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -38,13 +38,13 @@ STP_PRIORITY=512
+
+ hook_check_settings() {
+ assert ismac ADDRESS
++ assert isset MTU && assert mtu_is_valid "ethernet" "${MTU}"
+
+ # Spanning Tree Protocol
+ assert isbool STP
+ assert isinteger STP_HELLO
+ assert isinteger STP_FORWARD_DELAY
+ assert isinteger STP_PRIORITY
+- assert isinteger MTU
+ }
+
+ hook_parse_cmdline() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,127 @@
+From 1fc4b3cac15c709b3a6f4a3171265a5cff793f47 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:17:30 +0200
+Subject: [PATCH 004/304] bridge: Reorder functions into the common order
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/bridge | 96 +++++++++++++++++++++---------------------
+ 1 file changed, 49 insertions(+), 47 deletions(-)
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index d610814..fb81673 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -172,53 +172,6 @@ hook_up() {
+ exit ${EXIT_OK}
+ }
+
+-hook_hotplug() {
+- local zone="${1}"
+- assert isset zone
+-
+- case "$(hotplug_action)" in
+- add)
+- # Attach all ports when zone is coming up
+- if hotplug_event_interface_is_zone "${zone}"; then
+- # Bring up all ports
+- local port
+- for port in $(zone_get_ports "${zone}"); do
+- log DEBUG "Trying to attach port ${port} to ${zone}"
+-
+- hook_port_up "${zone}" "${port}"
+- done
+-
+- # Handle ports of this zone that have just been added
+- elif hotplug_event_interface_is_port_of_zone "${zone}"; then
+- # Attach the device if the parent bridge is up
+- if zone_is_active "${zone}"; then
+- hook_port_up "${zone}" "${INTERFACE}"
+- fi
+- fi
+- ;;
+- remove)
+- if hotplug_event_interface_is_zone "${zone}"; then
+- # Bring down/destroy all ports
+- local port
+- for port in $(zone_get_ports "${zone}"); do
+- log DEBUG "Trying to detach port ${port} from ${zone}"
+-
+- hook_port_down "${zone}" "${port}"
+- done
+-
+- # Handle ports of this zone that have just been removed
+- elif hotplug_event_interface_is_port_of_zone "${zone}"; then
+- hook_port_down "${zone}" "${INTERFACE}"
+- fi
+- ;;
+- *)
+- exit ${EXIT_NOT_HANDLED}
+- ;;
+- esac
+-
+- exit ${EXIT_OK}
+-}
+-
+ hook_down() {
+ local zone="${1}"
+ assert isset zone
+@@ -294,6 +247,55 @@ hook_status() {
+ exit ${EXIT_OK}
+ }
+
++hook_hotplug() {
++ local zone="${1}"
++ assert isset zone
++
++ case "$(hotplug_action)" in
++ add)
++ # Attach all ports when zone is coming up
++ if hotplug_event_interface_is_zone "${zone}"; then
++ # Bring up all ports
++ local port
++ for port in $(zone_get_ports "${zone}"); do
++ log DEBUG "Trying to attach port ${port} to ${zone}"
++
++ hook_port_up "${zone}" "${port}"
++ done
++
++ # Handle ports of this zone that have just been added
++ elif hotplug_event_interface_is_port_of_zone "${zone}"; then
++ # Attach the device if the parent bridge is up
++ if zone_is_active "${zone}"; then
++ hook_port_up "${zone}" "${INTERFACE}"
++ fi
++ fi
++ ;;
++
++ remove)
++ if hotplug_event_interface_is_zone "${zone}"; then
++ # Bring down/destroy all ports
++ local port
++ for port in $(zone_get_ports "${zone}"); do
++ log DEBUG "Trying to detach port ${port} from ${zone}"
++
++ hook_port_down "${zone}" "${port}"
++ done
++
++ # Handle ports of this zone that have just been removed
++ elif hotplug_event_interface_is_port_of_zone "${zone}"; then
++ hook_port_down "${zone}" "${INTERFACE}"
++ fi
++ ;;
++
++ *)
++ exit ${EXIT_NOT_HANDLED}
++ ;;
++ esac
++
++ exit ${EXIT_OK}
++}
++
+ hook_check_port_settings() {
+ if isset COST; then
+ assert isinteger COST
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,78 @@
+From c259c985bc98ad89350f81b68db58925163a43eb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:29:25 +0200
+Subject: [PATCH 005/304] bridge: Set proper defaults
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hook | 12 ++++++++++++
+ src/hooks/zones/bridge | 20 +++++++++++++-------
+ 2 files changed, 25 insertions(+), 7 deletions(-)
+
+diff --git a/src/functions/functions.hook b/src/functions/functions.hook
+index ad51ad5..2f3ced0 100644
+--- a/src/functions/functions.hook
++++ b/src/functions/functions.hook
+@@ -124,6 +124,18 @@ hook_help() {
+ exit $?
+ }
+
++# Sets all settings in HOOK_SETTINGS to their DEFAULT_* values
++hook_set_defaults() {
++ local setting
++ for setting in ${HOOK_SETTINGS}; do
++ local default="DEFAULT_${setting}"
++
++ if isset ${default}; then
++ assign "${setting}" "${!default}"
++ fi
++ done
++}
++
+ config_get_hook() {
+ local config=${1}
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index fb81673..1144ba0 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -29,12 +29,10 @@ HOOK_SETTINGS="${HOOK_SETTINGS} STP_PRIORITY MTU"
+ HOOK_PORT_SETTINGS="COST PRIORITY"
+
+ # Default values
+-MTU=1500
+-STP="on"
+-STP_FORWARD_DELAY=0
+-STP_HELLO=2
+-STP_MAXAGE=20
+-STP_PRIORITY=512
++DEFAULT_STP_FORWARD_DELAY=0
++DEFAULT_STP_HELLO=2
++DEFAULT_STP_MAXAGE=20
++DEFAULT_STP_PRIORITY=512
+
+ hook_check_settings() {
+ assert ismac ADDRESS
+@@ -117,10 +115,18 @@ hook_parse_cmdline() {
+ done
+
+ # Generate a random MAC address if the user passed no one
+- if isset ADDRESS; then
++ if ! isset ADDRESS; then
+ ADDRESS="$(mac_generate)"
+ fi
+
++ # Enable Spanning Tree Protocol by default
++ if ! isset STP; then
++ STP="on"
++ fi
++
++ # Set all other defaults
++ hook_set_defaults
++
+ return ${EXIT_OK}
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,46 @@
+From b76b7d88a5fc7271e9a16d4acb531cdfe45f3957 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:31:43 +0200
+Subject: [PATCH 006/304] bridge: Order arguments in alphabetical order
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/bridge | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 1144ba0..98aaef8 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -79,20 +79,20 @@ hook_parse_cmdline() {
+ fi
+ ;;
+
+- --stp-hello=*)
+- STP_HELLO="$(cli_get_val "${1}")"
++ --stp-forward-delay=*)
++ STP_FORWARD_DELAY="$(cli_get_val "${1}")"
+
+- if ! isinteger STP_HELLO; then
+- error "Invalid STP hello time: ${STP_HELLO}"
++ if ! isinteger STP_FORWARD_DELAY; then
++ error "Invalid STP forwarding delay: ${STP_FORWARD_DELAY}"
+ return ${EXIT_ERROR}
+ fi
+ ;;
+
+- --stp-forward-delay=*)
+- STP_FORWARD_DELAY="$(cli_get_val "${1}")"
++ --stp-hello=*)
++ STP_HELLO="$(cli_get_val "${1}")"
+
+- if ! isinteger STP_FORWARD_DELAY; then
+- error "Invalid STP forwarding delay: ${STP_FORWARD_DELAY}"
++ if ! isinteger STP_HELLO; then
++ error "Invalid STP hello time: ${STP_HELLO}"
+ return ${EXIT_ERROR}
+ fi
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,33 @@
+From 0f8d47058e6dedc5f20caf367a5296647ec950d1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Sep 2018 23:32:40 +0200
+Subject: [PATCH 007/304] bridge: Add option to missing --stp-max-age=
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/bridge | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 98aaef8..93a3a31 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -97,6 +97,15 @@ hook_parse_cmdline() {
+ fi
+ ;;
+
++ --stp-max-age=*)
++ STP_MAXAGE="$(cli_get_val "${1}")"
++
++ if ! isinteger STP_MAXAGE; then
++ error "Invalid STP max age: ${STP_MAXAGE}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
++
+ --stp-priority=*)
+ STP_PRIORITY="$(cli_get_val "${1}")"
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From 5b29153cd4527392d6ca4bf8d3cba491db8d490e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Oct 2018 00:07:37 +0200
+Subject: [PATCH 008/304] Remove unused function
+
+Fixes: #11423
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.zone | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/src/functions/functions.zone b/src/functions/functions.zone
+index b9d475f..57e0b71 100644
+--- a/src/functions/functions.zone
++++ b/src/functions/functions.zone
+@@ -619,10 +619,6 @@ zone_config_list() {
+ done
+ }
+
+-zone_config_show() {
+- zone_config_cmd "show" "$@"
+-}
+-
+ # Returns a list of all used ids for a zone
+ zone_config_list_ids() {
+ assert [ $# -eq 1 ]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,33 @@
+From 7b9557028a381206c573e42a7f5294d20aa0609b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Oct 2018 01:02:27 +0200
+Subject: [PATCH 009/304] bonding; Validate any MAC address passed
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/bonding | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/hooks/ports/bonding b/src/hooks/ports/bonding
+index 2880a78..40d849f 100644
+--- a/src/hooks/ports/bonding
++++ b/src/hooks/ports/bonding
+@@ -39,8 +39,14 @@ hook_parse_cmdline() {
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+ --address=*)
+- ADDRESS=$(cli_get_val "${1}")
++ ADDRESS="$(cli_get_val "${1}")"
++
++ if ! mac_is_valid "${ADDRESS}"; then
++ error "Invalid MAC address: ${ADDRESS}"
++ return ${EXIT_ERROR}
++ fi
+ ;;
++
+ --miimon=*)
+ MIIMON=$(cli_get_val "${1}")
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,34 @@
+From ae2c5b2b954bfc5282f0ef359d0960a2cd610e14 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Dec 2018 13:38:13 +0100
+Subject: [PATCH 010/304] ip-tunnel: Set TTL to 255 by default
+
+By default, the Linux kernel inherits the TTL of the transported
+packet. Usually with BGP, the TTL is deliberately set to 1 or very
+low numbers which causes the packet to be dropped after the first
+hop.
+
+Since the tunnel should be routed, we set this to a default value
+of 255 and ignore the TTL of the encapsulated packet.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.ip-tunnel | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.ip-tunnel b/src/functions/functions.ip-tunnel
+index 1184a84..11eb3c7 100644
+--- a/src/functions/functions.ip-tunnel
++++ b/src/functions/functions.ip-tunnel
+@@ -77,7 +77,7 @@ ip_tunnel_add() {
+ shift
+
+ local mode
+- local ttl
++ local ttl=255
+
+ local address
+ local remote_address
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,107 @@
+From 6a1b0fb170c7d66559935a6a4f8ee0e2bfdbf485 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 16 Dec 2018 17:10:47 +0000
+Subject: [PATCH 011/304] bird: Add some generic configuration file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ src/functions/functions.bird | 74 ++++++++++++++++++++++++++++++++++++
+ 2 files changed, 75 insertions(+)
+ create mode 100644 src/functions/functions.bird
+
+diff --git a/Makefile.am b/Makefile.am
+index 399652e..0139f95 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -127,6 +127,7 @@ EXTRA_DIST += \
+ dist_network_DATA = \
+ src/functions/functions.at \
+ src/functions/functions.auth \
++ src/functions/functions.bird \
+ src/functions/functions.bonding \
+ src/functions/functions.bridge \
+ src/functions/functions.cli \
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+new file mode 100644
+index 0000000..9c8b006
+--- /dev/null
++++ b/src/functions/functions.bird
+@@ -0,0 +1,74 @@
++#!/bin/bash
++###############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2018 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++###############################################################################
++
++BIRD_CONF="/etc/bird.conf"
++
++bird_start() {
++ service_start "bird.service"
++}
++
++bird_stop() {
++ service_stop "bird.service"
++}
++
++bird_reload() {
++ service_reload "bird.service"
++}
++
++bird_generate_config() {
++ log DEBUG "Write BIRD configuration file"
++
++ # Write header
++ config_header "bird" > ${BIRD_CONF}
++
++ # Write some basic settings
++ local proto
++ (
++ print "# Log everything to syslog"
++ print "log syslog all;"
++ print
++
++ print "# Turn on internal watchdog"
++ print "watchdog warning 5s;"
++ print "watchdog timeout 30s;"
++ print
++
++ print "# Define default route tables"
++ print "ipv6 table master6;"
++ print "ipv4 table master4;"
++
++ print "# Enable device configuration"
++ print "protocol device {}"
++ print
++
++ print "# Export all routes to kernel"
++ for proto in ipv6 ipv4; do
++ print "protocol kernel {"
++ print " ${proto} {"
++ print " table ${proto/ipv/master};"
++ print " export all;"
++ print " };"
++ print " learn;"
++ print "}"
++ print
++ done
++ ) >> ${BIRD_CONF}
++}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,146 @@
+From 0a5787976dd85db212fc5046c85d2aad6c64da5c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 16 Dec 2018 17:47:57 +0000
+Subject: [PATCH 012/304] bird: Apply static routes instead of doing that
+ manually with ip
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.bird | 52 ++++++++++++++++++++++++++++++++-
+ src/functions/functions.route | 38 +++---------------------
+ src/functions/functions.routing | 3 --
+ 3 files changed, 55 insertions(+), 38 deletions(-)
+
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index 9c8b006..c6fea32 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -60,7 +60,7 @@ bird_generate_config() {
+ print
+
+ print "# Export all routes to kernel"
+- for proto in ipv6 ipv4; do
++ for proto in ${IP_SUPPORTED_PROTOCOLS}; do
+ print "protocol kernel {"
+ print " ${proto} {"
+ print " table ${proto/ipv/master};"
+@@ -71,4 +71,54 @@ bird_generate_config() {
+ print
+ done
+ ) >> ${BIRD_CONF}
++
++ # Static routes
++ for proto in ${IP_SUPPORTED_PROTOCOLS}; do
++ print "protocol static {"
++ print " ${proto};"
++ print
++
++ # Read routes for this protocol from configuration
++ __bird_static_routes "${proto}"
++
++ print "}"
++ print
++ done >> ${BIRD_CONF}
++}
++
++__bird_static_routes() {
++ local proto="${1}"
++ assert isset proto
++
++ local ${NETWORK_CONFIG_ROUTES_PARAMS}
++ local line
++ while read line; do
++ route_parse_line "${line}"
++ [ $? -eq ${EXIT_OK} ] || continue
++
++ local type
++ local arg
++ for arg in unreachable prohibit blackhole; do
++ if enabled "${arg}"; then
++ type="${arg}"
++ break
++ fi
++ done
++
++ # Skip all routes of another protocol
++ local _proto="$(ip_detect_protocol "${network}")"
++ if [ "${proto}" != "${_proto}" ]; then
++ continue
++ fi
++
++ case "${type}" in
++ unreachable|prohibit|blackhole)
++ print " route ${network} ${type};"
++ ;;
++
++ *)
++ print " route ${network} via ${gateway};"
++ ;;
++ esac
++ done < ${NETWORK_CONFIG_ROUTES}
+ }
+diff --git a/src/functions/functions.route b/src/functions/functions.route
+index 7ca4f59..e6ea244 100644
+--- a/src/functions/functions.route
++++ b/src/functions/functions.route
+@@ -393,41 +393,11 @@ route_parse_line() {
+ }
+
+ route_apply() {
+- local table="static"
+- local type
++ # Re-generate BIRD configuration
++ bird_generate_config
+
+- log DEBUG "Applying static routes..."
+-
+- # Flush the routing table.
+- route_table_flush ${table}
+-
+- local ${NETWORK_CONFIG_ROUTES_PARAMS}
+- local line
+- while read line; do
+- route_parse_line ${line}
+- [ $? -eq ${EXIT_OK} ] || continue
+-
+- type="unicast"
+- local arg
+- for arg in unreachable prohibit blackhole; do
+- if enabled ${arg}; then
+- type="${arg}"
+- break
+- fi
+- done
+-
+- # Add the route.
+- route_entry_add ${network} --table="static" --proto="static" \
+- --type="${type}" --gateway="${gateway}" --mtu="${mtu}"
+- local ret=$?
+-
+- if [ ${ret} -ne ${EXIT_OK} ]; then
+- log WARNING "Could not set route '${network}'."
+- fi
+- done < ${NETWORK_CONFIG_ROUTES}
+-
+- # Create a lookup rule for the static routing table.
+- route_rule_add --lookup="static" --priority=1000
++ # Reload the daemon
++ bird_reload
+ }
+
+ route_entry_add() {
+diff --git a/src/functions/functions.routing b/src/functions/functions.routing
+index 2436585..c7aac09 100644
+--- a/src/functions/functions.routing
++++ b/src/functions/functions.routing
+@@ -181,7 +181,4 @@ routing_update() {
+ cmd ${routing_cmd}
+
+ cmd ${ip_cmd} rule add from ${local_ip_address} lookup ${table}
+-
+- # Apply all static routes
+- route_apply
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From eb6b47dcc7d5d541064ad90787ae55df3c3a8453 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 16 Dec 2018 17:55:25 +0000
+Subject: [PATCH 013/304] bird: (Re-)generate configuration when network is
+ initialised
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/network b/src/network
+index 69d77d5..b8f734e 100644
+--- a/src/network
++++ b/src/network
+@@ -1410,6 +1410,9 @@ case "${action}" in
+ # Update resolv.conf(5) when initializing the network
+ dns_generate_resolvconf
+
++ # Update bird configuration
++ bird_generate_config
++
+ # Also execute all triggers
+ triggers_execute_all "init"
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From c27b38b437fa82a2227d554f4855c116395995ce Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 13 Feb 2019 17:45:05 +0000
+Subject: [PATCH 014/304] dns: Always enable EDNS0
+
+This is for all DNS queries originating from the firewall.
+
+Since we have had DNS Flag Day, we are expecting all DNS servers
+to support this now. If not, then you are very unlucky.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.dns | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/functions/functions.dns b/src/functions/functions.dns
+index 4cd5cb4..890f1ac 100644
+--- a/src/functions/functions.dns
++++ b/src/functions/functions.dns
+@@ -245,6 +245,9 @@ dns_generate_resolvconf() {
+
+ config_header "resolver configutation file" > ${file}
+
++ # Always enable EDNS0
++ print "option edns0\n" >> "${file}"
++
+ if enabled DNS_RANDOMIZE; then
+ print "option rotate\n" >> ${file}
+ fi
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 469bc87f91538d668a32f9c38a3d8b1b4679c7ae Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 19:46:06 +0100
+Subject: [PATCH 015/304] wireless-ap: Use automatic channel selection (ACS) by
+ default
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/wireless-ap | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 32d1a5a..52ca238 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -28,7 +28,7 @@ HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY"
+
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+-CHANNEL=1
++CHANNEL=0
+ ENCRYPTION=""
+ KEY=""
+ SSID=
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,102 @@
+From 7b297fb22fb16db920d68224b232e5acc652688a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 19:58:25 +0100
+Subject: [PATCH 016/304] wireless-ap: Allow to disable DFS in configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 10 +++++++++-
+ src/helpers/hostapd-config-helper | 1 +
+ src/hooks/ports/wireless-ap | 16 ++++++++++++++++
+ 3 files changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 3f64e79..e19f9b3 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -36,6 +36,7 @@ hostapd_config_write() {
+ local broadcast_ssid
+ local channel
+ local country_code="$(wireless_get_reg_domain)"
++ local dfs="on"
+ local encryption
+ local key
+ local mode
+@@ -50,6 +51,9 @@ hostapd_config_write() {
+ --channel=*)
+ channel=$(cli_get_val "${1}")
+ ;;
++ --dfs=*)
++ dfs="$(cli_get_val "${1}")"
++ ;;
+ --encryption=*)
+ encryption=$(cli_get_val "${1}")
+ ;;
+@@ -177,7 +181,11 @@ hostapd_config_write() {
+ print "ieee80211d=1"
+
+ # Enable Radar Detection
+- print "ieee80211h=1"
++ if enabled dfs; then
++ print "ieee80211h=1"
++ else
++ print "ieee80211h=0"
++ fi
+
+ print # empty line
+
+diff --git a/src/helpers/hostapd-config-helper b/src/helpers/hostapd-config-helper
+index cb12af0..30d3456 100644
+--- a/src/helpers/hostapd-config-helper
++++ b/src/helpers/hostapd-config-helper
+@@ -40,6 +40,7 @@ case "${action}" in
+ hostapd_config_write ${port} ${config_file} \
+ --broadcast-ssid="${BROADCAST_SSID}" \
+ --channel="${CHANNEL}" \
++ --dfs="${DFS}" \
+ --encryption="${ENCRYPTION}" \
+ --key="${KEY}" \
+ --mode="${MODE}" \
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 52ca238..49c0a84 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -33,12 +33,16 @@ ENCRYPTION=""
+ KEY=""
+ SSID=
+
++# Perform radar detection by default when possible
++DFS="on"
++
+ hook_check_settings() {
+ assert isset ADDRESS
+ assert ismac ADDRESS
+ assert isset BROADCAST_SSID
+ assert isbool BROADCAST_SSID
+ assert isset CHANNEL
++ assert isbool DFS
+ assert isset MODE
+ assert isoneof MODE ${HOSTAPD_SUPPORTED_MODES}
+ assert isset PHY
+@@ -63,6 +67,18 @@ hook_parse_cmdline() {
+ --channel=*)
+ CHANNEL=$(cli_get_val "${1}")
+ ;;
++ --dfs=*)
++ DFS="$(cli_get_val "${1}")"
++
++ if enabled DFS; then
++ DFS="on"
++ elif disabled DFS; then
++ DFS="off"
++ else
++ error "Invalid value for DFS: ${DFS}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
+ --encryption=*)
+ ENCRYPTION=$(cli_get_val "${1}")
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,99 @@
+From dc6d97fbf2064365f5b84496a77227b4e3ca03d6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 20:10:56 +0100
+Subject: [PATCH 017/304] hostapd: Disable DFS automatically when not supported
+ by hardware
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 2 +-
+ src/functions/functions.phy | 22 ++++++++++++++++++++++
+ src/functions/functions.wireless | 13 +++++++++++++
+ src/network | 7 +++++++
+ 4 files changed, 43 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index e19f9b3..b855994 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -181,7 +181,7 @@ hostapd_config_write() {
+ print "ieee80211d=1"
+
+ # Enable Radar Detection
+- if enabled dfs; then
++ if enabled dfs && wireless_supports_dfs "${device}"; then
+ print "ieee80211h=1"
+ else
+ print "ieee80211h=0"
+diff --git a/src/functions/functions.phy b/src/functions/functions.phy
+index 96287a5..064ca7b 100644
+--- a/src/functions/functions.phy
++++ b/src/functions/functions.phy
+@@ -188,3 +188,25 @@ phy_supports_ht_capability() {
+
+ list_match "${capability}" $(__phy_list_ht_capabilities "${phy}")
+ }
++
++# Returns TRUE if the PHY supports DFS
++phy_supports_dfs() {
++ local phy="${1}"
++ assert isset phy
++
++ local driver="$(phy_get_driver "${phy}")"
++ if ! isset driver; then
++ return ${EXIT_ERROR}
++ fi
++
++ # This is basically a whilelist of drivers which support this
++ # There is no better detection
++ case "${driver}" in
++ ath10k_*|ath9k|ath5k)
++ return ${EXIT_TRUE}
++ ;;
++ *)
++ return ${EXIT_FALSE}
++ ;;
++ esac
++}
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 3608e11..221866e 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -515,3 +515,16 @@ wireless_get_vht_caps() {
+
+ network-phy-list-vht-caps "${phy}"
+ }
++
++wireless_supports_dfs() {
++ local device="${1}"
++ assert isset device
++
++ local phy="$(device_get_phy "${device}")"
++ if ! isset phy; then
++ log ERROR "Could not determine PHY for ${device}"
++ return ${EXIT_ERROR}
++ fi
++
++ phy_supports_dfs "${phy}"
++}
+diff --git a/src/network b/src/network
+index b8f734e..de2e663 100644
+--- a/src/network
++++ b/src/network
+@@ -277,6 +277,13 @@ cli_device_status_phy() {
+ cli_space
+ fi
+
++ cli_headline 2 "Features"
++
++ cli_print_fmt1 2 "DFS" \
++ "$(phy_supports_dfs "${phy}" && print "Supported" ||Â print "Not Supported")"
++
++ cli_space
++
+ return ${EXIT_OK}
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,87 @@
+From 54094fc7ae1bc17e8d8361f7758d9404f1eeff02 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 20:50:44 +0100
+Subject: [PATCH 018/304] wireless-ap: Add CLI to set channel bandwidth
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.wireless | 20 ++++++++++++++++++++
+ src/hooks/ports/wireless-ap | 10 ++++++++++
+ 2 files changed, 30 insertions(+)
+
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 221866e..0437d27 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -29,6 +29,14 @@ WIRELESS_DEFAULT_ENCRYPTION_MODE="NONE"
+ WIRELESS_VALID_ENCRYPTION_MODES="WPA2-PSK-SHA256 WPA2-PSK \
+ WPA-PSK-SHA256 WPA-PSK NONE"
+
++declare -A WIRELESS_CHANNEL_BANDWIDTHS=(
++ ["802.11ac"]="20 40 80 160 80+80"
++ ["802.11a/n"]="20 40"
++ ["802.11a"]="20 40"
++ ["802.11g/n"]="20 40"
++ ["802.11g"]="20 40"
++)
++
+ cli_wireless() {
+ local action=${1}
+ shift 1
+@@ -309,6 +317,18 @@ wireless_channel_is_valid() {
+ return ${EXIT_FALSE}
+ }
+
++wireless_channel_bandwidth_is_valid() {
++ local mode="${1}"
++ assert isset mode
++
++ local bandwidth="${2}"
++ assert isset bandwidth
++
++ local bandwidths="${WIRELESS_CHANNEL_BANDWIDTHS["${mode}"]}"
++
++ list_match "${bandwidth}" ${bandwidths}
++}
++
+ wireless_channel_is_ht40_plus() {
+ local channel="${1}"
+ assert isinteger channel
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 49c0a84..8b626bf 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -29,6 +29,7 @@ HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY"
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+ CHANNEL=0
++CHANNEL_BANDWIDTH=
+ ENCRYPTION=""
+ KEY=""
+ SSID=
+@@ -67,6 +68,9 @@ hook_parse_cmdline() {
+ --channel=*)
+ CHANNEL=$(cli_get_val "${1}")
+ ;;
++ --channel-bandwidth=*)
++ CHANNEL_BANDWIDTH="$(cli_get_val "${1}")"
++ ;;
+ --dfs=*)
+ DFS="$(cli_get_val "${1}")"
+
+@@ -121,6 +125,12 @@ hook_parse_cmdline() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Channel bandwidth must match the mode
++ if isset CHANNEL_BANDWIDTH && ! wireless_channel_bandwidth_is_valid "${MODE}" "${CHANNEL_BANDWIDTH}"; then
++ error "Channel Bandwidth '${CHANNEL_BANDWIDTH}' is not supported"
++ return ${EXIT_ERROR}
++ fi
++
+ # Save address of phy do identify it again
+ PHY=$(phy_get ${PHY})
+ PHY=$(phy_get_address ${PHY})
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From 40c95a6b261e8fdadca97f21ff7cd2a11af3bfb3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 21:21:37 +0100
+Subject: [PATCH 019/304] wireless-ap: Forgot to add configuration variables to
+ file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/wireless-ap | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 8b626bf..5e00014 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -23,8 +23,8 @@
+
+ HOOK_PORT_PATTERN="${PORT_PATTERN_ACCESSPOINT}"
+
+-HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL MODE PHY SSID"
+-HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY"
++HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL CHANNEL_BANDWIDTH DFS MODE PHY"
++HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY SSID"
+
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,109 @@
+From f9e980d91e081613e5dcc7899c28fbdfc7a4c172 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 18 Mar 2019 21:24:02 +0100
+Subject: [PATCH 020/304] hostapd: Apply channel bandwidth to configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 26 ++++++++++++++++++++++++++
+ src/helpers/hostapd-config-helper | 1 +
+ src/hooks/ports/wireless-ap | 2 +-
+ 3 files changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index b855994..57f8c1e 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -35,6 +35,7 @@ hostapd_config_write() {
+
+ local broadcast_ssid
+ local channel
++ local channel_bandwidth
+ local country_code="$(wireless_get_reg_domain)"
+ local dfs="on"
+ local encryption
+@@ -51,6 +52,9 @@ hostapd_config_write() {
+ --channel=*)
+ channel=$(cli_get_val "${1}")
+ ;;
++ --channel-bandwidth=*)
++ channel_bandwidth="$(cli_get_val "${1}")"
++ ;;
+ --dfs=*)
+ dfs="$(cli_get_val "${1}")"
+ ;;
+@@ -107,10 +111,17 @@ hostapd_config_write() {
+ assert isset key
+ fi
+
++ # Check channel bandwidth for validity
++ if isset channel_bandwidth && ! wireless_channel_bandwidth_is_valid "${mode}" "${channel_bandwidth}"; then
++ error "Invalid channel bandwidth for ${mode}: ${channel_bandwidth}"
++ return ${EXIT_ERROR}
++ fi
++
+ # 802.11ac/n flags
+ local ieee80211ac
+ local ieee80211n
+ local vht_caps
++ local vht_oper_chwidth="0"
+ local ht_caps
+
+ local hw_mode
+@@ -149,6 +160,18 @@ hostapd_config_write() {
+
+ # Fetch HT caps
+ ht_caps="$(wireless_get_ht_caps "${device}")"
++
++ case "${channel_bandwidth}" in
++ 80)
++ vht_oper_chwidth="1"
++ ;;
++ 160)
++ vht_oper_chwidth="2"
++ ;;
++ 80+80)
++ vht_oper_chwidth="3"
++ ;;
++ esac
+ ;;
+ esac
+
+@@ -221,6 +244,9 @@ hostapd_config_write() {
+ # Enable HT caps
+ print "ht_capab=${ht_caps}"
+
++ # Wider Channels
++ print "vht_oper_chwidth=${vht_oper_chwidth}"
++
+ print
+ ) >> ${file}
+
+diff --git a/src/helpers/hostapd-config-helper b/src/helpers/hostapd-config-helper
+index 30d3456..8af3097 100644
+--- a/src/helpers/hostapd-config-helper
++++ b/src/helpers/hostapd-config-helper
+@@ -40,6 +40,7 @@ case "${action}" in
+ hostapd_config_write ${port} ${config_file} \
+ --broadcast-ssid="${BROADCAST_SSID}" \
+ --channel="${CHANNEL}" \
++ --channel-bandwidth="${CHANNEL_BANDWIDTH}" \
+ --dfs="${DFS}" \
+ --encryption="${ENCRYPTION}" \
+ --key="${KEY}" \
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 5e00014..983f0f9 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -127,7 +127,7 @@ hook_parse_cmdline() {
+
+ # Channel bandwidth must match the mode
+ if isset CHANNEL_BANDWIDTH && ! wireless_channel_bandwidth_is_valid "${MODE}" "${CHANNEL_BANDWIDTH}"; then
+- error "Channel Bandwidth '${CHANNEL_BANDWIDTH}' is not supported"
++ error "Channel Bandwidth '${CHANNEL_BANDWIDTH}' is not supported for ${MODE}"
+ return ${EXIT_ERROR}
+ fi
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,117 @@
+From 1b4aa2ca01c5d0bd45213187e6a58b4cc0f57547 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 21 Mar 2019 20:22:56 +0100
+Subject: [PATCH 021/304] wireless-ap: Enable ACS only for ath* devices
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 6 ++++++
+ src/functions/functions.phy | 22 ++++++++++++++++++++++
+ src/functions/functions.wireless | 13 +++++++++++++
+ src/hooks/ports/wireless-ap | 9 ++++++++-
+ 4 files changed, 49 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 57f8c1e..9024ab2 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -111,6 +111,12 @@ hostapd_config_write() {
+ assert isset key
+ fi
+
++ # With channel 0, ACS must be supported
++ if [ ${channel} -eq 0 ] && ! wireless_supports_acs "${device}"; then
++ error "ACS requested, but not supported by ${device}"
++ return ${EXIT_ERROR}
++ fi
++
+ # Check channel bandwidth for validity
+ if isset channel_bandwidth && ! wireless_channel_bandwidth_is_valid "${mode}" "${channel_bandwidth}"; then
+ error "Invalid channel bandwidth for ${mode}: ${channel_bandwidth}"
+diff --git a/src/functions/functions.phy b/src/functions/functions.phy
+index 064ca7b..ee0f2a2 100644
+--- a/src/functions/functions.phy
++++ b/src/functions/functions.phy
+@@ -189,6 +189,28 @@ phy_supports_ht_capability() {
+ list_match "${capability}" $(__phy_list_ht_capabilities "${phy}")
+ }
+
++# Returns TRUE if the PHY supports ACS
++phy_supports_acs() {
++ local phy="${1}"
++ assert isset phy
++
++ local driver="$(phy_get_driver "${phy}")"
++ if ! isset driver; then
++ return ${EXIT_ERROR}
++ fi
++
++ # This is basically a whilelist of drivers which support this
++ # There is no better detection
++ case "${driver}" in
++ ath10k_*|ath9k|ath5k)
++ return ${EXIT_TRUE}
++ ;;
++ *)
++ return ${EXIT_FALSE}
++ ;;
++ esac
++}
++
+ # Returns TRUE if the PHY supports DFS
+ phy_supports_dfs() {
+ local phy="${1}"
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 0437d27..9e72fe0 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -536,6 +536,19 @@ wireless_get_vht_caps() {
+ network-phy-list-vht-caps "${phy}"
+ }
+
++wireless_supports_acs() {
++ local device="${1}"
++ assert isset device
++
++ local phy="$(device_get_phy "${device}")"
++ if ! isset phy; then
++ log ERROR "Could not determine PHY for ${device}"
++ return ${EXIT_ERROR}
++ fi
++
++ phy_supports_acs "${phy}"
++}
++
+ wireless_supports_dfs() {
+ local device="${1}"
+ assert isset device
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 983f0f9..0c42b61 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -28,7 +28,7 @@ HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY SSID"
+
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+-CHANNEL=0
++CHANNEL=
+ CHANNEL_BANDWIDTH=
+ ENCRYPTION=""
+ KEY=""
+@@ -125,6 +125,13 @@ hook_parse_cmdline() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Automatically enable ACS if no channel is set and ACS is available
++ if ! isset CHANNEL && phy_supports_acs "${PHY}"; then
++ CHANNEL="0"
++
++ log INFO "Automatic Channel Selection (ACS) enabled"
++ fi
++
+ # Channel bandwidth must match the mode
+ if isset CHANNEL_BANDWIDTH && ! wireless_channel_bandwidth_is_valid "${MODE}" "${CHANNEL_BANDWIDTH}"; then
+ error "Channel Bandwidth '${CHANNEL_BANDWIDTH}' is not supported for ${MODE}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,164 @@
+From 7842c2ce43d1f185e65bb9f2beead96376e2bd34 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 21 Mar 2019 22:14:43 +0100
+Subject: [PATCH 022/304] wireless-ap: Allow setting the wireless environment
+ (indoor/outdoor)
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 26 +++++++++++++++++++++++++-
+ src/functions/functions.wireless | 9 +++++++++
+ src/helpers/hostapd-config-helper | 1 +
+ src/hooks/ports/wireless-ap | 14 +++++++++++++-
+ 4 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 9024ab2..94b06db 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -39,6 +39,7 @@ hostapd_config_write() {
+ local country_code="$(wireless_get_reg_domain)"
+ local dfs="on"
+ local encryption
++ local environment="${WIRELESS_DEFAULT_ENVIRONMENT}"
+ local key
+ local mode
+ local ssid
+@@ -61,6 +62,9 @@ hostapd_config_write() {
+ --encryption=*)
+ encryption=$(cli_get_val "${1}")
+ ;;
++ --environment=*)
++ environment="$(cli_get_val "${1}")"
++ ;;
+ --key=*)
+ key=$(cli_get_val "${1}")
+ ;;
+@@ -111,6 +115,12 @@ hostapd_config_write() {
+ assert isset key
+ fi
+
++ # Check wireless environment
++ if ! wireless_environment_is_valid "${environment}"; then
++ error "Invalid wireless environment: ${environment}"
++ return ${EXIT_ERROR}
++ fi
++
+ # With channel 0, ACS must be supported
+ if [ ${channel} -eq 0 ] && ! wireless_supports_acs "${device}"; then
+ error "ACS requested, but not supported by ${device}"
+@@ -208,6 +218,21 @@ hostapd_config_write() {
+
+ # Advertise country code and maximum transmission power
+ print "ieee80211d=1"
++ print "country_code=${country_code}"
++
++ # Wireless Environment
++ case "${environment}" in
++ indoor)
++ print "country3=0x49"
++ country3
++ ;;
++ outdoor)
++ print "country3=0x4f"
++ ;;
++ indoor+outdoor)
++ print "country3=0x20"
++ ;;
++ esac
+
+ # Enable Radar Detection
+ if enabled dfs && wireless_supports_dfs "${device}"; then
+@@ -230,7 +255,6 @@ hostapd_config_write() {
+ fi
+
+ print "channel=${channel}"
+- print "country_code=${country_code}"
+ print "ignore_broadcast_ssid=${ignore_broadcast_ssid}"
+
+ if contains_spaces "${ssid}"; then
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 9e72fe0..12204c0 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -37,6 +37,9 @@ declare -A WIRELESS_CHANNEL_BANDWIDTHS=(
+ ["802.11g"]="20 40"
+ )
+
++WIRELESS_ENVIRONMENTS=( "indoor+outdoor" "indoor" "outdoor" )
++WIRELESS_DEFAULT_ENVIRONMENT="${WIRELESS_ENVIRONMENTS[0]}"
++
+ cli_wireless() {
+ local action=${1}
+ shift 1
+@@ -561,3 +564,9 @@ wireless_supports_dfs() {
+
+ phy_supports_dfs "${phy}"
+ }
++
++wireless_environment_is_valid() {
++ local environment="${1}"
++
++ list_match "${environment}" "${WIRELESS_ENVIRONMENTS[@]}"
++}
+diff --git a/src/helpers/hostapd-config-helper b/src/helpers/hostapd-config-helper
+index 8af3097..d3292c3 100644
+--- a/src/helpers/hostapd-config-helper
++++ b/src/helpers/hostapd-config-helper
+@@ -43,6 +43,7 @@ case "${action}" in
+ --channel-bandwidth="${CHANNEL_BANDWIDTH}" \
+ --dfs="${DFS}" \
+ --encryption="${ENCRYPTION}" \
++ --environment="${ENVIRONMENT}" \
+ --key="${KEY}" \
+ --mode="${MODE}" \
+ --ssid="${SSID}" \
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 0c42b61..6db39b8 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -24,7 +24,7 @@
+ HOOK_PORT_PATTERN="${PORT_PATTERN_ACCESSPOINT}"
+
+ HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL CHANNEL_BANDWIDTH DFS MODE PHY"
+-HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION KEY SSID"
++HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION ENVIRONMENT KEY SSID"
+
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+@@ -37,6 +37,8 @@ SSID=
+ # Perform radar detection by default when possible
+ DFS="on"
+
++ENVIRONMENT="${WIRELESS_DEFAULT_ENVIRONMENT}"
++
+ hook_check_settings() {
+ assert isset ADDRESS
+ assert ismac ADDRESS
+@@ -57,6 +59,8 @@ hook_check_settings() {
+ assert [ ${#KEY} -ge 8 ]
+ assert [ ${#KEY} -le 63 ]
+ fi
++
++ assert wireless_environment_is_valid "${ENVIRONMENT}"
+ }
+
+ hook_parse_cmdline() {
+@@ -86,6 +90,14 @@ hook_parse_cmdline() {
+ --encryption=*)
+ ENCRYPTION=$(cli_get_val "${1}")
+ ;;
++ --environment=*)
++ ENVIRONMENT="$(cli_get_val "${1}")"
++
++ if ! wireless_environment_is_valid "${ENVIRONMENT}"; then
++ error "Invalid wireless environment: ${ENVIRONMENT}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
+ --key=*)
+ KEY=$(cli_get_val "${1}")
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 09f00f0df436a3280b93b7570c6b9ae3152cf21e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 11:40:32 +0100
+Subject: [PATCH 023/304] hostapd: Remove now useless comment
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 94b06db..eb177fe 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -214,8 +214,6 @@ hostapd_config_write() {
+ fi
+
+ (
+- print "# Default settings"
+-
+ # Advertise country code and maximum transmission power
+ print "ieee80211d=1"
+ print "country_code=${country_code}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,30 @@
+From 9602617288e200c0935d5888746f58c23b2f7af7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 11:45:03 +0100
+Subject: [PATCH 024/304] hostapd: Always enable Transmit Power Control
+
+Also advertise this to clients
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index eb177fe..dd52e56 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -232,6 +232,10 @@ hostapd_config_write() {
+ ;;
+ esac
+
++ # Always advertise TPC
++ print "local_pwr_constraint=3"
++ print "spectrum_mgmt_required=1"
++
+ # Enable Radar Detection
+ if enabled dfs && wireless_supports_dfs "${device}"; then
+ print "ieee80211h=1"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,72 @@
+From fcdbed86e00c02550682c110d768ff9a557ba8d7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 12:02:25 +0100
+Subject: [PATCH 025/304] hostapd: Set default WMM settings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 47 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 46 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index dd52e56..911a141 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -265,8 +265,53 @@ hostapd_config_write() {
+ print "ssid=${ssid}"
+ fi
+
+- # WMM
++ # WMM & WMM-PS Unscheduled Automatic Power Save Delivery
+ print "wmm_enabled=${wmm}"
++ print "uapsd_advertisement_enabled=1"
++
++ # Low Priority / AC_BK = Background
++ print "wmm_ac_bk_cwmin=4"
++ print "wmm_ac_bk_cwmax=10"
++ print "wmm_ac_bk_aifs=7"
++ print "wmm_ac_bk_txop_limit=0"
++ print "wmm_ac_bk_acm=0"
++ print "tx_queue_data3_aifs=7"
++ print "tx_queue_data3_cwmin=15"
++ print "tx_queue_data3_cwmax=1023"
++ print "tx_queue_data3_burst=0"
++
++ # Normal Priority / AC_BE = Best Effort
++ print "wmm_ac_be_aifs=3"
++ print "wmm_ac_be_cwmin=4"
++ print "wmm_ac_be_cwmax=10"
++ print "wmm_ac_be_txop_limit=0"
++ print "wmm_ac_be_acm=0"
++ print "tx_queue_data2_aifs=3"
++ print "tx_queue_data2_cwmin=15"
++ print "tx_queue_data2_cwmax=63"
++ print "tx_queue_data2_burst=0"
++
++ # High Priority / AC_VI = Video
++ print "wmm_ac_vi_aifs=2"
++ print "wmm_ac_vi_cwmin=3"
++ print "wmm_ac_vi_cwmax=4"
++ print "wmm_ac_vi_txop_limit=94"
++ print "wmm_ac_vi_acm=0"
++ print "tx_queue_data1_aifs=1"
++ print "tx_queue_data1_cwmin=7"
++ print "tx_queue_data1_cwmax=15"
++ print "tx_queue_data1_burst=3.0"
++
++ # Highest Priority / AC_VO = Voice
++ print "wmm_ac_vo_aifs=2"
++ print "wmm_ac_vo_cwmin=2"
++ print "wmm_ac_vo_cwmax=3"
++ print "wmm_ac_vo_txop_limit=47"
++ print "wmm_ac_vo_acm=0"
++ print "tx_queue_data0_aifs=1"
++ print "tx_queue_data0_cwmin=3"
++ print "tx_queue_data0_cwmax=7"
++ print "tx_queue_data0_burst=1.5"
+
+ # Enable VHT caps
+ if isset vht_caps; then
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 4d4bca7eec3d036e1cbed28fc823d06d08008d78 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 12:02:46 +0100
+Subject: [PATCH 026/304] hostapd: Kick stations that are too far away
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 911a141..8b281cc 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -265,6 +265,9 @@ hostapd_config_write() {
+ print "ssid=${ssid}"
+ fi
+
++ # Kick stations that are too far away
++ print "disassoc_low_ack=1"
++
+ # WMM & WMM-PS Unscheduled Automatic Power Save Delivery
+ print "wmm_enabled=${wmm}"
+ print "uapsd_advertisement_enabled=1"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,34 @@
+From 4873f3299807fb0fde7c7f71736dd9318c708ca1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 12:08:08 +0100
+Subject: [PATCH 027/304] hostapd: Always qoute SSID
+
+hostapd has a new parameter that always allows us to set
+the SSID as a quoted UTF8 string
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 8b281cc..245b4cf 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -259,11 +259,8 @@ hostapd_config_write() {
+ print "channel=${channel}"
+ print "ignore_broadcast_ssid=${ignore_broadcast_ssid}"
+
+- if contains_spaces "${ssid}"; then
+- print "ssid=\"${ssid}\""
+- else
+- print "ssid=${ssid}"
+- fi
++ print "ssid2=\"${ssid}\""
++ print "utf8_ssid=1"
+
+ # Kick stations that are too far away
+ print "disassoc_low_ack=1"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,130 @@
+From 34ca39360410ab03c7909494e6291bbb65622e3d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 22 Mar 2019 12:27:38 +0100
+Subject: [PATCH 028/304] wireless-ap: Allow to enable/disable 802.11w
+ Management Frame Protection
+
+This is disabled by default, because loads of stations have issues
+associating with an AP that has 802.11w enabled.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 17 +++++++++++++++++
+ src/helpers/hostapd-config-helper | 1 +
+ src/hooks/ports/wireless-ap | 18 ++++++++++++++++++
+ 3 files changed, 36 insertions(+)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 245b4cf..bf0c5fc 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -41,6 +41,7 @@ hostapd_config_write() {
+ local encryption
+ local environment="${WIRELESS_DEFAULT_ENVIRONMENT}"
+ local key
++ local mfp="off"
+ local mode
+ local ssid
+ local wmm="1"
+@@ -68,6 +69,9 @@ hostapd_config_write() {
+ --key=*)
+ key=$(cli_get_val "${1}")
+ ;;
++ --mfp=*)
++ mfp="$(cli_get_val "${1}")"
++ ;;
+ --mode=*)
+ mode=$(cli_get_val "${1}")
+
+@@ -133,6 +137,12 @@ hostapd_config_write() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Management Frame Proection
++ if ! isbool mfp; then
++ error "Invalid value for --mfp: ${mfp}"
++ return ${EXIT_ERROR}
++ fi
++
+ # 802.11ac/n flags
+ local ieee80211ac
+ local ieee80211n
+@@ -325,6 +335,13 @@ hostapd_config_write() {
+ print "vht_oper_chwidth=${vht_oper_chwidth}"
+
+ print
++
++ # 802.11w - Management Frame Protection (MFP)
++ if enabled mfp; then
++ print "ieee80211w=2" # required
++ else
++ print "ieee80211w=0"
++ fi
+ ) >> ${file}
+
+ # Control interface.
+diff --git a/src/helpers/hostapd-config-helper b/src/helpers/hostapd-config-helper
+index d3292c3..7af723d 100644
+--- a/src/helpers/hostapd-config-helper
++++ b/src/helpers/hostapd-config-helper
+@@ -45,6 +45,7 @@ case "${action}" in
+ --encryption="${ENCRYPTION}" \
+ --environment="${ENVIRONMENT}" \
+ --key="${KEY}" \
++ --mfp="${MFP}" \
+ --mode="${MODE}" \
+ --ssid="${SSID}" \
+ || exit $?
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 6db39b8..7073cbc 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -25,6 +25,7 @@ HOOK_PORT_PATTERN="${PORT_PATTERN_ACCESSPOINT}"
+
+ HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL CHANNEL_BANDWIDTH DFS MODE PHY"
+ HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION ENVIRONMENT KEY SSID"
++HOOK_SETTINGS="${HOOK_SETTINGS} MFP"
+
+ ADDRESS=$(mac_generate)
+ BROADCAST_SSID=on
+@@ -37,6 +38,10 @@ SSID=
+ # Perform radar detection by default when possible
+ DFS="on"
+
++# 802.11w - Management Frame Protection
++# Disable by default because many clients cannot connect when enabled
++MFP="off"
++
+ ENVIRONMENT="${WIRELESS_DEFAULT_ENVIRONMENT}"
+
+ hook_check_settings() {
+@@ -46,6 +51,7 @@ hook_check_settings() {
+ assert isbool BROADCAST_SSID
+ assert isset CHANNEL
+ assert isbool DFS
++ assert isbool MFP
+ assert isset MODE
+ assert isoneof MODE ${HOSTAPD_SUPPORTED_MODES}
+ assert isset PHY
+@@ -104,6 +110,18 @@ hook_parse_cmdline() {
+ --mac=*)
+ ADDRESS=$(cli_get_val "${1}")
+ ;;
++ --mfp=*)
++ MFP="$(cli_get_val "${1}")"
++
++ if enabled MFP; then
++ MFP="on"
++ elif disabled MFP; then
++ MFP="off"
++ else
++ error "Invalid value for --mfp: ${MFP}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
+ --mode=*)
+ MODE=$(cli_get_val "${1}")
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 304b20a828e0987943ccda6f1c4321682195a67a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 29 Mar 2019 18:46:25 +0100
+Subject: [PATCH 029/304] network: Show when a PHY supports ACS
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/network b/src/network
+index de2e663..3535133 100644
+--- a/src/network
++++ b/src/network
+@@ -279,6 +279,8 @@ cli_device_status_phy() {
+
+ cli_headline 2 "Features"
+
++ cli_print_fmt1 2 "Automatic Channel Selection" \
++ "$(phy_supports_acs "${phy}" && print "Supported" ||Â print "Not Supported")"
+ cli_print_fmt1 2 "DFS" \
+ "$(phy_supports_dfs "${phy}" && print "Supported" ||Â print "Not Supported")"
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,112 @@
+From 01648ba604f9d0c922193553cfcb36dae0bfddaf Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 29 Mar 2019 18:47:47 +0100
+Subject: [PATCH 030/304] Move cli_device_status_phy() to functions.phy
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.phy | 38 +++++++++++++++++++++++++++++++++++++
+ src/network | 38 -------------------------------------
+ 2 files changed, 38 insertions(+), 38 deletions(-)
+
+diff --git a/src/functions/functions.phy b/src/functions/functions.phy
+index ee0f2a2..120117c 100644
+--- a/src/functions/functions.phy
++++ b/src/functions/functions.phy
+@@ -21,6 +21,44 @@
+
+ PHY_DIR="/sys/class/ieee80211"
+
++cli_device_status_phy() {
++ local phy="${1}"
++ assert phy_exists "${phy}"
++
++ local address="$(phy_get_address "${phy}")"
++ cli_print_fmt1 1 "Address" "${address}"
++
++ # Show kernel module
++ local driver="$(phy_get_driver "${phy}")"
++ if isset driver; then
++ cli_print_fmt1 1 "Driver" "${driver}"
++ fi
++
++ cli_space
++
++ local devices="$(phy_get_devices "${phy}")"
++ if isset devices; then
++ cli_headline 2 "Soft interfaces"
++
++ local device
++ for device in ${devices}; do
++ cli_print 2 "* %s" "${device}"
++ done
++ cli_space
++ fi
++
++ cli_headline 2 "Features"
++
++ cli_print_fmt1 2 "Automatic Channel Selection" \
++ "$(phy_supports_acs "${phy}" && print "Supported" ||Â print "Not Supported")"
++ cli_print_fmt1 2 "DFS" \
++ "$(phy_supports_dfs "${phy}" && print "Supported" ||Â print "Not Supported")"
++
++ cli_space
++
++ return ${EXIT_OK}
++}
++
+ phy_dir() {
+ local phy=${1}
+
+diff --git a/src/network b/src/network
+index 3535133..300ba94 100644
+--- a/src/network
++++ b/src/network
+@@ -251,44 +251,6 @@ cli_device_status_serial() {
+ fi
+ }
+
+-cli_device_status_phy() {
+- local phy="${1}"
+- assert phy_exists "${phy}"
+-
+- local address="$(phy_get_address "${phy}")"
+- cli_print_fmt1 1 "Address" "${address}"
+-
+- # Show kernel module
+- local driver="$(phy_get_driver "${phy}")"
+- if isset driver; then
+- cli_print_fmt1 1 "Driver" "${driver}"
+- fi
+-
+- cli_space
+-
+- local devices="$(phy_get_devices "${phy}")"
+- if isset devices; then
+- cli_headline 2 "Soft interfaces"
+-
+- local device
+- for device in ${devices}; do
+- cli_print 2 "* %s" "${device}"
+- done
+- cli_space
+- fi
+-
+- cli_headline 2 "Features"
+-
+- cli_print_fmt1 2 "Automatic Channel Selection" \
+- "$(phy_supports_acs "${phy}" && print "Supported" ||Â print "Not Supported")"
+- cli_print_fmt1 2 "DFS" \
+- "$(phy_supports_dfs "${phy}" && print "Supported" ||Â print "Not Supported")"
+-
+- cli_space
+-
+- return ${EXIT_OK}
+-}
+-
+ cli_device_discover() {
+ local device=${1}
+ shift
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,52 @@
+From 7c91c167d10cbe3d390f0dc8c426eed0abf243b4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 11:26:38 +0100
+Subject: [PATCH 031/304] hostapd: Dump config file in debug mode
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 3 +++
+ src/functions/functions.util | 13 +++++++++++++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index bf0c5fc..737bd1a 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -378,6 +378,9 @@ hostapd_config_write() {
+ ) >> ${file}
+ fi
+
++ # Log configuration file
++ file_to_log DEBUG "${file}"
++
+ return ${EXIT_OK}
+ }
+
+diff --git a/src/functions/functions.util b/src/functions/functions.util
+index b767423..4c1dbb4 100644
+--- a/src/functions/functions.util
++++ b/src/functions/functions.util
+@@ -248,6 +248,19 @@ file_get_age() {
+ return ${EXIT_ERROR}
+ }
+
++file_to_log() {
++ local level="${1}"
++ assert isset level
++
++ local file="${2}"
++ assert file_exists "${file}"
++
++ local line
++ while read line; do
++ log "${level}" "${line}"
++ done < "${file}"
++}
++
+ make_directory() {
+ local path="${1}"
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,519 @@
+From 2e4e3c88ba2543e5bf4bf3f92977990c281a00bb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 11:27:50 +0100
+Subject: [PATCH 032/304] wireless-ap: Automatically enable all supported
+ ciphers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 7 ++
+ src/functions/functions.hostapd | 65 +++++++++++-
+ src/functions/functions.phy | 17 +++
+ src/libnetwork/libnetwork.sym | 37 ++++---
+ src/libnetwork/network/phy.h | 22 ++++
+ src/libnetwork/phy.c | 149 +++++++++++++++++++++++++++
+ src/utils/.gitignore | 1 +
+ src/utils/network-phy-list-ciphers.c | 61 +++++++++++
+ 8 files changed, 340 insertions(+), 19 deletions(-)
+ create mode 100644 src/utils/network-phy-list-ciphers.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 0139f95..1b5e7e9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -301,6 +301,7 @@ EXTRA_DIST += \
+
+ util_PROGRAMS = \
+ src/utils/network-phy-list-channels \
++ src/utils/network-phy-list-ciphers \
+ src/utils/network-phy-list-ht-caps \
+ src/utils/network-phy-list-vht-caps
+
+@@ -310,6 +311,12 @@ src_utils_network_phy_list_channels_SOURCES = \
+ src_utils_network_phy_list_channels_LDADD = \
+ src/libnetwork.la
+
++src_utils_network_phy_list_ciphers_SOURCES = \
++ src/utils/network-phy-list-ciphers.c
++
++src_utils_network_phy_list_ciphers_LDADD = \
++ src/libnetwork.la
++
+ src_utils_network_phy_list_ht_caps_SOURCES = \
+ src/utils/network-phy-list-ht-caps.c
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 737bd1a..6111457 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -23,6 +23,19 @@ HOSTAPD_CONTROL_INTERFACE_DIR="/run/hostapd/ctrl"
+
+ HOSTAPD_SUPPORTED_MODES="802.11a 802.11a/n 802.11ac 802.11g 802.11g/n"
+
++HOSTAPD_SUPPORTED_PAIRWISE_CIPHERS=(
++ "GCMP-256" # Galois/counter mode protocol with 256 bit key
++ "CCMP-256" # AES in Counter mode with CBC-MAC with 256 bit key
++ "GCMP-128" # Galois/counter mode protocol with 128 bit key
++ "CCMP-128" # AES in Counter mode with CBC-MAC with 128 bit key
++)
++
++# This must be supported by all stations on the network and therefore
++# can effectively only be CCMP
++HOSTAPD_SUPPORTED_GROUP_CIPHERS=(
++ "CCMP-128"
++)
++
+ hostapd_config_write() {
+ local device=${1}
+ assert isset device
+@@ -33,6 +46,16 @@ hostapd_config_write() {
+ # Shift the device and file argument.
+ shift 2
+
++ # Device must exist
++ if ! device_exists "${device}"; then
++ error "Cannot write hostapd configuration for non-existant device: ${device}"
++ return ${EXIT_ERROR}
++ fi
++
++ # Get the phy for device
++ local phy="$(device_get_phy "${device}")"
++ assert isset phy
++
+ local broadcast_ssid
+ local channel
+ local channel_bandwidth
+@@ -201,6 +224,25 @@ hostapd_config_write() {
+ ;;
+ esac
+
++ # Cryptography
++ local cipher
++
++ # Get all supported pairwise ciphers
++ local pairwise_ciphers=()
++ for cipher in ${HOSTAPD_SUPPORTED_PAIRWISE_CIPHERS[*]}; do
++ if phy_supports_cipher "${phy}" "${cipher}"; then
++ pairwise_ciphers+=( "$(hostapd_cipher_name "${cipher}")" )
++ fi
++ done
++
++ # Get all supported group ciphers
++ local group_ciphers=()
++ for cipher in ${HOSTAPD_SUPPORTED_GROUP_CIPHERS[*]}; do
++ if phy_supports_cipher "${phy}" "${cipher}"; then
++ group_ciphers+=( "$(hostapd_cipher_name "${cipher}")" )
++ fi
++ done
++
+ # Create configuration directory.
+ local config_dir=$(dirname ${file})
+ mkdir -p ${HOSTAPD_CONTROL_INTERFACE_DIR} ${config_dir} 2>/dev/null
+@@ -372,8 +414,9 @@ hostapd_config_write() {
+ print "wpa=${encryption_mode}"
+ print "wpa_passphrase=${key}"
+ print "wpa_key_mgmt=WPA-PSK"
+- print "wpa_pairwise=TKIP"
+- print "rsn_pairwise=CCMP"
++ print "wpa_pairwise=${pairwise_ciphers[*]}"
++ print "rsn_pairwise=${pairwise_ciphers[*]}"
++ print "group_cipher=${group_ciphers[*]}"
+ print
+ ) >> ${file}
+ fi
+@@ -407,3 +450,21 @@ hostapd_stop() {
+
+ service_stop "hostapd@${device}.service"
+ }
++
++hostapd_cipher_name() {
++ local cipher="${1}"
++
++ case "${cipher}" in
++ CCMP-128)
++ print "CCMP"
++ ;;
++
++ GCMP-128)
++ print "GCMP"
++ ;;
++
++ *)
++ print "${cipher}"
++ ;;
++ esac
++}
+diff --git a/src/functions/functions.phy b/src/functions/functions.phy
+index 120117c..c06389c 100644
+--- a/src/functions/functions.phy
++++ b/src/functions/functions.phy
+@@ -208,6 +208,23 @@ phy_supports_channel() {
+ return ${EXIT_FALSE}
+ }
+
++phy_list_ciphers() {
++ local phy="${1}"
++ assert isset phy
++
++ network-phy-list-ciphers "${phy}"
++}
++
++phy_supports_cipher() {
++ local phy="${1}"
++ assert isset phy
++
++ local cipher="${2}"
++ assert isset cipher
++
++ list_match "${cipher}" $(phy_list_ciphers "${phy}")
++}
++
+ __phy_list_ht_capabilities() {
+ local phy="${1}"
+ assert isset phy
+diff --git a/src/libnetwork/libnetwork.sym b/src/libnetwork/libnetwork.sym
+index 593c4a2..034d43f 100644
+--- a/src/libnetwork/libnetwork.sym
++++ b/src/libnetwork/libnetwork.sym
+@@ -1,21 +1,24 @@
+ LIBNETWORK_0 {
+ global:
+- network_interface_get_name;
+- network_interface_new;
+- network_interface_ref;
+- network_interface_unref;
+- network_new;
+- network_phy_has_ht_capability;
+- network_phy_has_vht_capability;
+- network_phy_list_channels;
+- network_phy_list_ht_capabilities;
+- network_phy_list_vht_capabilities;
+- network_phy_new;
+- network_phy_ref;
+- network_phy_unref;
+- network_ref;
+- network_unref;
+- network_version;
++ network_interface_get_name;
++ network_interface_new;
++ network_interface_ref;
++ network_interface_unref;
++ network_new;
++ network_phy_get_cipher_string;
++ network_phy_has_ht_capability;
++ network_phy_has_vht_capability;
++ network_phy_list_channels;
++ network_phy_list_ciphers;
++ network_phy_list_ht_capabilities;
++ network_phy_list_vht_capabilities;
++ network_phy_supports_cipher;
++ network_phy_new;
++ network_phy_ref;
++ network_phy_unref;
++ network_ref;
++ network_unref;
++ network_version;
+ local:
+- *;
++ *;
+ };
+diff --git a/src/libnetwork/network/phy.h b/src/libnetwork/network/phy.h
+index 9059680..bc6dafb 100644
+--- a/src/libnetwork/network/phy.h
++++ b/src/libnetwork/network/phy.h
+@@ -30,6 +30,25 @@ int network_phy_new(struct network_ctx*, struct network_phy** phy, const char* n
+ struct network_phy* network_phy_ref(struct network_phy* phy);
+ struct network_phy* network_phy_unref(struct network_phy* phy);
+
++enum network_phy_ciphers {
++ NETWORK_PHY_CIPHER_WEP40 = (1 << 0),
++ NETWORK_PHY_CIPHER_TKIP = (1 << 1),
++ NETWORK_PHY_CIPHER_CCMP128 = (1 << 2),
++ NETWORK_PHY_CIPHER_WEP104 = (1 << 3),
++ NETWORK_PHY_CIPHER_CMAC128 = (1 << 4),
++ NETWORK_PHY_CIPHER_GCMP128 = (1 << 5),
++ NETWORK_PHY_CIPHER_GCMP256 = (1 << 6),
++ NETWORK_PHY_CIPHER_CCMP256 = (1 << 7),
++ NETWORK_PHY_CIPHER_GMAC128 = (1 << 8),
++ NETWORK_PHY_CIPHER_GMAC256 = (1 << 9),
++ NETWORK_PHY_CIPHER_CMAC256 = (1 << 10),
++ NETWORK_PHY_CIPHER_WPISMS4 = (1 << 11),
++};
++
++const char* network_phy_get_cipher_string(const enum network_phy_ciphers cipher);
++int network_phy_supports_cipher(struct network_phy* phy, const enum network_phy_ciphers cipher);
++char* network_phy_list_ciphers(struct network_phy* phy);
++
+ enum network_phy_ht_caps {
+ NETWORK_PHY_HT_CAP_RX_LDPC = (1 << 0),
+ NETWORK_PHY_HT_CAP_HT40 = (1 << 1),
+@@ -81,6 +100,9 @@ char* network_phy_list_ht_capabilities(struct network_phy* phy);
+ struct nl_msg* network_phy_make_netlink_message(struct network_phy* phy,
+ enum nl80211_commands cmd, int flags);
+
++#define foreach_cipher(cipher) \
++ for(enum network_phy_ciphers cipher = NETWORK_PHY_CIPHER_WEP40; cipher <= NETWORK_PHY_CIPHER_WPISMS4; cipher <<= 1)
++
+ #define foreach_vht_cap(cap) \
+ for(int cap = NETWORK_PHY_VHT_CAP_VHT160; cap <= NETWORK_PHY_VHT_CAP_TX_ANTENNA_PATTERN; cap <<= 1)
+
+diff --git a/src/libnetwork/phy.c b/src/libnetwork/phy.c
+index 0bf9c81..e3f2aad 100644
+--- a/src/libnetwork/phy.c
++++ b/src/libnetwork/phy.c
+@@ -52,6 +52,7 @@ struct network_phy {
+
+ TAILQ_HEAD(head, network_phy_channel) channels;
+
++ enum network_phy_ciphers ciphers;
+ ssize_t max_mpdu_length;
+ unsigned int vht_caps;
+ unsigned int ht_caps;
+@@ -80,6 +81,81 @@ static int phy_get_index(const char* name) {
+ return atoi(index);
+ }
+
++static void phy_parse_ciphers(struct network_phy* phy, __u32* ciphers, int num) {
++ enum network_phy_ciphers cipher;
++
++ // Reset value
++ phy->ciphers = 0;
++
++ for (int i = 0; i < num; i++) {
++ switch (ciphers[i]) {
++ case 0x000fac01:
++ cipher = NETWORK_PHY_CIPHER_WEP40;
++ break;
++
++ case 0x000fac02:
++ cipher = NETWORK_PHY_CIPHER_TKIP;
++ break;
++
++ case 0x000fac04:
++ cipher = NETWORK_PHY_CIPHER_CCMP128;
++ break;
++
++ case 0x000fac05:
++ cipher = NETWORK_PHY_CIPHER_WEP104;
++ break;
++
++ case 0x000fac06:
++ cipher = NETWORK_PHY_CIPHER_CMAC128;
++ break;
++
++ case 0x000fac08:
++ cipher = NETWORK_PHY_CIPHER_GCMP128;
++ break;
++
++ case 0x000fac09:
++ cipher = NETWORK_PHY_CIPHER_GCMP256;
++ break;
++
++ /*
++ I have no idea what these are. My card reports them but
++ I could not find out anything about them.
++ */
++ case 0x000fac0a:
++ case 0x000fac0b:
++ case 0x000fac0c:
++ case 0x000fac0d:
++ continue;
++
++ case 0x000fac10:
++ cipher = NETWORK_PHY_CIPHER_CCMP256;
++ break;
++
++ case 0x000fac11:
++ cipher = NETWORK_PHY_CIPHER_GMAC128;
++ break;
++
++ case 0x000fac12:
++ cipher = NETWORK_PHY_CIPHER_GMAC256;
++ break;
++
++ case 0x000fac13:
++ cipher = NETWORK_PHY_CIPHER_CMAC256;
++ break;
++
++ case 0x00147201:
++ cipher = NETWORK_PHY_CIPHER_WPISMS4;
++ break;
++
++ default:
++ ERROR(phy->ctx, "Unknown cipher found: %x\n", ciphers[i]);
++ continue;
++ }
++
++ phy->ciphers |= cipher;
++ }
++}
++
+ static void phy_parse_vht_capabilities(struct network_phy* phy, __u32 caps) {
+ // Max MPDU length
+ switch (caps & 0x3) {
+@@ -325,6 +401,13 @@ static int phy_parse_info(struct nl_msg* msg, void* data) {
+ nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
++ // Ciphers
++ if (attrs[NL80211_ATTR_CIPHER_SUITES]) {
++ int num = nla_len(attrs[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
++ __u32* ciphers = nla_data(attrs[NL80211_ATTR_CIPHER_SUITES]);
++ phy_parse_ciphers(phy, ciphers, num);
++ }
++
+ if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
+ struct nlattr* nl_band;
+ int i;
+@@ -464,6 +547,72 @@ nla_put_failure:
+ return NULL;
+ }
+
++NETWORK_EXPORT const char* network_phy_get_cipher_string(const enum network_phy_ciphers cipher) {
++ switch (cipher) {
++ case NETWORK_PHY_CIPHER_WEP40:
++ return "WEP40";
++
++ case NETWORK_PHY_CIPHER_TKIP:
++ return "TKIP";
++
++ case NETWORK_PHY_CIPHER_CCMP128:
++ return "CCMP-128";
++
++ case NETWORK_PHY_CIPHER_WEP104:
++ return "WEP-104";
++
++ case NETWORK_PHY_CIPHER_CMAC128:
++ return "CMAC-128";
++
++ case NETWORK_PHY_CIPHER_GCMP128:
++ return "GCMP-128";
++
++ case NETWORK_PHY_CIPHER_GCMP256:
++ return "GCMP-256";
++
++ case NETWORK_PHY_CIPHER_CCMP256:
++ return "CCMP-256";
++
++ case NETWORK_PHY_CIPHER_GMAC128:
++ return "GMAC-128";
++
++ case NETWORK_PHY_CIPHER_GMAC256:
++ return "GMAC-256";
++
++ case NETWORK_PHY_CIPHER_CMAC256:
++ return "CMAC-256";
++
++ case NETWORK_PHY_CIPHER_WPISMS4:
++ return "WPI-SMS4";
++ }
++
++ return NULL;
++}
++
++NETWORK_EXPORT int network_phy_supports_cipher(struct network_phy* phy, const enum network_phy_ciphers cipher) {
++ return phy->ciphers & cipher;
++}
++
++NETWORK_EXPORT char* network_phy_list_ciphers(struct network_phy* phy) {
++ char* buffer = NULL;
++
++ foreach_cipher(cipher) {
++ if (network_phy_supports_cipher(phy, cipher)) {
++ const char* s = network_phy_get_cipher_string(cipher);
++
++ if (!s)
++ continue;
++
++ if (buffer)
++ asprintf(&buffer, "%s %s", buffer, s);
++ else
++ asprintf(&buffer, "%s", s);
++ }
++ }
++
++ return buffer;
++}
++
+ NETWORK_EXPORT int network_phy_has_vht_capability(struct network_phy* phy, const enum network_phy_vht_caps cap) {
+ return phy->vht_caps & cap;
+ }
+diff --git a/src/utils/.gitignore b/src/utils/.gitignore
+index 11cf3b6..df712dc 100644
+--- a/src/utils/.gitignore
++++ b/src/utils/.gitignore
+@@ -1,3 +1,4 @@
+ /network-phy-list-channels
++/network-phy-list-ciphers
+ /network-phy-list-ht-caps
+ /network-phy-list-vht-caps
+diff --git a/src/utils/network-phy-list-ciphers.c b/src/utils/network-phy-list-ciphers.c
+new file mode 100644
+index 0000000..0132c0c
+--- /dev/null
++++ b/src/utils/network-phy-list-ciphers.c
+@@ -0,0 +1,61 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2019 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdio.h>
++#include <stdlib.h>
++
++#include <network/libnetwork.h>
++#include <network/logging.h>
++#include <network/phy.h>
++
++int main(int argc, char** argv) {
++ struct network_ctx* ctx = NULL;
++ struct network_phy* phy = NULL;
++ int r;
++
++ if (argc < 2) {
++ fprintf(stderr, "No enough arguments\n");
++ r = 2;
++ goto END;
++ }
++
++ // Initialise context
++ r = network_new(&ctx);
++ if (r)
++ return r;
++
++ r = network_phy_new(ctx, &phy, argv[1]);
++ if (r) {
++ fprintf(stderr, "Could not find %s\n", argv[1]);
++ goto END;
++ }
++
++ // Print all supported ciphers
++ char* ciphers = network_phy_list_ciphers(phy);
++ if (ciphers && *ciphers) {
++ printf("%s\n", ciphers);
++ free(ciphers);
++ }
++
++END:
++ network_phy_unref(phy);
++ network_unref(ctx);
++ return r;
++}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 27380e6e6343faa0b2c1a87234ecf21ecc6f0840 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 12:47:32 +0100
+Subject: [PATCH 033/304] hostapd: Enable WPA authentication with SHA256
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 6111457..79fb4db 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -413,7 +413,7 @@ hostapd_config_write() {
+ print "# Encryption settings"
+ print "wpa=${encryption_mode}"
+ print "wpa_passphrase=${key}"
+- print "wpa_key_mgmt=WPA-PSK"
++ print "wpa_key_mgmt=WPA-PSK-SHA256 WPA-PSK"
+ print "wpa_pairwise=${pairwise_ciphers[*]}"
+ print "rsn_pairwise=${pairwise_ciphers[*]}"
+ print "group_cipher=${group_ciphers[*]}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,98 @@
+From 4637109c42417e34c02631cd8391bccc7f2733cb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 13:03:59 +0100
+Subject: [PATCH 034/304] hooks: Automatically set defaults for all port hooks
+
+Before, this was broken so that all configuration parameters
+had to be passed all the time.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hook | 5 ++---
+ src/header-port | 4 ++++
+ src/hooks/ports/bonding | 5 ++---
+ src/hooks/ports/wireless-ap | 15 +++++----------
+ 4 files changed, 13 insertions(+), 16 deletions(-)
+
+diff --git a/src/functions/functions.hook b/src/functions/functions.hook
+index 2f3ced0..c0ebfcb 100644
+--- a/src/functions/functions.hook
++++ b/src/functions/functions.hook
+@@ -130,9 +130,8 @@ hook_set_defaults() {
+ for setting in ${HOOK_SETTINGS}; do
+ local default="DEFAULT_${setting}"
+
+- if isset ${default}; then
+- assign "${setting}" "${!default}"
+- fi
++ # Sets the default or empty
++ assign "${setting}" "${!default}"
+ done
+ }
+
+diff --git a/src/header-port b/src/header-port
+index ce1c192..141228a 100644
+--- a/src/header-port
++++ b/src/header-port
+@@ -44,6 +44,10 @@ hook_hotplug_rename() {
+
+ hook_default_new() {
+ local ${HOOK_SETTINGS}
++
++ # Import all default variables
++ hook_set_defaults
++
+ if ! hook_parse_cmdline "$@"; then
+ return ${EXIT_ERROR}
+ fi
+diff --git a/src/hooks/ports/bonding b/src/hooks/ports/bonding
+index 40d849f..f0572c3 100644
+--- a/src/hooks/ports/bonding
++++ b/src/hooks/ports/bonding
+@@ -23,9 +23,8 @@
+
+ HOOK_SETTINGS="ADDRESS MIIMON MODE OFFLOADING SLAVES"
+
+-SLAVES=""
+-MIIMON=100
+-MODE="balance-rr"
++DEFAULT_MIIMON=100
++DEFAULT_MODE="balance-rr"
+
+ hook_check_settings() {
+ assert isset ADDRESS
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 7073cbc..2bb4977 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -27,22 +27,17 @@ HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL CHANNEL_BANDWIDTH DFS MODE PHY"
+ HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION ENVIRONMENT KEY SSID"
+ HOOK_SETTINGS="${HOOK_SETTINGS} MFP"
+
+-ADDRESS=$(mac_generate)
+-BROADCAST_SSID=on
+-CHANNEL=
+-CHANNEL_BANDWIDTH=
+-ENCRYPTION=""
+-KEY=""
+-SSID=
++# Broadcast SSID by default
++DEFAULT_BROADCAST_SSID="on"
+
+ # Perform radar detection by default when possible
+-DFS="on"
++DEFAULT_DFS="on"
+
+ # 802.11w - Management Frame Protection
+ # Disable by default because many clients cannot connect when enabled
+-MFP="off"
++DEFAULT_MFP="off"
+
+-ENVIRONMENT="${WIRELESS_DEFAULT_ENVIRONMENT}"
++DEFAULT_ENVIRONMENT="${WIRELESS_DEFAULT_ENVIRONMENT}"
+
+ hook_check_settings() {
+ assert isset ADDRESS
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,176 @@
+From 53e764a73d5a04f653a4fda3c7f8810e8de13ed8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 13:10:58 +0100
+Subject: [PATCH 035/304] hooks: Import zone default settings, too
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/header-zone | 5 +++++
+ src/hooks/zones/bridge | 11 ++---------
+ src/hooks/zones/ip-tunnel | 8 +-------
+ src/hooks/zones/modem | 12 +-----------
+ src/hooks/zones/pppoe | 20 ++------------------
+ 5 files changed, 11 insertions(+), 45 deletions(-)
+
+diff --git a/src/header-zone b/src/header-zone
+index ead4a32..7ad3e39 100644
+--- a/src/header-zone
++++ b/src/header-zone
+@@ -34,6 +34,11 @@ hook_new() {
+ assert isset zone
+ shift
+
++ local ${HOOK_SETTINGS}
++
++ # Import all default variables
++ hook_set_defaults
++
+ if ! hook_parse_cmdline "$@"; then
+ return ${EXIT_ERROR}
+ fi
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 93a3a31..33d5811 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -29,6 +29,7 @@ HOOK_SETTINGS="${HOOK_SETTINGS} STP_PRIORITY MTU"
+ HOOK_PORT_SETTINGS="COST PRIORITY"
+
+ # Default values
++DEFAULT_STP="on"
+ DEFAULT_STP_FORWARD_DELAY=0
+ DEFAULT_STP_HELLO=2
+ DEFAULT_STP_MAXAGE=20
+@@ -123,19 +124,11 @@ hook_parse_cmdline() {
+ shift
+ done
+
+- # Generate a random MAC address if the user passed no one
++ # Generate a random MAC address if the user passed none
+ if ! isset ADDRESS; then
+ ADDRESS="$(mac_generate)"
+ fi
+
+- # Enable Spanning Tree Protocol by default
+- if ! isset STP; then
+- STP="on"
+- fi
+-
+- # Set all other defaults
+- hook_set_defaults
+-
+ return ${EXIT_OK}
+ }
+
+diff --git a/src/hooks/zones/ip-tunnel b/src/hooks/zones/ip-tunnel
+index c9c73ba..e4be361 100644
+--- a/src/hooks/zones/ip-tunnel
++++ b/src/hooks/zones/ip-tunnel
+@@ -26,13 +26,7 @@ SUPPORTED_IP_TUNNEL_MODES="gre vti"
+ HOOK_SETTINGS="HOOK MARK MODE PEER LOCAL_ADDRESS"
+
+ # Default mode of the tunnel
+-MODE="gre"
+-
+-# The IP address of the tunnel endpoint where to connect to
+-PEER=
+-
+-# The local IP address of the tunnel endpoint
+-LOCAL_ADDRESS=
++DEFAULT_MODE="gre"
+
+ hook_check_settings() {
+ assert isset MODE && assert isoneof MODE ${SUPPORTED_IP_TUNNEL_MODES}
+diff --git a/src/hooks/zones/modem b/src/hooks/zones/modem
+index 1b4c3c0..50d43c7 100644
+--- a/src/hooks/zones/modem
++++ b/src/hooks/zones/modem
+@@ -27,47 +27,37 @@ MODEM_ALLOWED_AUTH_METHODS="${PPP_ALLOWED_AUTH_METHODS}"
+ HOOK_SETTINGS="HOOK"
+
+ # Access Point Name.
+-APN=
+ HOOK_SETTINGS="${HOOK_SETTINGS} APN"
+
+ # Sets the authentication algortihm that must be used.
+-AUTH=
+ HOOK_SETTINGS="${HOOK_SETTINGS} AUTH"
+
+ # Baudrate.
+-BAUDRATE=921600
++DEFAULT_BAUDRATE=921600
+ HOOK_SETTINGS="${HOOK_SETTINGS} BAUDRATE"
+
+ # The device name of the serial device.
+ # XXX how can we make sure that this does not change all the time?
+-DEVICE=
+ HOOK_SETTINGS="${HOOK_SETTINGS} DEVICE"
+
+ # A monitor device.
+ # Send AT commands to this device, when the primary device is
+ # connected.
+-MONITOR_DEVICE=
+ HOOK_SETTINGS="${HOOK_SETTINGS} MONITOR_DEVICE"
+
+ # Maximum transmission unit.
+-MTU=
+ HOOK_SETTINGS="${HOOK_SETTINGS} MTU"
+
+ # User credentials.
+-USERNAME=
+-PASSWORD=
+ HOOK_SETTINGS="${HOOK_SETTINGS} USERNAME PASSWORD"
+
+ # PIN code.
+-PIN=
+ HOOK_SETTINGS="${HOOK_SETTINGS} PIN"
+
+ # Phone number.
+-PHONE_NUMBER=
+ HOOK_SETTINGS="${HOOK_SETTINGS} PHONE_NUMBER"
+
+ # IMSI
+-IMSI=
+ HOOK_SETTINGS="${HOOK_SETTINGS} IMSI"
+
+ hook_check_settings() {
+diff --git a/src/hooks/zones/pppoe b/src/hooks/zones/pppoe
+index e113c92..cd3913b 100644
+--- a/src/hooks/zones/pppoe
++++ b/src/hooks/zones/pppoe
+@@ -24,31 +24,15 @@
+ HOOK_SETTINGS="HOOK ACCESS_CONCENTRATOR AUTH USERNAME PASSWORD"
+ HOOK_SETTINGS="${HOOK_SETTINGS} SERVICE_NAME MTU IPV6 PREFIX_DELEGATION"
+
+-# User credentials for the dialin.
+-USERNAME=""
+-PASSWORD=""
+-
+-# Set the authentication mechanism.
+-AUTH=
+-
+-# Access Concentrator.
+-ACCESS_CONCENTRATOR=""
+-
+-# Service name.
+-SERVICE_NAME=""
+-
+-# Maximum Transmission Unit.
+-MTU=
+-
+ # This hook can work with all authentication methods supported by pppd.
+ PPPOE_SUPPORTED_AUTH_METHODS="${PPP_SUPPORTED_AUTH_METHODS}"
+ PPPOE_PLUGIN="rp-pppoe.so"
+
+ # Request an IPv6 address.
+-IPV6="true"
++DEFAULT_IPV6="true"
+
+ # Use IPv6 prefix delegation.
+-PREFIX_DELEGATION="true"
++DEFAULT_PREFIX_DELEGATION="true"
+
+ hook_check_settings() {
+ assert isset USERNAME
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,628 @@
+From d389e96b6c0a73fefd907bc99401b4ce4021bf97 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 13:49:08 +0100
+Subject: [PATCH 036/304] Convert HOOK_SETTINGS into an array
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hook | 2 +-
+ src/functions/functions.ports | 4 +--
+ src/functions/functions.zone | 6 ++---
+ src/header-port | 10 ++++----
+ src/header-zone | 2 +-
+ src/hooks/ports/bonding | 20 +++++++++------
+ src/hooks/ports/dummy | 10 +++++---
+ src/hooks/ports/ethernet | 19 +++++++++------
+ src/hooks/ports/ip-tunnel | 16 ++++++++----
+ src/hooks/ports/vlan | 14 +++++++----
+ src/hooks/ports/wireless-ap | 19 ++++++++++++---
+ src/hooks/ports/wireless-mesh | 12 ++++++---
+ src/hooks/zones/bridge | 17 ++++++++++---
+ src/hooks/zones/ip-tunnel | 7 +++++-
+ src/hooks/zones/modem | 46 ++++++++++-------------------------
+ src/hooks/zones/pppoe | 12 +++++++--
+ src/hooks/zones/wireless | 5 +++-
+ 17 files changed, 132 insertions(+), 89 deletions(-)
+
+diff --git a/src/functions/functions.hook b/src/functions/functions.hook
+index c0ebfcb..fb68037 100644
+--- a/src/functions/functions.hook
++++ b/src/functions/functions.hook
+@@ -127,7 +127,7 @@ hook_help() {
+ # Sets all settings in HOOK_SETTINGS to their DEFAULT_* values
+ hook_set_defaults() {
+ local setting
+- for setting in ${HOOK_SETTINGS}; do
++ for setting in ${HOOK_SETTINGS[*]}; do
+ local default="DEFAULT_${setting}"
+
+ # Sets the default or empty
+diff --git a/src/functions/functions.ports b/src/functions/functions.ports
+index f70adf6..fb22715 100644
+--- a/src/functions/functions.ports
++++ b/src/functions/functions.ports
+@@ -85,7 +85,7 @@ port_settings_read() {
+ # Save the HOOK variable.
+ local hook="${HOOK}"
+
+- settings_read "$(port_file "${port}")" ${HOOK_SETTINGS}
++ settings_read "$(port_file "${port}")" ${HOOK_SETTINGS[*]}
+
+ # Restore hook.
+ HOOK="${hook}"
+@@ -100,7 +100,7 @@ port_settings_write() {
+ if function_exists "hook_check_settings"; then
+ list_append args "--check=\"hook_check_settings\""
+ fi
+- list_append args HOOK ${HOOK_SETTINGS}
++ list_append args HOOK ${HOOK_SETTINGS[*]}
+
+ settings_write "$(port_file "${port}")" ${args}
+ }
+diff --git a/src/functions/functions.zone b/src/functions/functions.zone
+index 57e0b71..a0d3cfb 100644
+--- a/src/functions/functions.zone
++++ b/src/functions/functions.zone
+@@ -1248,8 +1248,8 @@ zone_settings_read() {
+ shift
+
+ local args
+- if [ $# -eq 0 ] && [ -n "${HOOK_SETTINGS}" ]; then
+- list_append args ${HOOK_SETTINGS}
++ if [ $# -eq 0 ] && [ -n "${HOOK_SETTINGS[*]}" ]; then
++ list_append args ${HOOK_SETTINGS[*]}
+ else
+ list_append args "$@"
+ fi
+@@ -1271,7 +1271,7 @@ zone_settings_write() {
+ if function_exists "hook_check_settings"; then
+ list_append args "--check=\"hook_check_settings\""
+ fi
+- list_append args ${HOOK_SETTINGS}
++ list_append args HOOK ${HOOK_SETTINGS[*]}
+
+ settings_write "${NETWORK_ZONES_DIR}/${zone}/settings" ${args}
+ }
+diff --git a/src/header-port b/src/header-port
+index 141228a..2d8a820 100644
+--- a/src/header-port
++++ b/src/header-port
+@@ -43,7 +43,7 @@ hook_hotplug_rename() {
+ }
+
+ hook_default_new() {
+- local ${HOOK_SETTINGS}
++ local ${HOOK_SETTINGS[*]}
+
+ # Import all default variables
+ hook_set_defaults
+@@ -57,7 +57,7 @@ hook_default_new() {
+ local port=$(port_find_free ${HOOK_PORT_PATTERN})
+ assert isset port
+
+- port_settings_write "${port}" ${HOOK_SETTINGS}
++ port_settings_write "${port}" ${HOOK_SETTINGS[*]}
+
+ exit ${EXIT_OK}
+ }
+@@ -72,7 +72,7 @@ hook_default_edit() {
+ shift
+
+ # Read settings
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS}; then
++ if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
+ error "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -83,7 +83,7 @@ hook_default_edit() {
+ fi
+
+ # Save settings
+- if ! port_settings_write "${port}" ${HOOK_SETTINGS}; then
++ if ! port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
+ error "Could not write settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -102,7 +102,7 @@ hook_edit() {
+ hook_children() {
+ local port="${1}"
+
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS}; then
++ if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
+ log ERROR "Could not read port settings: ${port}"
+ return ${EXIT_OK}
+ fi
+diff --git a/src/header-zone b/src/header-zone
+index 7ad3e39..2174b01 100644
+--- a/src/header-zone
++++ b/src/header-zone
+@@ -34,7 +34,7 @@ hook_new() {
+ assert isset zone
+ shift
+
+- local ${HOOK_SETTINGS}
++ local ${HOOK_SETTINGS[*]}
+
+ # Import all default variables
+ hook_set_defaults
+diff --git a/src/hooks/ports/bonding b/src/hooks/ports/bonding
+index f0572c3..09fb74f 100644
+--- a/src/hooks/ports/bonding
++++ b/src/hooks/ports/bonding
+@@ -21,7 +21,13 @@
+
+ . /usr/lib/network/header-port
+
+-HOOK_SETTINGS="ADDRESS MIIMON MODE OFFLOADING SLAVES"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "MIIMON"
++ "MODE"
++ "OFFLOADING"
++ "SLAVES"
++)
+
+ DEFAULT_MIIMON=100
+ DEFAULT_MODE="balance-rr"
+@@ -110,7 +116,7 @@ hook_new() {
+ assert isset port
+
+ # Save configuration
+- if port_settings_write "${port}" ${HOOK_SETTINGS}; then
++ if port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
+ log INFO "New port ${port} has been created"
+ else
+ error "Could not save configuration for ${port}"
+@@ -162,7 +168,7 @@ hook_create() {
+ # Exit silently if the device already exists
+ device_exists "${port}" && exit ${EXIT_OK}
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Create the bonding devices
+ bonding_create "${port}" \
+@@ -178,7 +184,7 @@ hook_remove() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Remove the bonding device
+ if device_exists "${port}"; then
+@@ -190,7 +196,7 @@ hook_up() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Auto-enable or disable hardware offloading
+ if ! isset OFFLOADING || enabled OFFLOADING; then
+@@ -213,7 +219,7 @@ hook_down() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Bring down all slaves
+ local slave
+@@ -234,7 +240,7 @@ hook_hotplug() {
+ # Handle events of the same interface
+ if hotplug_event_port_is_interface "${port}"; then
+ # Read configuration
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Bring up all slaves
+ # Attach those which already exist and try to create
+diff --git a/src/hooks/ports/dummy b/src/hooks/ports/dummy
+index 61d2f94..1c4b3c9 100644
+--- a/src/hooks/ports/dummy
++++ b/src/hooks/ports/dummy
+@@ -21,7 +21,9 @@
+
+ . /usr/lib/network/header-port
+
+-HOOK_SETTINGS="ADDRESS"
++HOOK_SETTINGS=(
++ "ADDRESS"
++)
+
+ hook_check_settings() {
+ assert ismac ADDRESS
+@@ -60,7 +62,7 @@ hook_new() {
+ local port=$(port_find_free ${DUMMY_PORT_PATTERN})
+ assert isset port
+
+- if port_settings_write "${port}" ${HOOK_SETTINGS}; then
++ if port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
+ log INFO "New dummy port '${port}' has been created"
+ fi
+
+@@ -72,7 +74,7 @@ hook_create() {
+ assert isset port
+
+ # Read configuration
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Create the dummy device
+ dummy_create "${port}" "${ADDRESS}"
+@@ -115,7 +117,7 @@ hook_hotplug_rename() {
+ local device=${2}
+ assert isset device
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ if [ "${ADDRESS}" = "$(device_get_address ${device})" ]; then
+ log DEBUG "Device '${device}' equals port '${port}'."
+diff --git a/src/hooks/ports/ethernet b/src/hooks/ports/ethernet
+index 0d9c5cd..f3e3f9f 100644
+--- a/src/hooks/ports/ethernet
++++ b/src/hooks/ports/ethernet
+@@ -21,10 +21,13 @@
+
+ . /usr/lib/network/header-port
+
+-# DEVICE equals the actual MAC address of the device.
+-# If ADDRESS is set, the device will get ADDRESS set for its MAC address.
+-
+-HOOK_SETTINGS="ADDRESS ADVERTISED_LINK_SPEEDS DEVICE OFFLOADING MTU"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "ADVERTISED_LINK_SPEEDS"
++ "DEVICE"
++ "OFFLOADING"
++ "MTU"
++)
+
+ hook_check_settings() {
+ assert ismac DEVICE
+@@ -114,7 +117,7 @@ hook_new() {
+
+ local DEVICE="$(device_get_address "${device}")"
+
+- if ! port_settings_write "${port}" ${HOOK_SETTINGS}; then
++ if ! port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
+ log ERROR "Could not write settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -129,8 +132,8 @@ hook_create() {
+ hook_up() {
+ local port="${1}"
+
+- local ${HOOK_SETTINGS}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS}; then
++ local ${HOOK_SETTINGS[*]}
++ if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -177,7 +180,7 @@ hook_hotplug_rename() {
+ assert isset device
+
+ # Read in the conifguration file.
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Get the current MAC address of the device.
+ local address=$(device_get_address ${device})
+diff --git a/src/hooks/ports/ip-tunnel b/src/hooks/ports/ip-tunnel
+index 3943e4c..b426963 100644
+--- a/src/hooks/ports/ip-tunnel
++++ b/src/hooks/ports/ip-tunnel
+@@ -23,7 +23,13 @@
+
+ SUPPORTED_IP_TUNNEL_MODES="gretap"
+
+-HOOK_SETTINGS="ADDRESS MARK MODE PEER LOCAL_ADDRESS"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "MARK"
++ "MODE"
++ "PEER"
++ "LOCAL_ADDRESS"
++)
+
+ hook_check_settings() {
+ assert isset MODE
+@@ -108,8 +114,8 @@ hook_create() {
+ local port="${1}"
+ assert isset port
+
+- local ${HOOK_SETTINGS}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS}; then
++ local ${HOOK_SETTINGS[*]}
++ if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -146,8 +152,8 @@ hook_hotplug_rename() {
+ local device="${2}"
+ assert isset device
+
+- local ${HOOK_SETTINGS}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS}; then
++ local ${HOOK_SETTINGS[*]}
++ if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index bc12a9e..e9aa545 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -21,7 +21,11 @@
+
+ . /usr/lib/network/header-port
+
+-HOOK_SETTINGS="ADDRESS PARENT_DEVICE TAG"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "PARENT_DEVICE"
++ "TAG"
++)
+
+ PORT_PARENTS_VAR="PARENT"
+
+@@ -68,7 +72,7 @@ hook_new() {
+
+ local port="${PARENT_DEVICE}${VLAN_PORT_INTERFIX}${TAG}"
+
+- port_settings_write "${port}" ${HOOK_SETTINGS}
++ port_settings_write "${port}" ${HOOK_SETTINGS[*]}
+
+ exit ${EXIT_OK}
+ }
+@@ -78,7 +82,7 @@ hook_edit() {
+ assert isset port
+ shift
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+@@ -92,7 +96,7 @@ hook_edit() {
+ shift
+ done
+
+- port_settings_write "${port}" ${HOOK_SETTINGS}
++ port_settings_write "${port}" ${HOOK_SETTINGS[*]}
+
+ exit ${EXIT_OK}
+ }
+@@ -104,7 +108,7 @@ hook_create() {
+ device_exists "${port}" && exit ${EXIT_OK}
+
+ # Read configruation
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Create the VLAN device
+ vlan_create "${port}" "${PARENT_DEVICE}" "${TAG}" "${ADDRESS}"
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 2bb4977..8d495d2 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -23,9 +23,20 @@
+
+ HOOK_PORT_PATTERN="${PORT_PATTERN_ACCESSPOINT}"
+
+-HOOK_SETTINGS="ADDRESS BROADCAST_SSID CHANNEL CHANNEL_BANDWIDTH DFS MODE PHY"
+-HOOK_SETTINGS="${HOOK_SETTINGS} ENCRYPTION ENVIRONMENT KEY SSID"
+-HOOK_SETTINGS="${HOOK_SETTINGS} MFP"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "BROADCAST_SSID"
++ "CHANNEL"
++ "CHANNEL_BANDWIDTH"
++ "DFS"
++ "ENCRYPTION"
++ "ENVIRONMENT"
++ "KEY"
++ "MFP"
++ "MODE"
++ "PHY"
++ "SSID"
++)
+
+ # Broadcast SSID by default
+ DEFAULT_BROADCAST_SSID="on"
+@@ -186,7 +197,7 @@ hook_create() {
+
+ device_exists "${port}" && exit ${EXIT_OK}
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Check if the PHY is present.
+ local phy=$(phy_get ${PHY})
+diff --git a/src/hooks/ports/wireless-mesh b/src/hooks/ports/wireless-mesh
+index 4fb4dc9..306263d 100644
+--- a/src/hooks/ports/wireless-mesh
++++ b/src/hooks/ports/wireless-mesh
+@@ -23,7 +23,13 @@
+
+ HOOK_PORT_PATTERN="${PORT_PATTERN_MESH}"
+
+-HOOK_SETTINGS="ADDRESS MESH_ID CHANNEL PHY PSK"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "CHANNEL"
++ "MESH_ID"
++ "PHY"
++ "PSK"
++)
+
+ hook_check_settings() {
+ assert ismac ADDRESS
+@@ -84,7 +90,7 @@ hook_create() {
+ assert isset port
+
+ # Read settings
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ # Check if the PHY is present.
+ local phy="$(phy_get "${PHY}")"
+@@ -143,7 +149,7 @@ hook_hotplug() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS}
++ port_settings_read "${port}" ${HOOK_SETTINGS[*]}
+
+ case "$(hotplug_action)" in
+ add)
+diff --git a/src/hooks/zones/bridge b/src/hooks/zones/bridge
+index 33d5811..0b18331 100644
+--- a/src/hooks/zones/bridge
++++ b/src/hooks/zones/bridge
+@@ -23,10 +23,19 @@
+
+ HOOK_MANPAGE="network-zone-bridge"
+
+-HOOK_SETTINGS="HOOK ADDRESS STP STP_FORWARD_DELAY STP_HELLO STP_MAXAGE"
+-HOOK_SETTINGS="${HOOK_SETTINGS} STP_PRIORITY MTU"
+-
+-HOOK_PORT_SETTINGS="COST PRIORITY"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "STP"
++ "STP_FORWARD_DELAY"
++ "STP_HELLO STP_MAXAGE"
++ "STP_PRIORITY"
++ "MTU"
++)
++
++HOOK_PORT_SETTINGS=(
++ "COST"
++ "PRIORITY"
++)
+
+ # Default values
+ DEFAULT_STP="on"
+diff --git a/src/hooks/zones/ip-tunnel b/src/hooks/zones/ip-tunnel
+index e4be361..634154e 100644
+--- a/src/hooks/zones/ip-tunnel
++++ b/src/hooks/zones/ip-tunnel
+@@ -23,7 +23,12 @@
+
+ SUPPORTED_IP_TUNNEL_MODES="gre vti"
+
+-HOOK_SETTINGS="HOOK MARK MODE PEER LOCAL_ADDRESS"
++HOOK_SETTINGS=(
++ "MARK"
++ "MODE"
++ "PEER"
++ "LOCAL_ADDRESS"
++)
+
+ # Default mode of the tunnel
+ DEFAULT_MODE="gre"
+diff --git a/src/hooks/zones/modem b/src/hooks/zones/modem
+index 50d43c7..e12b104 100644
+--- a/src/hooks/zones/modem
++++ b/src/hooks/zones/modem
+@@ -24,41 +24,21 @@
+ # Modems support all authentication methods, that pppd does support.
+ MODEM_ALLOWED_AUTH_METHODS="${PPP_ALLOWED_AUTH_METHODS}"
+
+-HOOK_SETTINGS="HOOK"
++HOOK_SETTINGS=(
++ "APN"
++ "AUTH"
++ "BAUDRATE"
++ "DEVICE"
++ "MONITOR_DEVICE"
++ "MTU"
++ "USERNAME"
++ "PASSWORD"
++ "PIN"
++ "PHONE_NUMBER"
++ "IMSI"
++)
+
+-# Access Point Name.
+-HOOK_SETTINGS="${HOOK_SETTINGS} APN"
+-
+-# Sets the authentication algortihm that must be used.
+-HOOK_SETTINGS="${HOOK_SETTINGS} AUTH"
+-
+-# Baudrate.
+ DEFAULT_BAUDRATE=921600
+-HOOK_SETTINGS="${HOOK_SETTINGS} BAUDRATE"
+-
+-# The device name of the serial device.
+-# XXX how can we make sure that this does not change all the time?
+-HOOK_SETTINGS="${HOOK_SETTINGS} DEVICE"
+-
+-# A monitor device.
+-# Send AT commands to this device, when the primary device is
+-# connected.
+-HOOK_SETTINGS="${HOOK_SETTINGS} MONITOR_DEVICE"
+-
+-# Maximum transmission unit.
+-HOOK_SETTINGS="${HOOK_SETTINGS} MTU"
+-
+-# User credentials.
+-HOOK_SETTINGS="${HOOK_SETTINGS} USERNAME PASSWORD"
+-
+-# PIN code.
+-HOOK_SETTINGS="${HOOK_SETTINGS} PIN"
+-
+-# Phone number.
+-HOOK_SETTINGS="${HOOK_SETTINGS} PHONE_NUMBER"
+-
+-# IMSI
+-HOOK_SETTINGS="${HOOK_SETTINGS} IMSI"
+
+ hook_check_settings() {
+ assert isset DEVICE
+diff --git a/src/hooks/zones/pppoe b/src/hooks/zones/pppoe
+index cd3913b..4f7ae51 100644
+--- a/src/hooks/zones/pppoe
++++ b/src/hooks/zones/pppoe
+@@ -21,8 +21,16 @@
+
+ . /usr/lib/network/header-zone
+
+-HOOK_SETTINGS="HOOK ACCESS_CONCENTRATOR AUTH USERNAME PASSWORD"
+-HOOK_SETTINGS="${HOOK_SETTINGS} SERVICE_NAME MTU IPV6 PREFIX_DELEGATION"
++HOOK_SETTINGS=(
++ "ACCESS_CONCENTRATOR"
++ "AUTH"
++ "USERNAME"
++ "PASSWORD"
++ "SERVICE_NAME"
++ "MTU"
++ "IPV6"
++ "PREFIX_DELEGATION"
++)
+
+ # This hook can work with all authentication methods supported by pppd.
+ PPPOE_SUPPORTED_AUTH_METHODS="${PPP_SUPPORTED_AUTH_METHODS}"
+diff --git a/src/hooks/zones/wireless b/src/hooks/zones/wireless
+index 553d917..9c52dce 100644
+--- a/src/hooks/zones/wireless
++++ b/src/hooks/zones/wireless
+@@ -21,7 +21,10 @@
+
+ . /usr/lib/network/header-zone
+
+-HOOK_SETTINGS="HOOK ADDRESS PHY"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "PHY"
++)
+
+ hook_check_settings() {
+ assert ismac ADDRESS
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,133 @@
+From 227d458f4fac10cbf0970515edd3227913fc1bf4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 14:04:35 +0100
+Subject: [PATCH 037/304] settings: Some code refactoring
+
+No functional changes
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hook | 13 +++++++++++++
+ src/functions/functions.ports | 10 ++--------
+ src/functions/functions.zone | 32 ++++++++------------------------
+ 3 files changed, 23 insertions(+), 32 deletions(-)
+
+diff --git a/src/functions/functions.hook b/src/functions/functions.hook
+index fb68037..11887cd 100644
+--- a/src/functions/functions.hook
++++ b/src/functions/functions.hook
+@@ -124,6 +124,19 @@ hook_help() {
+ exit $?
+ }
+
++# Dummy functions being overlayed by hooks
++hook_check_settings() {
++ :
++}
++
++hook_check_config_settings() {
++ :
++}
++
++hook_check_port_settings() {
++ :
++}
++
+ # Sets all settings in HOOK_SETTINGS to their DEFAULT_* values
+ hook_set_defaults() {
+ local setting
+diff --git a/src/functions/functions.ports b/src/functions/functions.ports
+index fb22715..d8a9140 100644
+--- a/src/functions/functions.ports
++++ b/src/functions/functions.ports
+@@ -94,15 +94,9 @@ port_settings_read() {
+ port_settings_write() {
+ local port="${1}"
+ assert isset port
+- shift
+-
+- local args
+- if function_exists "hook_check_settings"; then
+- list_append args "--check=\"hook_check_settings\""
+- fi
+- list_append args HOOK ${HOOK_SETTINGS[*]}
+
+- settings_write "$(port_file "${port}")" ${args}
++ settings_write "$(port_file "${port}")" \
++ --check="hook_check_settings" HOOK ${HOOK_SETTINGS[*]}
+ }
+
+ port_file() {
+diff --git a/src/functions/functions.zone b/src/functions/functions.zone
+index a0d3cfb..e81371b 100644
+--- a/src/functions/functions.zone
++++ b/src/functions/functions.zone
+@@ -1267,13 +1267,8 @@ zone_settings_write() {
+ local zone="${1}"
+ assert isset zone
+
+- local args
+- if function_exists "hook_check_settings"; then
+- list_append args "--check=\"hook_check_settings\""
+- fi
+- list_append args HOOK ${HOOK_SETTINGS[*]}
+-
+- settings_write "${NETWORK_ZONES_DIR}/${zone}/settings" ${args}
++ settings_write "${NETWORK_ZONES_DIR}/${zone}/settings" \
++ --check="hook_check_settings" HOOK ${HOOK_SETTINGS[*]}
+ }
+
+ zone_settings_set() {
+@@ -1328,7 +1323,7 @@ zone_config_settings_read() {
+ }
+
+ zone_config_settings_write() {
+- assert [ $# -ge 2 ]
++ assert [ $# -eq 2 ]
+
+ local zone="${1}"
+ local hook="${2}"
+@@ -1336,14 +1331,9 @@ zone_config_settings_write() {
+
+ assert isset id
+
+- local args
+- if function_exists "hook_check_config_settings"; then
+- list_append args "--check=\"hook_check_config_settings\""
+- fi
+- list_append args ${HOOK_CONFIG_SETTINGS}
+-
+ local path="${NETWORK_ZONES_DIR}/${zone}/configs/${hook}.${id}"
+- settings_write "${path}" ${args}
++ settings_write "${path}" \
++ --check="hook_check_config_settings" ${HOOK_CONFIG_SETTINGS[*]}
+ }
+
+ zone_config_settings_destroy() {
+@@ -1416,20 +1406,14 @@ zone_port_settings_read() {
+ }
+
+ zone_port_settings_write() {
+- assert [ $# -ge 2 ]
++ assert [ $# -eq 2 ]
+
+ local zone="${1}"
+ local port="${2}"
+- shift 2
+-
+- local args
+- if function_exists "hook_check_port_settings"; then
+- list_append args "--check=\"hook_check_port_settings\""
+- fi
+- list_append args ${HOOK_PORT_SETTINGS}
+
+ local path="${NETWORK_ZONES_DIR}/${zone}/ports/${port}"
+- settings_write "${path}" ${args}
++ settings_write "${path}" \
++ --check="hook_check_port_settings" ${HOOK_PORT_SETTINGS[*]}
+ }
+
+ zone_port_settings_remove() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,239 @@
+From eba9fa9c0b647552d8a43fb6ff5ab00c2ab21402 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 14:14:56 +0100
+Subject: [PATCH 038/304] ports: Drop HOOK_SETTINGS variable
+
+This does not need to be passed to the port_settings_* functions
+any more which makes them more easy to use
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/bonding | 12 ++++++------
+ src/hooks/ports/dummy | 6 +++---
+ src/hooks/ports/ethernet | 6 +++---
+ src/hooks/ports/ip-tunnel | 4 ++--
+ src/hooks/ports/vlan | 8 ++++----
+ src/hooks/ports/wireless-ap | 2 +-
+ src/hooks/ports/wireless-mesh | 4 ++--
+ 7 files changed, 21 insertions(+), 21 deletions(-)
+
+diff --git a/src/hooks/ports/bonding b/src/hooks/ports/bonding
+index 09fb74f..a0cf5c0 100644
+--- a/src/hooks/ports/bonding
++++ b/src/hooks/ports/bonding
+@@ -116,7 +116,7 @@ hook_new() {
+ assert isset port
+
+ # Save configuration
+- if port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
++ if port_settings_write "${port}"; then
+ log INFO "New port ${port} has been created"
+ else
+ error "Could not save configuration for ${port}"
+@@ -168,7 +168,7 @@ hook_create() {
+ # Exit silently if the device already exists
+ device_exists "${port}" && exit ${EXIT_OK}
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Create the bonding devices
+ bonding_create "${port}" \
+@@ -184,7 +184,7 @@ hook_remove() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Remove the bonding device
+ if device_exists "${port}"; then
+@@ -196,7 +196,7 @@ hook_up() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Auto-enable or disable hardware offloading
+ if ! isset OFFLOADING || enabled OFFLOADING; then
+@@ -219,7 +219,7 @@ hook_down() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Bring down all slaves
+ local slave
+@@ -240,7 +240,7 @@ hook_hotplug() {
+ # Handle events of the same interface
+ if hotplug_event_port_is_interface "${port}"; then
+ # Read configuration
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Bring up all slaves
+ # Attach those which already exist and try to create
+diff --git a/src/hooks/ports/dummy b/src/hooks/ports/dummy
+index 1c4b3c9..387c88b 100644
+--- a/src/hooks/ports/dummy
++++ b/src/hooks/ports/dummy
+@@ -62,7 +62,7 @@ hook_new() {
+ local port=$(port_find_free ${DUMMY_PORT_PATTERN})
+ assert isset port
+
+- if port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
++ if port_settings_write "${port}"; then
+ log INFO "New dummy port '${port}' has been created"
+ fi
+
+@@ -74,7 +74,7 @@ hook_create() {
+ assert isset port
+
+ # Read configuration
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Create the dummy device
+ dummy_create "${port}" "${ADDRESS}"
+@@ -117,7 +117,7 @@ hook_hotplug_rename() {
+ local device=${2}
+ assert isset device
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ if [ "${ADDRESS}" = "$(device_get_address ${device})" ]; then
+ log DEBUG "Device '${device}' equals port '${port}'."
+diff --git a/src/hooks/ports/ethernet b/src/hooks/ports/ethernet
+index f3e3f9f..5f76e15 100644
+--- a/src/hooks/ports/ethernet
++++ b/src/hooks/ports/ethernet
+@@ -117,7 +117,7 @@ hook_new() {
+
+ local DEVICE="$(device_get_address "${device}")"
+
+- if ! port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
++ if ! port_settings_write "${port}"; then
+ log ERROR "Could not write settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -133,7 +133,7 @@ hook_up() {
+ local port="${1}"
+
+ local ${HOOK_SETTINGS[*]}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
++ if ! port_settings_read "${port}"; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -180,7 +180,7 @@ hook_hotplug_rename() {
+ assert isset device
+
+ # Read in the conifguration file.
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Get the current MAC address of the device.
+ local address=$(device_get_address ${device})
+diff --git a/src/hooks/ports/ip-tunnel b/src/hooks/ports/ip-tunnel
+index b426963..fa7193c 100644
+--- a/src/hooks/ports/ip-tunnel
++++ b/src/hooks/ports/ip-tunnel
+@@ -115,7 +115,7 @@ hook_create() {
+ assert isset port
+
+ local ${HOOK_SETTINGS[*]}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
++ if ! port_settings_read "${port}"; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+@@ -153,7 +153,7 @@ hook_hotplug_rename() {
+ assert isset device
+
+ local ${HOOK_SETTINGS[*]}
+- if ! port_settings_read "${port}" ${HOOK_SETTINGS[*]}; then
++ if ! port_settings_read "${port}"; then
+ log ERROR "Could not read settings for port ${port}"
+ return ${EXIT_ERROR}
+ fi
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index e9aa545..f511986 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -72,7 +72,7 @@ hook_new() {
+
+ local port="${PARENT_DEVICE}${VLAN_PORT_INTERFIX}${TAG}"
+
+- port_settings_write "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_write "${port}"
+
+ exit ${EXIT_OK}
+ }
+@@ -82,7 +82,7 @@ hook_edit() {
+ assert isset port
+ shift
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+@@ -96,7 +96,7 @@ hook_edit() {
+ shift
+ done
+
+- port_settings_write "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_write "${port}"
+
+ exit ${EXIT_OK}
+ }
+@@ -108,7 +108,7 @@ hook_create() {
+ device_exists "${port}" && exit ${EXIT_OK}
+
+ # Read configruation
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Create the VLAN device
+ vlan_create "${port}" "${PARENT_DEVICE}" "${TAG}" "${ADDRESS}"
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 8d495d2..e393f5f 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -197,7 +197,7 @@ hook_create() {
+
+ device_exists "${port}" && exit ${EXIT_OK}
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Check if the PHY is present.
+ local phy=$(phy_get ${PHY})
+diff --git a/src/hooks/ports/wireless-mesh b/src/hooks/ports/wireless-mesh
+index 306263d..35f0950 100644
+--- a/src/hooks/ports/wireless-mesh
++++ b/src/hooks/ports/wireless-mesh
+@@ -90,7 +90,7 @@ hook_create() {
+ assert isset port
+
+ # Read settings
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ # Check if the PHY is present.
+ local phy="$(phy_get "${PHY}")"
+@@ -149,7 +149,7 @@ hook_hotplug() {
+ local port="${1}"
+ assert isset port
+
+- port_settings_read "${port}" ${HOOK_SETTINGS[*]}
++ port_settings_read "${port}"
+
+ case "$(hotplug_action)" in
+ add)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,162 @@
+From 12f9c8d2550c8fcab536bb8b971caddfa8ee0c80 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 14:58:12 +0100
+Subject: [PATCH 039/304] hotplug: Remove multiple copies of the same function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/header-port | 26 ++++++++++++++++++++++++++
+ src/hooks/ports/dummy | 36 +-----------------------------------
+ src/hooks/ports/ethernet | 22 +---------------------
+ src/hooks/ports/ip-tunnel | 23 +----------------------
+ 4 files changed, 29 insertions(+), 78 deletions(-)
+
+diff --git a/src/header-port b/src/header-port
+index 2d8a820..d75fdd8 100644
+--- a/src/header-port
++++ b/src/header-port
+@@ -42,6 +42,32 @@ hook_hotplug_rename() {
+ exit ${EXIT_FALSE}
+ }
+
++hook_hotplug_rename_by_address() {
++ local port="${1}"
++ assert isset port
++
++ local device="${2}"
++ assert isset device
++
++ # Read in the conifguration file.
++ if ! port_settings_read "${port}"; then
++ return ${EXIT_ERROR}
++ fi
++
++ # Get the current MAC address of the device.
++ local address="$(device_get_address "${device}")"
++ assert isset address
++
++ # Check if the address matches with the configuration.
++ if list_match "${address}" "${ADDRESS}" "${DEVICE}"; then
++ log DEBUG "Device '${device}' is port '${port}'"
++ return ${EXIT_OK}
++ fi
++
++ log DEBUG "Device '${device}' is not port '${port}'"
++ return ${EXIT_ERROR}
++}
++
+ hook_default_new() {
+ local ${HOOK_SETTINGS[*]}
+
+diff --git a/src/hooks/ports/dummy b/src/hooks/ports/dummy
+index 387c88b..3688831 100644
+--- a/src/hooks/ports/dummy
++++ b/src/hooks/ports/dummy
+@@ -90,40 +90,6 @@ hook_remove() {
+ dummy_remove "${port}"
+ }
+
+-hook_up() {
+- local port="${1}"
+- assert isset port
+-
+- # Bring up the port.
+- device_set_up ${port}
+-
+- exit ${EXIT_OK}
+-}
+-
+-hook_down() {
+- local port="${1}"
+- assert isset port
+-
+- # Tear down the port.
+- device_set_down ${port}
+-
+- exit ${EXIT_OK}
+-}
+-
+ hook_hotplug_rename() {
+- local port=${1}
+- assert isset port
+-
+- local device=${2}
+- assert isset device
+-
+- port_settings_read "${port}"
+-
+- if [ "${ADDRESS}" = "$(device_get_address ${device})" ]; then
+- log DEBUG "Device '${device}' equals port '${port}'."
+- exit ${EXIT_OK}
+- fi
+-
+- log DEBUG "Device '${device}' does not equal port '${port}'."
+- exit ${EXIT_ERROR}
++ hook_hotplug_rename_by_address "$@"
+ }
+diff --git a/src/hooks/ports/ethernet b/src/hooks/ports/ethernet
+index 5f76e15..82664fa 100644
+--- a/src/hooks/ports/ethernet
++++ b/src/hooks/ports/ethernet
+@@ -173,25 +173,5 @@ hook_remove() {
+ }
+
+ hook_hotplug_rename() {
+- local port=${1}
+- assert isset port
+-
+- local device=${2}
+- assert isset device
+-
+- # Read in the conifguration file.
+- port_settings_read "${port}"
+-
+- # Get the current MAC address of the device.
+- local address=$(device_get_address ${device})
+- assert isset address
+-
+- # Check if the address matches with the configuration.
+- if list_match "${address}" ${DEVICE} ${ADDRESS}; then
+- log DEBUG "Device '${device}' equals port '${port}'."
+- exit ${EXIT_OK}
+- fi
+-
+- log DEBUG "Device '${device}' does not equal port '${port}'."
+- exit ${EXIT_ERROR}
++ hook_hotplug_rename_by_address "$@"
+ }
+diff --git a/src/hooks/ports/ip-tunnel b/src/hooks/ports/ip-tunnel
+index fa7193c..482511e 100644
+--- a/src/hooks/ports/ip-tunnel
++++ b/src/hooks/ports/ip-tunnel
+@@ -146,26 +146,5 @@ hook_remove() {
+ }
+
+ hook_hotplug_rename() {
+- local port="${1}"
+- assert isset port
+-
+- local device="${2}"
+- assert isset device
+-
+- local ${HOOK_SETTINGS[*]}
+- if ! port_settings_read "${port}"; then
+- log ERROR "Could not read settings for port ${port}"
+- return ${EXIT_ERROR}
+- fi
+-
+- # Get the current MAC address of the device.
+- local address="$(device_get_address ${device})"
+- assert isset address
+-
+- # Return OK on match
+- if [ "${ADDRESS}" = "${address}" ]; then
+- return ${EXIT_OK}
+- fi
+-
+- return ${EXIT_ERROR}
++ hook_hotplug_rename_by_address "$@"
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,58 @@
+From 66fdbcaf15d3fb7ce4a1e0f7e6299818f4638c84 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 15:02:34 +0100
+Subject: [PATCH 040/304] wireless-ap: Remove support for WPA
+
+This is a deprecated protocol and not secure.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 8 +-------
+ src/hooks/ports/wireless-ap | 2 +-
+ 2 files changed, 2 insertions(+), 8 deletions(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 79fb4db..d3eaa74 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -138,7 +138,7 @@ hostapd_config_write() {
+
+ # Check if key is set when encryption is used.
+ if isset encryption; then
+- assert isoneof encryption WPA WPA2 WPA/WPA2
++ assert isoneof encryption WPA2
+ assert isset key
+ fi
+
+@@ -398,15 +398,9 @@ hostapd_config_write() {
+ if isset encryption; then
+ local encryption_mode=0
+ case "${encryption}" in
+- WPA)
+- encryption_mode=1
+- ;;
+ WPA2)
+ encryption_mode=2
+ ;;
+- WPA/WPA2)
+- encryption_mode=3
+- ;;
+ esac
+
+ (
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index e393f5f..a964fac 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -65,7 +65,7 @@ hook_check_settings() {
+ assert isset SSID
+
+ if isset ENCRYPTION; then
+- assert isoneof ENCRYPTION WPA WPA2 WPA/WPA2
++ assert isoneof ENCRYPTION WPA2
+
+ assert isset KEY
+ assert [ ${#KEY} -ge 8 ]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,280 @@
+From 0a4c5abab952ae0d864505f037f46cd0a27d6701 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:12:53 +0100
+Subject: [PATCH 041/304] wireless-ap: Add support for WPA3 and rewrite WPA2
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 90 +++++++++++++++++++++----------
+ src/helpers/hostapd-config-helper | 5 +-
+ src/hooks/ports/wireless-ap | 38 +++++++------
+ 3 files changed, 86 insertions(+), 47 deletions(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index d3eaa74..6c2fbd9 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -61,13 +61,14 @@ hostapd_config_write() {
+ local channel_bandwidth
+ local country_code="$(wireless_get_reg_domain)"
+ local dfs="on"
+- local encryption
+ local environment="${WIRELESS_DEFAULT_ENVIRONMENT}"
+- local key
+ local mfp="off"
+ local mode
++ local secret
+ local ssid
+ local wmm="1"
++ local wpa2_personal="off"
++ local wpa3_personal="off"
+
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+@@ -89,9 +90,6 @@ hostapd_config_write() {
+ --environment=*)
+ environment="$(cli_get_val "${1}")"
+ ;;
+- --key=*)
+- key=$(cli_get_val "${1}")
+- ;;
+ --mfp=*)
+ mfp="$(cli_get_val "${1}")"
+ ;;
+@@ -103,6 +101,9 @@ hostapd_config_write() {
+ return ${EXIT_ERROR}
+ fi
+ ;;
++ --secret=*)
++ secret="$(cli_get_val "${1}")"
++ ;;
+ --ssid=*)
+ ssid=$(cli_get_val "${1}")
+ ;;
+@@ -114,6 +115,12 @@ hostapd_config_write() {
+ wmm="0"
+ fi
+ ;;
++ --wpa2-personal=*)
++ wpa2_personal="$(cli_get_bool "${1}")"
++ ;;
++ --wpa3-personal=*)
++ wpa3_personal="$(cli_get_bool "${1}")"
++ ;;
+ *)
+ warning_log "Ignoring unknown argument '${1}'."
+ ;;
+@@ -136,12 +143,6 @@ hostapd_config_write() {
+ assert isset mode
+ assert isset ssid
+
+- # Check if key is set when encryption is used.
+- if isset encryption; then
+- assert isoneof encryption WPA2
+- assert isset key
+- fi
+-
+ # Check wireless environment
+ if ! wireless_environment_is_valid "${environment}"; then
+ error "Invalid wireless environment: ${environment}"
+@@ -166,6 +167,12 @@ hostapd_config_write() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Check if secret is set for personal authentication
++ if ! isset secret && (enabled WPA3_PERSONAL ||Â enabled WPA2_PERSONAL); then
++ error "Secret not set but personal authentication enabled"
++ return ${EXIT_ERROR}
++ fi
++
+ # 802.11ac/n flags
+ local ieee80211ac
+ local ieee80211n
+@@ -394,27 +401,52 @@ hostapd_config_write() {
+ print
+ ) >> ${file}
+
+- # Encryption settings
+- if isset encryption; then
+- local encryption_mode=0
+- case "${encryption}" in
+- WPA2)
+- encryption_mode=2
+- ;;
+- esac
++ # Authentication Settings
++ local wpa
++ local wpa_key_mgmt
++ local wpa_passphrase
++ local sae_password
++ local wpa_strict_rekey
++
++ # WPA3 Personal
++ if enabled WPA3_PERSONAL; then
++ # Enable RSN
++ wpa="2"
++
++ # Add WPA key management
++ list_append wpa_key_mgmt "SAE"
++ sae_password="${secret}"
++ fi
++
++ # WPA2 Personal
++ if enabled WPA2_PERSONAL; then
++ # Enable RSN
++ wpa="2"
++
++ # Add WPA key management
++ list_append wpa_key_mgmt "WPA-PSK-SHA256" "WPA-PSK"
++ wpa_passphrase="${secret}"
+
+- (
+- print "# Encryption settings"
+- print "wpa=${encryption_mode}"
+- print "wpa_passphrase=${key}"
+- print "wpa_key_mgmt=WPA-PSK-SHA256 WPA-PSK"
+- print "wpa_pairwise=${pairwise_ciphers[*]}"
+- print "rsn_pairwise=${pairwise_ciphers[*]}"
+- print "group_cipher=${group_ciphers[*]}"
+- print
+- ) >> ${file}
++ # Enable WPA strict rekey
++ wpa_strict_rekey="1"
+ fi
+
++ # Enable RSN ciphers when RSN is enabled
++ local rsn_pairwise
++ local group_cipher
++ if [ "${wpa}" = "2" ]; then
++ rsn_pairwise="${pairwise_ciphers[*]}"
++ group_cipher="${group_ciphers[*]}"
++ fi
++
++ local var
++ for var in wpa wpa_key_mgmt wpa_passphrase sae_password \
++ rsn_pairwise group_cipher wpa_strict_rekey; do
++ if [ -n "${!var}" ]; then
++ print "${var}=${!var}"
++ fi
++ done >> "${file}"
++
+ # Log configuration file
+ file_to_log DEBUG "${file}"
+
+diff --git a/src/helpers/hostapd-config-helper b/src/helpers/hostapd-config-helper
+index 7af723d..6d9f685 100644
+--- a/src/helpers/hostapd-config-helper
++++ b/src/helpers/hostapd-config-helper
+@@ -42,12 +42,13 @@ case "${action}" in
+ --channel="${CHANNEL}" \
+ --channel-bandwidth="${CHANNEL_BANDWIDTH}" \
+ --dfs="${DFS}" \
+- --encryption="${ENCRYPTION}" \
+ --environment="${ENVIRONMENT}" \
+- --key="${KEY}" \
++ --secret="${SECRET}" \
+ --mfp="${MFP}" \
+ --mode="${MODE}" \
+ --ssid="${SSID}" \
++ --wpa3-personal="${WPA3_PERSONAL}" \
++ --wpa2-personal="${WPA2_PERSONAL}" \
+ || exit $?
+ ;;
+
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index a964fac..7176ee5 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -29,15 +29,20 @@ HOOK_SETTINGS=(
+ "CHANNEL"
+ "CHANNEL_BANDWIDTH"
+ "DFS"
+- "ENCRYPTION"
+ "ENVIRONMENT"
+- "KEY"
+ "MFP"
+ "MODE"
+ "PHY"
++ "SECRET"
+ "SSID"
++ "WPA3_PERSONAL"
++ "WPA2_PERSONAL"
+ )
+
++# Disable WPA3+2 by default
++DEFAULT_WPA3_PERSONAL="off"
++DEFAULT_WPA2_PERSONAL="off"
++
+ # Broadcast SSID by default
+ DEFAULT_BROADCAST_SSID="on"
+
+@@ -64,14 +69,6 @@ hook_check_settings() {
+ assert ismac PHY
+ assert isset SSID
+
+- if isset ENCRYPTION; then
+- assert isoneof ENCRYPTION WPA2
+-
+- assert isset KEY
+- assert [ ${#KEY} -ge 8 ]
+- assert [ ${#KEY} -le 63 ]
+- fi
+-
+ assert wireless_environment_is_valid "${ENVIRONMENT}"
+ }
+
+@@ -99,9 +96,6 @@ hook_parse_cmdline() {
+ return ${EXIT_ERROR}
+ fi
+ ;;
+- --encryption=*)
+- ENCRYPTION=$(cli_get_val "${1}")
+- ;;
+ --environment=*)
+ ENVIRONMENT="$(cli_get_val "${1}")"
+
+@@ -110,9 +104,6 @@ hook_parse_cmdline() {
+ return ${EXIT_ERROR}
+ fi
+ ;;
+- --key=*)
+- KEY=$(cli_get_val "${1}")
+- ;;
+ --mac=*)
+ ADDRESS=$(cli_get_val "${1}")
+ ;;
+@@ -140,9 +131,18 @@ hook_parse_cmdline() {
+ --phy=*)
+ PHY=$(cli_get_val "${1}")
+ ;;
++ --secret=*)
++ SECRET="$(cli_get_val "${1}")"
++ ;;
+ --ssid=*)
+ SSID=$(cli_get_val "${1}")
+ ;;
++ --wpa2-personal=*)
++ WPA2_PERSONAL="$(cli_get_bool "${1}")"
++ ;;
++ --wpa3-personal=*)
++ WPA3_PERSONAL="$(cli_get_bool "${1}")"
++ ;;
+ *)
+ warning "Ignoring unknown argument '${1}'"
+ ;;
+@@ -174,6 +174,12 @@ hook_parse_cmdline() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Check if SECRET is set when WPA* is enabled
++ if ! isset SECRET && (enabled WPA3_PERSONAL || enabled WPA2_PERSONAL); then
++ error "Secret is not set when PSK authentication is enabled"
++ return ${EXIT_ERROR}
++ fi
++
+ # Save address of phy do identify it again
+ PHY=$(phy_get ${PHY})
+ PHY=$(phy_get_address ${PHY})
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 729cc3a2518ac4db00dd2ab390f7d253154f3333 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:19:24 +0100
+Subject: [PATCH 042/304] hotplug-rename: Drop unused variable
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/udev/network-hotplug-rename | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/src/udev/network-hotplug-rename b/src/udev/network-hotplug-rename
+index 903a07c..5f82f7c 100644
+--- a/src/udev/network-hotplug-rename
++++ b/src/udev/network-hotplug-rename
+@@ -28,9 +28,6 @@ LOG_DISABLE_STDOUT="true"
+ # Read network settings
+ network_settings_read
+
+-# Setup the locking
+-LOCKFILE="${LOCK_DIR}/.network-rename-lock"
+-
+ # Check if the INTERFACE variable is properly set.
+ assert isset INTERFACE
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From 21ef3b742e6031cb40d0da94015aced31fc18be2 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:22:45 +0100
+Subject: [PATCH 043/304] hostapd: Allow WPA2 authentication only with SHA256
+
+This experimental change disables support for the legacy WPA2
+authentication that does not support SHA256.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 6c2fbd9..095beb8 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -424,7 +424,7 @@ hostapd_config_write() {
+ wpa="2"
+
+ # Add WPA key management
+- list_append wpa_key_mgmt "WPA-PSK-SHA256" "WPA-PSK"
++ list_append wpa_key_mgmt "WPA-PSK-SHA256"
+ wpa_passphrase="${secret}"
+
+ # Enable WPA strict rekey
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,31 @@
+From 298a1ffe3f10ec14416c3aed19bb541553de160a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:23:55 +0100
+Subject: [PATCH 044/304] wireless-ap: Enable 802.11w by default
+
+This causes some problems on broken Intel systems, but I
+guess it is better to prefer security than compatibility in the
+default settings.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/wireless-ap | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 7176ee5..9676369 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -50,8 +50,7 @@ DEFAULT_BROADCAST_SSID="on"
+ DEFAULT_DFS="on"
+
+ # 802.11w - Management Frame Protection
+-# Disable by default because many clients cannot connect when enabled
+-DEFAULT_MFP="off"
++DEFAULT_MFP="on"
+
+ DEFAULT_ENVIRONMENT="${WIRELESS_DEFAULT_ENVIRONMENT}"
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,100 @@
+From f6659cc56ecdef375fb868a3a44ada37b4cbfc3c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:30:05 +0100
+Subject: [PATCH 045/304] hooks: Use cli_get_bool convenience function where
+ ever possible
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/bonding | 11 +----------
+ src/hooks/ports/ethernet | 11 +----------
+ src/hooks/ports/wireless-ap | 22 ++--------------------
+ 3 files changed, 4 insertions(+), 40 deletions(-)
+
+diff --git a/src/hooks/ports/bonding b/src/hooks/ports/bonding
+index a0cf5c0..96cb854 100644
+--- a/src/hooks/ports/bonding
++++ b/src/hooks/ports/bonding
+@@ -59,16 +59,7 @@ hook_parse_cmdline() {
+ MODE=$(cli_get_val "${1}")
+ ;;
+ --offloading=*)
+- OFFLOADING="$(cli_get_val "${1}")"
+-
+- if enabled OFFLOADING; then
+- OFFLOADING="on"
+- elif disabled OFFLOADING; then
+- OFFLOADING="off"
+- else
+- error "Invalid value for offloading: ${OFFLOADING}"
+- return ${EXIT_ERROR}
+- fi
++ OFFLOADING="$(cli_get_bool "${1}")"
+ ;;
+ +*)
+ local slave=$(cli_get_val "${1:1}")
+diff --git a/src/hooks/ports/ethernet b/src/hooks/ports/ethernet
+index 82664fa..80b5503 100644
+--- a/src/hooks/ports/ethernet
++++ b/src/hooks/ports/ethernet
+@@ -85,16 +85,7 @@ hook_parse_cmdline() {
+ ;;
+
+ --offloading=*)
+- OFFLOADING="$(cli_get_val "${1}")"
+-
+- if enabled OFFLOADING; then
+- OFFLOADING="on"
+- elif disabled OFFLOADING; then
+- OFFLOADING="off"
+- else
+- error "Invalid value for offloading: ${OFFLOADING}"
+- return ${EXIT_ERROR}
+- fi
++ OFFLOADING="$(cli_get_bool "${1}")"
+ ;;
+
+ *)
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 9676369..2528585 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -84,16 +84,7 @@ hook_parse_cmdline() {
+ CHANNEL_BANDWIDTH="$(cli_get_val "${1}")"
+ ;;
+ --dfs=*)
+- DFS="$(cli_get_val "${1}")"
+-
+- if enabled DFS; then
+- DFS="on"
+- elif disabled DFS; then
+- DFS="off"
+- else
+- error "Invalid value for DFS: ${DFS}"
+- return ${EXIT_ERROR}
+- fi
++ DFS="$(cli_get_bool "${1}")"
+ ;;
+ --environment=*)
+ ENVIRONMENT="$(cli_get_val "${1}")"
+@@ -107,16 +98,7 @@ hook_parse_cmdline() {
+ ADDRESS=$(cli_get_val "${1}")
+ ;;
+ --mfp=*)
+- MFP="$(cli_get_val "${1}")"
+-
+- if enabled MFP; then
+- MFP="on"
+- elif disabled MFP; then
+- MFP="off"
+- else
+- error "Invalid value for --mfp: ${MFP}"
+- return ${EXIT_ERROR}
+- fi
++ MFP="$(cli_get_bool "${1}")"
+ ;;
+ --mode=*)
+ MODE=$(cli_get_val "${1}")
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,196 @@
+From 636f1b96fc0b60c47cf5636f95b1ee6c856a701c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:54:04 +0100
+Subject: [PATCH 046/304] hook: Rename HOOK_CONFIG_SETTINGS to HOOK_SETTINGS
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.zone | 8 ++++----
+ src/header-config | 5 ++++-
+ src/hooks/configs/dhcp | 13 +++++++++----
+ src/hooks/configs/ipv6-auto | 15 +++++----------
+ src/hooks/configs/pppoe-server | 24 +++++++++---------------
+ src/hooks/configs/static | 6 +++++-
+ 6 files changed, 36 insertions(+), 35 deletions(-)
+
+diff --git a/src/functions/functions.zone b/src/functions/functions.zone
+index e81371b..28fbecd 100644
+--- a/src/functions/functions.zone
++++ b/src/functions/functions.zone
+@@ -1312,8 +1312,8 @@ zone_config_settings_read() {
+ shift 2
+
+ local args
+- if [ $# -eq 0 ] && [ -n "${HOOK_CONFIG_SETTINGS}" ]; then
+- list_append args ${HOOK_CONFIG_SETTINGS}
++ if [ $# -eq 0 ] && [ -n "${HOOK_SETTINGS[*]}" ]; then
++ list_append args ${HOOK_SETTINGS[*]}
+ else
+ list_append args "$@"
+ fi
+@@ -1323,7 +1323,7 @@ zone_config_settings_read() {
+ }
+
+ zone_config_settings_write() {
+- assert [ $# -eq 2 ]
++ assert [ $# -eq 3 ]
+
+ local zone="${1}"
+ local hook="${2}"
+@@ -1333,7 +1333,7 @@ zone_config_settings_write() {
+
+ local path="${NETWORK_ZONES_DIR}/${zone}/configs/${hook}.${id}"
+ settings_write "${path}" \
+- --check="hook_check_config_settings" ${HOOK_CONFIG_SETTINGS[*]}
++ --check="hook_check_config_settings" HOOK ${HOOK_SETTINGS[*]}
+ }
+
+ zone_config_settings_destroy() {
+diff --git a/src/header-config b/src/header-config
+index 4458eaa..baeca5e 100644
+--- a/src/header-config
++++ b/src/header-config
+@@ -26,6 +26,9 @@ hook_new() {
+ local id=$(zone_config_get_new_id ${zone})
+ log DEBUG "ID for the config is: ${id}"
+
++ # Import all default variables
++ hook_set_defaults
++
+ # Parse command line arguments
+ if ! hook_parse_cmdline "${id}" "$@"; then
+ # Return an error if the parsing of the cmd line fails
+@@ -64,7 +67,7 @@ hook_edit() {
+ fi
+ fi
+
+- local ${HOOK_CONFIG_SETTINGS}
++ local ${HOOK_SETTINGS}
+
+ # If reading the config fails we cannot go on
+ if ! zone_config_settings_read "${zone}" "${config}"; then
+diff --git a/src/hooks/configs/dhcp b/src/hooks/configs/dhcp
+index b643022..1ad0694 100644
+--- a/src/hooks/configs/dhcp
++++ b/src/hooks/configs/dhcp
+@@ -21,11 +21,13 @@
+
+ . /usr/lib/network/header-config
+
+-HOOK_CONFIG_SETTINGS="HOOK ENABLE_IPV6 ENABLE_IPV4"
++HOOK_SETTINGS=(
++ "ENABLE_IPV6"
++ "ENABLE_IPV4"
++)
+
+-# Default settings.
+-ENABLE_IPV6="on"
+-ENABLE_IPV4="on"
++DEFAULT_ENABLE_IPV6="on"
++DEFAULT_ENABLE_IPV4="on"
+
+ hook_check_config_settings() {
+ assert isset ENABLE_IPV6
+@@ -78,6 +80,9 @@ hook_new() {
+ local id=$(zone_config_get_new_id ${zone})
+ log DEBUG "ID for the config is: ${id}"
+
++ # Import defaults
++ hook_set_defaults
++
+ if ! hook_parse_cmdline "${id}" "$@"; then
+ # Return an error if the parsing of the cmd line fails
+ return ${EXIT_ERROR}
+diff --git a/src/hooks/configs/ipv6-auto b/src/hooks/configs/ipv6-auto
+index 8796723..6fd90a5 100644
+--- a/src/hooks/configs/ipv6-auto
++++ b/src/hooks/configs/ipv6-auto
+@@ -21,10 +21,12 @@
+
+ . /usr/lib/network/header-config
+
+-HOOK_CONFIG_SETTINGS="HOOK PRIVACY_EXTENSIONS"
++HOOK_SETTINGS=(
++ "PRIVACY_EXTENSIONS"
++)
+
+ # Privacy Extensions are disabled by default
+-PRIVACY_EXTENSIONS="off"
++DEFAULT_PRIVACY_EXTENSIONS="off"
+
+ hook_check_config_settings() {
+ assert isbool PRIVACY_EXTENSIONS
+@@ -35,17 +37,10 @@ hook_parse_cmdline() {
+ shift
+
+ local arg
+-
+ while read arg; do
+ case "${arg}" in
+ --privacy-extensions=*)
+- local val="$(cli_get_val "${arg}")"
+-
+- if enabled val; then
+- PRIVACY_EXTENSIONS="on"
+- else
+- PRIVACY_EXTENSIONS="off"
+- fi
++ PRIVACY_EXTENSIONS="$(cli_get_bool "${arg}")"
+ ;;
+ esac
+ done <<< "$(args "$@")"
+diff --git a/src/hooks/configs/pppoe-server b/src/hooks/configs/pppoe-server
+index 6a2c014..4d79549 100644
+--- a/src/hooks/configs/pppoe-server
++++ b/src/hooks/configs/pppoe-server
+@@ -21,21 +21,15 @@
+
+ . /usr/lib/network/header-config
+
+-HOOK_CONFIG_SETTINGS="HOOK DNS_SERVERS MTU SERVICE_NAME SUBNET MAX_SESSIONS"
+-
+-# Maximum Transmission Unit.
+-MTU=1492
+-
+-# Service Name.
+-SERVICE_NAME=
+-
+-# A subnet. Addresses from this subnet will be given to the remote hosts.
+-# The net address will be the gateway address for the PPPoE server.
+-SUBNET=
+-
+-# Defines the max. number of sessions per MAC address.
+-# 0 = unlimited.
+-MAX_SESSIONS=0
++HOOK_SETTINGS=(
++ "DNS_SERVERS"
++ "MTU"
++ "SERVICE_NAME"
++ "SUBNET MAX_SESSIONS"
++)
++
++DEFAULT_MTU=1492
++DEFAULT_MAX_SESSIONS=0
+
+ hook_check_config_settings() {
+ assert isset MTU
+diff --git a/src/hooks/configs/static b/src/hooks/configs/static
+index 23ae2d8..6fddc32 100644
+--- a/src/hooks/configs/static
++++ b/src/hooks/configs/static
+@@ -21,7 +21,11 @@
+
+ . /usr/lib/network/header-config
+
+-HOOK_CONFIG_SETTINGS="HOOK ADDRESS PREFIX GATEWAY"
++HOOK_SETTINGS=(
++ "ADDRESS"
++ "PREFIX"
++ "GATEWAY"
++)
+
+ hook_check_config_settings() {
+ local protocol="$(ip_detect_protocol "${ADDRESS}")"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,99 @@
+From 8ece5c30bf5917d4cd6dfb460207d1e85eb5df73 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 16:57:31 +0100
+Subject: [PATCH 047/304] dhcp: Rename "enabled" from configuration parameters
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/configs/dhcp | 38 +++++++++++++++-----------------------
+ 1 file changed, 15 insertions(+), 23 deletions(-)
+
+diff --git a/src/hooks/configs/dhcp b/src/hooks/configs/dhcp
+index 1ad0694..1c75193 100644
+--- a/src/hooks/configs/dhcp
++++ b/src/hooks/configs/dhcp
+@@ -22,18 +22,16 @@
+ . /usr/lib/network/header-config
+
+ HOOK_SETTINGS=(
+- "ENABLE_IPV6"
+- "ENABLE_IPV4"
++ "IPV6"
++ "IPV4"
+ )
+
+-DEFAULT_ENABLE_IPV6="on"
+-DEFAULT_ENABLE_IPV4="on"
++DEFAULT_IPV6="on"
++DEFAULT_IPV4="on"
+
+ hook_check_config_settings() {
+- assert isset ENABLE_IPV6
+- assert isbool ENABLE_IPV6
+- assert isset ENABLE_IPV4
+- assert isbool ENABLE_IPV4
++ assert isbool IPV6
++ assert isbool IPV4
+ }
+
+ hook_parse_cmdline() {
+@@ -42,17 +40,11 @@ hook_parse_cmdline() {
+
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+- --enable-ipv6)
+- ENABLE_IPV6="on"
++ --ipv6)
++ IPV6="$(cli_get_bool "${1}")"
+ ;;
+- --disable-ipv6)
+- ENABLE_IPV6="off"
+- ;;
+- --enable-ipv4)
+- ENABLE_IPV4="on"
+- ;;
+- --disable-ipv4)
+- ENABLE_IPV4="off"
++ --ipv4)
++ IPV4="$(cli_get_bool "${1}")"
+ ;;
+ *)
+ warning "Ignoring unknown option '${1}'"
+@@ -62,8 +54,8 @@ hook_parse_cmdline() {
+ done
+
+ # Check if the user disabled ipv6 and ipv4
+- if ! enabled ENABLE_IPV6 && ! enabled ENABLE_IPV4; then
+- log ERROR "You disabled IPv6 and IPv4. At least one must be enabled"
++ if ! enabled IPV6 && ! enabled IPV4; then
++ error "You disabled IPv6 and IPv4. At least one must be enabled"
+ return ${EXIT_ERROR}
+ fi
+ }
+@@ -106,12 +98,12 @@ hook_up() {
+ zone_config_settings_read "${zone}" "${config}"
+
+ # Start dhclient for IPv6 on this zone if enabled.
+- if enabled ENABLE_IPV6; then
++ if enabled IPV6; then
+ dhclient_start ${zone} ipv6
+ fi
+
+ # Start dhclient for IPv4 on this zone if enabled.
+- if enabled ENABLE_IPV4; then
++ if enabled IPV4; then
+ dhclient_start ${zone} ipv4
+ fi
+
+@@ -165,7 +157,7 @@ hook_status() {
+
+ cli_print_fmt1 3 "${proto}"
+
+- if enabled ENABLE_${proto^^}; then
++ if enabled "${proto^^}"; then
+ cli_print_fmt1 4 "Status" "enabled"
+
+ local address="$(db_get "${zone}/${_proto}/local-ip-address")"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,30 @@
+From e80eb68607dbdad381e3bb113521609c44fa8cd6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 17:05:44 +0100
+Subject: [PATCH 048/304] dhcp: Fix syntax error in last commit
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/configs/dhcp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/hooks/configs/dhcp b/src/hooks/configs/dhcp
+index 1c75193..ba5608a 100644
+--- a/src/hooks/configs/dhcp
++++ b/src/hooks/configs/dhcp
+@@ -40,10 +40,10 @@ hook_parse_cmdline() {
+
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+- --ipv6)
++ --ipv6=*)
+ IPV6="$(cli_get_bool "${1}")"
+ ;;
+- --ipv4)
++ --ipv4=*)
+ IPV4="$(cli_get_bool "${1}")"
+ ;;
+ *)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,158 @@
+From fdd9ac5fdd66b6cbdf014554281a9bb11ed0379d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 17:05:58 +0100
+Subject: [PATCH 049/304] hooks: Add HOOK_UNIQUE which stops us from creating
+ multiple instances
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/header-config | 9 +++++++++
+ src/hooks/configs/dhcp | 25 -------------------------
+ src/hooks/configs/ipv6-auto | 22 ----------------------
+ src/hooks/configs/pppoe-server | 22 ----------------------
+ src/hooks/configs/static | 3 +++
+ 5 files changed, 12 insertions(+), 69 deletions(-)
+
+diff --git a/src/header-config b/src/header-config
+index baeca5e..c6a775c 100644
+--- a/src/header-config
++++ b/src/header-config
+@@ -19,10 +19,19 @@
+ # #
+ ###############################################################################
+
++# Allow only one instance of this hook
++HOOK_UNIQUE="true"
++
+ hook_new() {
+ local zone="${1}"
+ shift
+
++ # Check if we are allowed to have multiple configurations of $HOOK
++ if enabled HOOK_UNIQUE && zone_config_hook_is_configured "${zone}" "${HOOK}"; then
++ error "You can only have one configuration of type ${HOOK}"
++ return ${EXIT_CONF_ERROR}
++ fi
++
+ local id=$(zone_config_get_new_id ${zone})
+ log DEBUG "ID for the config is: ${id}"
+
+diff --git a/src/hooks/configs/dhcp b/src/hooks/configs/dhcp
+index ba5608a..127ce59 100644
+--- a/src/hooks/configs/dhcp
++++ b/src/hooks/configs/dhcp
+@@ -60,31 +60,6 @@ hook_parse_cmdline() {
+ fi
+ }
+
+-hook_new() {
+- local zone="${1}"
+- shift
+-
+- if zone_config_hook_is_configured ${zone} "dhcp"; then
+- log ERROR "You can configure the dhcp hook only once for a zone"
+- return ${EXIT_ERROR}
+- fi
+-
+- local id=$(zone_config_get_new_id ${zone})
+- log DEBUG "ID for the config is: ${id}"
+-
+- # Import defaults
+- hook_set_defaults
+-
+- if ! hook_parse_cmdline "${id}" "$@"; then
+- # Return an error if the parsing of the cmd line fails
+- return ${EXIT_ERROR}
+- fi
+-
+- zone_config_settings_write "${zone}" "${HOOK}" "${id}"
+-
+- exit ${EXIT_OK}
+-}
+-
+ hook_up() {
+ local zone=${1}
+ local config=${2}
+diff --git a/src/hooks/configs/ipv6-auto b/src/hooks/configs/ipv6-auto
+index 6fd90a5..ecfafcd 100644
+--- a/src/hooks/configs/ipv6-auto
++++ b/src/hooks/configs/ipv6-auto
+@@ -46,28 +46,6 @@ hook_parse_cmdline() {
+ done <<< "$(args "$@")"
+ }
+
+-hook_new() {
+- local zone="${1}"
+- shift
+-
+- if zone_config_hook_is_configured ${zone} "ipv6-auto"; then
+- log ERROR "You can configure the ipv6-auto hook only once for a zone"
+- return ${EXIT_ERROR}
+- fi
+-
+- local id=$(zone_config_get_new_id ${zone})
+- log DEBUG "ID for the config is: ${id}"
+-
+- if ! hook_parse_cmdline "${id}" "$@"; then
+- # Return an error if the parsing of the cmd line fails
+- return ${EXIT_ERROR}
+- fi
+-
+- zone_config_settings_write "${zone}" "${HOOK}" "${id}"
+-
+- exit ${EXIT_OK}
+-}
+-
+ hook_up() {
+ local zone=${1}
+ shift
+diff --git a/src/hooks/configs/pppoe-server b/src/hooks/configs/pppoe-server
+index 4d79549..e800bf4 100644
+--- a/src/hooks/configs/pppoe-server
++++ b/src/hooks/configs/pppoe-server
+@@ -93,28 +93,6 @@ hook_parse_cmdline() {
+ done
+ }
+
+-hook_new() {
+- local zone=${1}
+- shift
+-
+- if zone_config_hook_is_configured ${zone} "pppoe-server"; then
+- log ERROR "You can configure the pppoe-server hook only once for a zone"
+- return ${EXIT_ERROR}
+- fi
+-
+- local id=$(zone_config_get_new_id ${zone})
+- log DEBUG "ID for the config is: ${id}"
+-
+- if ! hook_parse_cmdline "${id}" "$@"; then
+- # Return an error if the parsing of the cmd line fails
+- return ${EXIT_ERROR}
+- fi
+-
+- zone_config_settings_write "${zone}" "${HOOK}" "${id}"
+-
+- exit ${EXIT_OK}
+-}
+-
+ hook_up() {
+ local zone=${1}
+ local config=${2}
+diff --git a/src/hooks/configs/static b/src/hooks/configs/static
+index 6fddc32..046183a 100644
+--- a/src/hooks/configs/static
++++ b/src/hooks/configs/static
+@@ -21,6 +21,9 @@
+
+ . /usr/lib/network/header-config
+
++# Allow multiple instances of this hook
++HOOK_UNIQUE="false"
++
+ HOOK_SETTINGS=(
+ "ADDRESS"
+ "PREFIX"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,105 @@
+From d695b280e9972311ae8c4bc688c0898ade1281e6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 18:14:07 +0100
+Subject: [PATCH 050/304] wireless-ap: Check that secret has the correct length
+ and no invalid characters
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.util | 13 +++++++++++++
+ src/functions/functions.wireless | 23 +++++++++++------------
+ src/hooks/ports/wireless-ap | 14 +++++++++++---
+ 3 files changed, 35 insertions(+), 15 deletions(-)
+
+diff --git a/src/functions/functions.util b/src/functions/functions.util
+index 4c1dbb4..7379a98 100644
+--- a/src/functions/functions.util
++++ b/src/functions/functions.util
+@@ -745,6 +745,19 @@ contains_spaces() {
+ return ${EXIT_FALSE}
+ }
+
++contains_non_ascii_characters() {
++ local value="$@"
++
++ # Strip away all ASCII characters
++ local non_ascii="${value//[[:ascii:]]/}"
++
++ if isset non_ascii; then
++ return ${EXIT_TRUE}
++ fi
++
++ return ${EXIT_FALSE}
++}
++
+ string_match() {
+ local match=${1}
+ local string=${2}
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 12204c0..733a356 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -397,24 +397,23 @@ wireless_set_channel() {
+ }
+
+ wireless_pre_shared_key_is_valid() {
+- local encryption_mode="${1}"
+- local psk="${2}"
++ local psk="${1}"
+
+ # Length of the PSK
+ local l="${#psk}"
+
+- case "${encryption_mode}" in
+- # For WPA*, the key must be between 8 and 63 chars
+- WPA2-PSK|WPA2-PSK-SHA256|WPA-PSK|WPA-PSK-SHA256)
+- if [ ${l} -ge 8 ] && [ ${l} -le 63 ]; then
+- return ${EXIT_TRUE}
+- fi
++ # For WPA*, the key must be between 8 and 63 chars
++ if [ ${l} -lt 8 ] || [ ${l} -gt 63 ]; then
++ return ${EXIT_FALSE}
++ fi
+
+- return ${EXIT_FALSE}
+- ;;
+- esac
++ # Can only contain ASCII chararcters
++ if contains_non_ascii_characters "${psk}"; then
++ return ${EXIT_FALSE}
++ fi
+
+- return ${EXIT_ERROR}
++ # Seems OK
++ return ${EXIT_TRUE}
+ }
+
+ wireless_client_is_connected() {
+diff --git a/src/hooks/ports/wireless-ap b/src/hooks/ports/wireless-ap
+index 2528585..26e14d6 100644
+--- a/src/hooks/ports/wireless-ap
++++ b/src/hooks/ports/wireless-ap
+@@ -156,9 +156,17 @@ hook_parse_cmdline() {
+ fi
+
+ # Check if SECRET is set when WPA* is enabled
+- if ! isset SECRET && (enabled WPA3_PERSONAL || enabled WPA2_PERSONAL); then
+- error "Secret is not set when PSK authentication is enabled"
+- return ${EXIT_ERROR}
++ if enabled WPA3_PERSONAL || enabled WPA2_PERSONAL; then
++ if ! isset SECRET; then
++ error "Secret is not set when PSK authentication is enabled"
++ return ${EXIT_ERROR}
++ fi
++
++ # Check if SECRET is valid
++ if ! wireless_pre_shared_key_is_valid "${SECRET}"; then
++ error "The secret is in an invalid format"
++ return ${EXIT_ERROR}
++ fi
+ fi
+
+ # Save address of phy do identify it again
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,235 @@
+From d4564f2b7efa20ea025b6918b012656927fd342a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 18:51:13 +0100
+Subject: [PATCH 051/304] Drop old locking functions
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.device | 12 ++----
+ src/functions/functions.editor | 51 +++++++++--------------
+ src/functions/functions.firewall | 3 +-
+ src/functions/functions.lock | 70 --------------------------------
+ 4 files changed, 26 insertions(+), 110 deletions(-)
+
+diff --git a/src/functions/functions.device b/src/functions/functions.device
+index 48f2440..f52eee5 100644
+--- a/src/functions/functions.device
++++ b/src/functions/functions.device
+@@ -997,15 +997,11 @@ device_get_link_string() {
+ }
+
+ device_auto_configure_smp_affinity() {
+- assert [ $# -eq 1 ]
+-
+- local device=${1}
+-
+- if lock_acquire "smp-affinity" 60; then
+- device_set_smp_affinity ${device} auto
++ local device="${1}"
++ assert isset device
+
+- lock_release "smp-affinity"
+- fi
++ lock "smp-affinity" \
++ device_set_smp_affinity "${device}" "auto"
+ }
+
+ device_set_smp_affinity() {
+diff --git a/src/functions/functions.editor b/src/functions/functions.editor
+index 6edac62..8f0cc0b 100644
+--- a/src/functions/functions.editor
++++ b/src/functions/functions.editor
+@@ -19,17 +19,6 @@
+ # #
+ ###############################################################################
+
+-editor_cleanup() {
+- # Cleanup after a file was edited
+- assert [ $# -eq 2 ]
+-
+- local file=${1}
+- local temp_file=${2}
+-
+- lock_release "${file}.lock"
+- rm -f ${temp_file}
+-}
+-
+ editor_find_best() {
+ # Open a file with the best available editor
+ assert [ $# -eq 1 ]
+@@ -62,31 +51,26 @@ editor_find_best() {
+ }
+
+ editor() {
+- # This function open a file for editing and take care of all preperation and postprocessing
+- assert [ $# -ge 1 ]
++ local file="${1}"
++ assert isset file
+
+- local file=${1}
+ if [ ! -f ${file} ] || [ ! -w ${file} ]; then
+ error "${file} is not valid file or is not writeable"
+ return ${EXIT_ERROR}
+ fi
+
+- local check_func=${2}
++ lock "${file}.lock" __editor "$@"
++}
+
+- # check if the file is locked
+- if lock_exists "${file}.lock"; then
+- error "Cannot edit ${file} because it is locked"
+- return ${EXIT_ERROR}
+- fi
++__editor() {
++ # This function open a file for editing and take care of all preperation and postprocessing
++ assert [ $# -ge 1 ]
+
+- # lock the file
+- if ! lock_acquire "${file}.lock"; then
+- error "Cannot lock file ${file}"
+- return ${EXIT_ERROR}
+- fi
++ local file="${1}"
++ local check_func="${2}"
+
+ # create a temporary file
+- local temp_file=$(mktemp)
++ local temp_file="$(mktemp)"
+
+ if ! [ -f "${temp_file}" ]; then
+ error "Cannot create temporary file"
+@@ -98,21 +82,26 @@ editor() {
+ # edit the file
+ if ! editor_find_best "${temp_file}"; then
+ error "Could not edit ${file}"
+- # cleanup
+- editor_cleanup "${file}" "${temp_file}"
++
++ # Delete temporary file
++ file_delete "${temp_file}"
++
++ return ${EXIT_ERROR}
+ fi
+
+ # run the check if we have one
+ if isset check_func && ! editor_check "${check_func}" "${temp_file}"; then
++ # Delete temporary file
++ file_delete "${temp_file}"
++
+ return ${EXIT_ERROR}
+ fi
+
+ # copy the changes back
+ cp -f "${temp_file}" "${file}"
+
+- # cleanup
+- editor_cleanup "${file}" "${temp_file}"
+-
++ # Delete temporary file
++ file_delete "${temp_file}"
+ }
+
+ editor_check() {
+diff --git a/src/functions/functions.firewall b/src/functions/functions.firewall
+index 347916e..e22576b 100644
+--- a/src/functions/functions.firewall
++++ b/src/functions/functions.firewall
+@@ -269,7 +269,8 @@ firewall_panic() {
+ }
+
+ firewall_lock_acquire() {
+- lock_acquire ${RUN_DIR}/.firewall_lock
++ # XXX DEPRECATED
++ #lock_acquire ${RUN_DIR}/.firewall_lock
+
+ # Make sure the lock is released after the firewall
+ # script has crashed or exited early.
+diff --git a/src/functions/functions.lock b/src/functions/functions.lock
+index 6295a22..fd15e5e 100644
+--- a/src/functions/functions.lock
++++ b/src/functions/functions.lock
+@@ -19,16 +19,6 @@
+ # #
+ ###############################################################################
+
+-__lock_path() {
+- local name=${1}
+-
+- if [ "${name:0:1}" = "/" ]; then
+- echo "${name}"
+- else
+- echo "${LOCK_DIR}/network-${name}"
+- fi
+-}
+-
+ lock() {
+ local lock="${1}"
+ shift
+@@ -65,63 +55,3 @@ lock() {
+ exit ${ret}
+ ) 9>${lock} || exit $?
+ }
+-
+-lock_exists() {
+- local name=${1}
+- assert isset name
+-
+- local lockfile=$(__lock_path ${name})
+-
+- if [ -e "${lockfile}" ]; then
+- return ${EXIT_TRUE}
+- else
+- return ${EXIT_FALSE}
+- fi
+-}
+-
+-lock_acquire() {
+- local name=${1}
+- assert isset name
+-
+- # timeout value in seconds
+- local timeout=${2}
+-
+- if ! isset timeout; then
+- timeout=0
+- fi
+-
+- local lockfile=$(__lock_path ${name})
+-
+- timeout=$(( ${timeout} * 4 ))
+-
+- log DEBUG "Acquiring lock '${name}'"
+-
+- # Wait until lock is available
+- while [ ${timeout} -gt 0 ] && [ -e "${lockfile}" ]; do
+- timeout=$(( ${timeout} - 1 ))
+- sleep 0.25
+- done
+-
+- # If another lock still exists, we return an error
+- if [ -e "${lockfile}" ]; then
+- error "Could not acquire lock '${name}'"
+- return ${EXIT_ERROR}
+- fi
+-
+- # Write out pid to the lockfile and make sure that
+- # nobody else can access it.
+- echo "$$" > ${lockfile}
+- chmod 600 ${lockfile}
+-}
+-
+-lock_release() {
+- local name=${1}
+- assert isset name
+-
+- local lockfile=$(__lock_path ${name})
+-
+- log DEBUG "Releasing lock '${name}'"
+-
+- # Remove the lockfile (okay if it does not exist).
+- rm -f ${lockfile}
+-}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 55dcff454fa68dc2ff82f3dfbbafd75d3799b0ae Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 18:56:04 +0100
+Subject: [PATCH 052/304] ip-tunnel: Enable support for 6in4 tunnels
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/ip-tunnel | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/hooks/zones/ip-tunnel b/src/hooks/zones/ip-tunnel
+index 634154e..c4a4fb4 100644
+--- a/src/hooks/zones/ip-tunnel
++++ b/src/hooks/zones/ip-tunnel
+@@ -21,7 +21,7 @@
+
+ . /usr/lib/network/header-zone
+
+-SUPPORTED_IP_TUNNEL_MODES="gre vti"
++SUPPORTED_IP_TUNNEL_MODES="gre sit vti"
+
+ HOOK_SETTINGS=(
+ "MARK"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,37 @@
+From 1ed79f5432d0bd4c4f0c8f8692b488c268e379a4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 30 Mar 2019 19:03:24 +0100
+Subject: [PATCH 053/304] lock: Cleanup lock files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.lock | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.lock b/src/functions/functions.lock
+index fd15e5e..c01fcf3 100644
+--- a/src/functions/functions.lock
++++ b/src/functions/functions.lock
+@@ -29,6 +29,7 @@ lock() {
+ fi
+
+ local timeout="60"
++ local ret=0
+
+ # Make partent directory
+ make_parent_directory "${lock}"
+@@ -53,5 +54,10 @@ lock() {
+ log DEBUG "Released lock ${lock}"
+
+ exit ${ret}
+- ) 9>${lock} || exit $?
++ ) 9>${lock} || ret=$?
++
++ # Cleanup log file
++ file_delete "${lock}"
++
++ return ${ret}
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,45 @@
+From 1ef692c599a77fcb0683e3196b8f4b56f52644da Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 13:10:30 +0200
+Subject: [PATCH 054/304] hostapd: Require MFP for SAE when it is enabled
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.hostapd | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.hostapd b/src/functions/functions.hostapd
+index 095beb8..410e6e5 100644
+--- a/src/functions/functions.hostapd
++++ b/src/functions/functions.hostapd
+@@ -407,6 +407,7 @@ hostapd_config_write() {
+ local wpa_passphrase
+ local sae_password
+ local wpa_strict_rekey
++ local sae_require_mfp
+
+ # WPA3 Personal
+ if enabled WPA3_PERSONAL; then
+@@ -416,6 +417,10 @@ hostapd_config_write() {
+ # Add WPA key management
+ list_append wpa_key_mgmt "SAE"
+ sae_password="${secret}"
++
++ if enabled MFP; then
++ sae_require_mfp="1"
++ fi
+ fi
+
+ # WPA2 Personal
+@@ -441,7 +446,7 @@ hostapd_config_write() {
+
+ local var
+ for var in wpa wpa_key_mgmt wpa_passphrase sae_password \
+- rsn_pairwise group_cipher wpa_strict_rekey; do
++ rsn_pairwise group_cipher wpa_strict_rekeyi sae_require_mfp; do
+ if [ -n "${!var}" ]; then
+ print "${var}=${!var}"
+ fi
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,117 @@
+From 7a3747a1b0d2e219600979aa4286e8ffd96d5b59 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 14:14:55 +0200
+Subject: [PATCH 055/304] bird: Write IPv6 router advertisement configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.bird | 89 ++++++++++++++++++++++++++++++++++++
+ 1 file changed, 89 insertions(+)
+
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index c6fea32..950bb78 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -84,6 +84,9 @@ bird_generate_config() {
+ print "}"
+ print
+ done >> ${BIRD_CONF}
++
++ # Write IPv6 Router Advertisement configuration
++ __bird_ipv6_radv >> ${BIRD_CONF}
+ }
+
+ __bird_static_routes() {
+@@ -122,3 +125,89 @@ __bird_static_routes() {
+ esac
+ done < ${NETWORK_CONFIG_ROUTES}
+ }
++
++__bird_ipv6_radv() {
++ print "protocol radv {"
++
++ local zone
++ for zone in $(zones_get_local); do
++ log DEBUG "Writing bird radv configuration for ${zone}"
++
++ # Skip if there is no prefix or prefix is link-local.
++ local addr="$(db_get "${zone}/ipv6/local-ip-address")"
++ if [ -z "${addr}" ] || [ "${addr:0:5}" = "fe80:" ]; then
++ continue
++ fi
++
++ # Check if the subnet is configured by the DHCP server.
++ local dhcp="false"
++ local prefix="$(ipv6_get_network "${addr}")"
++ if isset prefix && dhcpd_subnet_match ipv6 "${prefix}"; then
++ dhcp="true"
++ fi
++
++ print " interface \"${zone}\" {"
++ # Failover to other routers within 10s
++ print " max ra interval 10;"
++
++ # Tell clients we are running DHCP
++ if enabled dhcp; then
++ print " managed yes;"
++ print " other config yes;"
++ fi
++
++ if device_exists "${zone}"; then
++ # Announce link MTU
++ local mtu="$(device_get_mtu "${zone}")"
++ print " link mtu ${mtu};"
++ fi
++
++ print # empty line
++
++ # Announce all prefixes
++ print " prefix ::/0 {"
++
++ if enabled dhcp; then
++ print " autonomous off;"
++ fi
++
++ print " };"
++ print " };\n"
++ done
++
++ # Advertise any DNS servers
++ if enabled DNS_ADVERTISE_SERVERS; then
++ # Get a list of all IPv6 name servers
++ local servers=()
++ local server
++ for server in $(dns_server_list_sorted); do
++ # Skip any non-IPv6 servers
++ ipv6_is_valid "${server}" || continue
++
++ servers+=( "${server}" )
++ done
++
++ if isset servers; then
++ print " rdnss {"
++
++ local server
++ for server in ${servers}; do
++ print " ns ${server};"
++ done
++
++ print " };"
++ fi
++ fi
++
++ # DNS Search Domain
++ print " dnssl {"
++
++ local domain
++ for domain in $(dns_get_search_domains); do
++ print " domain \"${domain}\";"
++ done
++
++ print " };"
++
++ print "}\n"
++}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,314 @@
+From 1cb20d39b29a1bd73cef2926cc4aae651f653ca7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 14:20:40 +0200
+Subject: [PATCH 056/304] Drop code for radvd
+
+This is now being replaced by bird.
+
+Bird is running anyways and can do this job just as well.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 -
+ src/functions/functions.bird | 11 +++
+ src/functions/functions.dns | 8 +-
+ src/functions/functions.radvd | 160 --------------------------------
+ src/functions/functions.routing | 4 +-
+ src/network-radvd-config | 35 -------
+ 6 files changed, 17 insertions(+), 203 deletions(-)
+ delete mode 100644 src/functions/functions.radvd
+ delete mode 100644 src/network-radvd-config
+
+diff --git a/Makefile.am b/Makefile.am
+index 1b5e7e9..ce587b7 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -173,7 +173,6 @@ dist_network_DATA = \
+ src/functions/functions.ports \
+ src/functions/functions.ppp \
+ src/functions/functions.pppoe-server \
+- src/functions/functions.radvd \
+ src/functions/functions.route \
+ src/functions/functions.routing \
+ src/functions/functions.serial \
+@@ -193,7 +192,6 @@ dist_network_DATA = \
+ src/functions/functions.wireless-networks \
+ src/functions/functions.wpa_supplicant \
+ src/functions/functions.zone \
+- src/network-radvd-config \
+ src/header-config \
+ src/header-port \
+ src/header-zone
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index 950bb78..55d43b5 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -33,6 +33,17 @@ bird_reload() {
+ service_reload "bird.service"
+ }
+
++# Update configuration any apply it in one go
++bird_update() {
++ if ! bird_generate_config; then
++ log ERROR "Could not write Bird configuration"
++ return ${EXIT_ERROR}
++ fi
++
++ # Reload bird
++ bird_reload
++}
++
+ bird_generate_config() {
+ log DEBUG "Write BIRD configuration file"
+
+diff --git a/src/functions/functions.dns b/src/functions/functions.dns
+index 890f1ac..0e058be 100644
+--- a/src/functions/functions.dns
++++ b/src/functions/functions.dns
+@@ -31,8 +31,8 @@ NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} DNS_RANDOMIZE"
+ DNS_SEARCH_DOMAINS=""
+ NETWORK_SETTINGS_FILE_PARAMS="${NETWORK_SETTINGS_FILE_PARAMS} DNS_SEARCH_DOMAINS"
+
+-# Set this option to true if the DNS servers should be advertised by
+-# radvd.
++# Set this option to true if the DNS servers should be advertised in
++# IPv6 router advertisements
+ DNS_ADVERTISE_SERVERS="true"
+
+ DNS_SERVER_CONFIG_FILE="${NETWORK_CONFIG_DIR}/dns-servers"
+@@ -234,8 +234,8 @@ dns_server_update() {
+ # Regenerate /etc/resolv.conf
+ dns_generate_resolvconf
+
+- # Restart radvd which propagates IPv6 DNS servers
+- radvd_update
++ # Update bird about IPv6 DNS server changes
++ bird_update
+ }
+
+ dns_generate_resolvconf() {
+diff --git a/src/functions/functions.radvd b/src/functions/functions.radvd
+deleted file mode 100644
+index 1c8b8d0..0000000
+--- a/src/functions/functions.radvd
++++ /dev/null
+@@ -1,160 +0,0 @@
+-#!/bin/bash
+-###############################################################################
+-# #
+-# IPFire.org - A linux based firewall #
+-# Copyright (C) 2010 Michael Tremer & Christian Schmidt #
+-# #
+-# 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, either version 3 of the License, or #
+-# (at your option) any later version. #
+-# #
+-# 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 <http://www.gnu.org/licenses/>. #
+-# #
+-###############################################################################
+-
+-RADVD_CONFIGFILE="/etc/radvd.conf"
+-
+-radvd_update() {
+- # (Re-)write the configuration file
+- if radvd_write_config; then
+- # Reload the radvd service if it is already running
+- if service_is_active radvd; then
+- service_reload radvd
+- return ${EXIT_OK}
+- fi
+-
+- # Start the radvd service
+- service_start radvd
+- fi
+-}
+-
+-radvd_clear_config() {
+- log DEBUG "Clearing radv daemon configuration file"
+-
+- config_header "radv daemon configuration file" > ${RADVD_CONFIGFILE}
+-
+- return ${EXIT_OK}
+-}
+-
+-radvd_write_config() {
+- radvd_clear_config
+-
+- # Write the configuration for all zones.
+- local zone
+-
+- # The return value determine if radvd is started or not
+- local return_value=${EXIT_FALSE}
+-
+- for zone in $(zones_get_local); do
+- if __radvd_config_interface ${zone}; then
+- # We return TRUE when __radvd_config_interface returns True
+- return_value=${EXIT_TRUE}
+- fi
+- done >> ${RADVD_CONFIGFILE}
+-
+- return ${return_value}
+-}
+-
+-# This function return ${EXIT_FALSE} if no radvd config was written and ${EXIT_TRUE} in all other cases
+-__radvd_config_interface() {
+- local zone=${1}
+- assert isset zone
+-
+- log DEBUG "Writing radvd configuration for ${zone}."
+-
+- # If the interface does not provide any routing information,
+- # we can skip this whole stuff.
+- if ! db_exists "${zone}/ipv6"; then
+- return ${EXIT_FALSE}
+- fi
+-
+- # Skip if zone is not active.
+- local active="$(db_get "${zone}/ipv6/active")"
+- [ "${active}" = "0" ] && return ${EXIT_FALSE}
+-
+- # Skip if there is no prefix or prefix is link-local.
+- local addr="$(db_get "${zone}/ipv6/local-ip-address")"
+- if [ -z "${addr}" ] || [ "${addr:0:5}" = "fe80:" ]; then
+- return ${EXIT_FALSE}
+- fi
+-
+- # Check if the subnet is configured by the DHCP server.
+- local dhcpd="false"
+- local prefix="$(ipv6_get_network "${addr}")"
+- if isset prefix && dhcpd_subnet_match ipv6 "${prefix}"; then
+- dhcpd="true"
+- fi
+-
+- print "interface ${zone} {"
+- print " AdvSendAdvert on;"
+- print " MinRtrAdvInterval 3;"
+- print " MaxRtrAdvInterval 10;"
+- print " IgnoreIfMissing on;"
+-
+- if enabled dhcpd; then
+- print " AdvManagedFlag on;"
+- print " AdvOtherConfigFlag on;"
+- fi
+-
+- print
+- print " prefix ::/64 {"
+- print " AdvOnLink on;"
+-
+- if enabled dhcpd; then
+- print " AdvRouterAddr off;"
+- print " AdvAutonomous off;"
+- else
+- print " AdvRouterAddr on;"
+- print " AdvAutonomous on;"
+- fi
+-
+- print " };"
+- print
+-
+- # Add the DNS configuration.
+- __radvd_config_dns ${zone}
+-
+- print "};"
+- print
+-
+- return ${EXIT_TRUE}
+-}
+-
+-__radvd_config_dns() {
+- local zone=${1}
+-
+- # Do nothing, when this option is not enabled.
+- enabled DNS_ADVERTISE_SERVERS || return ${EXIT_OK}
+-
+- # XXX it is kind of difficult to announce our local
+- # resolver.
+-
+- local server servers
+- for server in $(dns_server_list_sorted); do
+- # Filter out non IPv6 addresses.
+- ipv6_is_valid ${server} || continue
+-
+- servers="${servers} ${server}"
+- done
+-
+- # Remove whitespaces.
+- servers=$(echo ${servers})
+-
+- # If there are no servers to announce, we stop right here.
+- if ! isset servers; then
+- log DEBUG "No servers to announce."
+- return ${EXIT_OK}
+- fi
+-
+- print " RDNSS ${servers} {"
+- print " # Use the defaults here."
+- print " };"
+- print
+-}
+diff --git a/src/functions/functions.routing b/src/functions/functions.routing
+index c7aac09..351cc53 100644
+--- a/src/functions/functions.routing
++++ b/src/functions/functions.routing
+@@ -80,8 +80,8 @@ routing_default_update() {
+ # Remove too much spaces.
+ routes=$(echo ${routes})
+
+- # Reload radvd configuration
+- [[ "${proto}" = "ipv6" ]] && radvd_update
++ # Reload bird configuration
++ [[ "${proto}" = "ipv6" ]] && bird_update
+
+ # Remove all default routes.
+ if [ -z "${routes}" ]; then
+diff --git a/src/network-radvd-config b/src/network-radvd-config
+deleted file mode 100644
+index e9809e1..0000000
+--- a/src/network-radvd-config
++++ /dev/null
+@@ -1,35 +0,0 @@
+-#!/bin/bash
+-###############################################################################
+-# #
+-# IPFire.org - A linux based firewall #
+-# Copyright (C) 2011 Michael Tremer & Christian Schmidt #
+-# #
+-# 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, either version 3 of the License, or #
+-# (at your option) any later version. #
+-# #
+-# 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 <http://www.gnu.org/licenses/>. #
+-# #
+-###############################################################################
+-
+-. /lib/network/functions
+-
+-case "${1}" in
+- start)
+- # Write the radvd configuration file.
+- radvd_write_config
+- ;;
+- stop)
+- # Clear all contents in the configuration file.
+- radvd_clear_config
+- ;;
+-esac
+-
+-exit ${EXIT_OK}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From f116762cf279b39749bea053eca0e873c60e23f1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 14:21:18 +0200
+Subject: [PATCH 057/304] .gitignore: Ignore vim's swp files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ .gitignore | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/.gitignore b/.gitignore
+index a6df183..36c85a1 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -20,6 +20,7 @@
+ *.lo
+ *.o
+ *.stamp
++*.swp
+ *.trs
+ *~
+ .deps
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,73 @@
+From 39beacd0549be57fde9eb350c2c9292094537629 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 14:28:44 +0200
+Subject: [PATCH 058/304] bird: Make sure the daemon is always running
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.bird | 14 ++++++++++++++
+ src/functions/functions.route | 7 ++-----
+ src/network | 4 ++--
+ 3 files changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index 55d43b5..1bbac8c 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -33,6 +33,20 @@ bird_reload() {
+ service_reload "bird.service"
+ }
+
++bird_enable() {
++ # Generate configuration file
++ if ! bird_generate_config; then
++ log ERROR "Could not write Bird configuration"
++ return ${EXIT_ERROR}
++ fi
++
++ # Enable the service to be automatically started next time
++ service_enable "bird.service"
++
++ # Start it now
++ bird_start
++}
++
+ # Update configuration any apply it in one go
+ bird_update() {
+ if ! bird_generate_config; then
+diff --git a/src/functions/functions.route b/src/functions/functions.route
+index e6ea244..b833822 100644
+--- a/src/functions/functions.route
++++ b/src/functions/functions.route
+@@ -393,11 +393,8 @@ route_parse_line() {
+ }
+
+ route_apply() {
+- # Re-generate BIRD configuration
+- bird_generate_config
+-
+- # Reload the daemon
+- bird_reload
++ # Update bird
++ bird_update
+ }
+
+ route_entry_add() {
+diff --git a/src/network b/src/network
+index 300ba94..be06d8a 100644
+--- a/src/network
++++ b/src/network
+@@ -1381,8 +1381,8 @@ case "${action}" in
+ # Update resolv.conf(5) when initializing the network
+ dns_generate_resolvconf
+
+- # Update bird configuration
+- bird_generate_config
++ # Make sure bird is running
++ bird_enable
+
+ # Also execute all triggers
+ triggers_execute_all "init"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From 57496df2abdaa620e8ce68abfa5ad65b211a3484 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 26 Sep 2018 22:14:27 +0200
+Subject: [PATCH 059/304] configure: Require asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 08e9089..117850f 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -143,6 +143,10 @@ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
+
+ # ------------------------------------------------------------------------------
+
++AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
++
++# ------------------------------------------------------------------------------
++
+ AC_CONFIG_HEADERS(config.h)
+ AC_CONFIG_FILES([
+ Makefile
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From 8f591cfc10d1876523d608d9643f0a82517c2add Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 26 Sep 2018 22:42:36 +0200
+Subject: [PATCH 060/304] man: Add test page for asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 14 +++++++++++---
+ man/.gitignore | 1 +
+ man/test.txt | 11 +++++++++++
+ 3 files changed, 23 insertions(+), 3 deletions(-)
+ create mode 100644 man/test.txt
+
+diff --git a/Makefile.am b/Makefile.am
+index ce587b7..d01e223 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -89,6 +89,10 @@ INSTALL_EXEC_HOOKS += \
+
+ # ------------------------------------------------------------------------------
+
++AM_V_ASCIIDOC = $(AM_V_ASCIIDOC_$(V))
++AM_V_ASCIIDOC_ = $(AM_V_ASCIIDOC_$(AM_DEFAULT_VERBOSITY))
++AM_V_ASCIIDOC_0 = @echo " ASCIIDOC" $@;
++
+ AM_V_DOWNLOAD = $(AM_V_DOWNLOAD_$(V))
+ AM_V_DOWNLOAD_ = $(AM_V_DOWNLOAD_$(AM_DEFAULT_VERBOSITY))
+ AM_V_DOWNLOAD_0 = @echo " LOAD " $@;
+@@ -449,6 +453,7 @@ INSTALL_DIRS += \
+ # ------------------------------------------------------------------------------
+
+ MANPAGES = \
++ man/test.8 \
+ man/firewall-settings.8 \
+ man/network.8 \
+ man/network-color.8 \
+@@ -472,8 +477,8 @@ MANPAGES = \
+ man/network-zone-pppoe.8 \
+ man/network-zone-wireless.8
+
+-MANPAGES_XML = $(patsubst %.8,%.xml,$(MANPAGES))
+-MANPAGES_HTML = $(patsubst %.xml,%.html,$(MANPAGES_XML))
++MANPAGES_TXT = $(patsubst %.8,%.txt,$(MANPAGES))
++MANPAGES_HTML = $(patsubst %.txt,%.html,$(MANPAGES))
+
+ .PHONY: man
+ man: $(MANPAGES) $(MANPAGES_HTML)
+@@ -489,7 +494,7 @@ CLEANFILES += \
+ $(MANPAGES_HTML)
+
+ EXTRA_DIST += \
+- $(MANPAGES_XML) \
++ $(MANPAGES_TXT) \
+ man/custom-html.xsl
+
+ XSLTPROC_FLAGS = \
+@@ -507,6 +512,9 @@ XSLTPROC_COMMAND_MAN = \
+ XSLTPROC_COMMAND_HTML = \
+ $(AM_V_XSLT)$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(srcdir)/man/custom-html.xsl $<
+
++man/%.xml: man/%.txt
++ $(AM_V_ASCIIDOC)$(ASCIIDOC) -d manpage -b docbook -o $@ $<
++
+ man/%.8: man/%.xml
+ $(XSLTPROC_COMMAND_MAN)
+
+diff --git a/man/.gitignore b/man/.gitignore
+index 237049a..f891826 100644
+--- a/man/.gitignore
++++ b/man/.gitignore
+@@ -1,2 +1,3 @@
+ /*.[13578]
+ /*.html
++/*.xml
+diff --git a/man/test.txt b/man/test.txt
+new file mode 100644
+index 0000000..4c9d35d
+--- /dev/null
++++ b/man/test.txt
+@@ -0,0 +1,11 @@
++test(8)
++=======
++
++NAME
++----
++test - Hello World!
++
++SYNOPSIS
++--------
++[verse]
++'hello world' [<options>] <file>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,86 @@
+From a7d2fef75b529c8cc10c4d22fca3114e30542394 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 26 Sep 2018 23:04:35 +0200
+Subject: [PATCH 061/304] man: Use asciidoc to generate HTML pages directly
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 10 +++-------
+ man/custom-html.xsl | 31 -------------------------------
+ 2 files changed, 3 insertions(+), 38 deletions(-)
+ delete mode 100644 man/custom-html.xsl
+
+diff --git a/Makefile.am b/Makefile.am
+index d01e223..c4f8b45 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -494,8 +494,7 @@ CLEANFILES += \
+ $(MANPAGES_HTML)
+
+ EXTRA_DIST += \
+- $(MANPAGES_TXT) \
+- man/custom-html.xsl
++ $(MANPAGES_TXT)
+
+ XSLTPROC_FLAGS = \
+ --nonet \
+@@ -509,17 +508,14 @@ XSLTPROC_COMMAND_MAN = \
+ $(AM_V_XSLT)$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) \
+ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+-XSLTPROC_COMMAND_HTML = \
+- $(AM_V_XSLT)$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) $(srcdir)/man/custom-html.xsl $<
+-
+ man/%.xml: man/%.txt
+ $(AM_V_ASCIIDOC)$(ASCIIDOC) -d manpage -b docbook -o $@ $<
+
+ man/%.8: man/%.xml
+ $(XSLTPROC_COMMAND_MAN)
+
+-man/%.html: man/%.xml man/custom-html.xsl
+- $(XSLTPROC_COMMAND_HTML)
++man/%.html: man/%.txt
++ $(AM_V_ASCIIDOC)$(ASCIIDOC) -b html5 -a icons -a theme=flask -o $@ $<
+
+ # ------------------------------------------------------------------------------
+
+diff --git a/man/custom-html.xsl b/man/custom-html.xsl
+deleted file mode 100644
+index fe2b54e..0000000
+--- a/man/custom-html.xsl
++++ /dev/null
+@@ -1,31 +0,0 @@
+-<?xml version='1.0'?>
+-
+-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+-
+-<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+-
+-<!-- translate man page references to links to html pages -->
+-<xsl:template match="citerefentry">
+- <a>
+- <xsl:attribute name="href">
+- <xsl:value-of select="refentrytitle"/><xsl:text>.html</xsl:text>
+- </xsl:attribute>
+- <xsl:call-template name="inline.charseq"/>
+- </a>
+-</xsl:template>
+-
+-<!-- add Index link at top of page -->
+-<xsl:template name="user.header.content">
+- <a>
+- <xsl:attribute name="href">
+- <xsl:text>index.html</xsl:text>
+- </xsl:attribute>
+- <xsl:text>Index</xsl:text>
+- </a>
+- <hr/>
+-</xsl:template>
+-
+-<!-- Switch things to UTF-8, ISO-8859-1 is soo yesteryear -->
+-<xsl:output method="html" encoding="UTF-8" indent="no"/>
+-
+-</xsl:stylesheet>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,62 @@
+From baf429f17d664bbc6d141c13ce6ed52091803c3b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 27 Sep 2018 00:22:59 +0200
+Subject: [PATCH 062/304] man: Add asciidoc configuration file
+
+This adds a short command to link to other man pages
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 12 ++++++++----
+ man/asciidoc.conf | 12 ++++++++++++
+ 2 files changed, 20 insertions(+), 4 deletions(-)
+ create mode 100644 man/asciidoc.conf
+
+diff --git a/Makefile.am b/Makefile.am
+index c4f8b45..ebf3be7 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -508,14 +508,18 @@ XSLTPROC_COMMAND_MAN = \
+ $(AM_V_XSLT)$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) \
+ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+-man/%.xml: man/%.txt
+- $(AM_V_ASCIIDOC)$(ASCIIDOC) -d manpage -b docbook -o $@ $<
++man/%.xml: man/%.txt man/asciidoc.conf
++ $(AM_V_ASCIIDOC)$(ASCIIDOC) \
++ -f man/asciidoc.conf \
++ -d manpage -b docbook -o $@ $<
+
+ man/%.8: man/%.xml
+ $(XSLTPROC_COMMAND_MAN)
+
+-man/%.html: man/%.txt
+- $(AM_V_ASCIIDOC)$(ASCIIDOC) -b html5 -a icons -a theme=flask -o $@ $<
++man/%.html: man/%.txt man/asciidoc.conf
++ $(AM_V_ASCIIDOC)$(ASCIIDOC) \
++ -f man/asciidoc.conf \
++ -b html5 -a icons -a theme=flask -o $@ $<
+
+ # ------------------------------------------------------------------------------
+
+diff --git a/man/asciidoc.conf b/man/asciidoc.conf
+new file mode 100644
+index 0000000..243f81f
+--- /dev/null
++++ b/man/asciidoc.conf
+@@ -0,0 +1,12 @@
++ifdef::backend-docbook[]
++[link-inlinemacro]
++{0%{target}}
++{0#<citerefentry>}
++{0#<refentrytitle>{target}</refentrytitle><manvolnum>{0}</manvolnum>}
++{0#</citerefentry>}
++endif::backend-docbook[]
++
++ifdef::backend-html5[]
++[link-inlinemacro]
++<a href="{target}.html">{target}{0?({0})}</a>
++endif::backend-html5[]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,503 @@
+From 44d5ffe94daa496c95bf91860a5211272d8f3ff1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 27 Sep 2018 00:25:12 +0200
+Subject: [PATCH 063/304] man: Convert network(8) from docbook to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network.txt | 107 ++++++++++++++
+ man/network.xml | 368 ------------------------------------------------
+ 2 files changed, 107 insertions(+), 368 deletions(-)
+ create mode 100644 man/network.txt
+ delete mode 100644 man/network.xml
+
+diff --git a/man/network.txt b/man/network.txt
+new file mode 100644
+index 0000000..569449e
+--- /dev/null
++++ b/man/network.txt
+@@ -0,0 +1,107 @@
++network(8)
++==========
++
++NAME
++----
++network - IPFire Network Configuration Program
++
++SYNOPSIS
++--------
++[verse]
++'network' [<options>] <command> ...
++
++DESCRIPTION
++-----------
++The 'network' command is a tool which configures the network on every IPFire
++system. It is a fast and versatile way to create, edit and remove configurations,
++review the status of the network and it is working in the background of the
++system make sure that things are running smoothly.
++
++OPTIONS
++-------
++-d::
++--debug::
++ Enabled debugging mode.
++ In this mode, there wll be debug output on the console and written to
++ the log.
++ The debugging mode can be permanently enabled by setting 'DEBUG=1'.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'start' [ZONE]::
++ Starts a zone. That means the zone is being created and brought up.
++ If one or more zones are passed to the command, only these will be
++ started.
++
++'stop' [ZONE]::
++ Stops a zone. This is the inverse of the 'start' command.
++
++'restart' [ZONE]::
++ Restarts a zone.
++
++'status' [ZONE]::
++ Shows an overview of the status of the zone.
++
++'zone' ...::
++ Commands to configure zones. See link:network-zone[8] for details.
++
++'port' ...::
++ Commands to configure ports. See link:network-port[8] for details.
++
++'device' ...::
++ See the status or execute commands to network devices.
++ See link:network-device[8] for details.
++
++'hostname' [HOSTNAME]::
++ Without the optional 'HOSTNAME' argument, this command will print
++ the configured hostname.
++ Passing 'HOSTNAME' will set it as the new hostname.
++
++'settings' ...::
++ Shows and alters global configuration parameters.
++ See link:network-settings[8] for details.
++
++'dns-server' ...::
++ This command allows to configure DNS servers.
++ See link::network-dns-server[8] for details.
++
++'route' ...::
++ This command allows managing static routes.
++ See link:network-route[8] for details.
++
++'vpn' ...::
++ The command allows managing VPN connections.
++ See link:network-vpn[8] for details.
++
++'reset'::
++ This command will reset all network configuration.
++ All zones, ports and other settings will be removed.
++
++'help' ...::
++ Shows this man page.
++
++EXIT CODES
++----------
++The 'network' command will normally exit with code zero.
++If there has been aproblem and the requested action could not be performed,
++the exit code is unequal to zero.
++
++BUGS
++----
++Please report all bugs to the bugtracker at https://bugzilla.ipfire.org/.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network-settings[8]
++link:network-device[8]
++link:network-dns-server[8]
++link:network-performance-tuning[8]
++link:network-port[8]
++link:network-quick-start[8]
++link:network-zone[8]
+diff --git a/man/network.xml b/man/network.xml
+deleted file mode 100644
+index 0a97453..0000000
+--- a/man/network.xml
++++ /dev/null
+@@ -1,368 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network">
+- <refentryinfo>
+- <title>network</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network</command>
+- <arg choice="opt" rep="repeat">OPTIONS</arg>
+- <arg choice="plain">COMMAND</arg>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>network</command> command is a tool which configures
+- the network on every IPFire system. It is a fast and versatile
+- way to create, edit and remove configurations, review the status
+- of the network and it is working in the background of the system
+- making sure that things are running smoothly.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>-d</option>
+- </term>
+- <term>
+- <option>--debug</option>
+- </term>
+-
+- <listitem>
+- <para>
+- Enables the debugging mode.
+- In this mode, there will be debug output on
+- the console and written to the log.
+- </para>
+- <para>
+- The debugging mode can be permanently enabled by setting
+- <varname>DEBUG=1</varname>
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>start <replaceable>ZONE-NAME</replaceable>...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Starts a zone. That means the network zone will be created
+- and brought up.
+- If one or more zone names are passed to the command, only
+- these will be started.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>stop <replaceable>ZONE-NAME</replaceable>...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Stops a zone. This is the inverse of the <command>start</command>
+- command.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>restart <replaceable>ZONE-NAME</replaceable>...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Restarts a zone.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>status <replaceable>ZONE-NAME</replaceable>...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows a human-readable overview of the status
+- of the network zone.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>zone ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Configure a zone or show status information.
+- See <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>port ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Configure a port or show status information.
+- See <citerefentry>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>device ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Show status information about network devices.
+- See <citerefentry>
+- <refentrytitle>network-device</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>config <replaceable><varname>KEY=VALUE</varname></replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows and alters global configuration parameters.
+- See <citerefentry>
+- <refentrytitle>network-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>help [<replaceable>TYPE</replaceable>
+- <replaceable>HOOK</replaceable>|<replaceable>TYPE</replaceable> list-hooks]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Calling <command>network help</command> without any
+- arguments will show you this man page.
+- </para>
+- <para>
+- <command>network help <replaceable>TYPE</replaceable> list-hooks</command>
+- will print a list of all hooks of <replaceable>TYPE</replaceable>.
+- </para>
+- <para>
+- You may optionally pass two arguments, to view the help
+- of a certain hook.
+- The type of the hook <replaceable>TYPE</replaceable>
+- needs to be passed as well as the name of the hook
+- <replaceable>HOOK</replaceable>.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>hostname <replaceable>HOSTNAME</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>hostname</command> command will return the
+- currently configured hostname of the system.
+- </para>
+- <para>
+- If a new hostname is added to the command line,
+- it will be configured, but will be set after the next
+- reboot.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>dns-server ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>dns-server</command> command will help you
+- configuring the local DNS servers.
+- See <citerefentry>
+- <refentrytitle>network-dns-server</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>route ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>route</command> command allows managing static routes.
+- See <citerefentry>
+- <refentrytitle>network-route</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>vpn ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>vpn</command> allows managing VPN connections.
+- See <citerefentry>
+- <refentrytitle>network-vpn</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>reset</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>reset</command> command will reset all
+- network configuration. That means all zone configurations
+- will be removed and there will be no networking after the
+- next reboot.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>Exit Codes</title>
+-
+- <para>
+- The <command>network</command> command will normally exit with code 0.
+- If there has been a problem and the requested action could not be done,
+- the exit code is unequal to zero.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Bugs</title>
+-
+- <para>
+- Please report all bugs to the official bugtracker at
+- http://bugs.ipfire.org/.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-device</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-dns-server</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-performance-tuning</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-quick-start</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,152 @@
+From b2f5dc13f74d0b740885f99a7d1408480da582cf Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 27 Sep 2018 00:34:35 +0200
+Subject: [PATCH 064/304] man: Convert network-color(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-color.txt | 33 ++++++++++++++++
+ man/network-color.xml | 91 -------------------------------------------
+ 2 files changed, 33 insertions(+), 91 deletions(-)
+ create mode 100644 man/network-color.txt
+ delete mode 100644 man/network-color.xml
+
+diff --git a/man/network-color.txt b/man/network-color.txt
+new file mode 100644
+index 0000000..7c95e18
+--- /dev/null
++++ b/man/network-color.txt
+@@ -0,0 +1,33 @@
++network-color(8)
++================
++
++NAME
++----
++network-color - IPFire Network Configuration Control Program
++
++DESCRIPTION
++-----------
++The 'color' command helps to manage colors for zones and ports.
++The color is being used to make identification of a zone or port easier on the
++command line and web user interface.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'set' [AABBCC]::
++ The color of a zone or port is set with the 'set' command.
++ It is required to pass a color in hex formatting.
++
++'reset'::
++ Resets the color of a zone or port to blank.
++
++AUTHOR
++------
++Jonatan Schlag
++
++SEE ALSO
++--------
++link:network[8]
++link:network-zone[8]
++link:network-port[8]
+diff --git a/man/network-color.xml b/man/network-color.xml
+deleted file mode 100644
+index caf2349..0000000
+--- a/man/network-color.xml
++++ /dev/null
+@@ -1,91 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-color">
+- <refentryinfo>
+- <title>networ-color</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Jonatan</firstname>
+- <surname>Schlag</surname>
+- <email>jonatan.schlag@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-color</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-color</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>color</command> helps to manage colors for zone and ports.
+- The color is used to make identification of a zone or port easier on the
+- command line or web user interface.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>set <replaceable>00AABB</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- The color of a zone or port is set with the <command>set</command> command.
+- It is always required to pass a valid color hex value (e.g. 880400).
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command>reset</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command resets the color of a zone or port to blank.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,44 @@
+From 91305dee4f83ca35758e756903e3324117a26a7d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 27 Sep 2018 00:36:02 +0200
+Subject: [PATCH 065/304] man: Drop test page
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 -
+ man/test.txt | 11 -----------
+ 2 files changed, 12 deletions(-)
+ delete mode 100644 man/test.txt
+
+diff --git a/Makefile.am b/Makefile.am
+index ebf3be7..55d5d18 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -453,7 +453,6 @@ INSTALL_DIRS += \
+ # ------------------------------------------------------------------------------
+
+ MANPAGES = \
+- man/test.8 \
+ man/firewall-settings.8 \
+ man/network.8 \
+ man/network-color.8 \
+diff --git a/man/test.txt b/man/test.txt
+deleted file mode 100644
+index 4c9d35d..0000000
+--- a/man/test.txt
++++ /dev/null
+@@ -1,11 +0,0 @@
+-test(8)
+-=======
+-
+-NAME
+-----
+-test - Hello World!
+-
+-SYNOPSIS
+---------
+-[verse]
+-'hello world' [<options>] <file>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From 62191ec375cf7fc957690d88c663ae7ad479a1a4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 27 Sep 2018 00:47:19 +0200
+Subject: [PATCH 066/304] man: network-color: Add synopsis
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-color.txt | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/man/network-color.txt b/man/network-color.txt
+index 7c95e18..f3be474 100644
+--- a/man/network-color.txt
++++ b/man/network-color.txt
+@@ -3,7 +3,13 @@ network-color(8)
+
+ NAME
+ ----
+-network-color - IPFire Network Configuration Control Program
++network-color - Allows assigning a color to a zone or port
++
++SYNOPSIS
++--------
++[verse]
++'network' [zone ZONE|port PORT] color set AABBCC
++'network' [zone ZONE|port PORT] reset
+
+ DESCRIPTION
+ -----------
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,409 @@
+From 66fe74f95f4da254fc1162c591a40012c17aab07 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 21:16:10 +0200
+Subject: [PATCH 067/304] man: Convert firewall-settings to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/firewall-settings.txt | 97 +++++++++++++
+ man/firewall-settings.xml | 284 --------------------------------------
+ 2 files changed, 97 insertions(+), 284 deletions(-)
+ create mode 100644 man/firewall-settings.txt
+ delete mode 100644 man/firewall-settings.xml
+
+diff --git a/man/firewall-settings.txt b/man/firewall-settings.txt
+new file mode 100644
+index 0000000..20038e3
+--- /dev/null
++++ b/man/firewall-settings.txt
+@@ -0,0 +1,97 @@
++firewall-settings(8)
++====================
++
++NAME
++----
++firewall-settings - Global firewall settings
++
++SYNOPSIS
++--------
++[verse]
++'firewall settings'
++'firewall settings' KEY=VALUE ...
++
++DESCRIPTION
++-----------
++This command is used to set global firewall settings.
++Please have a look at the individual man pages for more options.
++
++COMMANDS
++--------
++If no argument is given, the configuration will be dumped to the console.
++
++You may set a new value by adding the variable name and the new value to
++the command line.
++
++SETTINGS
++--------
++=== CONNTRACK_MAX_CONNECTIONS = 16384
++Limits the max. number of simultaneous connections.
++
++Modify this if you want to handle a larger number of concurrent
++connections. Every connection will use approx. 16 kBytes of memory.
++
++=== CONNTRACK_UDP_TIMEOUT = 60
++Defines the timeout (in seconds) the kernel will wait until
++a half-assured UDP connection is fully established.
++
++=== FIREWALL_ACCEPT_ICMP_REDIRECTS = [true|false]
++Enable if you want to accept ICMP redirect messages.
++
++=== FIREWALL_CLAMP_PATH_MTU = [true|false]
++If Path MTU Discovery does not work well, enable this option.
++
++It sets the MSS value of a packet so that the remote site would
++never send a packet bigger than the MSS value.
++
++No ICMP packets are needed to make this work, so use this on
++networks with broken ICMP filtering.
++
++=== FIREWALL_DEFAULT_TTL = 64
++Here you can change the default TTL used for sending packets.
++
++The given value must be between 10 and 255.
++Don't mess with this unless you know what you are doing.
++
++=== FIREWALL_LOG_BAD_TCP_FLAGS = [true|false]
++Enable this to log TCP packets with bad flags or options.
++
++=== FIREWALL_LOG_INVALID_ICMP = [true|false]
++Enable this to log INVALID ICMP packets.
++
++=== FIREWALL_LOG_INVALID_TCP = [true|false]
++Enable this to log INVALID TCP packets.
++
++=== FIREWALL_LOG_INVALID_UDP = [true|false]
++Enable this to log INVALID UDP packets.
++
++=== FIREWALL_LOG_MARTIANS = [true|false]
++Enable this to log packets with impossible addresses.
++
++=== FIREWALL_LOG_STEALTH_SCANS = [true|false]
++Enable this to log all stealth scans.
++
++=== FIREWALL_PMTU_DISCOVERY = [true|false]
++Enables Path MTU Discovery.
++
++=== FIREWALL_RP_FILTER = [true|false]
++Enable to drop connection from non-routable IPs,
++e.g. prevent source routing.
++
++=== FIREWALL_SYN_COOKIES = [true|false]
++Enable for SYN-flood protection.
++
++=== FIREWALL_USE_ECN = [true|false]
++Enables the ECN (Explicit Congestion Notification) TCP flag.
++
++Some routers on the Internet still do not support ECN properly.
++When this setting is disabled, ECN is only advertised
++when asked for.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:firewall[8]
+diff --git a/man/firewall-settings.xml b/man/firewall-settings.xml
+deleted file mode 100644
+index 7357f4c..0000000
+--- a/man/firewall-settings.xml
++++ /dev/null
+@@ -1,284 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="firewall-settings">
+- <refentryinfo>
+- <title>firewall-settings</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>firewall-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>firewall-settings</refname>
+- <refpurpose>Firewall Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>firewall-settings</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>firewall-settings <replaceable>KEY=VALUE</replaceable></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>firewall-settings</command> command may be used to set
+- global firewall settingsuration options.
+- </para>
+- <para>
+- Please have a look at the individual man pages for more options.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- If no additional argument is given, running the command will
+- dump a list of all settingsuration variables and their current values.
+- </para>
+-
+- <para>
+- You may set a new value by adding the variable name and the new
+- value to the command line.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Variables</title>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <varname>CONNTRACK_MAX_CONNECTIONS</varname> = <replaceable>16384</replaceable>
+- </term>
+-
+- <listitem>
+- <para>
+- Limits the max. number of simultaneous connections.
+- </para>
+- <para>
+- Modify this if you want to handle a larger number of concurrent
+- connections. Every connection will use approx. 16 kBytes of memory.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>CONNTRACK_UDP_TIMEOUT</varname> = <replaceable>60</replaceable>
+- </term>
+-
+- <listitem>
+- <para>
+- Defines the timeout (in seconds) the kernel will wait until
+- a half-assured UDP connection is fully established.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_ACCEPT_ICMP_REDIRECTS</varname> = [true|<emphasis>false</emphasis>]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable if you want to accept ICMP redirect messages.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_CLAMP_PATH_MTU</varname> = [true|<emphasis>false</emphasis>]
+- </term>
+-
+- <listitem>
+- <para>
+- If Path MTU Discovery does not work well, enable this option.
+- It sets the MSS value of a packet so that the remote site would
+- never send a packet bigger than the MSS value.
+- </para>
+- <para>
+- No ICMP packets are needed to make this work, so use this on
+- networks with broken ICMP filtering.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_DEFAULT_TTL</varname> = <replaceable>64</replaceable>
+- </term>
+-
+- <listitem>
+- <para>
+- Here you can change the default TTL used for sending packets.
+- </para>
+- <para>
+- The given value must be between 10 and 255.
+- Don't mess with this unless you know what you are doing.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_BAD_TCP_FLAGS</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log TCP packets with bad flags or options.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_INVALID_ICMP</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log INVALID ICMP packets.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_INVALID_TCP</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log INVALID TCP packets.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_INVALID_UDP</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log INVALID UDP packets.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_MARTIANS</varname> = [true|<emphasis>false</emphasis>]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log packets with impossible addresses.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_LOG_STEALTH_SCANS</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable this to log all stealth scans.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_PMTU_DISCOVERY</varname> = [true|<emphasis>false</emphasis>]
+- </term>
+-
+- <listitem>
+- <para>
+- Enables Path MTU Discovery.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_RP_FILTER</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable to drop connection from non-routable IPs,
+- e.g. prevent source routing.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_SYN_COOKIES</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enable for SYN-flood protection.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>FIREWALL_USE_ECN</varname> = [<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- Enables the ECN (Explicit Congestion Notification) TCP flag.
+- </para>
+- <para>
+- Some routers on the Internet still do not support ECN properly,
+- so this is not enabled by default.
+- When this setting is disabled, ECN is only advertised
+- when asked for.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>firewall</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,144 @@
+From c601b69e5d8db595fee00241702ee8bd2689c49e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 21:24:48 +0200
+Subject: [PATCH 068/304] man: Convert network-description(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 -
+ man/include-description.txt | 11 +++++
+ man/network-description.xml | 92 -------------------------------------
+ 3 files changed, 11 insertions(+), 93 deletions(-)
+ create mode 100644 man/include-description.txt
+ delete mode 100644 man/network-description.xml
+
+diff --git a/Makefile.am b/Makefile.am
+index 55d5d18..287a111 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -456,7 +456,6 @@ MANPAGES = \
+ man/firewall-settings.8 \
+ man/network.8 \
+ man/network-color.8 \
+- man/network-description.8 \
+ man/network-device.8 \
+ man/network-dhcp.8 \
+ man/network-dns-server.8 \
+diff --git a/man/include-description.txt b/man/include-description.txt
+new file mode 100644
+index 0000000..a39ba55
+--- /dev/null
++++ b/man/include-description.txt
+@@ -0,0 +1,11 @@
++'description edit'::
++ This command opens an editor and allows you to edit title and description.
++
++ NOTE: The formation of the description is similar to a git commit.
++ Every description has a title, the first line of the description.
++ The title is shown on the status page and in the web user interface.
++ It should be something short like "Office Lan" or "DMZ".
++ After the title can follow a longer description.
++
++'description show'::
++ Prints the description.
+diff --git a/man/network-description.xml b/man/network-description.xml
+deleted file mode 100644
+index f1722b4..0000000
+--- a/man/network-description.xml
++++ /dev/null
+@@ -1,92 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-color">
+- <refentryinfo>
+- <title>networ-color</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Jonatan</firstname>
+- <surname>Schlag</surname>
+- <email>jonatan.schlag@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-description</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-description</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>description</command> command make it possible to add descriptions to zone and ports.
+- A description is similar to a git commit. Every description has a title, the first line of the describtion file.
+- The title is shown on the status page and in the webinterface. It should be something short like Office Lan or DMZ.
+- After the title can follow a longer description. You can write whatever you want.
+- This longer description is shown via the <command>show</command> command
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>edit</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command opens an editor and allows you to edit title and description.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command>show</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command prints title and the longer description in a nice way.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,254 @@
+From 9d2265232d8a1c399617e347bda66a8019d8b36d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 21:40:53 +0200
+Subject: [PATCH 069/304] man: Convert network-device(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-device.txt | 61 +++++++++++++++
+ man/network-device.xml | 165 -----------------------------------------
+ 2 files changed, 61 insertions(+), 165 deletions(-)
+ create mode 100644 man/network-device.txt
+ delete mode 100644 man/network-device.xml
+
+diff --git a/man/network-device.txt b/man/network-device.txt
+new file mode 100644
+index 0000000..33fcefa
+--- /dev/null
++++ b/man/network-device.txt
+@@ -0,0 +1,61 @@
++network(8)
++==========
++
++NAME
++----
++network-device - Controls network devices
++
++SYNOPSIS
++--------
++[verse]
++'network device' [<options>] <command> ...
++
++DESCRIPTION
++-----------
++The 'network device' command shows low-level status information
++of network devices and other things.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'list'::
++ This command shows a list of all device that are currently present
++ on this system. This includes PHYs and serial devices as well.
++
++'DEVICE discover'::
++ Runs a discovery for many hooks on the given device.
++
++ This will check if the hook can find for example a DHCP server or
++ DSLAM and thus predict for what the device should be used.
++
++'DEVICE identify'::
++ This command only works for Ethernet adapters and will make those
++ that support this feature flash for a few seconds.
++
++ It is handy to find the right device to put the cable in.
++
++'DEVICE monitor'::
++ This command creates a monitor interface for wireless modules.
++
++ An instance of link:tcpdump[8] will be started and show all
++ frames that are sent or received on the 802.11 layer (layer 2).
++
++'DEVICE status'::
++ This will show you very detailed information about the given device.
++
++'DEVICE unlock'::
++ This command will unlock the SIM card in a modem.
++ Only serial devices are supported which are the most 4G or 3G modems.
++
++ For the PIN or PUK code, the user will be prompted.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8]
++link:network-port[8]
++link:network-zone[8]
+diff --git a/man/network-device.xml b/man/network-device.xml
+deleted file mode 100644
+index 11dc04e..0000000
+--- a/man/network-device.xml
++++ /dev/null
+@@ -1,165 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-device">
+- <refentryinfo>
+- <title>network-device</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-device</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-device</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network device <arg choice="plain">COMMAND</arg></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>device</command> subcommands, it is very easy
+- to get status information about network devices and to do some more
+- things.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>list</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>list</command> command will show a list
+- of all devices that are currently plugged in or active
+- on the system.
+- This includes PHYs and serial devices as well.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>DEVICE</replaceable> status</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This will show you very detailed information about the given
+- device.
+- </para>
+- <para>
+- This is all about the ethernet parts of the device and
+- does not contain any IP information as this is defined
+- as a zone (<citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>).
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>DEVICE</replaceable> identify</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command only works for Ethernet adapters and will
+- make those that support this feature flash for a few
+- seconds.
+- It is handy to find the right device to put the cable in.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>DEVICE</replaceable> discover</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Runs a discovery for many hooks on the given device.
+- This will check if the hook can find for example a DHCP
+- server or DSLAM and thus predict for what the device
+- should be used.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>DEVICE</replaceable> unlock</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command will unlock the SIM card in a modem.
+- Only serial devices are supported which are the most
+- UMTS or 3G modems.
+- </para>
+- <para>
+- For the PIN or PUK code, the user will be prompted.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>DEVICE</replaceable> monitor</command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>monitor</command> command is used to
+- create a monitor interface for wireless modules.
+- An instance of tcpdump will be started and show
+- all frames that are sent or received on the 802.11
+- layer (layer 2) of the wireless network.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,217 @@
+From c6e12dc53a1e65a0089ee0ddb0573a29bc2acd8a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 21:46:49 +0200
+Subject: [PATCH 070/304] man: Convert network-dhcp(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-dhcp.txt | 44 +++++++++++++
+ man/network-dhcp.xml | 145 -------------------------------------------
+ 2 files changed, 44 insertions(+), 145 deletions(-)
+ create mode 100644 man/network-dhcp.txt
+ delete mode 100644 man/network-dhcp.xml
+
+diff --git a/man/network-dhcp.txt b/man/network-dhcp.txt
+new file mode 100644
+index 0000000..a448619
+--- /dev/null
++++ b/man/network-dhcp.txt
+@@ -0,0 +1,44 @@
++network(8)
++==========
++
++NAME
++----
++network-dhcp - Controls the DHCP Server
++
++SYNOPSIS
++--------
++[verse]
++'network dhcpv6' <command> ...
++'network dhcpv4' <command> ...
++
++DESCRIPTION
++-----------
++With help of the DHCP commands it is possible to configure DHCP
++servers for IPv6 and IPv4.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'start'::
++ Starts the DHCP server.
++
++'stop'::
++ Stops the DHCP server.
++
++'restart'::
++ Restarts the DHCP server.
++
++'reload'::
++ Reload the DHCP server configuration.
++
++'subnet ...'::
++ TODO
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8]
+diff --git a/man/network-dhcp.xml b/man/network-dhcp.xml
+deleted file mode 100644
+index cc081bb..0000000
+--- a/man/network-dhcp.xml
++++ /dev/null
+@@ -1,145 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-dhcp">
+- <refentryinfo>
+- <title>network-dhcp</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-dhcp</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-dhcp</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network <arg choice="plain">[dhcpv6|dhcpv4]</arg> <arg choice="plain">command</arg> ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>dhcp</command> commands it is possible to
+- configure DHCP servers for IPv6 and IPv4.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>start</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Starts the DHCP service.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>stop</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Stops the DHCP service.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>restart</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Restarts the DHCP service immediately.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>reload</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Reload the DHCP service configuration.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>show</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows the DHCP configuration.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>subnet ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Use this command to manage subnets.
+- See <citerefentry>
+- <refentrytitle>network-dhcp-subnet</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-dhcp-subnet</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,306 @@
+From 063089cbdb2745248bd8556e87de4a0d2bc8091d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 21:59:01 +0200
+Subject: [PATCH 071/304] man: Convert network-dns-server(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-dns-server.txt | 75 ++++++++++++++
+ man/network-dns-server.xml | 203 -------------------------------------
+ 2 files changed, 75 insertions(+), 203 deletions(-)
+ create mode 100644 man/network-dns-server.txt
+ delete mode 100644 man/network-dns-server.xml
+
+diff --git a/man/network-dns-server.txt b/man/network-dns-server.txt
+new file mode 100644
+index 0000000..bd01ca7
+--- /dev/null
++++ b/man/network-dns-server.txt
+@@ -0,0 +1,75 @@
++network-dns-server(8)
++=====================
++
++NAME
++----
++network-dns-server - Controls the DNS settings
++
++SYNOPSIS
++--------
++[verse]
++'network dns-server' add SERVER [PRIORITY]
++'network dns-server' remove SERVER
++'network dns-server' list
++'network dns-server' update
++
++DESCRIPTION
++-----------
++With this command, you will be able to configure the local DNS
++configuration.
++
++You may add and remove DNS servers as well as view the settings.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'add' SERVER [PRIORITY]::
++ A new DNS server may be added to the list by the
++ 'add' command.
++ A priority that will rank the server my optionally be given.
++
++ NOTE: SERVER must be a valid IP address and PRIORITY
++ must be a positive number.
++ The smaller this number, the higher is is the rank of
++ the server.
++
++'remove' SERVER::
++ The given server will be removed from the list of DNS servers.
++
++'list'::
++ Shows a list of all servers that are currently in use.
++
++'update'::
++ This command will re-create the system's configuration
++ files. It should not be required to use this command
++ very often.
++
++SETTINGS
++--------
++The following settings may be set using link:network-settings[8]:
++
++'DNS_USE_LOCAL_RESOLVER = [true|false]'::
++ This option defines whether the local DNS resolver should
++ be used or not.
++
++ Basically, the option adds localhost to the list of nameservers
++ in link:resolv.conf[5].
++
++'DNS_SEARCH_DOMAINS ='::
++ This setting configures the search domains for DNS queries
++ made by the local system.
++
++'DNS_RANDOMIZE = [true|false]'::
++ This option will break the DNS server ranks and will query
++ them in a random order which is useful to load-balance
++ multiple DNS servers.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8],
++link:network-settings[8]
+diff --git a/man/network-dns-server.xml b/man/network-dns-server.xml
+deleted file mode 100644
+index aec52d4..0000000
+--- a/man/network-dns-server.xml
++++ /dev/null
+@@ -1,203 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-dns-server">
+- <refentryinfo>
+- <title>network-dns-server</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-dns-server</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-dns-server</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network dns-server <arg choice="plain">[add|remove]</arg> <arg choice="plain">SERVER</arg> [<arg choice="plain">PRIORITY</arg>]</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network dns-server <arg choice="plain">[list|update]</arg></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>dns-server</command> subcommand, you will
+- be able to configure the local DNS configuration.
+- DNS is short for Domain Name System.
+- </para>
+- <para>
+- You may add and remove DNS servers as well as view the settings.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>
+- add
+- <replaceable>SERVER</replaceable>
+- [<replaceable>PRIORITY</replaceable>]
+- </command>
+- </term>
+-
+- <listitem>
+- <para>
+- A new DNS server may be added to the list by the
+- <command>add</command> command. A priority that will
+- rank the server my optionally be given.
+- </para>
+- <para>
+- <replaceable>SERVER</replaceable> must be a valid IP address
+- and <replaceable>PRIORITY</replaceable> must be a positive
+- integer number. The smaller this number, the higher is
+- is the rank of the server.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>
+- remove
+- <replaceable>SERVER</replaceable>
+- </command>
+- </term>
+-
+- <listitem>
+- <para>
+- The given server will be removed from the list of
+- DNS servers.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>list</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows a list of all servers that are currently in use.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>update</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command will re-create the system's configuration
+- files. It should not be required to use this command
+- very often.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>Variables</title>
+-
+- <para>
+- These variables may be set by using the <citerefentry>
+- <refentrytitle>network-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> command.
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <varname>DNS_USE_LOCAL_RESOLVER</varname>=[<emphasis>true</emphasis>|false]
+- </term>
+-
+- <listitem>
+- <para>
+- This option defines whether the local DNS resolver should
+- be used or not.
+- </para>
+- <para>
+- Basically, the option adds localhost to the list of
+- nameservers in <citerefentry>
+- <refentrytitle>resolv.conf</refentrytitle>
+- <manvolnum>5</manvolnum>
+- </citerefentry>.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>DNS_SEARCH_DOMAINS</varname>=
+- </term>
+-
+- <listitem>
+- <para>
+- This setting configures the search domains for DNS queries
+- made by the local system.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>DNS_RANDOMIZE</varname>=[true|<emphasis>false</emphasis>]
+- </term>
+-
+- <listitem>
+- <para>
+- This option will break the DNS server ranks and will query
+- them in a random order which is useful to load-balance
+- multiple DNS servers.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,135 @@
+From c20f292770a6423c112b7f96d724bb13c4019d2a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:04:08 +0200
+Subject: [PATCH 072/304] man: Convert network-performance-tuning(8) to
+ asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-performance-tuning.txt | 33 ++++++++++++++
+ man/network-performance-tuning.xml | 73 ------------------------------
+ 2 files changed, 33 insertions(+), 73 deletions(-)
+ create mode 100644 man/network-performance-tuning.txt
+ delete mode 100644 man/network-performance-tuning.xml
+
+diff --git a/man/network-performance-tuning.txt b/man/network-performance-tuning.txt
+new file mode 100644
+index 0000000..763ee21
+--- /dev/null
++++ b/man/network-performance-tuning.txt
+@@ -0,0 +1,33 @@
++network-performance-tuning(8)
++=============================
++
++NAME
++----
++network-performance-tuning - Performance Tuning for Networking
++
++DESCRIPTION
++-----------
++This page contains a summary of some performance tuning techniques
++that this system is using.
++
++=== SMP Affinity
++
++This system is automatically using SMP affinity for every physical
++network controller, if supported.
++
++A processor core is assigned to handle all interrupts of a certain
++network controller which will result in minimising cache misses,
++reducing network latency and quite possibly increasing throughput.
++
++The algorithm is trying to balance all network controllers across
++all processors.
++
++See /proc/interrups for the distribution of interrupts.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8]
+diff --git a/man/network-performance-tuning.xml b/man/network-performance-tuning.xml
+deleted file mode 100644
+index 898f142..0000000
+--- a/man/network-performance-tuning.xml
++++ /dev/null
+@@ -1,73 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-performance-tuning">
+- <refentryinfo>
+- <title>network-performance-tuning</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-performance-tuning</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-performance-tuning</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- This page contains a summary of some performance tuning techniques
+- that this system is using.
+- </para>
+- </refsect1>
+-
+- <refsect2>
+- <title>SMP Affinity</title>
+-
+- <para>
+- This system is automatically using SMP affinity for every physical
+- network controller, if supported.
+- </para>
+-
+- <para>
+- A processor core is assigned to handle all interrupts of a certain
+- network controller which will result in minimising cache misses,
+- reducing network latency and quite possibly increasing throughput.
+- </para>
+-
+- <para>
+- The algorithm is trying to balance all network controllers across
+- all processors.
+- </para>
+-
+- <para>
+- See /proc/interrups for the distribution of interrupts.
+- </para>
+- </refsect2>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,370 @@
+From 065346332054e3b2be85bee3f6d71a3dc34d6275 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:27:40 +0200
+Subject: [PATCH 073/304] man: Convert network-port(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-port.txt | 97 +++++++++++++++++
+ man/network-port.xml | 245 -------------------------------------------
+ 2 files changed, 97 insertions(+), 245 deletions(-)
+ create mode 100644 man/network-port.txt
+ delete mode 100644 man/network-port.xml
+
+diff --git a/man/network-port.txt b/man/network-port.txt
+new file mode 100644
+index 0000000..08b9e90
+--- /dev/null
++++ b/man/network-port.txt
+@@ -0,0 +1,97 @@
++network-port(8)
++===============
++
++NAME
++----
++network-port - Controls Network Ports
++
++SYNOPSIS
++--------
++[verse]
++'network port' new HOOK ...
++'network port' destroy PORT
++'network port' PORT color
++'network port' PORT create
++'network port' PORT description edit
++'network port' PORT description show
++'network port' PORT down
++'network port' PORT edit ...
++'network port' PORT identify
++'network port' PORT remove
++'network port' PORT status
++'network port' PORT up
++
++DESCRIPTION
++-----------
++This command creates, deletes, changes and views the configuration
++and status of ports.
++
++NOTE: A port is a physical or virtual device that is directly connected
++to an other network. It connects those and zones together.
++The 'network device' command shows status information of network devices
++and other things.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'new' HOOK ...::
++ A new port may be created with this command.
++ HOOK must be a valid hook which may require more options.
++
++'destroy' PORT::
++ Destroys the port PORT.
++ The port is removed from any zones it is attached to and shut down.
++
++For all other commands, the name of the port needs to be passed first:
++
++'color'::
++ This command allows settings a color for a port.
++ See link:network-color[8] for more information.
++
++'create'::
++ This will create devices for the existing port PORT.
++
++ This does not create a new port. It will just create the (possibly
++ virtual) interface this port (i.e. create an interface for a WiFi
++ module or a VLAN device).
++
++ The interface is not brought up. Use the 'up' command to do that.
++
++include::include-description.txt[]
++
++'down'::
++ Shuts down the port.
++
++'edit'::
++ This command can be used to alter the configuration of a port.
++ Consult the documentation of the port hook to find out what is supported.
++
++'identify'::
++ This command will make the port flash for a few seconds
++ so that you can identify the correct network adapters
++ in the system.
++
++ This is not supported by all network adapters.
++
++'remove'::
++ This will remove an existing PORT.
++
++ This does not destroy the port. It inverses the operation performed
++ by the 'create' command.
++
++'status'::
++ This will show some detailed information about the status
++ of the specified port.
++
++'up'::
++ Brings up the port. It has to be created first.
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-port.xml b/man/network-port.xml
+deleted file mode 100644
+index 5c0a8ae..0000000
+--- a/man/network-port.xml
++++ /dev/null
+@@ -1,245 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-port">
+- <refentryinfo>
+- <title>network-port</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-port</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network port <arg choice="plain">[new|destroy]</arg> <replaceable>PORT</replaceable> ...</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network port <replaceable>PORT</replaceable> <arg choice="plain">command</arg> ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>port</command> command, you can create, delete,
+- change and view the configuration and status of ports.
+- </para>
+-
+- <para>
+- A port is a physical or virtual device that is directly connected
+- to an other network. If connects those and zones together.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>new <replaceable>HOOK</replaceable> <arg choice="opt" rep="repeat">ARGUMENTS</arg></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A new port may be created by the <command>new</command>
+- command.
+- </para>
+- <para>
+- <replaceable>HOOK</replaceable> must be a valid
+- hook which may require more <replaceable>ARGUMENTS</replaceable>.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>destroy <replaceable>PORT</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A port can be destroyed with this command.
+- </para>
+- <para>
+- The port is removed from any zones it is attached
+- to and shut down.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+-
+- <para>
+- For all other commands, the name of the port needs to be passed first:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> create</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This will create an existing <replaceable>PORT</replaceable>.
+- </para>
+- <para>
+- This does not create a new port. It will just create the (possibly
+- virtual) interface this port (i.e. create an interface for a WiFi
+- module or a VLAN device).
+- </para>
+- <para>
+- The interface is not brought up. Use the <command>up</command> command
+- to do that.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> remove</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This will remove an existing <replaceable>PORT</replaceable>.
+- </para>
+- <para>
+- This does not destroy the port. It inverses the operation performed
+- by the <command>create</command> command.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> [up|down]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- These commands will bring the port up or down. It has to be
+- created first.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> color</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows settings a color for a port.
+- See
+- <citerefentry>
+- <refentrytitle>network-color</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- for more information.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable>description</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows you to add a description to a port.
+- See
+- <citerefentry>
+- <refentrytitle>network-description</refentrytitle>
+- <manvolnum>8</manvolnum>,
+- </citerefentry>
+- for more information.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> edit <arg choice="opt" rep="repeat">ARGUMENTS</arg></command>
+- </term>
+-
+- <listitem>
+- <para>
+- The <command>edit</command> command can be used to alter
+- the configuration of a port. Consult the documentation of the
+- port hook to find out which <replaceable>ARGUMENTS</replaceable>
+- are supported.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> status</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This will show some detailed information about the state
+- if the specified port.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>PORT</replaceable> identify</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command will make the port flash for a few seconds
+- so that you can identify the correct network adapters
+- in the system.
+- </para>
+- <para>
+- This is not supported by all network adapters.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>,
+- </citerefentry>
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,275 @@
+From 27b9807e568edee69afa758481be164662770901 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:40:35 +0200
+Subject: [PATCH 074/304] man: Converting network-quick-start(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-quick-start.txt | 92 +++++++++++++++++++++
+ man/network-quick-start.xml | 155 ------------------------------------
+ 2 files changed, 92 insertions(+), 155 deletions(-)
+ create mode 100644 man/network-quick-start.txt
+ delete mode 100644 man/network-quick-start.xml
+
+diff --git a/man/network-quick-start.txt b/man/network-quick-start.txt
+new file mode 100644
+index 0000000..02ebfe0
+--- /dev/null
++++ b/man/network-quick-start.txt
+@@ -0,0 +1,92 @@
++network-quick-start(8)
++======================
++
++NAME
++----
++network-quick-start - Quick Start Guide for Networking
++
++DESCRIPTION
++-----------
++The link:network[8] is a very powerful command that allows you to configure
++the entire networking stack.
++Unfortunately that makes it quite complicated to use as well.
++
++This guide tries to be a good starting point to set up basic networking with
++the 'network' command.
++
++=== Adding an Uplink Zone
++
++The first step is to create a new uplink zone with name 'upl0'.
++
++This zone will be of the link:network-zone-bridge[8] type which is the default
++for all local networks.
++
++------------
++# network zone new upl0 bridge
++------------
++
++The zone will be created and brought up immediately.
++
++=== Attaching Ports
++
++To connect the zone to the physical world outside of our box we will need
++to attach ports to the zone. That is done with a single command.
++To execute this command, we will need to know which ports are available.
++One of the easiest way to find out about that is to use the auto-completion
++feature of the shell like this:
++
++------------
++# network zone upl0 port attach [TAB] [TAB]
++------------
++
++That will list all not yet attached ports. The following command will actually
++attach the port (which is 'p0' in this example).
++
++-----------
++# network zone upl0 port attach p0
++-----------
++
++You can as well get a list of all detected devices, zones and ports by running:
++
++-----------
++# network device list
++-----------
++
++To a zone of the 'bridge' type you may attach more than just one port if you
++wish to.
++
++=== IP Connectivity
++
++After a zone has been created and ports have been attached, you are now
++able to add IP connectivity.
++
++The easiest way to do that is using DHCP which can be enabled by this simple command:
++
++------------
++# network zone upl0 config new dhcp
++------------
++
++=== Debugging
++
++You may see the current status of the network by running this command:
++
++------------
++# network status
++------------
++
++The entire network can be restarted by running:
++
++------------
++# network restart
++------------
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8],
++link:network-device[8],
++link:network-port[8],
++link:network-zone[8]
+diff --git a/man/network-quick-start.xml b/man/network-quick-start.xml
+deleted file mode 100644
+index ea79700..0000000
+--- a/man/network-quick-start.xml
++++ /dev/null
+@@ -1,155 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network">
+- <refentryinfo>
+- <title>network-quick-start</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-quick-start</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-quick-start</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsect1>
+- <title>Quick Start Guide</title>
+-
+- <para>
+- The <command>network</command> is a very powerful command that allows
+- you to configure the entire networking stack. Unfortunately that makes
+- it quite complicated to use as well.
+- This guide tries to be a good starting point to set up basic networking
+- with the <command>network</command> command.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Add an uplink zone</title>
+-
+- <para>
+- The first step is to create a new uplink zone with name
+- <replaceable>upl0</replaceable>.
+- This zone will be of the <replaceable>bridge</replaceable> type which is
+- the default for all local networks.
+- </para>
+-
+- <programlisting># network zone new <replaceable>upl0</replaceable> <replaceable>bridge</replaceable></programlisting>
+-
+- <para>
+- The zone will be created and brought up immediately.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Attaching ports</title>
+-
+- <para>
+- To connect the zone to the physical world outside of our box we will need
+- to attach ports to the zone.
+- That is done with a single command.
+- To execute this command, we will need to know which ports are available.
+- One of the easiest way to find out about that is to use the auto-completion
+- feature of the shell like this:
+- </para>
+-
+- <programlisting># network zone <replaceable>upl0</replaceable> port attach [TAB] [TAB]</programlisting>
+-
+- <para>
+- That will list all not yet attached ports.
+- The following command will actually attach the port
+- (which is <replaceable>p0</replaceable> in this example).
+- </para>
+-
+- <programlisting># network zone <replaceable>upl0</replaceable> port attach <replaceable>p0</replaceable></programlisting>
+-
+- <para>
+- You can as well get a list of all detected devices,
+- zones and ports by running:
+- </para>
+-
+- <programlisting># network device list</programlisting>
+-
+- <para>
+- To a zone of the <replaceable>bridge</replaceable> type you may attach more
+- than just one port if you wish so.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>IP connectivity</title>
+-
+- <para>
+- After a zone has been created and ports have been attached, you are now
+- able to add IP connectivity.
+- The easiest way to do that is using DHCP which can be enabled by this
+- simple command:
+- </para>
+-
+- <programlisting># network zone <replaceable>upl0</replaceable> config new <replaceable>ipv6-dhcp</replaceable></programlisting>
+-
+- <para>
+- And for IPv4:
+- </para>
+-
+- <programlisting># network zone <replaceable>upl0</replaceable> config new <replaceable>ipv4-dhcp</replaceable></programlisting>
+- </refsect1>
+-
+- <refsect1>
+- <title>Debugging</title>
+-
+- <para>
+- You may see the current status of the network by running this command:
+- </para>
+-
+- <programlisting># network status</programlisting>
+-
+- <para>
+- The entire network can be restarted by running:
+- </para>
+-
+- <programlisting># network restart</programlisting>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-config</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-device</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-port</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,55 @@
+From d28ccf91678256bc299fed2c10b066682487b1e9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:53:20 +0200
+Subject: [PATCH 075/304] man: Use include for color commands
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/include-color.txt | 9 +++++++++
+ man/network-port.txt | 7 +++----
+ 2 files changed, 12 insertions(+), 4 deletions(-)
+ create mode 100644 man/include-color.txt
+
+diff --git a/man/include-color.txt b/man/include-color.txt
+new file mode 100644
+index 0000000..073c01b
+--- /dev/null
++++ b/man/include-color.txt
+@@ -0,0 +1,9 @@
++'color set <color>'::
++ The color is set with this command and required to be passed in
++ RGB hex formatting
++
++ NOTE: The color is being used to make identification of network devices
++ easier on the command line and web user interface.
++
++'color reset'::
++ Resets the color to blank.
+diff --git a/man/network-port.txt b/man/network-port.txt
+index 08b9e90..0c26f33 100644
+--- a/man/network-port.txt
++++ b/man/network-port.txt
+@@ -10,7 +10,8 @@ SYNOPSIS
+ [verse]
+ 'network port' new HOOK ...
+ 'network port' destroy PORT
+-'network port' PORT color
++'network port' PORT color set <color>
++'network port' PORT color reset
+ 'network port' PORT create
+ 'network port' PORT description edit
+ 'network port' PORT description show
+@@ -45,9 +46,7 @@ The following commands are understood:
+
+ For all other commands, the name of the port needs to be passed first:
+
+-'color'::
+- This command allows settings a color for a port.
+- See link:network-color[8] for more information.
++include::include-color.txt[]
+
+ 'create'::
+ This will create devices for the existing port PORT.
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,72 @@
+From 7c329515f1e23231c315d41b55c4d9bea58c7d1c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:54:02 +0200
+Subject: [PATCH 076/304] man: Drop old network-color(8) man page
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 -
+ man/network-color.txt | 39 ---------------------------------------
+ 2 files changed, 40 deletions(-)
+ delete mode 100644 man/network-color.txt
+
+diff --git a/Makefile.am b/Makefile.am
+index 287a111..26f2e9c 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -455,7 +455,6 @@ INSTALL_DIRS += \
+ MANPAGES = \
+ man/firewall-settings.8 \
+ man/network.8 \
+- man/network-color.8 \
+ man/network-device.8 \
+ man/network-dhcp.8 \
+ man/network-dns-server.8 \
+diff --git a/man/network-color.txt b/man/network-color.txt
+deleted file mode 100644
+index f3be474..0000000
+--- a/man/network-color.txt
++++ /dev/null
+@@ -1,39 +0,0 @@
+-network-color(8)
+-================
+-
+-NAME
+-----
+-network-color - Allows assigning a color to a zone or port
+-
+-SYNOPSIS
+---------
+-[verse]
+-'network' [zone ZONE|port PORT] color set AABBCC
+-'network' [zone ZONE|port PORT] reset
+-
+-DESCRIPTION
+------------
+-The 'color' command helps to manage colors for zones and ports.
+-The color is being used to make identification of a zone or port easier on the
+-command line and web user interface.
+-
+-COMMANDS
+---------
+-The following commands are understood:
+-
+-'set' [AABBCC]::
+- The color of a zone or port is set with the 'set' command.
+- It is required to pass a color in hex formatting.
+-
+-'reset'::
+- Resets the color of a zone or port to blank.
+-
+-AUTHOR
+-------
+-Jonatan Schlag
+-
+-SEE ALSO
+---------
+-link:network[8]
+-link:network-zone[8]
+-link:network-port[8]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,38 @@
+From ec3a18b8cf262977d6fd73cee231338ce1b96ffd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 30 Sep 2018 22:55:51 +0200
+Subject: [PATCH 077/304] man: Fix page headers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-device.txt | 4 ++--
+ man/network-dhcp.txt | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/man/network-device.txt b/man/network-device.txt
+index 33fcefa..4f1c1b0 100644
+--- a/man/network-device.txt
++++ b/man/network-device.txt
+@@ -1,5 +1,5 @@
+-network(8)
+-==========
++network-device(8)
++=================
+
+ NAME
+ ----
+diff --git a/man/network-dhcp.txt b/man/network-dhcp.txt
+index a448619..bcb768e 100644
+--- a/man/network-dhcp.txt
++++ b/man/network-dhcp.txt
+@@ -1,5 +1,5 @@
+-network(8)
+-==========
++network-dhcp(8)
++===============
+
+ NAME
+ ----
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,157 @@
+From 70172845e300fb2bf491d471224bd087b0c4e0f4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 15:08:46 +0200
+Subject: [PATCH 078/304] man: Convert network-route(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-route.txt | 32 ++++++++++++++
+ man/network-route.xml | 97 -------------------------------------------
+ 2 files changed, 32 insertions(+), 97 deletions(-)
+ create mode 100644 man/network-route.txt
+ delete mode 100644 man/network-route.xml
+
+diff --git a/man/network-route.txt b/man/network-route.txt
+new file mode 100644
+index 0000000..bf3ddb4
+--- /dev/null
++++ b/man/network-route.txt
+@@ -0,0 +1,32 @@
++network-route(8)
++================
++
++NAME
++----
++network-route - Manage Routing
++
++SYNOPSIS
++--------
++[verse]
++'network route' COMMAND ...
++
++DESCRIPTION
++-----------
++This command helps to manage routes.
++
++COMMANDS
++--------
++The following commands are understood:
++
++'static' ...::
++ Static routes are managed by the 'static' command followed by the options
++ for static routes which are described in link:network-route-static[8]
++
++AUTHORS
++-------
++Michael Tremer
++
++SEE ALSO
++--------
++link:network[8],
++link:network-route-static[8]
+diff --git a/man/network-route.xml b/man/network-route.xml
+deleted file mode 100644
+index 207a5ce..0000000
+--- a/man/network-route.xml
++++ /dev/null
+@@ -1,97 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-route">
+- <refentryinfo>
+- <title>network-route</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-route</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-route</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network route <arg choice="plain">COMMAND</arg></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>route</command> helps to manage routes.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>static</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Static routes are managed by the <command>static</command> command
+- followed by the options for static routes which are described in:
+- <citerefentry>
+- <refentrytitle>network-route-static</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>Route Types</title>
+-
+- <refsect2>
+- <title>static</title>
+-
+- <para>
+- A static route is a route which does not change when the network changes.
+- </para>
+- </refsect2>
+-</refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-route-static</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 82003431a4998e04e0e67f12ee6c3b6e5e802901 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 15:10:49 +0200
+Subject: [PATCH 079/304] .gitignore: Ignore DS_Store
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ .gitignore | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/.gitignore b/.gitignore
+index 36c85a1..bb093d3 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -14,6 +14,8 @@
+ /*.tar.bz2
+ /*.tar.gz
+ /*.tar.xz
++.DS_Store
++._.DS_Store
+ *.log
+ *.cache
+ *.la
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,280 @@
+From d715390e9b1c4cc72bd22b915a842acc96912108 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 15:34:19 +0200
+Subject: [PATCH 080/304] man: Convert network-route-static(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-route-static.txt | 69 +++++++++++++
+ man/network-route-static.xml | 183 -----------------------------------
+ 2 files changed, 69 insertions(+), 183 deletions(-)
+ create mode 100644 man/network-route-static.txt
+ delete mode 100644 man/network-route-static.xml
+
+diff --git a/man/network-route-static.txt b/man/network-route-static.txt
+new file mode 100644
+index 0000000..d4774b2
+--- /dev/null
++++ b/man/network-route-static.txt
+@@ -0,0 +1,69 @@
++= network-route-static(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-route - Manage Static Routing
++
++== SYNOPSIS
++[verse]
++'network route static' COMMAND ...
++'network route static add' NETWORK [--gateway=GATEWAY,--unreachable,--prohibit,--blackhole] [--mtu=MTU]
++'network route static remove' NETWORK
++'network route static list' [--protocol=ipv6|ipv4]
++
++== DESCRIPTION
++This command helps to manage routes.
++
++== COMMANDS
++The following commands are understood:
++
++'add' NETWORK ...::
++ A new route may be added by the 'add' command. It is required to pass a
++ valid network prefix NETWORK, which can be either IPv6 or IPv4.
++ +
++ For unicast routes, the '--gateway=GATEWAY' option must be passed, where
++ GATEWAY is a valid IP address of the same protocol type as the network
++ prefix is.
++ +
++ Use '--unreachable', '--prohibit', '--blackhole' can be used to create of
++ that type. See ROUTE TYPES below for more information about these options.
++ +
++ The optional '--mtu=MTU' parameter defines the MTU along the path to the
++ destination and must be an integer number. This will show you very
++ detailed information about the given device.
++
++'remove' NETWORK::
++ A route can be removed with this command.
++ +
++ NETWORK is the network prefix of an existing route.
++
++'list'::
++ Shows a list of all configured routes.
++ +
++ Output can be filtered by passing --protocol=[ipv6|ipv4].
++
++== ROUTE TYPES
++
++[horizontal]
++'unicast'::
++ A unicast route is the most common route in routing tables. It is a route to
++ a destination network address, which describes the path to the destination.
++ Use the '--gateway=GATEWAY' option to create such a route.
++
++'unreachable'::
++ When a route is determined and the routing decision process returns a
++ destination with an unreachable route type, an ICMP unreachable message is
++ generated and returned to the source address.
++
++'prohibit'::
++ This works like an _unreachable_ route, but the returned ICMP message is an
++ ICMP prohibited message.
++
++'blackhole'::
++ Packets matching this kind of route are silently discarded.
++ There will be no ICMP message sent to the source and no packet be forwarded.
++
++== SEE ALSO
++link:network[8],
++link:network-route[8],
++link:ip-route[8]
+diff --git a/man/network-route-static.xml b/man/network-route-static.xml
+deleted file mode 100644
+index d43eb62..0000000
+--- a/man/network-route-static.xml
++++ /dev/null
+@@ -1,183 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-route-static">
+- <refentryinfo>
+- <title>network-route-static</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-route-static</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-route-static</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network route static <arg choice="plain">COMMAND</arg></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>route static</command> helps to manage static routes.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>add <replaceable>NETWORK</replaceable> [<option>--gateway=GATEWAY</option>, <option>--unreachable</option>, <option>--prohibit</option>, <option>--blackhole</option>] [<option>--mtu=MTU</option>]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- A new route may be added by the <command>add</command> command.
+- It is always required to pass a valid network prefix
+- <replaceable>NETWORK</replaceable>, which can be either
+- IPv6 or IPv4.
+- </para>
+- <para>
+- For unicast routes, the <option>--gateway=GATEWAY</option>
+- option must be passed, where <varname>GATEWAY</varname>
+- is a valid IP address of the same protocol type as the
+- network prefix is.
+- </para>
+- <para>
+- Use <option>--unreachable</option>, <option>--prohibit</option>,
+- <option>--blackhole</option> can be used to create of that
+- type. See <emphasis>ROUTE TYPES</emphasis> below for more
+- information about these options.
+- </para>
+- <para>
+- The optional <option>--mtu=MTU</option> parameter defines the
+- MTU along the path to the destination and must be an integer
+- number. This will show you very detailed information about
+- the given device.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>remove <replaceable>NETWORK</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A route can be removed with the command.
+- </para>
+- <para>
+- <replaceable>NETWORK</replaceable> is the network prefix
+- of an existing route.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>list [<option>--protocol=ipv6|ipv4</option>]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows a list of all configured routes.
+- </para>
+- <para>
+- Pass the protocol option to filter the output only for the
+- given protocol.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>Route Types</title>
+-
+- <refsect2>
+- <title>unicast</title>
+-
+- <para>
+- A unicast route is the most common route in routing tables.
+- It is a route to a destination network address, which describes
+- the path to the destination.
+- Use the <option>--gateway=GATEWAY</option> option to create such
+- a route.
+- </para>
+- </refsect2>
+-
+- <refsect2>
+- <title>unreachable</title>
+-
+- <para>
+- When a route is determined and the routing decision process
+- returns a destination with an unreachable route type, an ICMP
+- unreachable message is generated and returned to the source
+- address.
+- </para>
+- </refsect2>
+-
+- <refsect2>
+- <title>prohibit</title>
+-
+- <para>
+- This works like an <emphasis>unreachable</emphasis> route, but
+- the returned ICMP message is an ICMP prohibited message.
+- </para>
+- </refsect2>
+-
+- <refsect2>
+- <title>blackhole</title>
+-
+- <para>
+- Packets matching this kind of route are silently discarded.
+- There will be no ICMP message sent to the source and no packet
+- be forwarded.
+- </para>
+- </refsect2>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-route</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>ip-route</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,190 @@
+From daebec37ca3cd19e000d1a9c1a77448d8c155fcd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 15:47:14 +0200
+Subject: [PATCH 081/304] man: Convert network-settings(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-settings.txt | 44 +++++++++++++++
+ man/network-settings.xml | 118 ---------------------------------------
+ 2 files changed, 44 insertions(+), 118 deletions(-)
+ create mode 100644 man/network-settings.txt
+ delete mode 100644 man/network-settings.xml
+
+diff --git a/man/network-settings.txt b/man/network-settings.txt
+new file mode 100644
+index 0000000..e77f038
+--- /dev/null
++++ b/man/network-settings.txt
+@@ -0,0 +1,44 @@
++= network-settings(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-settings - Change global network settings
++
++== SYNOPSIS
++'network settings'
++'network settings' KEY=VALUE
++
++== DESCRIPTION
++The 'network settings' command may be used to set global settings.
++
++Please have a look at the individual man pages for more options.
++
++== COMMANDS
++If no additional argument is given, running the command will dump a list of
++all settings variables and their current values.
++
++You may set a new value by adding the variable name and the new
++value to the command line.
++
++== VARIABLES
++
++'DEBUG=[true|_false_]'::
++ The DEBUG will control whether debug logging is enabled or not.
++ Additionally to writing debug log messages to the log files, the messages
++ will be displayed on the console as well.
++
++'WIRELESS_REGULATORY_DOMAIN=_00_'::
++ The wireless regulatory domain is set globally for the entire system with
++ the WIRELESS_REGULATORY_DOMAIN setting.
++ +
++ The default is '00' which is the _world_ setting.
++ +
++ Valid values are country codes for countries which have their own
++ regulatory domain.
++
++== AUTHORS
++Michael Tremer
++
++== SEE ALSO
++link:network[8],
++link:network-dns-server[8]
+diff --git a/man/network-settings.xml b/man/network-settings.xml
+deleted file mode 100644
+index 7d1c70d..0000000
+--- a/man/network-settings.xml
++++ /dev/null
+@@ -1,118 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-settings">
+- <refentryinfo>
+- <title>network-settings</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-settings</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-settings</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network settings</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network settings <replaceable>KEY=VALUE</replaceable></command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>network settings</command> command may be used to set
+- global settingsuration options.
+- </para>
+- <para>
+- Please have a look at the individual man pages for more options.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- If no additional argument is given, running the command will
+- dump a list of all settingsuration variables and their current values.
+- </para>
+-
+- <para>
+- You may set a new value by adding the variable name and the new
+- value to the command line.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Variables</title>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <varname>DEBUG</varname>=[<emphasis>0</emphasis>|1]
+- </term>
+-
+- <listitem>
+- <para>
+- The <varname>DEBUG</varname> will control whether debug
+- logging is enabled or not. Additionally to writing debug
+- log messages to the log files, the messages will be displayed
+- on the console as well.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <varname>WIRELESS_REGULATORY_DOMAIN</varname>=<emphasis>00</emphasis>
+- </term>
+-
+- <listitem>
+- <para>
+- The wireless regulatory domain is set globally for the
+- entire system with the <varname>WIRELESS_REGULATORY_DOMAIN</varname>
+- setting. The default is <emphasis>00</emphasis> which
+- is the <emphasis>world</emphasis> setting.
+- Valid values are country codes for countries which have their
+- own regulatory domain.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-dns-server</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,135 @@
+From 9848b81e6e8c2732920d9a7a115110723e2b07bb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 15:59:21 +0200
+Subject: [PATCH 082/304] man: Convert network-vpn(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-vpn.txt | 26 +++++++++++++++
+ man/network-vpn.xml | 81 ---------------------------------------------
+ 2 files changed, 26 insertions(+), 81 deletions(-)
+ create mode 100644 man/network-vpn.txt
+ delete mode 100644 man/network-vpn.xml
+
+diff --git a/man/network-vpn.txt b/man/network-vpn.txt
+new file mode 100644
+index 0000000..5a905db
+--- /dev/null
++++ b/man/network-vpn.txt
+@@ -0,0 +1,26 @@
++= network-vpn(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-vpn - Configure Virtual Private Networks
++
++== SYNOPSIS
++'network vpn' COMMAND ...
++
++== DESCRIPTION
++The 'vpn' command allows to create, delete, edit and show the status of VPN
++connections and the configuration around it.
++
++== COMMANDS
++The following commands are understood:
++
++'security-policies' ...::
++ Use this command to manage security policies.
++ +
++ See link:network-vpn-security-policies[8] for details.
++
++== AUTHORS
++Michael Tremer
++
++== SEE ALSO
++link:network[8]
+diff --git a/man/network-vpn.xml b/man/network-vpn.xml
+deleted file mode 100644
+index d71d14a..0000000
+--- a/man/network-vpn.xml
++++ /dev/null
+@@ -1,81 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-vpn">
+- <refentryinfo>
+- <title>network-vpn</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-vpn</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-vpn</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network <arg choice="plain">vpn</arg> <arg choice="plain">command</arg> ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The <command>vpn</command> command allows to create, delete, edit
+- and show the status of VPN connections and the configuration around it.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>security-policies ...</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Use this command to manage security policies.
+- See <citerefentry>
+- <refentrytitle>network-vpn-security-policies</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry> for details.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,453 @@
+From 0a31681e96ee9ed656bf5ce531d4057079a897be Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 16:30:26 +0200
+Subject: [PATCH 083/304] man: Convert network-vpn-security-policies(8) to
+ asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-vpn-security-policies.txt | 111 +++++++++
+ man/network-vpn-security-policies.xml | 313 --------------------------
+ 2 files changed, 111 insertions(+), 313 deletions(-)
+ create mode 100644 man/network-vpn-security-policies.txt
+ delete mode 100644 man/network-vpn-security-policies.xml
+
+diff --git a/man/network-vpn-security-policies.txt b/man/network-vpn-security-policies.txt
+new file mode 100644
+index 0000000..f9dc91a
+--- /dev/null
++++ b/man/network-vpn-security-policies.txt
+@@ -0,0 +1,111 @@
++= network-vpn-security-policies(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-vpn-security-policies - Configure VPN Security Policies
++
++== SYNOPSIS
++[verse]
++'network vpn security-policies [new|destroy]' NAME...
++'network vpn security-policies' NAME COMMAND ...
++
++== DESCRIPTION
++With help of the 'vpn security-policies', it is possible to create, destroy
++and edit VPN security policies.
++
++A security policy is a definition of ciphers and algorithms for integrity
++and key-exchanges for VPN connections.
++
++== COMMANDS
++The following commands are understood:
++
++'new NAME'::
++ A new security policy may be created with the 'new' command.
++ +
++ NAME does not allow any spaces.
++
++'destroy NAME'::
++ A security policy can be destroyed with this command.
++ +
++ If the policy is still in use, it cannot be deleted.
++
++For all other commands, the name of the security policy needs to be passed first:
++
++'NAME show'::
++ Shows the configuration of the security policy.
++
++'NAME key-exchange' [IKEv2|IKEv1]::
++ Defines the key exchange algorithm that should be used to initiate an
++ IPsec VPN connection.
++
++'NAME ciphers' [CIPHER-LIST|+CIPHER ...|-CIPHER ...]::
++ This command allows modifying the cipher list.
++ A new CIPHER-LIST can be passed which will replace the current configuration.
++ Alternatively, new ciphers can be added by prepending a + sign to the cipher
++ name and can removed likewise using -.
++ +
++ A cipher is an algorithm that encrypts and decrypts data to be able to
++ transmit it over an insecure channel.
++
++'NAME integrities' [INTEGRITY-LIST|+INTEGRITY ...|-INTEGRITY ...]::
++ This command allows modifying the integrity list similar to the
++ 'ciphers' command.
++ +
++ Integrity algorithms are used to be able to determine if data has been
++ altered when being transferred over an untrusted channel.
++
++'NAME pseudo-random-functions' [PSEUDO-RANDOM-FUNCTION-LIST|+PSEUDO-RANDOM-FUNCTION...|-PSEUDO-RANDOM-FUNCTION]::
++ This command allows modifying the list of pseudo random functions
++ similar to the 'ciphers' command.
++ +
++ These functions are used in combination with an AEAD cipher only.
++
++'NAME group-types' [GROUP-TYPES-LIST|+GROUP-TYPE ...|-GROUP-TYPE]::
++ This command allows modifying the list of group types similar to the
++ 'ciphers' command.
++ +
++ These algorithms are used to negotiate a shared secret of an insecure channel.
++
++'NAME pfs' [on|off]::
++ This command allows to enable or disable Perfect Forward Secrecy (PFS).
++ If PFS is enabled, the encrypted channels of a VPN connection will be
++ renegotiated regularly to avoid that the same keys are used for too long.
++ If an attacker is able to obtain a key that was used to encrypt the
++ data, it is only possible to decrypt a certain amount of data.
++ +
++ It is strongly recommended to enable PFS at all times.
++
++'NAME lifetime' LIFETIME::
++ This command allows to define how often the VPN connection is
++ renegotiated if PFS is enabled.
++
++'NAME compression' [on|off]::
++ This command allows to enable or disable compression.
++ If compression is enabled, all data is being compressed before being
++ sent through the VPN.
++ This setting is ignored if the peer does not support this.
++
++== System Policies
++
++The system comes with builtin policies that cannot be modified by the user.
++They are intended to provide good defaults for various situations.
++
++[horizontal]
++'system'::
++ This policy is the default for every VPN connection and allows using
++ all ciphers, integrity and key-exchange algorithms that are recommended
++ to use and have not been proven or assumed to be broken, yet.
++ +
++ Over time, this policy will change whenever an algorithm has been broken
++ and is not recommended to be used any more.
++
++'performance'::
++ This policy is recommended to be used on systems that are not very powerful.
++ Algorithms with smaller key lengths, but still considered to be secure
++ are being used.
++
++System policies cannot be deleted.
++
++== SEE ALSO
++link:network[8],
++link:network-vpn[8]
+diff --git a/man/network-vpn-security-policies.xml b/man/network-vpn-security-policies.xml
+deleted file mode 100644
+index 40e6213..0000000
+--- a/man/network-vpn-security-policies.xml
++++ /dev/null
+@@ -1,313 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone">
+- <refentryinfo>
+- <title>network-vpn-security-policies</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-vpn-security-policies</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-vpn-security-policies</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network vpn security-policies <arg choice="plain">[new|destroy]</arg> <replaceable>NAME</replaceable> ...</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network vpn security-policies <replaceable>NAME</replaceable> <arg choice="plain">command</arg> ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>vpn security-policies</command>, it is possible
+- to create, destroy and edit VPN security policies.
+- </para>
+- <para>
+- A security policy is a definition of ciphers and algorithms for integrity
+- and key-exchanges for VPN connections.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>new <replaceable>NAME</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A new security policy may be created with the
+- <command>new</command> command.
+- </para>
+-
+- <para>
+- <replaceable>NAME</replaceable> does not allow any spaces.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>destroy <replaceable>NAME</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A security policy can be destroyed with this command.
+- </para>
+- <para>
+- If the policy is still in use, it cannot be deleted.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+-
+- <para>
+- For all other commands, the name of the security policy needs to be passed first:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> show</command>
+- </term>
+-
+- <listitem>
+- <para>
+- Shows the configuration of the security policy.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> key-exchange <replaceable>[IKEv2|IKEv1]</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- Defines the key exchange algorithm that should be used to
+- initiate an IPsec VPN connection.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> ciphers <replaceable>[CIPHER-LIST|+CIPHER ...|-CIPHER ...]</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows modifying the cipher list.
+- </para>
+-
+- <para>
+- A new <replaceable>CIPHER-LIST</replaceable> can be passed
+- which will replace the current configuration.
+- Alternatively, new ciphers can be added by prepending a
+- + sign to the cipher name and can removed likewise
+- using -.
+- </para>
+-
+- <para>
+- A cipher is an algorithm that encrypts and decrypts data
+- to be able to transmit it over an insecure channel.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> integrities <replaceable>[INTEGRITY-LIST|+INTEGRITY ...|-INTEGRITY ...]</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows modifying the integrity list
+- similar to the <command>ciphers</command> command.
+- </para>
+-
+- <para>
+- Integrity algorithms are used to be able to determine
+- if data has been altered when being transfered over
+- an untrusted channel.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> pseudo-random-functions <replaceable>[PSEUDO-RANDOM-FUNCTION-LIST|+PSEUDO-RANDOM-FUNCTION...|-PSEUDO-RANDOM-FUNCTION]</replaceable>
+- </command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows modifying the list of pseudo random functions
+- similar to the <command>ciphers</command> command.
+- </para>
+-
+- <para>
+- These functions are used in combination with an AEAD cipher only.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> group-types <replaceable>[GROUP-TYPES-LIST|+GROUP-TYPE ...|-GROUP-TYPE]</replaceable>
+- </command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows modifying the list of group types
+- similar to the <command>ciphers</command> command.
+- </para>
+-
+- <para>
+- These algorithms are used to negotiate a shared secret
+- of an insecure channel.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> pfs <replaceable>[on|off]</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows to enable or disable Perfect Forward Secrecy (PFS).
+- </para>
+-
+- <para>
+- If PFS is enabled, the encrypted channels of a VPN connection will be
+- renegotiated regularly to avoid that the same keys are used for too long.
+- If an attacker is able to obtain a key that was used to encrypt the
+- data, it is only possible to decrypt a certain amount of data.
+- </para>
+-
+- <para>
+- It is strongly recommended to enable PFS at all times.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> lifetime <replaceable>LIFETIME</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows to define how often the VPN connection is
+- renegotiated if PFS is enabled.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>NAME</replaceable> compression <replaceable>[on|off]</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows to enable or disable compression.
+- </para>
+-
+- <para>
+- If compression is enabled, all data is being compressed before being
+- sent through the VPN.
+- This setting is ignored if the peer does not support this.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>System Policies</title>
+-
+- <para>
+- The system comes with builtin policies that cannot be modified by the user.
+- They are intended to provide good defaults for various situations.
+- </para>
+-
+- <refsect2>
+- <title>system</title>
+-
+- <para>
+- This policy is the default for every VPN connection and allows using
+- all ciphers, integrity and key-exchange algorithms that are recommended
+- to use and have not been proven or assumed to be broken, yet.
+- </para>
+-
+- <para>
+- Over time, this policy will change whenever an algorithm has been broken
+- and is not recommended to be used any more.
+- </para>
+- </refsect2>
+-
+- <refsect2>
+- <title>performance</title>
+-
+- <para>
+- This policy is recommended to be used on systems that are not very powerful.
+- Algorithms with smaller key lengths, but still considered to be secure
+- are being used.
+- </para>
+- </refsect2>
+-
+- <para>
+- System policies cannot be deleted.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-vpn</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,375 @@
+From 357723e90cb0f700c4315b6016543db4230df5fb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 16:46:07 +0200
+Subject: [PATCH 084/304] man: Convert network-zone(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/include-color.txt | 2 +-
+ man/include-description.txt | 2 +-
+ man/network-zone.txt | 73 +++++++++++
+ man/network-zone.xml | 247 ------------------------------------
+ 4 files changed, 75 insertions(+), 249 deletions(-)
+ create mode 100644 man/network-zone.txt
+ delete mode 100644 man/network-zone.xml
+
+diff --git a/man/include-color.txt b/man/include-color.txt
+index 073c01b..4b417a5 100644
+--- a/man/include-color.txt
++++ b/man/include-color.txt
+@@ -1,7 +1,7 @@
+ 'color set <color>'::
+ The color is set with this command and required to be passed in
+ RGB hex formatting
+-
++ +
+ NOTE: The color is being used to make identification of network devices
+ easier on the command line and web user interface.
+
+diff --git a/man/include-description.txt b/man/include-description.txt
+index a39ba55..49bac64 100644
+--- a/man/include-description.txt
++++ b/man/include-description.txt
+@@ -1,6 +1,6 @@
+ 'description edit'::
+ This command opens an editor and allows you to edit title and description.
+-
++ +
+ NOTE: The formation of the description is similar to a git commit.
+ Every description has a title, the first line of the description.
+ The title is shown on the status page and in the web user interface.
+diff --git a/man/network-zone.txt b/man/network-zone.txt
+new file mode 100644
+index 0000000..88a1988
+--- /dev/null
++++ b/man/network-zone.txt
+@@ -0,0 +1,73 @@
++= network-zone(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone - Manage network zones
++
++== SYNOPSIS
++[verse]
++'network zone [new|destroy]' ZONE
++'network zone' ZONE ...
++
++== DESCRIPTION
++With help of the 'zone' command, it is very easy to configure network zones.
++
++It is possible to create zones and remove them. Zones may also be brought up
++and down and reconfigured. Their status may be viewed as well.
++
++== COMMANDS
++The following commands are understood:
++
++'new ZONE HOOK OPTIONS'::
++ A new zone may be created by the 'create' command.
++ There are at least two arguments required.
++ +
++ ZONE must be valid name for a zone which does not already exist.
++ HOOK is a valid zone hook which may require additional options.
++
++'destroy ZONE'::
++ A zone can be destroyed with this command.
++ +
++ There are two possible ways to remove a zone. The case is when the zone is
++ not up. Then, it will be removed immediately. When the zone is current up
++ and used, it will tagged to be remove later, after it has been brought down.
++
++For all other commands, the name of the zone needs to be passed first:
++
++'edit OPTIONS'::
++ The settings of a zone may be edited after it has been created.
++ The options that can be passed depend on the hook that is used for the zone.
++ Run 'network zone ZONE edit --help' to learn more about that.
++ +
++ It usually is required to restart/reload the zone until the new settings
++ are taken into account.
++
++'[up|down]'::
++ These commands will bring the zone up/down. This is done without control
++ of systemd, therefore not intended to be done in a productive environment.
++ However, these commands may be used for debugging.
++
++'[enable|disable]'::
++ These commands will enable or disable the zone. An enabled zone will
++ automatically be started either during the boot process or a hotplug event
++ of an associated port or other device.
++
++'status'::
++ This will show some detailed information about the state if the specified zone.
++
++include::include-color.txt[]
++
++include::include-description.txt[]
++
++'identify'::
++ This command will make all ports of the zone flash for a few seconds so
++ that you can identify the correct network adapters in the system.
++
++'rename' NAME::
++ Renames the zone to NAME.
++ +
++ The command will shut down the zone if it is up and start it again with
++ the new name. If the zone is not up it won't be started.
++
++== SEE ALSO
++link:network[8]
+diff --git a/man/network-zone.xml b/man/network-zone.xml
+deleted file mode 100644
+index 99fa8b8..0000000
+--- a/man/network-zone.xml
++++ /dev/null
+@@ -1,247 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone">
+- <refentryinfo>
+- <title>network-zone</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone <arg choice="plain">[new|destroy]</arg> <replaceable>ZONE</replaceable> ...</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network zone <replaceable>ZONE</replaceable> <arg choice="plain">command</arg> ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- With help of the <command>zone</command> command, it is very easy to
+- configure network zones.
+- </para>
+- <para>
+- It is possible to create zones and remove them. Zones may also
+- be brought up and down and reconfigured. Their status may be viewed
+- as well.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Commands</title>
+-
+- <para>
+- The following commands are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command>new <replaceable>ZONE</replaceable> <replaceable>HOOK</replaceable> <arg choice="opt" rep="repeat">OPTIONS</arg></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A new zone may be created by the <command>create</command>
+- command. There are at least two arguments required.
+- </para>
+- <para>
+- <replaceable>ZONE</replaceable> must be valid name for a
+- zone which does not already exist.
+- <replaceable>HOOK</replaceable> is a valid zone hook which
+- may require additional options.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command>destroy <replaceable>ZONE</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- A zone can be destroyed with this command.
+- </para>
+- <para>
+- There are two possible ways to remove a zone. The case
+- is when the zone is not up. Then, it will be removed
+- immediately. When the zone is current up and used, it
+- will tagged to be remove later, after it has been brought
+- down.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+-
+- <para>
+- For all other commands, the name of the zone needs to be passed first:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> edit <arg choice="opt" rep="repeat">OPTIONS</arg></command>
+- </term>
+-
+- <listitem>
+- <para>
+- The settings of a zone may be edited after it has been created.
+- The options that can be passed depend on the hook that is used
+- for the zone.
+- Run <command>network zone <replaceable>ZONE</replaceable> edit --help</command>
+- to learn more about that.
+- </para>
+- <para>
+- It usually is required to restart/reload the zone until
+- the new settings are taken into account.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> [up|down]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- These commands will bring the zone up/down. This is done
+- without control of systemd, therefore not intended to be
+- done in a productive environment.
+- However, these commands may be used for debugging.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> [enable|disable]</command>
+- </term>
+-
+- <listitem>
+- <para>
+- These commands will enable or disable the zone. An enabled
+- zone will automatically be started either during the boot process
+- or a hotplug event of an associated port or other device.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> status</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This will show some detailed information about the state
+- if the specified zone.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> color</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command allows settings a color for a zone.
+- See
+- <citerefentry>
+- <refentrytitle>network-color</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- for more information.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable>description</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command make is possible to add a description to a zone.
+- See
+- <citerefentry>
+- <refentrytitle>network-description</refentrytitle>
+- <manvolnum>8</manvolnum>,
+- </citerefentry>
+- for more information.
+- </para>
+- </listitem>
+- </varlistentry>
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> identify</command>
+- </term>
+-
+- <listitem>
+- <para>
+- This command will make all ports of the zone flash for
+- a few seconds so that you can identify the correct network
+- adapters in the system.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <command><replaceable>ZONE</replaceable> rename <replaceable>NAME</replaceable></command>
+- </term>
+-
+- <listitem>
+- <para>
+- Renames the zone to <replaceable>NAME</replaceable>.
+- </para>
+- <para>
+- The command will shut down the zone if it is up and
+- start it again with the new name. If the zone is not
+- up it won't be started.
+- </para>
+- <para>
+- Zones that are marked to be destroyed cannot be renamed.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,255 @@
+From 010f24cfc6e363815ae6a408a16e8b07c069c1a7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 16:59:10 +0200
+Subject: [PATCH 085/304] man: Convert network-zone-bridge(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-bridge.txt | 55 ++++++++++++
+ man/network-zone-bridge.xml | 172 ------------------------------------
+ 2 files changed, 55 insertions(+), 172 deletions(-)
+ create mode 100644 man/network-zone-bridge.txt
+ delete mode 100644 man/network-zone-bridge.xml
+
+diff --git a/man/network-zone-bridge.txt b/man/network-zone-bridge.txt
+new file mode 100644
+index 0000000..2e4f839
+--- /dev/null
++++ b/man/network-zone-bridge.txt
+@@ -0,0 +1,55 @@
++= network-zone-bridge(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone-bridge - Manage network zones
++
++== SYNOPSIS
++[verse]
++'network zone new ZONE bridge' ...
++'network zone ZONE edit' ...
++
++== DESCRIPTION
++The bridge hook creates an ethernet bridge which acts as an unmanaged network
++switch. It contains one or multiple physical network interfaces or virtual
++devices which will be connected to each other.
++
++The bridge hook is the preferred hook for local area network zones which are
++connected to an ethernet network.
++
++== OPTIONS
++The following options are understood:
++
++'--address=ADDRESS'::
++ By this option, you may define the MAC address of the bridge. If this option
++ is missing, a random MAC address will be generated.
++
++'--mtu=MTU'::
++ Sets the default MTU of the bridge.
++ All ports in the bridge must support this MTU value.
++
++'--stp=[_on_|off]'::
++ This option enables or disables use of the _Spanning Tree Protocol_ (STP).
++ This protocol is used to avoid loops in networks by dynamically disabling
++ packet forwarding on links.
++ +
++ It is highly recommended to leave this option enabled when you add more
++ than one device to the zone. Read below how the behaviour of STP can be changed.
++
++Spanning Tree Protocol (802.1D) configuration options:
++
++'--stp-forward-delay=_0_'::
++ This sets the default time the interfaces are hold off after they have been
++ added to a bridge. The default value is 0.
++
++'--stp-hello=_2_'::
++ This option defines how often a hello message should be sent. The value is
++ given in seconds and the default is 2.
++
++'--stp-priority=512'::
++ The STP priority sets the ranking of this network device within the network.
++ The bridge with the best rank (0 is best) will become the root bridge.
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-zone-bridge.xml b/man/network-zone-bridge.xml
+deleted file mode 100644
+index a77118b..0000000
+--- a/man/network-zone-bridge.xml
++++ /dev/null
+@@ -1,172 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-bridge">
+- <refentryinfo>
+- <title>network-zone-bridge</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-bridge</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-bridge</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone new <replaceable>ZONE</replaceable> bridge ...</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network zone <replaceable>ZONE</replaceable> edit ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The bridge hook creates an ethernet bridge which acts as an unmanaged network
+- switch. It contains one or multiple phyisical network interfaces or virtual
+- devices which will be connected to each other.
+- </para>
+- <para>
+- The bridge hook is the prefered hook for local area network zones which are
+- connected to an ethernet network.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--stp=[<emphasis>on</emphasis>|off]</option>
+- </term>
+-
+- <listitem>
+- <para>
+- This option enables or disable the use of the
+- <emphasis>Spanning Tree Protocol</emphasis> (STP).
+- This protocol is used to avoid loops in networks by
+- dynamically disabling packet forwarding on links.
+- </para>
+- <para>
+- It is highly recommended to leave this option enabled
+- when you add more than one device to the zone.
+- Read below how the behaviour of STP can be changed.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--mtu=<replaceable>MTU</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the default MTU of the bridge.
+- All ports in the bridge must support this MTU value.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--address=<replaceable>ADDRESS</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- By this option, you may define the MAC address of the
+- bridge. If this option is missing, a random MAC address
+- will be generated.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+-
+- <para>
+- Spanning Tree Protocol (802.1D) configuration options:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--stp-forward-delay=<replaceable>0</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- This sets the default time the interfaces are hold off
+- after they have been added to a bridge.
+- The default value is 0.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--stp-hello=<replaceable>2</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- This option defines how often a hello message should be
+- sent. The value is given in seconds and the default is 2.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--stp-priority=<replaceable>512</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The STP priority sets the ranking of this network device
+- within the network. The bridge with the best rank
+- (0 is best) will become the root bridge.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,222 @@
+From 718371b565fdb93719f68b5a2dcf719dd57a4e93 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 17:15:50 +0200
+Subject: [PATCH 086/304] man: Convert network-zone-config-pppoe-server(8) to
+ asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-config-pppoe-server.txt | 50 ++++++++
+ man/network-zone-config-pppoe-server.xml | 143 -----------------------
+ 2 files changed, 50 insertions(+), 143 deletions(-)
+ create mode 100644 man/network-zone-config-pppoe-server.txt
+ delete mode 100644 man/network-zone-config-pppoe-server.xml
+
+diff --git a/man/network-zone-config-pppoe-server.txt b/man/network-zone-config-pppoe-server.txt
+new file mode 100644
+index 0000000..72dff8e
+--- /dev/null
++++ b/man/network-zone-config-pppoe-server.txt
+@@ -0,0 +1,50 @@
++= network-zone-config-pppoe-server(8)
++
++== NAME
++network-zone-config-pppoe-server - PPPoE Server Settings
++
++== SYNOPSIS
++[verse]
++`network zone ZONE config create pppoe-server ...`
++`network zone ZONE config pppoe-server edit ...`
++
++== DESCRIPTION
++This configuration hook enables a **PPPoE Server** on a zone.
++
++== OPTIONS
++The following options are understood:
++
++`--subnet=SUBNET`::
++ The `--subnet` option defines an IPv4 pool of which IP addresses are
++ assigned to the remote hosts. The first address of the subnet will be used
++ for the gateway which is the PPPoE server itself.
++ +
++ The subnet must at least have two IP addresses.
++
++`--mtu=MTU`::
++ Set the required MTU (Maximum Transmission Unit) for the PPP connection.
++ The default value is 1492 bytes which is a common MTU for DSL connections.
++
++`--service-name=SERVICE NAME`::
++ This option receives a string which will be used as the service name. The
++ service name is sent out to the clients and used for identification but
++ not authorisation purposes.
++ +
++ The default is an empty value.
++
++`--max-sessions=0`::
++ Limit the number of sessions that may be established by the same MAC address.
++ This must be a positive number.
++ 0 permits an unlimited number of sessions per MAC address.
++
++== EXAMPLES
++
++This command creates a PPPoE server that will assign an IP address from the
++192.168.0.0/16 subnet:
++
++ network zone net0 config create pppoe-server --subnet=192.168.0.0/16
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8],
++link:network-zone-config[8]
+diff --git a/man/network-zone-config-pppoe-server.xml b/man/network-zone-config-pppoe-server.xml
+deleted file mode 100644
+index e6d497e..0000000
+--- a/man/network-zone-config-pppoe-server.xml
++++ /dev/null
+@@ -1,143 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-config-pppoe-server">
+- <refentryinfo>
+- <title>network-zone-config-pppoe-server</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-config-pppoe-server</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-config-pppoe-server</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone <replaceable>ZONE</replaceable> config create pppoe-server ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- This configuration hook enables a <emphasis>PPPoE server</emphasis>
+- functionality to a zone which is of an ethernet-like type.
+- </para>
+- <para>
+- The PPPoE server is mostly for development purpose and performs pretty
+- well. However, it is not recommended to use it in production environments.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--subnet=<replaceable>SUBNET</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The <option>--subnet</option> option defines an IPv4 pool
+- of which IP addresses are assigned to the remote hosts.
+- The first address of the subnet will be used for the
+- gateway which is the PPPoE server itself.
+- </para>
+- <para>
+- The subnet must at least have two IP addresses.
+- Broadcast and network addresses will be used as well.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--mtu=<replaceable>MTU</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Set the required MTU (Maximum Transmission Unit) for
+- the PPP connection.
+- </para>
+- <para>
+- The default value is 1492 bytes which is a common MTU for
+- DSL connections.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--service-name=<replaceable>STRING</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- This options receives a string which will be used as the
+- service name. The service name is sent out to the clients
+- and used for identification but not authorization purposes.
+- </para>
+- <para>
+- The default is an empty value.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--max-sessions=<emphasis>0</emphasis></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Limit the max. number of sessions that may be established
+- by the same MAC address.
+- </para>
+- <para>
+- This must be a positive number. 0 permits an unlimited
+- number of sessions per MAC address.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone-config</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,184 @@
+From 6e94de3efa35088eb322ced2653efeec5f5c29fd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 17:23:07 +0200
+Subject: [PATCH 087/304] man: Convert network-zone-ip-tunnel(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-ip-tunnel.txt | 35 ++++++++++
+ man/network-zone-ip-tunnel.xml | 121 ---------------------------------
+ 2 files changed, 35 insertions(+), 121 deletions(-)
+ create mode 100644 man/network-zone-ip-tunnel.txt
+ delete mode 100644 man/network-zone-ip-tunnel.xml
+
+diff --git a/man/network-zone-ip-tunnel.txt b/man/network-zone-ip-tunnel.txt
+new file mode 100644
+index 0000000..cb30731
+--- /dev/null
++++ b/man/network-zone-ip-tunnel.txt
+@@ -0,0 +1,35 @@
++= network-zone-ip-tunnel(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone-ip-tunnel - Manage IP Tunnels
++
++== SYNOPSIS
++[verse]
++`network zone new ZONE ip-tunnel ...`
++`network zone ZONE edit ...`
++
++== DESCRIPTION
++The ip-tunnel hook is used to create IP tunnels that use protocols like GRE to
++encapsulate IP packets.
++
++== OPTIONS
++The following options are understood:
++
++`--mode=MODE`::
++ Sets the protocol that is being used to encapsulate IP packets.
++ Currently only **GRE** is supported.
++
++`--peer=PEER`::
++ The address of the peer that terminates the remote end of this tunnel.
++ +
++ If left empty, connections from any IP address will be accepted.
++
++`--local-address=LOCAL-ADDRESS`::
++ The local IP address the tunnel originates from.
++ +
++ This is optional and if unset a useful default will be used.
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-zone-ip-tunnel.xml b/man/network-zone-ip-tunnel.xml
+deleted file mode 100644
+index a1cc257..0000000
+--- a/man/network-zone-ip-tunnel.xml
++++ /dev/null
+@@ -1,121 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-ip-tunnel">
+- <refentryinfo>
+- <title>network-zone-ip-tunnel</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-ip-tunnel</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-ip-tunnel</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone new <replaceable>ZONE</replaceable> ip-tunnel ...</command>
+- </cmdsynopsis>
+-
+- <cmdsynopsis>
+- <command>network zone <replaceable>ZONE</replaceable> edit ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The ip-tunnel hook is used to create IP tunnels that use protocols
+- like GRE to encapsulate IP packets.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--mode=<replaceable>MODE</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the protocol that is being used to encapsulate
+- IP packets.
+- Currently only <replaceable>gre</replaceable> is supported.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--peer=<replaceable>PEER</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The address of the peer that terminates the remote
+- end of this tunnel.
+- </para>
+-
+- <para>
+- If left empty, connections from any IP address will
+- be accepted.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--local-address=<replaceable>LOCAL-ADDRESS</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The local IP address the tunnel originates from.
+- </para>
+-
+- <para>
+- This is optional and if unset a useful default will be used.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,329 @@
+From 23eec7d08e289749759927bcf4c2387cbfcbdce2 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 17:34:10 +0200
+Subject: [PATCH 088/304] man: Convert network-zone-modem(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-modem.txt | 66 +++++++++++
+ man/network-zone-modem.xml | 235 -------------------------------------
+ 2 files changed, 66 insertions(+), 235 deletions(-)
+ create mode 100644 man/network-zone-modem.txt
+ delete mode 100644 man/network-zone-modem.xml
+
+diff --git a/man/network-zone-modem.txt b/man/network-zone-modem.txt
+new file mode 100644
+index 0000000..6b09622
+--- /dev/null
++++ b/man/network-zone-modem.txt
+@@ -0,0 +1,66 @@
++= network-zone-modem(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone-modem - Configure serial modems
++
++== SYNOPSIS
++[verse]
++`network zone new ZONE modem ...`
++
++== DESCRIPTION
++The modem hook uses a serial interface to establish a PPP session to an Internet
++Service Provider. This method is used by 56k modems and mobile networks like
++LTE, GSM and 3G.
++
++== OPTIONS
++The following options are understood:
++
++`--device=DEVICE`::
++ Sets the serial device that is used to connect. Example: /dev/ttyUSB0
++
++`--monitor-device=DEVICE`::
++ The optional monitor device is used to collect status information like
++ signal strength and link quality while the connection is established.
++
++`--imsi=IMSI`::
++ Set the IMSI of the SIM card inside the wireless modem to identify it when
++ it is plugged in at runtime.
++
++`--pin=PIN`::
++ The PIN number of the SIM card.
++ This will be used to unlock the SIM card when it is locked.
++
++`--apn=APN`::
++ Sets the Access Point Name (APN) that the modem connects to.
++
++`--phone-number=PHONE-NUMBER`::
++ Sets the phone number that is dialled by the modem when the connection is
++ to be established.
++
++`--username=USERNAME`::
++ Sets the username for authentication.
++
++`--password=PASSWORD`::
++ Sets the password for authentication.
++ +
++ Use the `--auth=` option to transmit it in a secure manner to the provider.
++
++`--baudrate=921600`::
++ The baudrate for the serial link to the modem.
++
++`--mtu=N`::
++ Sets the default MTU of the PPP connection.
++
++`--auth=[chap|pap]`::
++ Define the authentication method that is used to authenticate against your
++ provider. The default is to use the provider's preference.
++
++ * _Challange-Handshake Authentication Protocol_ (`chap`) is the preferred,
++ secure method.
++ * _Password Authentication Protocol_ (`pap`) sends the plaintext password
++ to the authentication server which is the reason why it should be avoided.
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-zone-modem.xml b/man/network-zone-modem.xml
+deleted file mode 100644
+index 97a1d35..0000000
+--- a/man/network-zone-modem.xml
++++ /dev/null
+@@ -1,235 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-modem">
+- <refentryinfo>
+- <title>network-zone-modem</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-modem</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-modem</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone new <replaceable>ZONE</replaceable> modem ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The modem hook uses a serial interface to establish a PPP session to an
+- Internet Service Provider. This method is used by 56k modems and mobile
+- networks like LTE, GSM and 3G.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--device=<replaceable>DEVICE</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the serial device that is used to connect.
+- </para>
+- <para>
+- Example: /dev/ttyUSB0
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--monitor-device=<replaceable>DEVICE</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The optional monitor device is used to collect status
+- information like signal strength and link quality while
+- the connection is established.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--imsi=<replaceable>IMSI</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Set the IMSI of the SIM card inside the wireless modem
+- to identify it when it is plugged in at runtime.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--pin=<replaceable>PIN</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The PIN number of the SIM card.
+- </para>
+- <para>
+- This will be used to unlock the SIM card when it
+- is locked.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--apn=<replaceable>APN</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the Access Point Name (<replaceable>APN</replaceable>)
+- that the modem connects to.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--phone-number=<replaceable>PHONE-NUMBER</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the phone number that is dialed by the modem when
+- the connection is to be established.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--username=<replaceable>USERNAME</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the username for authentication.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--password=<replaceable>PASSWORD</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the password for authentication.
+- </para>
+- <para>
+- Use the <option>--auth=</option> option to transmit it
+- in a secure manner to the provider.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--baudrate=<emphasis>921600</emphasis></option>
+- </term>
+-
+- <listitem>
+- <para>
+- The baudrate for the serial link to the modem.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--mtu=<emphasis>N</emphasis></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the default MTU of the PPP connection.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--auth=[chap|pap]</option>
+- </term>
+-
+- <listitem>
+- <para>
+- Define the authentication method that is used to
+- authenticate against your provider.
+- The default is to use the provider's preference.
+- </para>
+- <itemizedlist>
+- <listitem>
+- <para>
+- <emphasis>Challange-Handshake Authentication Protocol</emphasis>
+- (chap) is the preferred secure method.
+- </para>
+- </listitem>
+- <listitem>
+- <para>
+- <emphasis>Password Authentication Protocol</emphasis>
+- (pap) sends the plaintext password to the authentication
+- server which is the reason why it should be avoided to use PAP.
+- </para>
+- </listitem>
+- </itemizedlist>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,266 @@
+From d4f0a25cd61fe6a7d0cb711e269e9b75925edf23 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 17:42:10 +0200
+Subject: [PATCH 089/304] man: Convert network-zone-pppoe(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-pppoe.txt | 52 +++++++++++
+ man/network-zone-pppoe.xml | 186 -------------------------------------
+ 2 files changed, 52 insertions(+), 186 deletions(-)
+ create mode 100644 man/network-zone-pppoe.txt
+ delete mode 100644 man/network-zone-pppoe.xml
+
+diff --git a/man/network-zone-pppoe.txt b/man/network-zone-pppoe.txt
+new file mode 100644
+index 0000000..93b55f2
+--- /dev/null
++++ b/man/network-zone-pppoe.txt
+@@ -0,0 +1,52 @@
++= network-zone-pppoe(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone-pppoe - PPP over Ethernet
++
++== SYNOPSIS
++[verse]
++`network zone new ZONE pppoe ...`
++
++== DESCRIPTION
++The `pppoe` hook creates a PPPoE connection to your ISP.
++
++== OPTIONS
++The following options are understood:
++
++`--username=USERNAME`::
++ Sets the username for authentication.
++
++`--password=PASSWORD`::
++ Sets the password for authentication.
++ +
++ Use the `--auth=` option to transmit it in a secure manner to the provider.
++
++`--mtu=N`::
++ Sets the default MTU of the PPP connection.
++
++`--auth=[chap|pap]`::
++ Define the authentication method that is used to authenticate against your
++ provider. The default is to use the provider's preference.
++
++ * _Challange-Handshake Authentication Protocol_ (`chap`) is the preferred,
++ secure method.
++ * _Password Authentication Protocol_ (`pap`) sends the plaintext password
++ to the authentication server which is the reason why it should be avoided.
++
++`--access-concentrator=STRING`::
++ By this option, you may define the name of the access concentrator.
++
++`--service-name=STRING`::
++ By this option, you may define the service name.
++
++`--ipv6=[on|off]`::
++ By this option, you may enable or disable IPv6.
++
++`--prefix-delegation=[on|off]`::
++ By this option, you may enable or disable the delegation through your
++ provider of one IPv6 prefix to your system.
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-zone-pppoe.xml b/man/network-zone-pppoe.xml
+deleted file mode 100644
+index 36c4d0e..0000000
+--- a/man/network-zone-pppoe.xml
++++ /dev/null
+@@ -1,186 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-pppoe">
+- <refentryinfo>
+- <title>network-zone-pppoe</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-pppoe</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-pppoe</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone new <replaceable>ZONE</replaceable> pppoe ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The pppoe hook creates a PPPoE connection to your ISP.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+-
+- <varlistentry>
+- <term>
+- <option>--username=<replaceable>USERNAME</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the username for authentication.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--password=<replaceable>PASSWORD</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the password for authentication.
+- </para>
+- <para>
+- Use the <option>--auth=</option> option to transmit it
+- in a secure manner to the provider.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--mtu=<emphasis>N</emphasis></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Sets the default MTU of the PPP connection.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--auth=[chap|pap]</option>
+- </term>
+-
+- <listitem>
+- <para>
+- Define the authentication method that is used to
+- authenticate against your provider.
+- The default is to use the provider's preference.
+- </para>
+- <itemizedlist>
+- <listitem>
+- <para>
+- <emphasis>Challange-Handshake Authentication Protocol</emphasis>
+- (chap) is the preferred secure method.
+- </para>
+- </listitem>
+- <listitem>
+- <para>
+- <emphasis>Password Authentication Protocol</emphasis>
+- (pap) sends the plaintext password to the authentication
+- server which is the reason why it should be avoided to use PAP.
+- </para>
+- </listitem>
+- </itemizedlist>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--access-concentrator=<replaceable>STRING</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- By this option, you may define the name of the access concentrator.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--service-name=<replaceable>STRING</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- By this option, you may define the service name.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--ipv6=[<emphasis>on</emphasis>|off]</option>
+- </term>
+-
+- <listitem>
+- <para>
+- By this option, you may enable or disable IPv6
+- </para> </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--prefix-delegation=[<emphasis>on</emphasis>|off]</option>
+- </term>
+-
+- <listitem>
+- <para>
+- By this option, you may enable or disable the delegation through your provider of one IPv6 prefix to your system.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,167 @@
+From bc2b9c75cd5b73e1c2de5463fc1c0bc94b6dad93 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 17:53:20 +0200
+Subject: [PATCH 090/304] man: Convert network-zone-wireless(8) to asciidoc
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-zone-wireless.txt | 32 ++++++++++
+ man/network-zone-wireless.xml | 107 ----------------------------------
+ 2 files changed, 32 insertions(+), 107 deletions(-)
+ create mode 100644 man/network-zone-wireless.txt
+ delete mode 100644 man/network-zone-wireless.xml
+
+diff --git a/man/network-zone-wireless.txt b/man/network-zone-wireless.txt
+new file mode 100644
+index 0000000..368ac2a
+--- /dev/null
++++ b/man/network-zone-wireless.txt
+@@ -0,0 +1,32 @@
++= network-zone-wireless(8)
++Michael Tremer <michael.tremer@ipfire.org>
++
++== NAME
++network-zone-wireless - Wireless Networks
++
++== SYNOPSIS
++[verse]
++`network zone new ZONE wireless ...`
++
++== DESCRIPTION
++The wireless hook uses a WiFi interface and connects to a wireless access point
++in station mode.
++
++Configuration and credentials for any wireless networks to connect to can be
++configured by using link:network-wireless-network[8].
++
++== OPTIONS
++The following options are understood:
++
++`--phy=PHY`::
++ Takes the MAC address or name of the physical layer that is used to create
++ a virtual wireless interface.
++
++`--address=ADDRESS`::
++ Define a MAC address that is used for the virtual wireless device. This
++ parameter is optional and a random MAC address will be generated when
++ omitted.
++
++== SEE ALSO
++link:network[8],
++link:network-zone[8]
+diff --git a/man/network-zone-wireless.xml b/man/network-zone-wireless.xml
+deleted file mode 100644
+index 0931245..0000000
+--- a/man/network-zone-wireless.xml
++++ /dev/null
+@@ -1,107 +0,0 @@
+-<?xml version="1.0"?>
+-<!DOCTYPE refentry PUBLIC "-//OASIS/DTD DocBook XML V4.2//EN"
+- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+-
+-<refentry id="network-zone-wireless">
+- <refentryinfo>
+- <title>network-zone-wireless</title>
+- <productname>network</productname>
+-
+- <authorgroup>
+- <author>
+- <contrib>Developer</contrib>
+- <firstname>Michael</firstname>
+- <surname>Tremer</surname>
+- <email>michael.tremer@ipfire.org</email>
+- </author>
+- </authorgroup>
+- </refentryinfo>
+-
+- <refmeta>
+- <refentrytitle>network-zone-wireless</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </refmeta>
+-
+- <refnamediv>
+- <refname>network-zone-wireless</refname>
+- <refpurpose>Network Configuration Control Program</refpurpose>
+- </refnamediv>
+-
+- <refsynopsisdiv>
+- <cmdsynopsis>
+- <command>network zone new <replaceable>ZONE</replaceable> wireless ...</command>
+- </cmdsynopsis>
+- </refsynopsisdiv>
+-
+- <refsect1>
+- <title>Description</title>
+-
+- <para>
+- The wireless hook uses a WiFi interface and connects to a
+- wireless access point in station mode.
+- </para>
+-
+- <para>
+- Configuration and credentials for any wireless networks to
+- connect to can be configured by using the
+- <command>network wireless network</command> command.
+-
+- See <citerefentry>
+- <refentrytitle>network-wireless-networks</refentrytitle>
+- <manvolnum>8</manvolnum>,
+- </citerefentry> for details.
+- </para>
+- </refsect1>
+-
+- <refsect1>
+- <title>Options</title>
+-
+- <para>
+- The following options are understood:
+- </para>
+-
+- <variablelist>
+- <varlistentry>
+- <term>
+- <option>--phy=<replaceable>PHY</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Takes the MAC address or name of the physical layer
+- that is used to create a virtual wireless interface.
+- </para>
+- </listitem>
+- </varlistentry>
+-
+- <varlistentry>
+- <term>
+- <option>--address=<replaceable>ADDRESS</replaceable></option>
+- </term>
+-
+- <listitem>
+- <para>
+- Define a MAC address that is used for the virtual
+- wireless device. This parameter is optional and
+- a random MAC address will be generated when omitted.
+- </para>
+- </listitem>
+- </varlistentry>
+- </variablelist>
+- </refsect1>
+-
+- <refsect1>
+- <title>See Also</title>
+-
+- <para>
+- <citerefentry>
+- <refentrytitle>network</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>,
+- <citerefentry>
+- <refentrytitle>network-zone</refentrytitle>
+- <manvolnum>8</manvolnum>
+- </citerefentry>
+- </para>
+- </refsect1>
+-</refentry>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,43 @@
+From d746901bf5d4b4eb7591d1e009fad2960647e034 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 20:01:00 +0200
+Subject: [PATCH 091/304] man: Cleanup XML files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 26f2e9c..d79b0f2 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -475,7 +475,8 @@ MANPAGES = \
+ man/network-zone-wireless.8
+
+ MANPAGES_TXT = $(patsubst %.8,%.txt,$(MANPAGES))
+-MANPAGES_HTML = $(patsubst %.txt,%.html,$(MANPAGES))
++MANPAGES_HTML = $(patsubst %.txt,%.html,$(MANPAGES_TXT))
++MANPAGES_XML = $(patsubst %.txt,%.xml,$(MANPAGES_TXT))
+
+ .PHONY: man
+ man: $(MANPAGES) $(MANPAGES_HTML)
+@@ -484,11 +485,13 @@ man_MANS = \
+ $(MANPAGES)
+
+ noinst_DATA += \
+- $(MANPAGES_HTML)
++ $(MANPAGES_HTML) \
++ $(MANPAGES_XML)
+
+ CLEANFILES += \
+ $(man_MANS) \
+- $(MANPAGES_HTML)
++ $(MANPAGES_HTML) \
++ $(MANPAGES_XML)
+
+ EXTRA_DIST += \
+ $(MANPAGES_TXT)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,54 @@
+From 5d881996d1a5cf6211ae1fa0d4c4cd6fe6867f79 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 20:08:00 +0200
+Subject: [PATCH 092/304] man: Make distcheck happy
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index d79b0f2..0257b02 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -494,6 +494,7 @@ CLEANFILES += \
+ $(MANPAGES_XML)
+
+ EXTRA_DIST += \
++ man/asciidoc.conf \
+ $(MANPAGES_TXT)
+
+ XSLTPROC_FLAGS = \
+@@ -505,20 +506,23 @@ XSLTPROC_FLAGS = \
+ --stringparam man.copyright.section.enabled 1
+
+ XSLTPROC_COMMAND_MAN = \
+- $(AM_V_XSLT)$(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) \
++ $(AM_V_XSLT)$(MKDIR_P) $(dir $@) && \
++ $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) \
+ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+ man/%.xml: man/%.txt man/asciidoc.conf
+- $(AM_V_ASCIIDOC)$(ASCIIDOC) \
+- -f man/asciidoc.conf \
++ $(AM_V_ASCIIDOC)$(MKDIR_P) $(dir $@) && \
++ $(ASCIIDOC) \
++ -f $(abs_srcdir)/man/asciidoc.conf \
+ -d manpage -b docbook -o $@ $<
+
+ man/%.8: man/%.xml
+ $(XSLTPROC_COMMAND_MAN)
+
+ man/%.html: man/%.txt man/asciidoc.conf
+- $(AM_V_ASCIIDOC)$(ASCIIDOC) \
+- -f man/asciidoc.conf \
++ $(AM_V_ASCIIDOC)$(MKDIR_P) $(dir $@) && \
++ $(ASCIIDOC) \
++ -f $(abs_srcdir)/man/asciidoc.conf \
+ -b html5 -a icons -a theme=flask -o $@ $<
+
+ # ------------------------------------------------------------------------------
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From ff43523863b7ad7f50f5dfd4fdf80251ef01fa51 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 20:14:34 +0200
+Subject: [PATCH 093/304] man: Include include files in tarball
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 0257b02..6b77f0a 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -474,6 +474,10 @@ MANPAGES = \
+ man/network-zone-pppoe.8 \
+ man/network-zone-wireless.8
+
++EXTRA_DIST += \
++ man/include-color.txt \
++ man/include-description.txt
++
+ MANPAGES_TXT = $(patsubst %.8,%.txt,$(MANPAGES))
+ MANPAGES_HTML = $(patsubst %.txt,%.html,$(MANPAGES_TXT))
+ MANPAGES_XML = $(patsubst %.txt,%.xml,$(MANPAGES_TXT))
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 71bdead694bdae2e40e8a9f99403b4ec2db77914 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 31 Mar 2019 20:17:09 +0200
+Subject: [PATCH 094/304] man: network-route-static: Fix name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-route-static.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/man/network-route-static.txt b/man/network-route-static.txt
+index d4774b2..c6c3fea 100644
+--- a/man/network-route-static.txt
++++ b/man/network-route-static.txt
+@@ -2,7 +2,7 @@
+ Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+-network-route - Manage Static Routing
++network-route-static - Manage Static Routing
+
+ == SYNOPSIS
+ [verse]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From 2d2e96269516032b3bc4f2222067f6b82398a70a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Apr 2019 12:31:53 +0200
+Subject: [PATCH 095/304] Makefile: Add target to upload HTML man pages
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 6b77f0a..955f2b7 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -529,6 +529,10 @@ man/%.html: man/%.txt man/asciidoc.conf
+ -f $(abs_srcdir)/man/asciidoc.conf \
+ -b html5 -a icons -a theme=flask -o $@ $<
+
++.PHONY: upload-man
++upload-man: $(MANPAGES_HTML)
++ rsync -avHz --delete --progress $(MANPAGES_HTML) ms@people.ipfire.org:/pub/man-pages/$(PACKAGE_NAME)/
++
+ # ------------------------------------------------------------------------------
+
+ substitutions = \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From 95556ed6aa03a160df0ed6e929389c3d7283b87b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Apr 2019 12:45:55 +0200
+Subject: [PATCH 096/304] man: Do not generate HTML documentation in normal
+ build
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 955f2b7..4c26a9d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -488,10 +488,6 @@ man: $(MANPAGES) $(MANPAGES_HTML)
+ man_MANS = \
+ $(MANPAGES)
+
+-noinst_DATA += \
+- $(MANPAGES_HTML) \
+- $(MANPAGES_XML)
+-
+ CLEANFILES += \
+ $(man_MANS) \
+ $(MANPAGES_HTML) \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,205 @@
+From 6b1e747472ac60192146fc5ddba12b4a5d021194 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Apr 2019 12:47:02 +0200
+Subject: [PATCH 097/304] man: Fix authorship warnings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-route-static.txt | 4 +++-
+ man/network-settings.txt | 1 -
+ man/network-vpn-security-policies.txt | 4 +++-
+ man/network-zone-bridge.txt | 4 +++-
+ man/network-zone-config-pppoe-server.txt | 3 +++
+ man/network-zone-ip-tunnel.txt | 4 +++-
+ man/network-zone-modem.txt | 4 +++-
+ man/network-zone-pppoe.txt | 4 +++-
+ man/network-zone-wireless.txt | 4 +++-
+ man/network-zone.txt | 4 +++-
+ 10 files changed, 27 insertions(+), 9 deletions(-)
+
+diff --git a/man/network-route-static.txt b/man/network-route-static.txt
+index c6c3fea..4ba97eb 100644
+--- a/man/network-route-static.txt
++++ b/man/network-route-static.txt
+@@ -1,5 +1,4 @@
+ = network-route-static(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-route-static - Manage Static Routing
+@@ -63,6 +62,9 @@ The following commands are understood:
+ Packets matching this kind of route are silently discarded.
+ There will be no ICMP message sent to the source and no packet be forwarded.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-route[8],
+diff --git a/man/network-settings.txt b/man/network-settings.txt
+index e77f038..a1c1ae3 100644
+--- a/man/network-settings.txt
++++ b/man/network-settings.txt
+@@ -1,5 +1,4 @@
+ = network-settings(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-settings - Change global network settings
+diff --git a/man/network-vpn-security-policies.txt b/man/network-vpn-security-policies.txt
+index f9dc91a..3c843d7 100644
+--- a/man/network-vpn-security-policies.txt
++++ b/man/network-vpn-security-policies.txt
+@@ -1,5 +1,4 @@
+ = network-vpn-security-policies(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-vpn-security-policies - Configure VPN Security Policies
+@@ -106,6 +105,9 @@ They are intended to provide good defaults for various situations.
+
+ System policies cannot be deleted.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-vpn[8]
+diff --git a/man/network-zone-bridge.txt b/man/network-zone-bridge.txt
+index 2e4f839..46c78a6 100644
+--- a/man/network-zone-bridge.txt
++++ b/man/network-zone-bridge.txt
+@@ -1,5 +1,4 @@
+ = network-zone-bridge(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone-bridge - Manage network zones
+@@ -50,6 +49,9 @@ Spanning Tree Protocol (802.1D) configuration options:
+ The STP priority sets the ranking of this network device within the network.
+ The bridge with the best rank (0 is best) will become the root bridge.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-zone-config-pppoe-server.txt b/man/network-zone-config-pppoe-server.txt
+index 72dff8e..7d83bd1 100644
+--- a/man/network-zone-config-pppoe-server.txt
++++ b/man/network-zone-config-pppoe-server.txt
+@@ -44,6 +44,9 @@ This command creates a PPPoE server that will assign an IP address from the
+
+ network zone net0 config create pppoe-server --subnet=192.168.0.0/16
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8],
+diff --git a/man/network-zone-ip-tunnel.txt b/man/network-zone-ip-tunnel.txt
+index cb30731..8e2f30a 100644
+--- a/man/network-zone-ip-tunnel.txt
++++ b/man/network-zone-ip-tunnel.txt
+@@ -1,5 +1,4 @@
+ = network-zone-ip-tunnel(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone-ip-tunnel - Manage IP Tunnels
+@@ -30,6 +29,9 @@ The following options are understood:
+ +
+ This is optional and if unset a useful default will be used.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-zone-modem.txt b/man/network-zone-modem.txt
+index 6b09622..082bb21 100644
+--- a/man/network-zone-modem.txt
++++ b/man/network-zone-modem.txt
+@@ -1,5 +1,4 @@
+ = network-zone-modem(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone-modem - Configure serial modems
+@@ -61,6 +60,9 @@ The following options are understood:
+ * _Password Authentication Protocol_ (`pap`) sends the plaintext password
+ to the authentication server which is the reason why it should be avoided.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-zone-pppoe.txt b/man/network-zone-pppoe.txt
+index 93b55f2..1a1c4f3 100644
+--- a/man/network-zone-pppoe.txt
++++ b/man/network-zone-pppoe.txt
+@@ -1,5 +1,4 @@
+ = network-zone-pppoe(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone-pppoe - PPP over Ethernet
+@@ -47,6 +46,9 @@ The following options are understood:
+ By this option, you may enable or disable the delegation through your
+ provider of one IPv6 prefix to your system.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-zone-wireless.txt b/man/network-zone-wireless.txt
+index 368ac2a..531f8ff 100644
+--- a/man/network-zone-wireless.txt
++++ b/man/network-zone-wireless.txt
+@@ -1,5 +1,4 @@
+ = network-zone-wireless(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone-wireless - Wireless Networks
+@@ -27,6 +26,9 @@ The following options are understood:
+ parameter is optional and a random MAC address will be generated when
+ omitted.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-zone.txt b/man/network-zone.txt
+index 88a1988..2c2c6f0 100644
+--- a/man/network-zone.txt
++++ b/man/network-zone.txt
+@@ -1,5 +1,4 @@
+ = network-zone(8)
+-Michael Tremer <michael.tremer@ipfire.org>
+
+ == NAME
+ network-zone - Manage network zones
+@@ -69,5 +68,8 @@ include::include-description.txt[]
+ The command will shut down the zone if it is up and start it again with
+ the new name. If the zone is not up it won't be started.
+
++== AUTHORS
++Michael Tremer
++
+ == SEE ALSO
+ link:network[8]
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,583 @@
+From 39cfece88a2978f946e1713fbf1e2be3faf124d6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 1 Apr 2019 19:49:01 +0200
+Subject: [PATCH 098/304] man: Make syntax format more similar across files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/firewall-settings.txt | 29 +++++------
+ man/network-device.txt | 37 ++++++--------
+ man/network-dhcp.txt | 35 ++++++--------
+ man/network-dns-server.txt | 50 ++++++++-----------
+ man/network-performance-tuning.txt | 17 +++----
+ man/network-port.txt | 77 ++++++++++++++----------------
+ man/network-quick-start.txt | 15 ++----
+ man/network-route-static.txt | 18 +++----
+ 8 files changed, 116 insertions(+), 162 deletions(-)
+
+diff --git a/man/firewall-settings.txt b/man/firewall-settings.txt
+index 20038e3..81c9bd9 100644
+--- a/man/firewall-settings.txt
++++ b/man/firewall-settings.txt
+@@ -1,30 +1,25 @@
+-firewall-settings(8)
+-====================
++= firewall-settings(8)
+
+-NAME
+-----
++== NAME
+ firewall-settings - Global firewall settings
+
+-SYNOPSIS
+---------
++== SYNOPSIS
+ [verse]
+-'firewall settings'
+-'firewall settings' KEY=VALUE ...
++`firewall settings`
++`firewall settings` KEY=VALUE ...
+
+-DESCRIPTION
+------------
++== DESCRIPTION
+ This command is used to set global firewall settings.
+ Please have a look at the individual man pages for more options.
+
+-COMMANDS
+---------
++== COMMANDS
+ If no argument is given, the configuration will be dumped to the console.
+
+ You may set a new value by adding the variable name and the new value to
+ the command line.
+
+-SETTINGS
+---------
++== SETTINGS
++
+ === CONNTRACK_MAX_CONNECTIONS = 16384
+ Limits the max. number of simultaneous connections.
+
+@@ -88,10 +83,8 @@ Some routers on the Internet still do not support ECN properly.
+ When this setting is disabled, ECN is only advertised
+ when asked for.
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:firewall[8]
+diff --git a/man/network-device.txt b/man/network-device.txt
+index 4f1c1b0..d70536e 100644
+--- a/man/network-device.txt
++++ b/man/network-device.txt
+@@ -1,61 +1,54 @@
+-network-device(8)
+-=================
++= network-device(8)
+
+-NAME
+-----
++== NAME
+ network-device - Controls network devices
+
+-SYNOPSIS
+---------
++== SYNOPSIS
+ [verse]
+-'network device' [<options>] <command> ...
++`network device` COMMAND ...
+
+-DESCRIPTION
+------------
+-The 'network device' command shows low-level status information
++== DESCRIPTION
++The `network device` command shows low-level status information
+ of network devices and other things.
+
+-COMMANDS
+---------
++== COMMANDS
+ The following commands are understood:
+
+-'list'::
++`list`::
+ This command shows a list of all device that are currently present
+ on this system. This includes PHYs and serial devices as well.
+
+-'DEVICE discover'::
++`DEVICE discover`::
+ Runs a discovery for many hooks on the given device.
+
+ This will check if the hook can find for example a DHCP server or
+ DSLAM and thus predict for what the device should be used.
+
+-'DEVICE identify'::
++`DEVICE identify`::
+ This command only works for Ethernet adapters and will make those
+ that support this feature flash for a few seconds.
+
+ It is handy to find the right device to put the cable in.
+
+-'DEVICE monitor'::
++`DEVICE monitor`::
+ This command creates a monitor interface for wireless modules.
+
+ An instance of link:tcpdump[8] will be started and show all
+ frames that are sent or received on the 802.11 layer (layer 2).
+
+-'DEVICE status'::
++`DEVICE status`::
+ This will show you very detailed information about the given device.
+
+-'DEVICE unlock'::
++`DEVICE unlock`::
+ This command will unlock the SIM card in a modem.
+ Only serial devices are supported which are the most 4G or 3G modems.
+
+ For the PIN or PUK code, the user will be prompted.
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8]
+ link:network-port[8]
+ link:network-zone[8]
+diff --git a/man/network-dhcp.txt b/man/network-dhcp.txt
+index bcb768e..11e5fb4 100644
+--- a/man/network-dhcp.txt
++++ b/man/network-dhcp.txt
+@@ -1,44 +1,37 @@
+-network-dhcp(8)
+-===============
++= network-dhcp(8)
+
+-NAME
+-----
++== NAME
+ network-dhcp - Controls the DHCP Server
+
+-SYNOPSIS
+---------
++== SYNOPSIS
+ [verse]
+-'network dhcpv6' <command> ...
+-'network dhcpv4' <command> ...
++`network dhcpv6` COMMAND ...
++`network dhcpv4` COMMAND ...
+
+-DESCRIPTION
+------------
++== DESCRIPTION
+ With help of the DHCP commands it is possible to configure DHCP
+ servers for IPv6 and IPv4.
+
+-COMMANDS
+---------
++== COMMANDS
+ The following commands are understood:
+
+-'start'::
++`start`::
+ Starts the DHCP server.
+
+-'stop'::
++`stop`::
+ Stops the DHCP server.
+
+-'restart'::
++`restart`::
+ Restarts the DHCP server.
+
+-'reload'::
++`reload`::
+ Reload the DHCP server configuration.
+
+-'subnet ...'::
++`subnet ...`::
+ TODO
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8]
+diff --git a/man/network-dns-server.txt b/man/network-dns-server.txt
+index bd01ca7..f5019ce 100644
+--- a/man/network-dns-server.txt
++++ b/man/network-dns-server.txt
+@@ -1,75 +1,67 @@
+-network-dns-server(8)
+-=====================
++= network-dns-server(8)
+
+-NAME
+-----
++== NAME
+ network-dns-server - Controls the DNS settings
+
+-SYNOPSIS
+---------
++== SYNOPSIS
+ [verse]
+-'network dns-server' add SERVER [PRIORITY]
+-'network dns-server' remove SERVER
+-'network dns-server' list
+-'network dns-server' update
++`network dns-server add` SERVER [PRIORITY]
++`network dns-server remove` SERVER
++`network dns-server list`
++`network dns-server update`
+
+-DESCRIPTION
+------------
++== DESCRIPTION
+ With this command, you will be able to configure the local DNS
+ configuration.
+
+ You may add and remove DNS servers as well as view the settings.
+
+-COMMANDS
+---------
++== COMMANDS
+ The following commands are understood:
+
+-'add' SERVER [PRIORITY]::
++`add SERVER [PRIORITY]`::
+ A new DNS server may be added to the list by the
+ 'add' command.
+ A priority that will rank the server my optionally be given.
+-
++ +
+ NOTE: SERVER must be a valid IP address and PRIORITY
+ must be a positive number.
+ The smaller this number, the higher is is the rank of
+ the server.
+
+-'remove' SERVER::
++`remove SERVER`::
+ The given server will be removed from the list of DNS servers.
+
+-'list'::
++`list`::
+ Shows a list of all servers that are currently in use.
+
+-'update'::
++`update`::
+ This command will re-create the system's configuration
+ files. It should not be required to use this command
+ very often.
+
+-SETTINGS
+---------
++== SETTINGS
+ The following settings may be set using link:network-settings[8]:
+
+-'DNS_USE_LOCAL_RESOLVER = [true|false]'::
++`DNS_USE_LOCAL_RESOLVER = [true|false]`::
+ This option defines whether the local DNS resolver should
+ be used or not.
+-
++ +
+ Basically, the option adds localhost to the list of nameservers
+ in link:resolv.conf[5].
+
+-'DNS_SEARCH_DOMAINS ='::
++`DNS_SEARCH_DOMAINS =`::
+ This setting configures the search domains for DNS queries
+ made by the local system.
+
+-'DNS_RANDOMIZE = [true|false]'::
++`DNS_RANDOMIZE = [true|false]`::
+ This option will break the DNS server ranks and will query
+ them in a random order which is useful to load-balance
+ multiple DNS servers.
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8],
+ link:network-settings[8]
+diff --git a/man/network-performance-tuning.txt b/man/network-performance-tuning.txt
+index 763ee21..4672bbc 100644
+--- a/man/network-performance-tuning.txt
++++ b/man/network-performance-tuning.txt
+@@ -1,12 +1,9 @@
+-network-performance-tuning(8)
+-=============================
++= network-performance-tuning(8)
+
+-NAME
+-----
++== NAME
+ network-performance-tuning - Performance Tuning for Networking
+
+-DESCRIPTION
+------------
++== DESCRIPTION
+ This page contains a summary of some performance tuning techniques
+ that this system is using.
+
+@@ -22,12 +19,10 @@ reducing network latency and quite possibly increasing throughput.
+ The algorithm is trying to balance all network controllers across
+ all processors.
+
+-See /proc/interrups for the distribution of interrupts.
++See /proc/interrupts for the distribution of interrupts.
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8]
+diff --git a/man/network-port.txt b/man/network-port.txt
+index 0c26f33..54cd58c 100644
+--- a/man/network-port.txt
++++ b/man/network-port.txt
+@@ -1,29 +1,25 @@
+-network-port(8)
+-===============
++= network-port(8)
+
+-NAME
+-----
++== NAME
+ network-port - Controls Network Ports
+
+-SYNOPSIS
+---------
++== SYNOPSIS
+ [verse]
+-'network port' new HOOK ...
+-'network port' destroy PORT
+-'network port' PORT color set <color>
+-'network port' PORT color reset
+-'network port' PORT create
+-'network port' PORT description edit
+-'network port' PORT description show
+-'network port' PORT down
+-'network port' PORT edit ...
+-'network port' PORT identify
+-'network port' PORT remove
+-'network port' PORT status
+-'network port' PORT up
+-
+-DESCRIPTION
+------------
++`network port new HOOK ...`
++`network port destroy PORT`
++`network port PORT color set COLOR`
++`network port PORT color reset`
++`network port PORT create`
++`network port PORT description edit`
++`network port PORT description show`
++`network port PORT down`
++`network port PORT edit ...`
++`network port PORT identify`
++`network port PORT remove`
++`network port PORT status`
++`network port PORT up`
++
++== DESCRIPTION
+ This command creates, deletes, changes and views the configuration
+ and status of ports.
+
+@@ -32,15 +28,14 @@ to an other network. It connects those and zones together.
+ The 'network device' command shows status information of network devices
+ and other things.
+
+-COMMANDS
+---------
++== COMMANDS
+ The following commands are understood:
+
+-'new' HOOK ...::
++`new HOOK ...`::
+ A new port may be created with this command.
+ HOOK must be a valid hook which may require more options.
+
+-'destroy' PORT::
++`destroy PORT`::
+ Destroys the port PORT.
+ The port is removed from any zones it is attached to and shut down.
+
+@@ -48,49 +43,47 @@ For all other commands, the name of the port needs to be passed first:
+
+ include::include-color.txt[]
+
+-'create'::
++`create`::
+ This will create devices for the existing port PORT.
+-
++ +
+ This does not create a new port. It will just create the (possibly
+ virtual) interface this port (i.e. create an interface for a WiFi
+ module or a VLAN device).
+-
+- The interface is not brought up. Use the 'up' command to do that.
++ +
++ The interface is not brought up. Use the `up` command to do that.
+
+ include::include-description.txt[]
+
+-'down'::
++`down`::
+ Shuts down the port.
+
+-'edit'::
++`edit`::
+ This command can be used to alter the configuration of a port.
+ Consult the documentation of the port hook to find out what is supported.
+
+-'identify'::
++`identify`::
+ This command will make the port flash for a few seconds
+ so that you can identify the correct network adapters
+ in the system.
+-
++ +
+ This is not supported by all network adapters.
+
+-'remove'::
++`remove`::
+ This will remove an existing PORT.
+-
++ +
+ This does not destroy the port. It inverses the operation performed
+ by the 'create' command.
+
+-'status'::
++`status`::
+ This will show some detailed information about the status
+ of the specified port.
+
+-'up'::
++`up`::
+ Brings up the port. It has to be created first.
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8],
+ link:network-zone[8]
+diff --git a/man/network-quick-start.txt b/man/network-quick-start.txt
+index 02ebfe0..1ab5866 100644
+--- a/man/network-quick-start.txt
++++ b/man/network-quick-start.txt
+@@ -1,12 +1,9 @@
+-network-quick-start(8)
+-======================
++= network-quick-start(8)
+
+-NAME
+-----
++== NAME
+ network-quick-start - Quick Start Guide for Networking
+
+-DESCRIPTION
+------------
++== DESCRIPTION
+ The link:network[8] is a very powerful command that allows you to configure
+ the entire networking stack.
+ Unfortunately that makes it quite complicated to use as well.
+@@ -80,12 +77,10 @@ The entire network can be restarted by running:
+ # network restart
+ ------------
+
+-AUTHORS
+--------
++== AUTHORS
+ Michael Tremer
+
+-SEE ALSO
+---------
++== SEE ALSO
+ link:network[8],
+ link:network-device[8],
+ link:network-port[8],
+diff --git a/man/network-route-static.txt b/man/network-route-static.txt
+index 4ba97eb..43a1277 100644
+--- a/man/network-route-static.txt
++++ b/man/network-route-static.txt
+@@ -5,10 +5,10 @@ network-route-static - Manage Static Routing
+
+ == SYNOPSIS
+ [verse]
+-'network route static' COMMAND ...
+-'network route static add' NETWORK [--gateway=GATEWAY,--unreachable,--prohibit,--blackhole] [--mtu=MTU]
+-'network route static remove' NETWORK
+-'network route static list' [--protocol=ipv6|ipv4]
++`network route static COMMAND ...`
++`network route static add NETWORK [--gateway=GATEWAY,--unreachable,--prohibit,--blackhole] [--mtu=MTU]`
++`network route static remove NETWORK`
++`network route static list` [--protocol=ipv6|ipv4]`
+
+ == DESCRIPTION
+ This command helps to manage routes.
+@@ -20,14 +20,14 @@ The following commands are understood:
+ A new route may be added by the 'add' command. It is required to pass a
+ valid network prefix NETWORK, which can be either IPv6 or IPv4.
+ +
+- For unicast routes, the '--gateway=GATEWAY' option must be passed, where
++ For unicast routes, the `--gateway=GATEWAY` option must be passed, where
+ GATEWAY is a valid IP address of the same protocol type as the network
+ prefix is.
+ +
+- Use '--unreachable', '--prohibit', '--blackhole' can be used to create of
++ Use `--unreachable`, `--prohibit`, `--blackhole` can be used to create of
+ that type. See ROUTE TYPES below for more information about these options.
+ +
+- The optional '--mtu=MTU' parameter defines the MTU along the path to the
++ The optional `--mtu=MTU` parameter defines the MTU along the path to the
+ destination and must be an integer number. This will show you very
+ detailed information about the given device.
+
+@@ -39,7 +39,7 @@ The following commands are understood:
+ 'list'::
+ Shows a list of all configured routes.
+ +
+- Output can be filtered by passing --protocol=[ipv6|ipv4].
++ Output can be filtered by passing `--protocol=[ipv6|ipv4]`.
+
+ == ROUTE TYPES
+
+@@ -47,7 +47,7 @@ The following commands are understood:
+ 'unicast'::
+ A unicast route is the most common route in routing tables. It is a route to
+ a destination network address, which describes the path to the destination.
+- Use the '--gateway=GATEWAY' option to create such a route.
++ Use the `--gateway=GATEWAY` option to create such a route.
+
+ 'unreachable'::
+ When a route is determined and the routing decision process returns a
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,53 @@
+From d673165c5456e29013def29ed1fa9f202110665a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 11:50:13 +0200
+Subject: [PATCH 099/304] hooks: Add overwritable function to determine the
+ port name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/header-port | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/src/header-port b/src/header-port
+index d75fdd8..7d0b272 100644
+--- a/src/header-port
++++ b/src/header-port
+@@ -68,6 +68,12 @@ hook_hotplug_rename_by_address() {
+ return ${EXIT_ERROR}
+ }
+
++# Returns the suggested name of the port
++hook_find_port_name() {
++ assert isset HOOK_PORT_PATTERN
++ port_find_free "${HOOK_PORT_PATTERN}"
++}
++
+ hook_default_new() {
+ local ${HOOK_SETTINGS[*]}
+
+@@ -78,14 +84,16 @@ hook_default_new() {
+ return ${EXIT_ERROR}
+ fi
+
+- assert isset HOOK_PORT_PATTERN
+-
+- local port=$(port_find_free ${HOOK_PORT_PATTERN})
++ # Determine a name for this port
++ local port="$(hook_find_port_name)"
+ assert isset port
+
+- port_settings_write "${port}" ${HOOK_SETTINGS[*]}
++ # Save settings
++ if ! port_settings_write "${port}" ${HOOK_SETTINGS[*]}; then
++ return ${EXIT_ERROR}
++ fi
+
+- exit ${EXIT_OK}
++ return ${EXIT_OK}
+ }
+
+ hook_new() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,76 @@
+From 96045e9c044a709407b40df4145011e335929a3e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 11:55:35 +0200
+Subject: [PATCH 100/304] vlan: Convert hook to use parse_cmdline function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 42 ++++++++----------------------------------
+ 1 file changed, 8 insertions(+), 34 deletions(-)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index f511986..69f5144 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -51,7 +51,14 @@ hook_check_settings() {
+ done
+ }
+
+-hook_new() {
++hook_find_port_name() {
++ assert isset PARENT_DEVICE
++ assert isset TAG
++
++ print "${PARENT_DEVICE}${VLAN_PORT_INTERFIX}${TAG}"
++}
++
++hook_parse_cmdline() {
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+ --parent-device=*)
+@@ -63,42 +70,9 @@ hook_new() {
+ --tag=*)
+ TAG=$(cli_get_val "${1}")
+ ;;
+- *)
+- warning "Unknown argument '${1}'"
+- ;;
+- esac
+- shift
+- done
+-
+- local port="${PARENT_DEVICE}${VLAN_PORT_INTERFIX}${TAG}"
+-
+- port_settings_write "${port}"
+-
+- exit ${EXIT_OK}
+-}
+-
+-hook_edit() {
+- local port=${1}
+- assert isset port
+- shift
+-
+- port_settings_read "${port}"
+-
+- while [ $# -gt 0 ]; do
+- case "${1}" in
+- --address=*)
+- ADDRESS=$(cli_get_val "${1}")
+- ;;
+- *)
+- warning "Unknown argument '${1}'"
+- ;;
+ esac
+ shift
+ done
+-
+- port_settings_write "${port}"
+-
+- exit ${EXIT_OK}
+ }
+
+ hook_create() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,42 @@
+From abb655547c79f72b97451c02ba285b13c68e5a2a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:00:02 +0200
+Subject: [PATCH 101/304] vlan: Validate and always set MAC address
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 69f5144..0147e54 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -66,6 +66,12 @@ hook_parse_cmdline() {
+ ;;
+ --address=*)
+ ADDRESS=$(cli_get_val "${1}")
++
++ # Validate address
++ if ! mac_is_valid "${ADDRESS}"; then
++ error "Invalid MAC address given: ${ADDRESS}"
++ return ${EXIT_CONF_ERROR}
++ fi
+ ;;
+ --tag=*)
+ TAG=$(cli_get_val "${1}")
+@@ -73,6 +79,11 @@ hook_parse_cmdline() {
+ esac
+ shift
+ done
++
++ # Generate a random MAC address if none given
++ if ! isset ADDRESS; then
++ ADDRESS="$(mac_generate)"
++ fi
+ }
+
+ hook_create() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From 0cf39f2d5178f624161b8c4329140bd00b06019c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:02:15 +0200
+Subject: [PATCH 102/304] vlan: Fail when unknown command line parameters are
+ being passed
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 0147e54..39dbfff 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -76,6 +76,10 @@ hook_parse_cmdline() {
+ --tag=*)
+ TAG=$(cli_get_val "${1}")
+ ;;
++ -*)
++ error "Unknown argument '${1}'"
++ return ${EXIT_CONF_ERROR}
++ ;;
+ esac
+ shift
+ done
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,102 @@
+From a2f35a67d83bd3a4a4438c2b7b8cbc2ee0002e38 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:08:05 +0200
+Subject: [PATCH 103/304] vlan: Rename PARENT_DEVICE to PARENT_PORT
+
+It technically is a port
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 26 ++++++++++++++++----------
+ test/nitsi/test/port-vlan/recipe | 2 +-
+ 2 files changed, 17 insertions(+), 11 deletions(-)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 39dbfff..f19eda4 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -23,14 +23,14 @@
+
+ HOOK_SETTINGS=(
+ "ADDRESS"
+- "PARENT_DEVICE"
++ "PARENT_PORT"
+ "TAG"
+ )
+
+-PORT_PARENTS_VAR="PARENT"
++PORT_PARENTS_VAR="PARENT_PORT"
+
+ hook_check_settings() {
+- assert isset PARENT_DEVICE
++ assert isset PARENT_PORT
+ assert isinteger TAG
+
+ if isset ADDRESS; then
+@@ -52,18 +52,15 @@ hook_check_settings() {
+ }
+
+ hook_find_port_name() {
+- assert isset PARENT_DEVICE
++ assert isset PARENT_PORT
+ assert isset TAG
+
+- print "${PARENT_DEVICE}${VLAN_PORT_INTERFIX}${TAG}"
++ print "${PARENT_PORT}${VLAN_PORT_INTERFIX}${TAG}"
+ }
+
+ hook_parse_cmdline() {
+ while [ $# -gt 0 ]; do
+ case "${1}" in
+- --parent-device=*)
+- PARENT_DEVICE=$(cli_get_val "${1}")
+- ;;
+ --address=*)
+ ADDRESS=$(cli_get_val "${1}")
+
+@@ -73,10 +70,19 @@ hook_parse_cmdline() {
+ return ${EXIT_CONF_ERROR}
+ fi
+ ;;
++ --port=*)
++ PARENT_PORT=$(cli_get_val "${1}")
++
++ # Check if PARENT_PORT exists
++ if ! port_exists "${PARENT_PORT}"; then
++ error "Port '${PARENT_PORT}' does not exist"
++ return ${EXIT_CONF_ERROR}
++ fi
++ ;;
+ --tag=*)
+ TAG=$(cli_get_val "${1}")
+ ;;
+- -*)
++ *)
+ error "Unknown argument '${1}'"
+ return ${EXIT_CONF_ERROR}
+ ;;
+@@ -100,7 +106,7 @@ hook_create() {
+ port_settings_read "${port}"
+
+ # Create the VLAN device
+- vlan_create "${port}" "${PARENT_DEVICE}" "${TAG}" "${ADDRESS}"
++ vlan_create "${port}" "${PARENT_PORT}" "${TAG}" "${ADDRESS}"
+
+ exit ${EXIT_OK}
+ }
+diff --git a/test/nitsi/test/port-vlan/recipe b/test/nitsi/test/port-vlan/recipe
+index d41377b..7a99251 100644
+--- a/test/nitsi/test/port-vlan/recipe
++++ b/test/nitsi/test/port-vlan/recipe
+@@ -17,7 +17,7 @@ bob: network zone upl0 config new static 192.168.100.102/24
+ all: network status
+
+ # Create a vlan device with parent port attached to net1
+-all: network port new vlan --parent-device=${p_net1} --tag=42
++all: network port new vlan --port="${p_net1}" --tag=42
+ all: network zone upl0 port attach "${p_net1}v42"
+
+ # Test if the vlan works by pinging bob
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,35 @@
+From 4776723194ad4d1ba75d1b373c1892e44ddcbf97 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:15:01 +0200
+Subject: [PATCH 104/304] vlan: Check if parent device exists before bringing
+ it up
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index f19eda4..98178e3 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -103,7 +103,15 @@ hook_create() {
+ device_exists "${port}" && exit ${EXIT_OK}
+
+ # Read configruation
+- port_settings_read "${port}"
++ if ! port_settings_read "${port}"; then
++ return ${EXIT_ERROR}
++ fi
++
++ # Check if the parent port exists
++ if ! port_exists "${PARENT_PORT}"; then
++ error "Port '${PARENT_PORT}' does not exist"
++ return ${EXIT_ERROR}
++ fi
+
+ # Create the VLAN device
+ vlan_create "${port}" "${PARENT_PORT}" "${TAG}" "${ADDRESS}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From 23ddd3765e344e06f379a5ccc5c2cfcbfca9c7b7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:15:41 +0200
+Subject: [PATCH 105/304] vlan: Simplify vlan_remove()
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index 97028b0..d83e3ad 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -88,13 +88,7 @@ vlan_create() {
+ }
+
+ vlan_remove() {
+- local device=${1}
+- assert isset device
+-
+- # Set down device (if not already done).
+- device_set_down ${device}
+-
+- device_delete ${device}
++ device_delete "$@"
+ }
+
+ vlan_get_parent() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,146 @@
+From d3a0f73d7b2b6d4f634083f5620752e57a7a691b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:28:17 +0200
+Subject: [PATCH 106/304] vlan: Refactor vlan_create()
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 81 +++++++++++++++++++++++-------------
+ src/hooks/ports/vlan | 10 ++++-
+ 2 files changed, 60 insertions(+), 31 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index d83e3ad..99a8baa 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -38,53 +38,76 @@ EOF
+ }
+
+ vlan_create() {
+- local device=${1}
+- assert isset device
++ local device="${1}"
++ shift
+
+- local parent=${2}
+- assert isset parent
++ assert isset device
+
+- local tag=${3}
+- assert isinteger tag
++ local address
++ local parent
++ local tag
++
++ # Parse command line arguments
++ while [ $# -gt 0 ]; do
++ case "${1}" in
++ --address=*)
++ address=$(cli_get_val "${1}")
++ ;;
++ --parent=*)
++ parent=$(cli_get_val "${1}")
++ ;;
++ --tag=*)
++ tag=$(cli_get_val "${1}")
++ ;;
++ *)
++ error "Unrecognized argument: ${1}"
++ return ${EXIT_ERROR}
++ ;;
++ esac
++ shift
++ done
++
++ # Generate a random MAC address if none was passed
++ if ! isset address; then
++ address="$(mac_generate)"
++ fi
+
+- local address=${4}
+- if isset address; then
+- assert ismac address
++ # Check if address is valid
++ if ! ismac address; then
++ log ERROR "Invalid mac address: ${address}"
++ return ${EXIT_ERROR}
+ fi
+
+- # Check if a device with the name does already exist.
+- if device_exists ${device}; then
+- log ERROR "device '${device}' does already exist"
++ # Check if a device with the name does already exist
++ if device_exists "${device}"; then
++ log ERROR "Device '${device}' already exists"
+ return ${EXIT_ERROR}
+ fi
+
+- # Check if the parent device exists.
+- if ! device_exists ${parent}; then
+- log ERROR "parent device '${parent}' does not exist"
++ # Check if the parent device exists
++ if ! device_exists "${parent}"; then
++ log ERROR "Parent device '${parent}' does not exist"
+ return ${EXIT_ERROR}
+ fi
+
+ # Load ebtables stuff.
+ vlan_init
+
+- local command="ip link add link ${parent} name ${device}"
++ # Make the command
++ local command=(
++ ip link add link "${parent}" name "${device}"
++ address "${address}" type vlan id "${tag}"
++ )
+
+- if isset address; then
+- command="${command} address ${address}"
++ # Run the command
++ if ! cmd_quiet "${command[*]}"; then
++ log ERROR "Could not create VLAN device ${device}: $?"
++ return ${EXIT_ERROR}
+ fi
+
+- command="${command} type vlan id ${tag}"
+-
+- cmd_quiet ${command}
+- local ret=$?
+-
+- if [ ${ret} -eq ${EXIT_OK} ]; then
+- log DEBUG "vlan device '${device}' has been created"
+- else
+- log ERROR "could not create vlan device '${device}': ${ret}"
+- fi
++ log DEBUG "Created VLAN device ${device} (parent = ${parent}, id = ${tag})"
+
+- return ${ret}
++ return ${EXIT_OK}
+ }
+
+ vlan_remove() {
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 98178e3..4715b1f 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -114,9 +114,15 @@ hook_create() {
+ fi
+
+ # Create the VLAN device
+- vlan_create "${port}" "${PARENT_PORT}" "${TAG}" "${ADDRESS}"
++ if ! vlan_create "${port}" \
++ --address="${ADDRESS}" \
++ --parent="${PARENT_PORT}" \
++ --tag="${TAG}"; then
++ error "Could not create port: ${port}"
++ return ${EXIT_ERROR}
++ fi
+
+- exit ${EXIT_OK}
++ return ${EXIT_OK}
+ }
+
+ hook_remove() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,30 @@
+From 68cacd23226f401f1676e8bfc975467647cefef0 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:29:57 +0200
+Subject: [PATCH 107/304] vlan: Create partent port (if necessary)
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/ports/vlan | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 4715b1f..384ad50 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -113,6 +113,12 @@ hook_create() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Create the partent port first
++ if ! port_create "${PARENT_PORT}"; then
++ error "Could not bring up parent port: ${PARENT_PORT}"
++ return ${EXIT_ERROR}
++ fi
++
+ # Create the VLAN device
+ if ! vlan_create "${port}" \
+ --address="${ADDRESS}" \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,49 @@
+From 5338fb1423a84f9faeb597a2b67606fff1f6d6ab Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:30:48 +0200
+Subject: [PATCH 108/304] vlan: Drop ebtables stuff
+
+We no longer have ebtables
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 16 ----------------
+ 1 file changed, 16 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index 99a8baa..c542bb5 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -24,19 +24,6 @@ PROC_NET_VLAN_CONFIG="${PROC_NET_VLAN}/config"
+
+ VLAN_PORT_INTERFIX="v"
+
+-vlan_init() {
+- ebtables-restore <<EOF
+-*filter
+-:INPUT ACCEPT
+-:FORWARD ACCEPT
+-:OUTPUT ACCEPT
+-
+-*broute
+-:BROUTING ACCEPT
+--A BROUTING -p 802_1Q -j DROP
+-EOF
+-}
+-
+ vlan_create() {
+ local device="${1}"
+ shift
+@@ -90,9 +77,6 @@ vlan_create() {
+ return ${EXIT_ERROR}
+ fi
+
+- # Load ebtables stuff.
+- vlan_init
+-
+ # Make the command
+ local command=(
+ ip link add link "${parent}" name "${device}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,159 @@
+From f24529e498b1c3fe60196c34356e5b005a22ae4c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:34:49 +0200
+Subject: [PATCH 109/304] vlan: Rename tag to id
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 12 ++++++------
+ src/hooks/ports/vlan | 26 +++++++++++++-------------
+ test/nitsi/test/port-vlan/recipe | 2 +-
+ 3 files changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index c542bb5..9a70c95 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -31,8 +31,8 @@ vlan_create() {
+ assert isset device
+
+ local address
++ local id
+ local parent
+- local tag
+
+ # Parse command line arguments
+ while [ $# -gt 0 ]; do
+@@ -40,12 +40,12 @@ vlan_create() {
+ --address=*)
+ address=$(cli_get_val "${1}")
+ ;;
++ --id=*)
++ id=$(cli_get_val "${1}")
++ ;;
+ --parent=*)
+ parent=$(cli_get_val "${1}")
+ ;;
+- --tag=*)
+- tag=$(cli_get_val "${1}")
+- ;;
+ *)
+ error "Unrecognized argument: ${1}"
+ return ${EXIT_ERROR}
+@@ -80,7 +80,7 @@ vlan_create() {
+ # Make the command
+ local command=(
+ ip link add link "${parent}" name "${device}"
+- address "${address}" type vlan id "${tag}"
++ address "${address}" type vlan id "${id}"
+ )
+
+ # Run the command
+@@ -89,7 +89,7 @@ vlan_create() {
+ return ${EXIT_ERROR}
+ fi
+
+- log DEBUG "Created VLAN device ${device} (parent = ${parent}, id = ${tag})"
++ log DEBUG "Created VLAN device ${device} (parent = ${parent}, id = ${id})"
+
+ return ${EXIT_OK}
+ }
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 384ad50..97b6985 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -23,39 +23,39 @@
+
+ HOOK_SETTINGS=(
+ "ADDRESS"
++ "ID"
+ "PARENT_PORT"
+- "TAG"
+ )
+
+ PORT_PARENTS_VAR="PARENT_PORT"
+
+ hook_check_settings() {
+ assert isset PARENT_PORT
+- assert isinteger TAG
++ assert isinteger ID
+
+ if isset ADDRESS; then
+ assert ismac ADDRESS
+ fi
+
+- if [ ${TAG} -gt 4096 ]; then
+- error "TAG is greater than 4096."
++ if [ ${ID} -gt 4096 ]; then
++ error "ID is greater than 4096."
+ exit ${EXIT_ERROR}
+ fi
+
+ local reserved
+ for reserved in 0 4095; do
+- if [ "${TAG}" = "${reserved}" ]; then
+- error "TAG=${reserved} is reserved."
++ if [ "${ID}" = "${reserved}" ]; then
++ error "ID=${reserved} is reserved."
+ exit ${EXIT_ERROR}
+ fi
+ done
+ }
+
+ hook_find_port_name() {
++ assert isset ID
+ assert isset PARENT_PORT
+- assert isset TAG
+
+- print "${PARENT_PORT}${VLAN_PORT_INTERFIX}${TAG}"
++ print "${PARENT_PORT}${VLAN_PORT_INTERFIX}${ID}"
+ }
+
+ hook_parse_cmdline() {
+@@ -70,6 +70,9 @@ hook_parse_cmdline() {
+ return ${EXIT_CONF_ERROR}
+ fi
+ ;;
++ --id=*)
++ ID=$(cli_get_val "${1}")
++ ;;
+ --port=*)
+ PARENT_PORT=$(cli_get_val "${1}")
+
+@@ -79,9 +82,6 @@ hook_parse_cmdline() {
+ return ${EXIT_CONF_ERROR}
+ fi
+ ;;
+- --tag=*)
+- TAG=$(cli_get_val "${1}")
+- ;;
+ *)
+ error "Unknown argument '${1}'"
+ return ${EXIT_CONF_ERROR}
+@@ -122,8 +122,8 @@ hook_create() {
+ # Create the VLAN device
+ if ! vlan_create "${port}" \
+ --address="${ADDRESS}" \
+- --parent="${PARENT_PORT}" \
+- --tag="${TAG}"; then
++ --id="${id}" \
++ --parent="${PARENT_PORT}"; then
+ error "Could not create port: ${port}"
+ return ${EXIT_ERROR}
+ fi
+diff --git a/test/nitsi/test/port-vlan/recipe b/test/nitsi/test/port-vlan/recipe
+index 7a99251..2341e19 100644
+--- a/test/nitsi/test/port-vlan/recipe
++++ b/test/nitsi/test/port-vlan/recipe
+@@ -17,7 +17,7 @@ bob: network zone upl0 config new static 192.168.100.102/24
+ all: network status
+
+ # Create a vlan device with parent port attached to net1
+-all: network port new vlan --port="${p_net1}" --tag=42
++all: network port new vlan --port="${p_net1}" --id=42
+ all: network zone upl0 port attach "${p_net1}v42"
+
+ # Test if the vlan works by pinging bob
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,110 @@
+From fc1e91cca425c8e929df76dad4488066070879dd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 12:41:36 +0200
+Subject: [PATCH 110/304] vlan: Validate ID
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 25 ++++++++++++++++++++++++-
+ src/hooks/ports/vlan | 26 +++++++++-----------------
+ 2 files changed, 33 insertions(+), 18 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index 9a70c95..ee2fb34 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -24,6 +24,23 @@ PROC_NET_VLAN_CONFIG="${PROC_NET_VLAN}/config"
+
+ VLAN_PORT_INTERFIX="v"
+
++vlan_valid_id() {
++ local id="${1}"
++
++ # Must be an integer
++ if ! isinteger id; then
++ return ${EXIT_FALSE}
++ fi
++
++ # Must be between 1 and 4095
++ if [ ${id} -ge 1 ] && [ ${id} -le 4096 ]; then
++ return ${EXIT_TRUE}
++ fi
++
++ # Otherwise this is invalid
++ return ${EXIT_FALSE}
++}
++
+ vlan_create() {
+ local device="${1}"
+ shift
+@@ -31,7 +48,7 @@ vlan_create() {
+ assert isset device
+
+ local address
+- local id
++ local id=1
+ local parent
+
+ # Parse command line arguments
+@@ -65,6 +82,12 @@ vlan_create() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Check VLAN ID
++ if ! vlan_valid_id "${id}"; then
++ log ERROR "Invalid VLAN ID: ${id}"
++ return ${EXIT_ERROR}
++ fi
++
+ # Check if a device with the name does already exist
+ if device_exists "${device}"; then
+ log ERROR "Device '${device}' already exists"
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 97b6985..7f99dbc 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -30,25 +30,11 @@ HOOK_SETTINGS=(
+ PORT_PARENTS_VAR="PARENT_PORT"
+
+ hook_check_settings() {
++ assert ismac ADDRESS
+ assert isset PARENT_PORT
+- assert isinteger ID
+-
+- if isset ADDRESS; then
+- assert ismac ADDRESS
+- fi
+-
+- if [ ${ID} -gt 4096 ]; then
+- error "ID is greater than 4096."
+- exit ${EXIT_ERROR}
+- fi
+
+- local reserved
+- for reserved in 0 4095; do
+- if [ "${ID}" = "${reserved}" ]; then
+- error "ID=${reserved} is reserved."
+- exit ${EXIT_ERROR}
+- fi
+- done
++ assert isinteger ID
++ assert vlan_valid_id "${ID}"
+ }
+
+ hook_find_port_name() {
+@@ -72,6 +58,12 @@ hook_parse_cmdline() {
+ ;;
+ --id=*)
+ ID=$(cli_get_val "${1}")
++
++ # Validate VLAN ID
++ if ! vlan_valid_id "${ID}"; then
++ error "Invalid VLAN ID: ${ID}"
++ return ${EXIT_CONF_ERROR}
++ fi
+ ;;
+ --port=*)
+ PARENT_PORT=$(cli_get_val "${1}")
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,48 @@
+From 9532462fe04658d728ecbf263b586111f73fe2b2 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 13:16:47 +0200
+Subject: [PATCH 111/304] util: Add abort() which will stop the program
+ immediately
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.util | 17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/src/functions/functions.util b/src/functions/functions.util
+index 7379a98..39ad861 100644
+--- a/src/functions/functions.util
++++ b/src/functions/functions.util
+@@ -469,14 +469,27 @@ assert() {
+ local assertion="$@"
+
+ if ! ${assertion}; then
+- error_log "Assertion '${assertion}' failed."
+ backtrace
+- exit ${EXIT_ERROR_ASSERT}
++
++ # End the program here
++ abort "Assertion failed: ${assertion}"
+ fi
+
+ return ${EXIT_OK}
+ }
+
++# Ends the program immediately without cleaning up
++abort() {
++ local msg="$@"
++
++ # Print message
++ if isset msg; then
++ log ERROR "${msg}"
++ fi
++
++ exit ${EXIT_ERROR_ASSERT}
++}
++
+ # This function checks, if the given argument is an assert error
+ # exit code. If this is the case, the script will halt immediately.
+ assert_check_retval() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,139 @@
+From 2eb7011cb5447f9568c8136940f59a047e1b8dae Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 3 Jun 2019 13:17:06 +0200
+Subject: [PATCH 112/304] vlan: Add support for 802.1ad (QinQ)
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.vlan | 24 +++++++++++++++++++++++-
+ src/hooks/ports/vlan | 22 ++++++++++++++++++++--
+ 2 files changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/src/functions/functions.vlan b/src/functions/functions.vlan
+index ee2fb34..fbaa34f 100644
+--- a/src/functions/functions.vlan
++++ b/src/functions/functions.vlan
+@@ -22,6 +22,11 @@
+ PROC_NET_VLAN="/proc/net/vlan"
+ PROC_NET_VLAN_CONFIG="${PROC_NET_VLAN}/config"
+
++VLAN_SUPPORTED_PROTOCOLS=(
++ "802.1Q" # default
++ "802.1ad"
++)
++
+ VLAN_PORT_INTERFIX="v"
+
+ vlan_valid_id() {
+@@ -41,6 +46,13 @@ vlan_valid_id() {
+ return ${EXIT_FALSE}
+ }
+
++vlan_supported_protocol() {
++ local proto="${1}"
++ assert isset proto
++
++ list_match "${proto}" "${VLAN_SUPPORTED_PROTOCOLS[@]}"
++}
++
+ vlan_create() {
+ local device="${1}"
+ shift
+@@ -50,6 +62,7 @@ vlan_create() {
+ local address
+ local id=1
+ local parent
++ local protocol="${VLAN_SUPPORTED_PROTOCOLS[0]}"
+
+ # Parse command line arguments
+ while [ $# -gt 0 ]; do
+@@ -63,6 +76,9 @@ vlan_create() {
+ --parent=*)
+ parent=$(cli_get_val "${1}")
+ ;;
++ --protocol=*)
++ protocol=$(cli_get_val "${1}")
++ ;;
+ *)
+ error "Unrecognized argument: ${1}"
+ return ${EXIT_ERROR}
+@@ -82,6 +98,12 @@ vlan_create() {
+ return ${EXIT_ERROR}
+ fi
+
++ # Check protocol
++ if ! vlan_supported_protocol "${protocol}"; then
++ log ERROR "Invalid protocol: ${protocol}"
++ return ${EXIT_ERROR}
++ fi
++
+ # Check VLAN ID
+ if ! vlan_valid_id "${id}"; then
+ log ERROR "Invalid VLAN ID: ${id}"
+@@ -103,7 +125,7 @@ vlan_create() {
+ # Make the command
+ local command=(
+ ip link add link "${parent}" name "${device}"
+- address "${address}" type vlan id "${id}"
++ address "${address}" type vlan proto "${protocol}" id "${id}"
+ )
+
+ # Run the command
+diff --git a/src/hooks/ports/vlan b/src/hooks/ports/vlan
+index 7f99dbc..af563ee 100644
+--- a/src/hooks/ports/vlan
++++ b/src/hooks/ports/vlan
+@@ -25,14 +25,21 @@ HOOK_SETTINGS=(
+ "ADDRESS"
+ "ID"
+ "PARENT_PORT"
++ "PROTOCOL"
+ )
+
++# Set the default to 802.1Q
++DEFAULT_PROTOCOL="${VLAN_SUPPORTED_PROTOCOLS[0]}"
++
+ PORT_PARENTS_VAR="PARENT_PORT"
+
+ hook_check_settings() {
+ assert ismac ADDRESS
+ assert isset PARENT_PORT
+
++ assert isset PROTOCOL
++ assert vlan_supported_protocol "${PROTOCOL}"
++
+ assert isinteger ID
+ assert vlan_valid_id "${ID}"
+ }
+@@ -74,6 +81,16 @@ hook_parse_cmdline() {
+ return ${EXIT_CONF_ERROR}
+ fi
+ ;;
++ --protocol=*)
++ PROTOCOL="$(cli_get_val "${1}")"
++
++ # Check if PROTOCOL is supported
++ if ! vlan_supported_protocol "${PROTOCOL}"; then
++ error "Protocol '${PROTOCOL}' is not supported"
++ error "Choose one of ${VLAN_SUPPORTED_PROTOCOLS[*]}"
++ return ${EXIT_CONF_ERROR}
++ fi
++ ;;
+ *)
+ error "Unknown argument '${1}'"
+ return ${EXIT_CONF_ERROR}
+@@ -114,8 +131,9 @@ hook_create() {
+ # Create the VLAN device
+ if ! vlan_create "${port}" \
+ --address="${ADDRESS}" \
+- --id="${id}" \
+- --parent="${PARENT_PORT}"; then
++ --id="${ID}" \
++ --parent="${PARENT_PORT}" \
++ --protocol="${PROTOCOL}"; then
+ error "Could not create port: ${port}"
+ return ${EXIT_ERROR}
+ fi
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,57 @@
+From ecc7067479d165f4178f04248d86898cf50e3d95 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 20 Jun 2019 23:09:01 +0200
+Subject: [PATCH 113/304] Do not try to start Bird during boot process
+
+We should not do this in the network script and let just
+systemd take care of this. Otherwise we would end up in
+an infinite loop during the boot process.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.bird | 14 --------------
+ src/network | 3 ---
+ 2 files changed, 17 deletions(-)
+
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index 1bbac8c..55d43b5 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -33,20 +33,6 @@ bird_reload() {
+ service_reload "bird.service"
+ }
+
+-bird_enable() {
+- # Generate configuration file
+- if ! bird_generate_config; then
+- log ERROR "Could not write Bird configuration"
+- return ${EXIT_ERROR}
+- fi
+-
+- # Enable the service to be automatically started next time
+- service_enable "bird.service"
+-
+- # Start it now
+- bird_start
+-}
+-
+ # Update configuration any apply it in one go
+ bird_update() {
+ if ! bird_generate_config; then
+diff --git a/src/network b/src/network
+index be06d8a..30f87a0 100644
+--- a/src/network
++++ b/src/network
+@@ -1381,9 +1381,6 @@ case "${action}" in
+ # Update resolv.conf(5) when initializing the network
+ dns_generate_resolvconf
+
+- # Make sure bird is running
+- bird_enable
+-
+ # Also execute all triggers
+ triggers_execute_all "init"
+ ;;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 9665b7963d263fd83ac132a84a3809fc6a03287a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 21 Jun 2019 06:30:44 +0100
+Subject: [PATCH 114/304] configure: Break when asciidoc cannot be found
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 117850f..340cfd6 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -144,6 +144,9 @@ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
+ # ------------------------------------------------------------------------------
+
+ AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
++if test -z "${ASCIIDOC}"; then
++ AC_MSG_ERROR([Required program 'asciidoc' not found])
++fi
+
+ # ------------------------------------------------------------------------------
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,29 @@
+From f1081966991d55ccd182b45f58fc0fde31437f77 Mon Sep 17 00:00:00 2001
+From: Stefan Schantl <stefan.schantl@ipfire.org>
+Date: Sat, 22 Jun 2019 09:52:37 +0000
+Subject: [PATCH 115/304] Fix creating new configs
+
+The id argument was missing for the zone_config_settings_write.
+
+Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/header-config | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/header-config b/src/header-config
+index c6a775c..ed647cd 100644
+--- a/src/header-config
++++ b/src/header-config
+@@ -45,7 +45,7 @@ hook_new() {
+ fi
+
+ # Write configuration to disk
+- if ! zone_config_settings_write "${zone}" "${HOOK}"; then
++ if ! zone_config_settings_write "${zone}" "${HOOK}" "${id}"; then
+ return ${EXIT_ERROR}
+ fi
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,30 @@
+From d07532fad069c51d188ba7b93539488499d5dbf9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 13:31:03 +0000
+Subject: [PATCH 116/304] inetcalc: Fix compiler warnings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/inetcalc.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/inetcalc.c b/src/inetcalc.c
+index 1841c84..7c072e9 100644
+--- a/src/inetcalc.c
++++ b/src/inetcalc.c
+@@ -134,9 +134,9 @@ static int default_prefix(const int family) {
+ static int ip_address_parse_simple(ip_address_t* ip, const int family, const char* address) {
+ assert(family == AF_INET || family == AF_INET6);
+
+- size_t address_length = strlen(address);
+- char buffer[address_length + 1];
+- strncpy(buffer, address, sizeof(buffer));
++ // Copy input to stack
++ char buffer[512];
++ strncpy(buffer, address, sizeof(buffer) - 1);
+
+ // Search for a prefix or subnet mask
+ char* prefix = strchr(buffer, '/');
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,70 @@
+From 0c5d22de5c22c9264dcb839df72440a1d11faa0c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 21 Jun 2019 06:34:23 +0100
+Subject: [PATCH 117/304] firewall: Drop separate scripts for IPv6 and IPv4
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 +--
+ src/{firewall6 => firewall} | 0
+ src/firewall4 | 29 -----------------------------
+ 3 files changed, 1 insertion(+), 31 deletions(-)
+ rename src/{firewall6 => firewall} (100%)
+ delete mode 100644 src/firewall4
+
+diff --git a/Makefile.am b/Makefile.am
+index 4c26a9d..a36a4ab 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -115,8 +115,7 @@ dist_doc_DATA = \
+ dist_sbin_SCRIPTS = \
+ src/dhclient-script \
+ src/firewall-config \
+- src/firewall4 \
+- src/firewall6 \
++ src/firewall \
+ src/network
+
+ network_DATA = \
+diff --git a/src/firewall6 b/src/firewall
+similarity index 100%
+rename from src/firewall6
+rename to src/firewall
+diff --git a/src/firewall4 b/src/firewall4
+deleted file mode 100644
+index 55eed2c..0000000
+--- a/src/firewall4
++++ /dev/null
+@@ -1,29 +0,0 @@
+-#!/bin/bash
+-###############################################################################
+-# #
+-# IPFire.org - A linux based firewall #
+-# Copyright (C) 2012 IPFire Network Development Team #
+-# #
+-# 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, either version 3 of the License, or #
+-# (at your option) any later version. #
+-# #
+-# 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 <http://www.gnu.org/licenses/>. #
+-# #
+-###############################################################################
+-
+-. /usr/lib/network/functions
+-
+-# Read firewall settings
+-firewall_settings_read
+-
+-firewall_cli "ipv4" "$@"
+-
+-exit ${EXIT_ERROR}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,76 @@
+From 67131768c979c66ad3717e46cb81a068b14eafee Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 13:43:04 +0000
+Subject: [PATCH 118/304] systemd: Remove double firewall scripts
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 +--
+ .../{firewall4.service.in => firewall.service} | 6 +++---
+ src/systemd/firewall6.service.in | 14 --------------
+ 3 files changed, 4 insertions(+), 19 deletions(-)
+ rename src/systemd/{firewall4.service.in => firewall.service} (62%)
+ delete mode 100644 src/systemd/firewall6.service.in
+
+diff --git a/Makefile.am b/Makefile.am
+index a36a4ab..81cf50d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -363,8 +363,7 @@ UNINSTALL_EXEC_HOOKS += ppp-uninstall-hook
+
+ if HAVE_SYSTEMD
+ systemdsystemunit_DATA = \
+- src/systemd/firewall4.service \
+- src/systemd/firewall6.service \
++ src/systemd/firewall.service \
+ src/systemd/firewall-init.service \
+ src/systemd/network-init.service \
+ src/systemd/network@.service
+diff --git a/src/systemd/firewall4.service.in b/src/systemd/firewall.service
+similarity index 62%
+rename from src/systemd/firewall4.service.in
+rename to src/systemd/firewall.service
+index 568f5e7..34797e5 100644
+--- a/src/systemd/firewall4.service.in
++++ b/src/systemd/firewall.service
+@@ -1,5 +1,5 @@
+ [Unit]
+-Description=Firewall for IPv4
++Description=Firewall for IPFire
+ After=firewall-init.service
+ Before=network.target
+ Requires=firewall-init.service
+@@ -7,8 +7,8 @@ Requires=firewall-init.service
+ [Service]
+ Type=oneshot
+ RemainAfterExit=yes
+-ExecStart=@sbindir@/firewall4 start
+-ExecStop=@sbindir@/firewall4 stop
++ExecStart=@sbindir@/firewall start
++ExecStop=@sbindir@/firewall stop
+
+ [Install]
+ WantedBy=multi-user.target
+diff --git a/src/systemd/firewall6.service.in b/src/systemd/firewall6.service.in
+deleted file mode 100644
+index 873bfe6..0000000
+--- a/src/systemd/firewall6.service.in
++++ /dev/null
+@@ -1,14 +0,0 @@
+-[Unit]
+-Description=Firewall for IPv6
+-After=firewall-init.service
+-Before=network.target
+-Requires=firewall-init.service
+-
+-[Service]
+-Type=oneshot
+-RemainAfterExit=yes
+-ExecStart=@sbindir@/firewall6 start
+-ExecStop=@sbindir@/firewall6 stop
+-
+-[Install]
+-WantedBy=multi-user.target
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,54 @@
+From 3e446cf0bff8c1dc409479bf02b0fc8912847c13 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 13:50:00 +0000
+Subject: [PATCH 119/304] firewall: Add init action to main script
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/firewall | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/src/firewall b/src/firewall
+index db7284c..c47ac61 100644
+--- a/src/firewall
++++ b/src/firewall
+@@ -19,11 +19,35 @@
+ # #
+ ###############################################################################
+
++# Parse the command line
++while [ $# -gt 0 ]; do
++ case "${1}" in
++ -d|--debug)
++ DEBUG=1
++ ;;
++ *)
++ action=${1}
++ ;;
++ esac
++ shift
++ [ -n "${action}" ] && break
++done
++
+ . /usr/lib/network/functions
+
+ # Read firewall settings
+ firewall_settings_read
+
+-firewall_cli "ipv6" "$@"
++case "${action}" in
++ # Initialise kernel with firewall settings
++ init)
++ firewall_kernel_init
++ exit $?
++ ;;
++
++ *)
++ firewall_cli "ipv6" "${action}" "$@"
++ ;;
++esac
+
+ exit ${EXIT_ERROR}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,79 @@
+From 19b14da45fb83638878b14e77303194733679bc1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 13:52:09 +0000
+Subject: [PATCH 120/304] firewall: Drop initialisation helper script
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 -
+ src/helpers/firewall-kernel-init | 30 ----------------------------
+ src/systemd/firewall-init.service.in | 4 ++--
+ 3 files changed, 2 insertions(+), 33 deletions(-)
+ delete mode 100644 src/helpers/firewall-kernel-init
+
+diff --git a/Makefile.am b/Makefile.am
+index 81cf50d..0974ba8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -206,7 +206,6 @@ dist_network_SCRIPTS = \
+
+ dist_helpers_SCRIPTS = \
+ src/helpers/dhcpd-config-helper \
+- src/helpers/firewall-kernel-init \
+ src/helpers/hostapd-config-helper \
+ src/helpers/ipsec-updown \
+ src/helpers/pppd-angel \
+diff --git a/src/helpers/firewall-kernel-init b/src/helpers/firewall-kernel-init
+deleted file mode 100644
+index aea82c4..0000000
+--- a/src/helpers/firewall-kernel-init
++++ /dev/null
+@@ -1,30 +0,0 @@
+-#!/bin/bash
+-###############################################################################
+-# #
+-# IPFire.org - A linux based firewall #
+-# Copyright (C) 2012 IPFire Network Development Team #
+-# #
+-# 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, either version 3 of the License, or #
+-# (at your option) any later version. #
+-# #
+-# 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 <http://www.gnu.org/licenses/>. #
+-# #
+-###############################################################################
+-
+-. /usr/lib/network/functions
+-
+-# Read firewall settings
+-firewall_setttings_read
+-
+-# Initialize kernel parameters for the firewall.
+-firewall_kernel_init
+-
+-exit ${EXIT_OK}
+diff --git a/src/systemd/firewall-init.service.in b/src/systemd/firewall-init.service.in
+index 24497e9..91dd058 100644
+--- a/src/systemd/firewall-init.service.in
++++ b/src/systemd/firewall-init.service.in
+@@ -1,8 +1,8 @@
+ [Unit]
+-Description=Initialize kernel parameters for the firewalls
++Description=Initialize kernel parameters for the firewall
+ Before=network.target
+
+ [Service]
+ Type=oneshot
+ RemainAfterExit=yes
+-ExecStart=@helpersdir@/firewall-kernel-init
++ExecStart=@sbindir@/firewall init
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From 70c56486267789a3767e22833548694a9b69e1b8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 13:55:00 +0000
+Subject: [PATCH 121/304] Revert "firewall: Disable PMTU by default"
+
+This reverts commit b3a66a5c00bc4e39ce0db34e2ac96c4911b4e31a.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.constants-firewall | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.constants-firewall b/src/functions/functions.constants-firewall
+index 4f7f503..d42189a 100644
+--- a/src/functions/functions.constants-firewall
++++ b/src/functions/functions.constants-firewall
+@@ -78,7 +78,7 @@ FIREWALL_USE_ECN="true"
+ FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_USE_ECN"
+
+ # Path MTU discovery
+-FIREWALL_PMTU_DISCOVERY="false"
++FIREWALL_PMTU_DISCOVERY="true"
+ FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_PMTU_DISCOVERY"
+
+ # Default TTL
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,147 @@
+From c69adafd8ad8abf4f14b6fe110bbd8efb5eca596 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 14:11:15 +0000
+Subject: [PATCH 122/304] firewall: Fix reading/writing settings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.constants-firewall | 40 ++++++++++++----------
+ src/functions/functions.settings | 6 ++--
+ 2 files changed, 25 insertions(+), 21 deletions(-)
+
+diff --git a/src/functions/functions.constants-firewall b/src/functions/functions.constants-firewall
+index d42189a..2ca9390 100644
+--- a/src/functions/functions.constants-firewall
++++ b/src/functions/functions.constants-firewall
+@@ -19,6 +19,10 @@
+ # #
+ ###############################################################################
+
++# Firewall file configuration
++FIREWALL_SETTINGS_DIR="/etc/firewall"
++FIREWALL_SETTINGS_FILE="${FIREWALL_SETTINGS_DIR}/settings"
++
+ # This variable is used to point to a directory
+ # in which the iptables ruleset will be generated.
+ IPTABLES_TMPDIR=
+@@ -32,78 +36,78 @@ FIREWALL_MACROS_DIRS="${FIREWALL_CONFIG_DIR}/macros"
+ FIREWALL_MACROS_DIRS="${FIREWALL_MACROS_DIRS} /usr/share/firewall/macros"
+
+ # List of parameters which are saved in the configuration file.
+-FIREWALL_CONFIG_PARAMS=""
++FIREWALL_SETTINGS=( "DEBUG" )
+
+ # Valid arguments in the rules file.
+ FIREWALL_RULES_CONFIG_PARAMS="src dst proto action sport dport in out"
+
+ # Define the default logging method (nflog or syslog).
+ FIREWALL_LOG_METHOD="nflog"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_METHOD"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_METHOD" )
+
+ # Set the default threshold for the nflog method.
+ FIREWALL_NFLOG_THRESHOLD=30
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_NFLOG_THRESHOLD"
++FIREWALL_SETTINGS+=( "FIREWALL_NFLOG_THRESHOLD" )
+
+ # Enable clamping MSS for braindead ISPs which filter ICMP packets.
+ FIREWALL_CLAMP_PATH_MTU="false"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_CLAMP_PATH_MTU"
++FIREWALL_SETTINGS+=( "FIREWALL_CLAMP_PATH_MTU" )
+
+ # Conntrack: Max. amount of simultaneous connections.
+ CONNTRACK_MAX_CONNECTIONS="16384"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} CONNTRACK_MAX_CONNECTIONS"
++FIREWALL_SETTINGS+=( "CONNTRACK_MAX_CONNECTIONS" )
+
+ # Conntrack: UDP timeout
+ CONNTRACK_UDP_TIMEOUT="60"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} CONNTRACK_UDP_TIMEOUT"
++FIREWALL_SETTINGS+=( "CONNTRACK_UDP_TIMEOUT" )
+
+ # Use SYN cookies or not
+ FIREWALL_SYN_COOKIES="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_SYN_COOKIES"
++FIREWALL_SETTINGS+=( "FIREWALL_SYN_COOKIES" )
+
+ # rp_filter
+ FIREWALL_RP_FILTER="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_RP_FILTER"
++FIREWALL_SETTINGS+=( "FIREWALL_RP_FILTER" )
+
+ # Log martians
+ FIREWALL_LOG_MARTIANS="false"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_MARTIANS"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_MARTIANS" )
+
+ # Accept ICMP redirects
+ FIREWALL_ACCEPT_ICMP_REDIRECTS="false"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_ACCEPT_ICMP_REDIRECTS"
++FIREWALL_SETTINGS+=( "FIREWALL_ACCEPT_ICMP_REDIRECTS" )
+
+ # ECN (Explicit Congestion Notification)
+ FIREWALL_USE_ECN="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_USE_ECN"
++FIREWALL_SETTINGS+=( "FIREWALL_USE_ECN" )
+
+ # Path MTU discovery
+ FIREWALL_PMTU_DISCOVERY="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_PMTU_DISCOVERY"
++FIREWALL_SETTINGS+=( "FIREWALL_PMTU_DISCOVERY" )
+
+ # Default TTL
+ FIREWALL_DEFAULT_TTL="64"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_DEFAULT_TTL"
++FIREWALL_SETTINGS+=( "FIREWALL_DEFAULT_TTL" )
+
+ # Log stealth scans
+ FIREWALL_LOG_STEALTH_SCANS="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_STEALTH_SCANS"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_STEALTH_SCANS" )
+
+ # Log packets with bad TCP flags
+ FIREWALL_LOG_BAD_TCP_FLAGS="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_BAD_TCP_FLAGS"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_BAD_TCP_FLAGS" )
+
+ # Log INVALID TCP packets
+ FIREWALL_LOG_INVALID_TCP="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_INVALID_TCP"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_INVALID_TCP" )
+
+ # Log INVALID UDP packets
+ FIREWALL_LOG_INVALID_UDP="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_INVALID_UDP"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_INVALID_UDP" )
+
+ # Log INVALID ICMP packets
+ FIREWALL_LOG_INVALID_ICMP="true"
+-FIREWALL_CONFIG_PARAMS="${FIREWALL_CONFIG_PARAMS} FIREWALL_LOG_INVALID_ICMP"
++FIREWALL_SETTINGS+=( "FIREWALL_LOG_INVALID_ICMP" )
+
+ FIREWALL_SUPPORTED_PROTOCOLS="tcp udp icmp igmp esp ah gre"
+ FIREWALL_PROTOCOLS_SUPPORTING_PORTS="tcp udp"
+diff --git a/src/functions/functions.settings b/src/functions/functions.settings
+index 69f4c23..5728e72 100644
+--- a/src/functions/functions.settings
++++ b/src/functions/functions.settings
+@@ -297,13 +297,13 @@ network_settings_list() {
+ }
+
+ firewall_settings_read() {
+- settings_read "${FIREWALL_SETTINGS_FILE}" "${FIREWALL_SETTINGS_PARAMS}"
++ settings_read "${FIREWALL_SETTINGS_FILE}" "${FIREWALL_SETTINGS[*]}"
+ }
+
+ firewall_settings_write() {
+- settings_write "${FIREWALL_SETTINGS_FILE}" "${FIREWALL_SETTINGS_PARAMS}"
++ settings_write "${FIREWALL_SETTINGS_FILE}" "${FIREWALL_SETTINGS[*]}"
+ }
+
+ firewall_settings_print() {
+- settings_print "${FIREWALL_SETTINGS_PARAMS}"
++ settings_print "${FIREWALL_SETTINGS[*]}"
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,80 @@
+From 12c8f41a0791a517d5cc7cd30bd566896891f092 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 22 Jun 2019 14:16:07 +0000
+Subject: [PATCH 123/304] firewall: Drop firewall-config command in favour of
+ "firewall settings"
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 -
+ src/firewall | 5 +++++
+ src/firewall-config | 29 -----------------------------
+ 3 files changed, 5 insertions(+), 30 deletions(-)
+ delete mode 100644 src/firewall-config
+
+diff --git a/Makefile.am b/Makefile.am
+index 0974ba8..4fe5068 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -114,7 +114,6 @@ dist_doc_DATA = \
+
+ dist_sbin_SCRIPTS = \
+ src/dhclient-script \
+- src/firewall-config \
+ src/firewall \
+ src/network
+
+diff --git a/src/firewall b/src/firewall
+index c47ac61..569f413 100644
+--- a/src/firewall
++++ b/src/firewall
+@@ -45,6 +45,11 @@ case "${action}" in
+ exit $?
+ ;;
+
++ settings)
++ firewall_cli_settings "$@"
++ exit $?
++ ;;
++
+ *)
+ firewall_cli "ipv6" "${action}" "$@"
+ ;;
+diff --git a/src/firewall-config b/src/firewall-config
+deleted file mode 100644
+index 53ec175..0000000
+--- a/src/firewall-config
++++ /dev/null
+@@ -1,29 +0,0 @@
+-#!/bin/bash
+-###############################################################################
+-# #
+-# IPFire.org - A linux based firewall #
+-# Copyright (C) 2012 IPFire Network Development Team #
+-# #
+-# 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, either version 3 of the License, or #
+-# (at your option) any later version. #
+-# #
+-# 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 <http://www.gnu.org/licenses/>. #
+-# #
+-###############################################################################
+-
+-. /usr/lib/network/functions
+-
+-# Read firewall settings
+-firewall_settings_read
+-
+-firewall_cli_settings "$@"
+-
+-exit ${EXIT_ERROR}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,30 @@
+From 038a7f3628f6b7648f89bb3ef6813e757fed6fec Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 23 Jun 2019 10:30:17 +0000
+Subject: [PATCH 124/304] wireless: Do not attempt DFS when reg domain is set
+ to world
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.wireless | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/functions/functions.wireless b/src/functions/functions.wireless
+index 733a356..860b2dd 100644
+--- a/src/functions/functions.wireless
++++ b/src/functions/functions.wireless
+@@ -555,6 +555,11 @@ wireless_supports_dfs() {
+ local device="${1}"
+ assert isset device
+
++ # DFS is not supported if wireless reg domain is set to world
++ if [ -n "${WIRELESS_REGULATORY_DOMAIN}" ] || [ "${WIRELESS_REGULATORY_DOMAIN}" = "00" ]; then
++ return ${EXIT_FALSE}
++ fi
++
+ local phy="$(device_get_phy "${device}")"
+ if ! isset phy; then
+ log ERROR "Could not determine PHY for ${device}"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,42 @@
+From 0d99f882ea0f8c4b1c55f7107067a0cb35fedfb3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 23 Jun 2019 10:33:48 +0000
+Subject: [PATCH 125/304] bird: Start service when needed and not already
+ running
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/functions/functions.bird | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/src/functions/functions.bird b/src/functions/functions.bird
+index 55d43b5..cbcb6e2 100644
+--- a/src/functions/functions.bird
++++ b/src/functions/functions.bird
+@@ -21,6 +21,10 @@
+
+ BIRD_CONF="/etc/bird.conf"
+
++bird_is_active() {
++ service_is_active "bird.service"
++}
++
+ bird_start() {
+ service_start "bird.service"
+ }
+@@ -41,7 +45,11 @@ bird_update() {
+ fi
+
+ # Reload bird
+- bird_reload
++ if bird_is_active; then
++ bird_reload
++ else
++ bird_start
++ fi
+ }
+
+ bird_generate_config() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,60 @@
+From 9515b03940a0fac2db3fff105638f49a53f85e7d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 23 Jun 2019 11:57:17 +0000
+Subject: [PATCH 126/304] ip-tunnel: Support setting MTU on tunnels
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/hooks/zones/ip-tunnel | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+diff --git a/src/hooks/zones/ip-tunnel b/src/hooks/zones/ip-tunnel
+index c4a4fb4..3c885cb 100644
+--- a/src/hooks/zones/ip-tunnel
++++ b/src/hooks/zones/ip-tunnel
+@@ -26,6 +26,7 @@ SUPPORTED_IP_TUNNEL_MODES="gre sit vti"
+ HOOK_SETTINGS=(
+ "MARK"
+ "MODE"
++ "MTU"
+ "PEER"
+ "LOCAL_ADDRESS"
+ )
+@@ -33,6 +34,9 @@ HOOK_SETTINGS=(
+ # Default mode of the tunnel
+ DEFAULT_MODE="gre"
+
++# Default MTU
++DEFAULT_MTU="1480"
++
+ hook_check_settings() {
+ assert isset MODE && assert isoneof MODE ${SUPPORTED_IP_TUNNEL_MODES}
+
+@@ -67,6 +71,16 @@ hook_parse_cmdline() {
+ fi
+ ;;
+
++ --mtu=*)
++ MTU="$(cli_get_val "${1}")"
++
++ # Validate MTU
++ if ! mtu_is_valid "ipv6" "${MTU}"; then
++ error "Invalid MTU: ${MTU}"
++ return ${EXIT_ERROR}
++ fi
++ ;;
++
+ --peer=*)
+ PEER="$(cli_get_val "${1}")"
+ ;;
+@@ -116,6 +130,7 @@ hook_up() {
+ if ! device_exists "${zone}"; then
+ ip_tunnel_add "${zone}" \
+ --mode="${MODE}" \
++ --mtu="${MTU}" \
+ --remote-address="${PEER}" \
+ --local-address="${LOCAL_ADDRESS}" \
+ --ikey="${MARK}" \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,33 @@
+From b41f1f866ef816e6ea7dd9e23e11e36a588ed611 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 23 Jun 2019 15:37:41 +0000
+Subject: [PATCH 127/304] firewall: Fix generating systemd file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 +--
+ src/systemd/{firewall.service => firewall.service.in} | 0
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+ rename src/systemd/{firewall.service => firewall.service.in} (100%)
+
+diff --git a/Makefile.am b/Makefile.am
+index 4fe5068..78da25f 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -374,8 +374,7 @@ INSTALL_DIRS += \
+ endif
+
+ EXTRA_DIST += \
+- src/systemd/firewall4.service.in \
+- src/systemd/firewall6.service.in \
++ src/systemd/firewall.service.in \
+ src/systemd/firewall-init.service.in \
+ src/systemd/network-init.service.in \
+ src/systemd/network@.service.in
+diff --git a/src/systemd/firewall.service b/src/systemd/firewall.service.in
+similarity index 100%
+rename from src/systemd/firewall.service
+rename to src/systemd/firewall.service.in
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,69 @@
+From dbe28a055de31302f0b8101e4e294394c6c2b63c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 21 Jun 2019 14:37:03 +0100
+Subject: [PATCH 128/304] Make generating man-pages optional
+
+Fixes: #11862
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 +++-
+ configure.ac | 12 +++++++++++-
+ 2 files changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 78da25f..b6ba5ac 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -480,11 +480,13 @@ MANPAGES_XML = $(patsubst %.txt,%.xml,$(MANPAGES_TXT))
+ .PHONY: man
+ man: $(MANPAGES) $(MANPAGES_HTML)
+
++if ENABLE_MANPAGES
+ man_MANS = \
+ $(MANPAGES)
++endif
+
+ CLEANFILES += \
+- $(man_MANS) \
++ $(MANPAGES) \
+ $(MANPAGES_HTML) \
+ $(MANPAGES_XML)
+
+diff --git a/configure.ac b/configure.ac
+index 340cfd6..37c17e3 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -123,6 +123,14 @@ AS_IF([test "x$enable_debug" = "xyes"], [
+ AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.])
+ ])
+
++# ------------------------------------------------------------------------------
++
++have_manpages=no
++AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-man-pages],
++ [do not install man pages]))
++AS_IF([test "x$enable_manpages" != xno], [have_manpages=yes])
++AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"])
++
+ # ------------------------------------------------------------------------------
+ AC_ARG_WITH([systemdsystemunitdir],
+ AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
+@@ -144,7 +152,7 @@ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
+ # ------------------------------------------------------------------------------
+
+ AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
+-if test -z "${ASCIIDOC}"; then
++if test "${have_manpages}" = "yes" && test -z "${ASCIIDOC}"; then
+ AC_MSG_ERROR([Required program 'asciidoc' not found])
+ fi
+
+@@ -167,4 +175,6 @@ AC_MSG_RESULT([
+
+ systemdsystemunitdir: $systemdsystemunitdir
+ udevdir: $udevdir
++
++ Generate man-pages: ${have_manpages}
+ ])
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,135 @@
+From 18bace574c15e966b8e3571cc00be287236162b5 Mon Sep 17 00:00:00 2001
+From: Jonatan Schlag <jonatan.schlag@ipfire.org>
+Date: Mon, 24 Jun 2019 13:30:14 +0200
+Subject: [PATCH 129/304] Add documentation for the IPsec VPN
+
+Signed-off-by: Jonatan Schlag <jonatan.schlag@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-vpn-ipsec.txt | 97 +++++++++++++++++++++++++++++++++++++++
+ man/network-vpn.txt | 5 ++
+ 2 files changed, 102 insertions(+)
+ create mode 100644 man/network-vpn-ipsec.txt
+
+diff --git a/man/network-vpn-ipsec.txt b/man/network-vpn-ipsec.txt
+new file mode 100644
+index 0000000..25347a8
+--- /dev/null
++++ b/man/network-vpn-ipsec.txt
+@@ -0,0 +1,97 @@
++= network-vpn-security-policies(8)
++
++== NAME
++network-ipsec - Configure IPsec VPN connections
++
++== SYNOPSIS
++[verse]
++'network vpn ipsec [new|destroy]' NAME...
++'network vpn ipsec' NAME COMMAND ...
++
++== DESCRIPTION
++With help of the 'vpn ipsec', it is possible to create, destroy
++and edit IPsec VPN connections.
++
++
++== COMMANDS
++The following commands are understood:
++
++'new NAME'::
++ A new IPsec VPN connection may be created with the 'new' command.
++ +
++ NAME does not allow any spaces.
++
++'destroy NAME'::
++ A IPsec VPN connection can be destroyed with this command.
++
++For all other commands, the name of the IPsec VPN connection needs to be passed first:
++
++'NAME show'::
++ Shows the configuration of the IPsec VPN connection
++
++'NAME authentication mode'::
++ Set the authentication mode out of the following available modes:
++ * psk
++
++'NAME authentication psk PSK'::
++ Set the pre-shared-key to PSK, only useful when the authentication mode is psk:
++
++include::include-color.txt[]
++
++include::include-description.txt[]
++
++'NAME down'::
++ Shutdown a etablished IPsec VPN connection
++
++'NAME inactivity-timeout TIME'::
++ Set the inactivity timeout with TIME in seconds or in the format hh:mm:ss
++
++'NAME local id ID'::
++ Specify the identity of the local system.
++ +
++ The ID must be in one of the following formats:
++ * IP address
++ * FQDN
++ * a string which starts with @
++
++'NAME local prefix [PREFIX-LIST|+PREFIX ...|-PREFIX ...]'::
++ Specify the subnets of the local system which should be made available to the remote peer.
++
++'NAME mode [transport|tunnel]'::
++ Set the mode of the IPsec VPN connection.
++
++'NAME peer PEER'::
++ Set the peer to which the IPsec VPN connection should be etablished.
++
++'NAME remote id ID'::
++ Specify the identity of the remote machine.
++ +
++ The ID must be in one of the following formats:
++ * IP address
++ * FQDN
++ * A string which starts with @
++
++'NAME remote prefix [PREFIX-LIST|+PREFIX ...|-PREFIX ...]'::
++ Specify the subnets which the remote side makes available to us.
++
++'NAME security-policy'::
++ Set the security policy which the connection uses.
++ +
++ See link:network-vpn-security-policies[8] for details.
++
++'NAME up'::
++ Establishes the IPsec VPN connection to the remote peer.
++
++'NAME zone'::
++ When you specify a zone of type ip-tunnel here the IPsec connection is established over a vti tunnel.
++ The remote and local prefixes are ignored. Imagine a fiber connection between this two machines, and how you would use it.
++ The IPsec VPN connection works in the same way. You must configure routes and IP addresses of the ip-tunnel hook manually.
++
++
++== AUTHORS
++Michael Tremer,
++Jonatan Schlag
++
++== SEE ALSO
++link:network[8],
++link:network-vpn[8]
+diff --git a/man/network-vpn.txt b/man/network-vpn.txt
+index 5a905db..be33606 100644
+--- a/man/network-vpn.txt
++++ b/man/network-vpn.txt
+@@ -19,6 +19,11 @@ The following commands are understood:
+ +
+ See link:network-vpn-security-policies[8] for details.
+
++'ipsec' ...::
++ Use this command to manage ipsec vpn connections.
++ +
++ See link:network-vpn-ipsec[8] for details.
++
+ == AUTHORS
+ Michael Tremer
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From 2612a6f4bb0bcc3e155425a653705146eb65d7cd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Jun 2019 13:28:01 +0100
+Subject: [PATCH 130/304] Makefile: Add network-vpn-ipsec(8)
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index b6ba5ac..a5ea123 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -460,6 +460,7 @@ MANPAGES = \
+ man/network-route-static.8 \
+ man/network-settings.8 \
+ man/network-vpn.8 \
++ man/network-vpn-ipsec.8 \
+ man/network-vpn-security-policies.8 \
+ man/network-zone.8 \
+ man/network-zone-bridge.8 \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,23 @@
+From 2cb783babd59716366984c8908e70285f23347f3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 24 Jun 2019 13:28:12 +0100
+Subject: [PATCH 131/304] security-policies: performance: Remove CBC ciphers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ config/vpn/security-policies/performance | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/config/vpn/security-policies/performance b/config/vpn/security-policies/performance
+index 9b8e943..b226d8d 100644
+--- a/config/vpn/security-policies/performance
++++ b/config/vpn/security-policies/performance
+@@ -1,4 +1,4 @@
+-CIPHERS="CHACHA20-POLY1305 AES128-GCM128 AES128-CBC"
++CIPHERS="CHACHA20-POLY1305 AES128-GCM128"
+ COMPRESSION="off"
+ GROUP_TYPES="ECP521 ECP384 ECP256 ECP224 ECP192 CURVE25519"
+ INTEGRITIES="SHA256"
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,65 @@
+From 27208caa363cad7c2250bdff5b99a9bc16a5ca91 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 2 Oct 2019 10:36:13 +0000
+Subject: [PATCH 132/304] IPsec: Add support for Curve448
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ config/vpn/security-policies/performance | 2 +-
+ config/vpn/security-policies/system | 2 +-
+ src/functions/functions.vpn-security-policies | 6 +++++-
+ 3 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/config/vpn/security-policies/performance b/config/vpn/security-policies/performance
+index b226d8d..209f43d 100644
+--- a/config/vpn/security-policies/performance
++++ b/config/vpn/security-policies/performance
+@@ -1,6 +1,6 @@
+ CIPHERS="CHACHA20-POLY1305 AES128-GCM128"
+ COMPRESSION="off"
+-GROUP_TYPES="ECP521 ECP384 ECP256 ECP224 ECP192 CURVE25519"
++GROUP_TYPES="CURVE25519 CURVE448 ECP521 ECP384 ECP256 ECP224 ECP192"
+ INTEGRITIES="SHA256"
+ PSEUDO_RANDOM_FUNCTIONS="SHA256"
+ KEY_EXCHANGE="ikev2"
+diff --git a/config/vpn/security-policies/system b/config/vpn/security-policies/system
+index db30e69..6ceb0c4 100644
+--- a/config/vpn/security-policies/system
++++ b/config/vpn/security-policies/system
+@@ -1,7 +1,7 @@
+ KEY_EXCHANGE="ikev2"
+ CIPHERS="CHACHA20-POLY1305 AES256-GCM128 AES256-CBC AES192-GCM128 AES192-CBC AES128-GCM128 AES128-CBC"
+ INTEGRITIES="SHA512 SHA384 SHA256"
+-GROUP_TYPES="CURVE25519 ECP521 ECP384 ECP256 ECP224 ECP192 MODP8192 MODP6144 MODP4096 MODP2048"
++GROUP_TYPES="CURVE25519 CURVE448 ECP521 ECP384 ECP256 ECP224 ECP192 MODP8192 MODP6144 MODP4096 MODP2048"
+ PSEUDO_RANDOM_FUNCTIONS="SHA512 SHA384 SHA256"
+ LIFETIME="28800"
+ PFS="on"
+diff --git a/src/functions/functions.vpn-security-policies b/src/functions/functions.vpn-security-policies
+index d1d720b..138e821 100644
+--- a/src/functions/functions.vpn-security-policies
++++ b/src/functions/functions.vpn-security-policies
+@@ -263,6 +263,9 @@ declare -A VPN_SUPPORTED_GROUP_TYPES=(
+
+ # Curve25519
+ [CURVE25519]="256 bit Elliptic Curve 25519"
++
++ # Curve448
++ [CURVE448]="224 bit Elliptic Curve 448"
+ )
+
+ declare -A GROUP_TYPE_TO_STRONGSWAN=(
+@@ -289,8 +292,9 @@ declare -A GROUP_TYPE_TO_STRONGSWAN=(
+ [ECP384BP]="ecp384bp"
+ [ECP512BP]="ecp512bp"
+
+- # Curve25519
++ # More Curves
+ [CURVE25519]="curve25519"
++ [CURVE448]="curve448"
+ )
+
+ cli_vpn_security_policies() {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,112 @@
+From ea4abb82bc6e613ddebd6235f792dd5bbbc469c9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 7 May 2020 20:30:03 +0100
+Subject: [PATCH 133/304] Disable copybreak
+
+Some network interface drivers employ a scheme known as "copybreak"
+in which they make a copy of a received skb if the size of the
+buffer is below a particular threshold, then return the original
+receive skb back to the pool. Since these drivers initially
+allocate a buffer size that is larger than the largest possible
+packet, this scheme returns that large buffer to the pool quickly,
+and uses a smaller one.
+
+The primary benefit of copybreak is better memory utilization. On
+systems where the data is ultimately going to be copied out to user
+space, the copybreak scheme is "low cost" because it has the side
+benefit of priming the cache for that later copy. But on a router
+that only touches the header fields of a received packet, the cost
+can be relatively higher. And on modern systems the memory savings
+is rarely an important consideration.
+
+Some of the drivers that employ copybreak make the feature
+configurable via a module parameter. This file disables copybreak
+in some of those drivers. Generally this results in an improvement
+in forwarding performance for traffic using these drivers.
+
+Fixes: #11930
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+Signed-off-by: Arne Fitzenreiter <arne_f@ipfire.org>
+---
+ Makefile.am | 6 +++++
+ src/modprobe.d/no-copybreak.conf | 44 ++++++++++++++++++++++++++++++++
+ 2 files changed, 50 insertions(+)
+ create mode 100644 src/modprobe.d/no-copybreak.conf
+
+diff --git a/Makefile.am b/Makefile.am
+index a5ea123..4aa7314 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -31,6 +31,7 @@ AUTOMAKE_OPTIONS = color-tests
+ configdir = $(sysconfdir)/network
+ bashcompletiondir= $(datadir)/bash-completion/completions
+ libexecdir = $(prefix)/lib
++modprobedir = $(prefix)/lib/modprobe.d
+ pkgconfigdir = $(libdir)/pkgconfig
+ pppdir = $(sysconfdir)/ppp
+ systemconfigdir = $(datadir)/network
+@@ -396,6 +397,11 @@ dist_sysctl_DATA = \
+
+ # ------------------------------------------------------------------------------
+
++dist_modprobe_DATA = \
++ src/modprobe.d/no-copybreak.conf
++
++# ------------------------------------------------------------------------------
++
+ dist_bashcompletion_SCRIPTS = \
+ src/bash-completion/network
+
+diff --git a/src/modprobe.d/no-copybreak.conf b/src/modprobe.d/no-copybreak.conf
+new file mode 100644
+index 0000000..97ea886
+--- /dev/null
++++ b/src/modprobe.d/no-copybreak.conf
+@@ -0,0 +1,44 @@
++#
++# Some network interface drivers employ a scheme known as "copybreak"
++# in which they make a copy of a received skb if the size of the
++# buffer is below a particular threshold, then return the original
++# receive skb back to the pool. Since these drivers initially
++# allocate a buffer size that is larger than the largest possible
++# packet, this scheme returns that large buffer to the pool quickly,
++# and uses a smaller one.
++#
++# The primary benefit of copybreak is better memory utilization. On
++# systems where the data is ultimately going to be copied out to user
++# space, the copybreak scheme is "low cost" because it has the side
++# benefit of priming the cache for that later copy. But on a router
++# that only touches the header fields of a received packet, the cost
++# can be relatively higher. And on modern systems the memory savings
++# is rarely an important consideration.
++#
++# Some of the drivers that employ copybreak make the feature
++# configurable via a module parameter. This file disables copybreak
++# in some of those drivers. Generally this results in an improvement
++# in forwarding performance for traffic using these drivers.
++#
++
++options 3c515 rx_copybreak=0
++options 3c59x rx_copybreak=0
++options bcm63xx copybreak=0
++options cxgb copybreak=0
++options e1000 copybreak=0
++options e1000e copybreak=0
++options epic100 rx_copybreak=0
++options fealnx rx_copybreak=0
++options hamachi rx_copybreak=0
++options ixgb copybreak=0
++options natsemi rx_copybreak=0
++options pch_gbe copybreak=0
++options pcnet32 rx_copybreak=0
++options sis190 rx_copybreak=0
++options sky2 copybreak=0
++options starfire rx_copybreak=0
++options sundance rx_copybreak=0
++options typhoon rx_copybreak=0
++options via-rhine rx_copybreak=0
++options via-velocity rx_copybreak=0
++options yellowfin rx_copybreak=0
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From ed993fc9d436da0788eca6f80374c9cd85b8bb9b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 22 Jan 2023 12:33:11 +0000
+Subject: [PATCH 134/304] configure: Check for libsystemd
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/configure.ac b/configure.ac
+index 37c17e3..f3a9c17 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -148,6 +148,7 @@ AM_CONDITIONAL(HAVE_UDEV, [test -n "$with_udevdir"])
+ # ------------------------------------------------------------------------------
+
+ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
++PKG_CHECK_MODULES([SYSTEMD], [libsystemd])
+
+ # ------------------------------------------------------------------------------
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,93 @@
+From 050f4ece8900b9212de57b3564381d82540323aa Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 22 Jan 2023 12:41:47 +0000
+Subject: [PATCH 135/304] Makefile: Add scaffolding for networkd
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ .gitignore | 1 +
+ Makefile.am | 18 ++++++++++++++++++
+ src/networkd/main.c | 23 +++++++++++++++++++++++
+ 3 files changed, 42 insertions(+)
+ create mode 100644 src/networkd/main.c
+
+diff --git a/.gitignore b/.gitignore
+index bb093d3..e3bae67 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -3,6 +3,7 @@
+ /config.*
+ /libtool
+ /missing
++/networkd
+ /src/functions/functions
+ /src/inetcalc
+ /src/libnetwork/libnetwork.pc
+diff --git a/Makefile.am b/Makefile.am
+index 4aa7314..64ad94d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -61,6 +61,7 @@ INSTALL_DIRS =
+ INSTALL_EXEC_HOOKS =
+ UNINSTALL_EXEC_HOOKS =
+ noinst_DATA =
++sbin_PROGRAMS =
+
+ AM_CPPFLAGS = \
+ $(OUR_CPPFLAGS) \
+@@ -299,6 +300,23 @@ EXTRA_DIST += \
+
+ # ------------------------------------------------------------------------------
+
++sbin_PROGRAMS += \
++ networkd
++
++dist_networkd_SOURCES = \
++ src/networkd/main.c
++
++networkd_CPPFLAGS = \
++ $(AM_CPPFLAGS)
++
++networkd_CFLAGS = \
++ $(AM_CFLAGS)
++
++networkd_LDFLAGS = \
++ $(AM_LDFLAGS)
++
++# ------------------------------------------------------------------------------
++
+ util_PROGRAMS = \
+ src/utils/network-phy-list-channels \
+ src/utils/network-phy-list-ciphers \
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+new file mode 100644
+index 0000000..14aafdd
+--- /dev/null
++++ b/src/networkd/main.c
+@@ -0,0 +1,23 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++int main(int argc, char** argv) {
++ return 0;
++}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,34 @@
+From 5d326bbb3f564cdb7031d80850bd3fe3c7565233 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:18:34 +0000
+Subject: [PATCH 136/304] networkd: Link against systemd
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 64ad94d..74b2fae 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -310,11 +310,15 @@ networkd_CPPFLAGS = \
+ $(AM_CPPFLAGS)
+
+ networkd_CFLAGS = \
+- $(AM_CFLAGS)
++ $(AM_CFLAGS) \
++ $(SYSTEMD_CFLAGS)
+
+ networkd_LDFLAGS = \
+ $(AM_LDFLAGS)
+
++networkd_LDADD = \
++ $(SYSTEMD_LIBS)
++
+ # ------------------------------------------------------------------------------
+
+ util_PROGRAMS = \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,47 @@
+From 26acbb4e03e3a44e6046884eab25f6c7e376c105 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:18:53 +0000
+Subject: [PATCH 137/304] networkd: Tell systemd about the daemon status
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/main.c | 24 +++++++++++++++++++++++-
+ 1 file changed, 23 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 14aafdd..2429ff5 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -18,6 +18,28 @@
+ # #
+ #############################################################################*/
+
++#include <errno.h>
++#include <stdlib.h>
++
++#include <systemd/sd-daemon.h>
++#include <systemd/sd-event.h>
++
+ int main(int argc, char** argv) {
+- return 0;
++ // XXX Drop privileges
++
++ // We are now ready to process any requests
++ sd_notify(0, "READY=1\n" "STATUS=Processing requests...");
++
++ // Run event loop
++ // XXX TODO
++
++ // Let systemd know that we are shutting down
++ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
++
++ return EXIT_SUCCESS;
++
++ERROR:
++ sd_notifyf(0, "ERRNO=%i", errno);
++
++ return EXIT_FAILURE;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,170 @@
+From 112358f3e2d40e148a259b1a7ab466c99947ed4b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:29:09 +0000
+Subject: [PATCH 138/304] networkd: Create a simple daemon class
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/daemon.c | 56 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/daemon.h | 31 ++++++++++++++++++++++++
+ src/networkd/main.c | 16 ++++++++++++-
+ 4 files changed, 104 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/daemon.c
+ create mode 100644 src/networkd/daemon.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 74b2fae..40d900e 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -304,6 +304,8 @@ sbin_PROGRAMS += \
+ networkd
+
+ dist_networkd_SOURCES = \
++ src/networkd/daemon.c \
++ src/networkd/daemon.h \
+ src/networkd/main.c
+
+ networkd_CPPFLAGS = \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+new file mode 100644
+index 0000000..f635f38
+--- /dev/null
++++ b/src/networkd/daemon.c
+@@ -0,0 +1,56 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++
++#include "daemon.h"
++
++struct nw_daemon {
++ int nrefs;
++};
++
++int nw_daemon_create(struct nw_daemon** daemon) {
++ struct nw_daemon* d = calloc(1, sizeof(*d));
++ if (!d)
++ return 1;
++
++ // Initialize reference counter
++ d->nrefs = 1;
++
++ return 0;
++}
++
++static void nw_daemon_free(struct nw_daemon* daemon) {
++ free(daemon);
++}
++
++struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon) {
++ daemon->nrefs++;
++
++ return daemon;
++}
++
++struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon) {
++ if (--daemon->nrefs > 0)
++ return daemon;
++
++ nw_daemon_free(daemon);
++ return NULL;
++}
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+new file mode 100644
+index 0000000..309794e
+--- /dev/null
++++ b/src/networkd/daemon.h
+@@ -0,0 +1,31 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_DAEMON_H
++#define NETWORKD_DAEMON_H
++
++struct nw_daemon;
++
++int nw_daemon_create(struct nw_daemon** daemon);
++
++struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon);
++struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon);
++
++#endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 2429ff5..48fb2ae 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -24,9 +24,19 @@
+ #include <systemd/sd-daemon.h>
+ #include <systemd/sd-event.h>
+
++#include "daemon.h"
++
+ int main(int argc, char** argv) {
++ struct nw_daemon* daemon = NULL;
++ int r;
++
+ // XXX Drop privileges
+
++ // Create the daemon
++ r = nw_daemon_create(&daemon);
++ if (r)
++ goto ERROR;
++
+ // We are now ready to process any requests
+ sd_notify(0, "READY=1\n" "STATUS=Processing requests...");
+
+@@ -36,10 +46,14 @@ int main(int argc, char** argv) {
+ // Let systemd know that we are shutting down
+ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
+
+- return EXIT_SUCCESS;
++ goto CLEANUP;
+
+ ERROR:
+ sd_notifyf(0, "ERRNO=%i", errno);
+
++CLEANUP:
++ if (daemon)
++ nw_daemon_unref(daemon);
++
+ return EXIT_FAILURE;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,147 @@
+From c7e1b5db6903099797cf700516706a827ae9cc3e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:49:22 +0000
+Subject: [PATCH 139/304] networkd: Create an event loop
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 63 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/daemon.h | 2 ++
+ src/networkd/main.c | 7 ++---
+ 3 files changed, 69 insertions(+), 3 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index f635f38..25fdbdb 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -20,13 +20,44 @@
+
+ #include <stdlib.h>
+
++#include <systemd/sd-event.h>
++
+ #include "daemon.h"
+
+ struct nw_daemon {
+ int nrefs;
++
++ // Event Loop
++ sd_event* loop;
+ };
+
++static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
++ int r;
++
++ // Fetch a reference to the default event loop
++ r = sd_event_default(&daemon->loop);
++ if (r < 0) {
++ //ERROR("Could not setup event loop: %m\n");
++ return 1;
++ }
++
++ return 0;
++}
++
++static int nw_daemon_setup(struct nw_daemon* daemon) {
++ int r;
++
++ // Setup the event loop
++ r = nw_daemon_setup_loop(daemon);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ int nw_daemon_create(struct nw_daemon** daemon) {
++ int r;
++
+ struct nw_daemon* d = calloc(1, sizeof(*d));
+ if (!d)
+ return 1;
+@@ -34,10 +65,26 @@ int nw_daemon_create(struct nw_daemon** daemon) {
+ // Initialize reference counter
+ d->nrefs = 1;
+
++ // Setup the daemon
++ r = nw_daemon_setup(d);
++ if (r)
++ goto ERROR;
++
++ // Set the reference
++ *daemon = d;
++
+ return 0;
++
++ERROR:
++ nw_daemon_unref(d);
++
++ return r;
+ }
+
+ static void nw_daemon_free(struct nw_daemon* daemon) {
++ if (daemon->loop)
++ sd_event_unref(daemon->loop);
++
+ free(daemon);
+ }
+
+@@ -54,3 +101,19 @@ struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon) {
+ nw_daemon_free(daemon);
+ return NULL;
+ }
++
++/*
++ This function contains the main loop of the daemon...
++*/
++int nw_daemon_run(struct nw_daemon* daemon) {
++ int r;
++
++ // Launch the event loop
++ r = sd_event_loop(daemon->loop);
++ if (r) {
++ //ERROR("Could not run the event loop: %m\n");
++ return r;
++ }
++
++ return 0;
++}
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 309794e..215972d 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -28,4 +28,6 @@ int nw_daemon_create(struct nw_daemon** daemon);
+ struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon);
+ struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon);
+
++int nw_daemon_run(struct nw_daemon* daemon);
++
+ #endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 48fb2ae..80123ad 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -22,7 +22,6 @@
+ #include <stdlib.h>
+
+ #include <systemd/sd-daemon.h>
+-#include <systemd/sd-event.h>
+
+ #include "daemon.h"
+
+@@ -40,8 +39,10 @@ int main(int argc, char** argv) {
+ // We are now ready to process any requests
+ sd_notify(0, "READY=1\n" "STATUS=Processing requests...");
+
+- // Run event loop
+- // XXX TODO
++ // Run the daemon
++ r = nw_daemon_run(daemon);
++ if (r)
++ goto ERROR;
+
+ // Let systemd know that we are shutting down
+ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,31 @@
+From 3b2316e448ad4353964779b4c31edfe20b96ea4f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:53:19 +0000
+Subject: [PATCH 140/304] networkd: Enable the service watchdog
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 25fdbdb..c6acefe 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -41,6 +41,13 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ return 1;
+ }
+
++ // Enable the watchdog
++ r = sd_event_set_watchdog(daemon->loop, 1);
++ if (r < 0) {
++ //ERROR("Could not activate watchdog: %m\n");
++ return 1;
++ }
++
+ return 0;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,104 @@
+From c251a9ddf0771b85af69f226e886bdd511e2353d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 21:57:32 +0000
+Subject: [PATCH 141/304] networkd: Add some very simple logging
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ src/networkd/daemon.c | 7 ++++---
+ src/networkd/logging.h | 32 ++++++++++++++++++++++++++++++++
+ 3 files changed, 37 insertions(+), 3 deletions(-)
+ create mode 100644 src/networkd/logging.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 40d900e..abba646 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -306,6 +306,7 @@ sbin_PROGRAMS += \
+ dist_networkd_SOURCES = \
+ src/networkd/daemon.c \
+ src/networkd/daemon.h \
++ src/networkd/logging.h \
+ src/networkd/main.c
+
+ networkd_CPPFLAGS = \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index c6acefe..4d21bb6 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -23,6 +23,7 @@
+ #include <systemd/sd-event.h>
+
+ #include "daemon.h"
++#include "logging.h"
+
+ struct nw_daemon {
+ int nrefs;
+@@ -37,14 +38,14 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ // Fetch a reference to the default event loop
+ r = sd_event_default(&daemon->loop);
+ if (r < 0) {
+- //ERROR("Could not setup event loop: %m\n");
++ ERROR("Could not setup event loop: %m\n");
+ return 1;
+ }
+
+ // Enable the watchdog
+ r = sd_event_set_watchdog(daemon->loop, 1);
+ if (r < 0) {
+- //ERROR("Could not activate watchdog: %m\n");
++ ERROR("Could not activate watchdog: %m\n");
+ return 1;
+ }
+
+@@ -118,7 +119,7 @@ int nw_daemon_run(struct nw_daemon* daemon) {
+ // Launch the event loop
+ r = sd_event_loop(daemon->loop);
+ if (r) {
+- //ERROR("Could not run the event loop: %m\n");
++ ERROR("Could not run the event loop: %m\n");
+ return r;
+ }
+
+diff --git a/src/networkd/logging.h b/src/networkd/logging.h
+new file mode 100644
+index 0000000..9d51f21
+--- /dev/null
++++ b/src/networkd/logging.h
+@@ -0,0 +1,32 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_LOGGING_H
++#define NETWORKD_LOGGING_H
++
++#include <stdio.h>
++
++/*
++ This is just something simple which will work for now...
++*/
++#define ERROR(...) fprintf(stderr, __VA_ARGS__)
++#define DEBUG(...) printf(__VA_ARGS__)
++
++#endif /* NETWORKD_LOGGING_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,90 @@
+From 025f60f1f2f22db43edd4bb52b6cff48ceb718a0 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 22:24:53 +0000
+Subject: [PATCH 142/304] networkd: Register SIGTERM/SIGINT/SIGHUP
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 46 +++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 44 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 4d21bb6..483252d 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -18,7 +18,9 @@
+ # #
+ #############################################################################*/
+
++#include <errno.h>
+ #include <stdlib.h>
++#include <string.h>
+
+ #include <systemd/sd-event.h>
+
+@@ -32,20 +34,60 @@ struct nw_daemon {
+ sd_event* loop;
+ };
+
++static int nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
++ void* data) {
++ DEBUG("Received signal to terminate...\n");
++
++ return sd_event_exit(sd_event_source_get_event(source), 0);
++}
++
++static int nw_daemon_reload(sd_event_source* source, const struct signalfd_siginfo* si,
++ void* data) {
++ DEBUG("Received signal to reload...\n");
++
++ // TODO
++
++ return 0;
++}
++
+ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ int r;
+
+ // Fetch a reference to the default event loop
+ r = sd_event_default(&daemon->loop);
+ if (r < 0) {
+- ERROR("Could not setup event loop: %m\n");
++ ERROR("Could not setup event loop: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Enable the watchdog
+ r = sd_event_set_watchdog(daemon->loop, 1);
+ if (r < 0) {
+- ERROR("Could not activate watchdog: %m\n");
++ ERROR("Could not activate watchdog: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Listen for SIGTERM
++ r = sd_event_add_signal(daemon->loop, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK,
++ nw_daemon_terminate, daemon);
++ if (r < 0) {
++ ERROR("Could not register handling SIGTERM: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Listen for SIGINT
++ r = sd_event_add_signal(daemon->loop, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK,
++ nw_daemon_terminate, daemon);
++ if (r < 0) {
++ ERROR("Could not register handling SIGINT: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Listen for SIGHUP
++ r = sd_event_add_signal(daemon->loop, NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK,
++ nw_daemon_reload, daemon);
++ if (r < 0) {
++ ERROR("Could not register handling SIGHUP: %s\n", strerror(-r));
+ return 1;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,93 @@
+From 27a3a5969dcc76cc7af7b0dd491703ee2c8baa70 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 22:29:05 +0000
+Subject: [PATCH 143/304] networkd: Add scaffolding to reload the daemon
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 23 +++++++++++++++++------
+ src/networkd/daemon.h | 2 ++
+ 2 files changed, 19 insertions(+), 6 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 483252d..98fb5bd 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -34,18 +34,21 @@ struct nw_daemon {
+ sd_event* loop;
+ };
+
+-static int nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
++static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+ void* data) {
+ DEBUG("Received signal to terminate...\n");
+
+ return sd_event_exit(sd_event_source_get_event(source), 0);
+ }
+
+-static int nw_daemon_reload(sd_event_source* source, const struct signalfd_siginfo* si,
++static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_siginfo* si,
+ void* data) {
++ struct nw_daemon* daemon = (struct nw_daemon*)daemon;
++
+ DEBUG("Received signal to reload...\n");
+
+- // TODO
++ // Reload the daemon
++ nw_daemon_reload(daemon);
+
+ return 0;
+ }
+@@ -69,7 +72,7 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+
+ // Listen for SIGTERM
+ r = sd_event_add_signal(daemon->loop, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK,
+- nw_daemon_terminate, daemon);
++ __nw_daemon_terminate, daemon);
+ if (r < 0) {
+ ERROR("Could not register handling SIGTERM: %s\n", strerror(-r));
+ return 1;
+@@ -77,7 +80,7 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+
+ // Listen for SIGINT
+ r = sd_event_add_signal(daemon->loop, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK,
+- nw_daemon_terminate, daemon);
++ __nw_daemon_terminate, daemon);
+ if (r < 0) {
+ ERROR("Could not register handling SIGINT: %s\n", strerror(-r));
+ return 1;
+@@ -85,7 +88,7 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+
+ // Listen for SIGHUP
+ r = sd_event_add_signal(daemon->loop, NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK,
+- nw_daemon_reload, daemon);
++ __nw_daemon_reload, daemon);
+ if (r < 0) {
+ ERROR("Could not register handling SIGHUP: %s\n", strerror(-r));
+ return 1;
+@@ -167,3 +170,11 @@ int nw_daemon_run(struct nw_daemon* daemon) {
+
+ return 0;
+ }
++
++int nw_daemon_reload(struct nw_daemon* daemon) {
++ DEBUG("Reloading daemon...\n");
++
++ // XXX TODO
++
++ return 0;
++}
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 215972d..5b14ef3 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -30,4 +30,6 @@ struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon);
+
+ int nw_daemon_run(struct nw_daemon* daemon);
+
++int nw_daemon_reload(struct nw_daemon* daemon);
++
+ #endif /* NETWORKD_DAEMON_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 374d06ada7c1a3381aa08496abc7c9f173fd8d2f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 29 Jan 2023 22:52:37 +0000
+Subject: [PATCH 144/304] configure: Enable system extensions to define
+ _GNU_SOURCE
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index f3a9c17..b820794 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -65,6 +65,8 @@ AC_PROG_CC_C99
+ AC_PROG_CC_C_O
+ AC_PROG_GCC_TRADITIONAL
+
++AC_USE_SYSTEM_EXTENSIONS
++
+ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
+ -pipe \
+ -Wall \
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,255 @@
+From 09a6af1750714828bf109e17882ac0e7d0a6c738 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 02:08:50 +0000
+Subject: [PATCH 145/304] networkd: Add scaffolding to connect to dbus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/bus.c | 131 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/bus.h | 35 +++++++++++
+ src/networkd/daemon.c | 12 ++++
+ 4 files changed, 180 insertions(+)
+ create mode 100644 src/networkd/bus.c
+ create mode 100644 src/networkd/bus.h
+
+diff --git a/Makefile.am b/Makefile.am
+index abba646..60a1bcd 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -304,6 +304,8 @@ sbin_PROGRAMS += \
+ networkd
+
+ dist_networkd_SOURCES = \
++ src/networkd/bus.c \
++ src/networkd/bus.h \
+ src/networkd/daemon.c \
+ src/networkd/daemon.h \
+ src/networkd/logging.h \
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+new file mode 100644
+index 0000000..95862d2
+--- /dev/null
++++ b/src/networkd/bus.c
+@@ -0,0 +1,131 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++#include <string.h>
++
++#include <systemd/sd-bus.h>
++#include <systemd/sd-event.h>
++
++#include "bus.h"
++#include "daemon.h"
++#include "logging.h"
++
++static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* ret_error) {
++ return 1;
++}
++
++static const sd_bus_vtable daemon_vtable[] = {
++ SD_BUS_VTABLE_START(0),
++ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
++ nw_bus_daemon_reload, SD_BUS_VTABLE_UNPRIVILEGED),
++ SD_BUS_VTABLE_END,
++};
++
++int nw_bus_connect(sd_bus* bus, sd_event* loop) {
++ int r;
++
++ // Create a bus object
++ r = sd_bus_new(&bus);
++ if (r < 0) {
++ ERROR("Could not allocate a bus object: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Set description
++ r = sd_bus_set_description(bus, NETWORKD_BUS_DESCRIPTION);
++ if (r < 0) {
++ ERROR("Could not set bus description: %s\n", strerror(-r));
++ return 1;
++ }
++
++ const char* address = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
++ if (!address)
++ address = DEFAULT_SYSTEM_BUS_ADDRESS;
++
++ // Set bus address
++ r = sd_bus_set_address(bus, address);
++ if (r < 0) {
++ ERROR("Could not set bus address: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Set bus client
++ r = sd_bus_set_bus_client(bus, 1);
++ if (r < 0) {
++ ERROR("Could not set bus client: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Request some credentials for all messages
++ r = sd_bus_negotiate_creds(bus, 1,
++ SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
++ if (r < 0) {
++ ERROR("Could not negotiate creds: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Automatically bind when the socket is available
++ r = sd_bus_set_watch_bind(bus, 1);
++ if (r < 0) {
++ ERROR("Could not watch socket: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Emit a connected signal when we are connected
++ r = sd_bus_set_connected_signal(bus, 1);
++ if (r < 0) {
++ ERROR("Could not enable sending a connect signal: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Connect to the bus
++ r = sd_bus_start(bus);
++ if (r < 0) {
++ ERROR("Could not connect to bus: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Register any functions
++ r = sd_bus_add_object_vtable(bus, NULL,
++ NETWORKD_BUS_OBJECT_PATH, NETWORKD_BUS_INTERFACE_NAME, daemon_vtable, NULL);
++ if (r < 0) {
++ ERROR("Could not add object vtable: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Request interface name
++ // XXX Should this be async?
++ // XXX How do we get the actual error message from dbus?
++ r = sd_bus_request_name(bus, NETWORKD_BUS_INTERFACE_NAME, 0);
++ if (r < 0) {
++ ERROR("Could not request bus name: %s\n", strerror(-r));
++ return 1;
++ }
++
++ // Attach the event loop
++ r = sd_bus_attach_event(bus, loop, 0);
++ if (r < 0) {
++ ERROR("Could not attach bus to event loop: %s\n", strerror(-r));
++ return 1;
++ }
++
++ return 0;
++}
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+new file mode 100644
+index 0000000..90c1556
+--- /dev/null
++++ b/src/networkd/bus.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_BUS_H
++#define NETWORKD_BUS_H
++
++#define NETWORKD_BUS_DESCRIPTION "networkd"
++#define NETWORKD_BUS_OBJECT_PATH "/org/ipfire/network1"
++#define NETWORKD_BUS_INTERFACE_NAME "org.ipfire.network1"
++
++#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket"
++
++#include <systemd/sd-bus.h>
++#include <systemd/sd-event.h>
++
++int nw_bus_connect(sd_bus* bus, sd_event* loop);
++
++#endif /* NETWORKD_BUS_H */
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 98fb5bd..3aee8ca 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -22,8 +22,10 @@
+ #include <stdlib.h>
+ #include <string.h>
+
++#include <systemd/sd-bus.h>
+ #include <systemd/sd-event.h>
+
++#include "bus.h"
+ #include "daemon.h"
+ #include "logging.h"
+
+@@ -32,6 +34,9 @@ struct nw_daemon {
+
+ // Event Loop
+ sd_event* loop;
++
++ // DBus Connection
++ sd_bus* bus;
+ };
+
+ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+@@ -105,6 +110,11 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Connect to the system bus
++ r = nw_bus_connect(daemon->bus, daemon->loop);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+@@ -135,6 +145,8 @@ ERROR:
+ }
+
+ static void nw_daemon_free(struct nw_daemon* daemon) {
++ if (daemon->bus)
++ sd_bus_unref(daemon->bus);
+ if (daemon->loop)
+ sd_event_unref(daemon->loop);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,131 @@
+From 6b666d6221c5797f78fa4942561c931609d8f656 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 02:16:06 +0000
+Subject: [PATCH 146/304] networkd: Add scaffolding for config objects
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/config.c | 58 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/config.h | 31 +++++++++++++++++++++++
+ 3 files changed, 91 insertions(+)
+ create mode 100644 src/networkd/config.c
+ create mode 100644 src/networkd/config.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 60a1bcd..50a6034 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -306,6 +306,8 @@ sbin_PROGRAMS += \
+ dist_networkd_SOURCES = \
+ src/networkd/bus.c \
+ src/networkd/bus.h \
++ src/networkd/config.c \
++ src/networkd/config.h \
+ src/networkd/daemon.c \
+ src/networkd/daemon.h \
+ src/networkd/logging.h \
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+new file mode 100644
+index 0000000..d18fb12
+--- /dev/null
++++ b/src/networkd/config.c
+@@ -0,0 +1,58 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++
++#include "config.h"
++
++struct nw_config {
++ int nrefs;
++};
++
++static void nw_config_free(struct nw_config* config) {
++ free(config);
++}
++
++int nw_config_create(struct nw_config** config) {
++ struct nw_config* c = calloc(1, sizeof(*c));
++ if (!c)
++ return 1;
++
++ // Initialize reference counter
++ c->nrefs = 1;
++
++ *config = c;
++
++ return 0;
++}
++
++struct nw_config* nw_config_ref(struct nw_config* config) {
++ config->nrefs++;
++
++ return config;
++}
++
++struct nw_config* nw_config_unref(struct nw_config* config) {
++ if (--config->nrefs > 0)
++ return config;
++
++ nw_config_free(config);
++ return NULL;
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+new file mode 100644
+index 0000000..a8aed7f
+--- /dev/null
++++ b/src/networkd/config.h
+@@ -0,0 +1,31 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_CONFIG_H
++#define NETWORKD_CONFIG_H
++
++struct nw_config;
++
++int nw_config_create(struct nw_config** config);
++
++struct nw_config* nw_config_ref(struct nw_config* config);
++struct nw_config* nw_config_unref(struct nw_config* config);
++
++#endif /* NETWORKD_CONFIG_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From c81a6335fd0e1912fa3e8268ba85b9cd048cf243 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 02:26:41 +0000
+Subject: [PATCH 147/304] networkd: Add scaffolding to read configuration files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 50 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/config.h | 3 +++
+ 2 files changed, 53 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index d18fb12..e808531 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -18,9 +18,11 @@
+ # #
+ #############################################################################*/
+
++#include <stdio.h>
+ #include <stdlib.h>
+
+ #include "config.h"
++#include "logging.h"
+
+ struct nw_config {
+ int nrefs;
+@@ -56,3 +58,51 @@ struct nw_config* nw_config_unref(struct nw_config* config) {
+ nw_config_free(config);
+ return NULL;
+ }
++
++static int nw_config_parse(struct nw_config* config, FILE* f) {
++ // XXX TODO
++
++ return 0;
++}
++
++int nw_config_readf(struct nw_config** config, FILE* f) {
++ int r;
++
++ // Create a new config object
++ r = nw_config_create(config);
++ if (r)
++ return r;
++
++ // Parse the configuration
++ r = nw_config_parse(*config, f);
++ if (r)
++ goto ERROR;
++
++ return 0;
++
++ERROR:
++ nw_config_free(*config);
++ return r;
++}
++
++int nw_config_read(struct nw_config** config, const char* path) {
++ FILE* f = NULL;
++ int r;
++
++ // Open the file
++ f = fopen(path, "r");
++ if (!f) {
++ ERROR("Could not read configuration file %s: %m\n", path);
++ r = 1;
++ goto ERROR;
++ }
++
++ // Read from file
++ r = nw_config_readf(config, f);
++
++ERROR:
++ if (f)
++ fclose(f);
++
++ return r;
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index a8aed7f..559ba94 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -28,4 +28,7 @@ int nw_config_create(struct nw_config** config);
+ struct nw_config* nw_config_ref(struct nw_config* config);
+ struct nw_config* nw_config_unref(struct nw_config* config);
+
++int nw_config_readf(struct nw_config** config, FILE* f);
++int nw_config_read(struct nw_config** config, const char* path);
++
+ #endif /* NETWORKD_CONFIG_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,283 @@
+From 4237caa2ed47385e3471822348d908846c745b29 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:04:01 +0000
+Subject: [PATCH 148/304] networkd: Implement setting configuration values
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 +-
+ src/networkd/config.c | 114 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/config.h | 7 +++
+ src/networkd/string.h | 76 ++++++++++++++++++++++++++++
+ 4 files changed, 199 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/string.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 50a6034..1b18f62 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -311,7 +311,8 @@ dist_networkd_SOURCES = \
+ src/networkd/daemon.c \
+ src/networkd/daemon.h \
+ src/networkd/logging.h \
+- src/networkd/main.c
++ src/networkd/main.c \
++ src/networkd/string.h
+
+ networkd_CPPFLAGS = \
+ $(AM_CPPFLAGS)
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index e808531..72a0cfb 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -18,17 +18,72 @@
+ # #
+ #############################################################################*/
+
++#include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <string.h>
++#include <sys/queue.h>
+
+ #include "config.h"
+ #include "logging.h"
++#include "string.h"
++
++struct nw_config_entry {
++ STAILQ_ENTRY(nw_config_entry) nodes;
++
++ char key[NETWORK_CONFIG_KEY_MAX_LENGTH];
++ char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
++};
+
+ struct nw_config {
+ int nrefs;
++
++ STAILQ_HEAD(entries, nw_config_entry) entries;
+ };
+
++static void nw_config_entry_free(struct nw_config_entry* entry) {
++ free(entry);
++}
++
++static struct nw_config_entry* nw_config_entry_create(
++ struct nw_config* config, const char* key) {
++ int r;
++
++ // Check input value
++ if (!key) {
++ errno = EINVAL;
++ return NULL;
++ }
++
++ // Allocate a new object
++ struct nw_config_entry* entry = calloc(1, sizeof(*entry));
++ if (!entry)
++ return NULL;
++
++ // Store the key
++ r = nw_string_set(entry->key, key);
++ if (r)
++ goto ERROR;
++
++ // Append the new entry
++ STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
++
++ERROR:
++ nw_config_entry_free(entry);
++ return NULL;
++}
++
+ static void nw_config_free(struct nw_config* config) {
++ struct nw_config_entry* entry = NULL;
++
++ while (!STAILQ_EMPTY(&config->entries)) {
++ entry = STAILQ_FIRST(&config->entries);
++ STAILQ_REMOVE_HEAD(&config->entries, nodes);
++
++ // Free the entry
++ nw_config_entry_free(entry);
++ }
++
+ free(config);
+ }
+
+@@ -40,6 +95,9 @@ int nw_config_create(struct nw_config** config) {
+ // Initialize reference counter
+ c->nrefs = 1;
+
++ // Initialise entries
++ STAILQ_INIT(&c->entries);
++
+ *config = c;
+
+ return 0;
+@@ -106,3 +164,59 @@ ERROR:
+
+ return r;
+ }
++
++static struct nw_config_entry* nw_config_find(struct nw_config* config, const char* key) {
++ struct nw_config_entry* entry = NULL;
++
++ STAILQ_FOREACH(entry, &config->entries, nodes) {
++ // Key must match
++ if (strcmp(entry->key, key) != 0)
++ continue;
++
++ // Match!
++ return entry;
++ }
++
++ // No match
++ return NULL;
++}
++
++int nw_config_del(struct nw_config* config, const char* key) {
++ struct nw_config_entry* entry = NULL;
++
++ // Find an entry matching the key
++ entry = nw_config_find(config, key);
++
++ // If there is no entry, there is nothing to do
++ if (!entry)
++ return 0;
++
++ // Otherwise remove the object
++ STAILQ_REMOVE(&config->entries, entry, nw_config_entry, nodes);
++
++ // Free the entry
++ nw_config_entry_free(entry);
++
++ return 0;
++}
++
++int nw_config_set(struct nw_config* config, const char* key, const char* value) {
++ struct nw_config_entry* entry = NULL;
++
++ // Delete the entry if val is NULL
++ if (!value)
++ return nw_config_del(config, key);
++
++ // Find any existing entries
++ entry = nw_config_find(config, key);
++
++ // Create a new entry if it doesn't exist, yet
++ if (!entry) {
++ entry = nw_config_entry_create(config, key);
++ if (!entry)
++ return 1;
++ }
++
++ // Store the new value
++ return nw_string_set(entry->value, value);
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 559ba94..a793229 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -21,6 +21,9 @@
+ #ifndef NETWORKD_CONFIG_H
+ #define NETWORKD_CONFIG_H
+
++#define NETWORK_CONFIG_KEY_MAX_LENGTH 128
++#define NETWORK_CONFIG_VALUE_MAX_LENGTH 2048
++
+ struct nw_config;
+
+ int nw_config_create(struct nw_config** config);
+@@ -31,4 +34,8 @@ struct nw_config* nw_config_unref(struct nw_config* config);
+ int nw_config_readf(struct nw_config** config, FILE* f);
+ int nw_config_read(struct nw_config** config, const char* path);
+
++int nw_config_del(struct nw_config* config, const char* key);
++
++int nw_config_set(struct nw_config* config, const char* key, const char* value);
++
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+new file mode 100644
+index 0000000..3ac4846
+--- /dev/null
++++ b/src/networkd/string.h
+@@ -0,0 +1,76 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_STRING_H
++#define NETWORKD_STRING_H
++
++#include <stdarg.h>
++
++static inline int __nw_string_vformat(char* s, const size_t length,
++ const char* format, va_list args) {
++ // Write string to buffer
++ const ssize_t required = vsnprintf(s, length, format, args);
++
++ // Catch any errors
++ if (required < 0)
++ return 1;
++
++ // Check if the entire string could be written
++ if ((size_t)required >= length) {
++ errno = ENOMEM;
++ return 1;
++ }
++
++ // Success
++ return 0;
++}
++
++#define nw_string_format(s, format, ...) \
++ __nw_string_format(s, sizeof(s), format, __VA_ARGS__)
++
++static inline int __nw_string_format(char* s, const size_t length,
++ const char* format, ...) {
++ va_list args;
++ int r;
++
++ // Call __nw_string_vformat
++ va_start(args, format);
++ r = __nw_string_vformat(s, length, format, args);
++ va_end(args);
++
++ return r;
++}
++
++#define nw_string_set(s, value) __nw_string_set(s, sizeof(s), value)
++
++static inline int __nw_string_set(char* s, const size_t length, const char* value) {
++ // If value is NULL, we will overwrite the buffer with just zeros
++ if (!value) {
++ for (unsigned int i = 0; i < length; i++)
++ s[i] = '\0';
++
++ return 0;
++ }
++
++ // Otherwise just copy
++ return __nw_string_format(s, length, "%s", value);
++}
++
++#endif /* NETWORKD_STRING_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,55 @@
+From d39683a626fd7f5fe2aea15365a27b3f7727ad0b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:09:57 +0000
+Subject: [PATCH 149/304] networkd: Implement reading configuration values
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 21 +++++++++++++++++++++
+ src/networkd/config.h | 3 +++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 72a0cfb..a3db7c6 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -220,3 +220,24 @@ int nw_config_set(struct nw_config* config, const char* key, const char* value)
+ // Store the new value
+ return nw_string_set(entry->value, value);
+ }
++
++const char* nw_config_get(struct nw_config* config, const char* key) {
++ struct nw_config_entry* entry = nw_config_find(config, key);
++
++ // Return the value if found and set
++ if (entry && *entry->value)
++ return entry->value;
++
++ // Otherwise return NULL
++ return NULL;
++}
++
++unsigned int nw_config_get_unsigned_int(struct nw_config* config, const char* key) {
++ const char* value = nw_config_get(config, key);
++
++ // Return zero if not set
++ if (!value)
++ return 0;
++
++ return strtoul(value, NULL, 10);
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index a793229..c5a2f7f 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -38,4 +38,7 @@ int nw_config_del(struct nw_config* config, const char* key);
+
+ int nw_config_set(struct nw_config* config, const char* key, const char* value);
+
++const char* nw_config_get(struct nw_config* config, const char* key);
++unsigned int nw_config_get_unsigned_int(struct nw_config* config, const char* key);
++
+ #endif /* NETWORKD_CONFIG_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,79 @@
+From d3dfdb77779004950cf3afb19e456b7d5c39c940 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:17:30 +0000
+Subject: [PATCH 150/304] networkd: Implement writing configuration files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ src/networkd/config.h | 3 +++
+ 2 files changed, 43 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index a3db7c6..b2a23f0 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -165,6 +165,46 @@ ERROR:
+ return r;
+ }
+
++int nw_config_writef(struct nw_config* config, FILE* f) {
++ struct nw_config_entry* entry = NULL;
++ int r;
++
++ STAILQ_FOREACH(entry, &config->entries, nodes) {
++ // Skip if value is NULL
++ if (!*entry->value)
++ continue;
++
++ // Write the entry
++ r = fprintf(f, "%s=\"%s\"\n", entry->key, entry->value);
++ if (r < 0) {
++ ERROR("Failed to write configuration: %m\n");
++ return r;
++ }
++ }
++
++ return 0;
++}
++
++int nw_config_write(struct nw_config* config, const char* path) {
++ int r;
++
++ FILE* f = fopen(path, "w");
++ if (!f) {
++ ERROR("Failed to open %s for writing: %m\n", path);
++ r = 1;
++ goto ERROR;
++ }
++
++ // Write configuration
++ r = nw_config_writef(config, f);
++
++ERROR:
++ if (f)
++ fclose(f);
++
++ return r;
++}
++
+ static struct nw_config_entry* nw_config_find(struct nw_config* config, const char* key) {
+ struct nw_config_entry* entry = NULL;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index c5a2f7f..90e73f4 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -34,6 +34,9 @@ struct nw_config* nw_config_unref(struct nw_config* config);
+ int nw_config_readf(struct nw_config** config, FILE* f);
+ int nw_config_read(struct nw_config** config, const char* path);
+
++int nw_config_writef(struct nw_config* config, FILE* f);
++int nw_config_write(struct nw_config* config, const char* path);
++
+ int nw_config_del(struct nw_config* config, const char* key);
+
+ int nw_config_set(struct nw_config* config, const char* key, const char* value);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,54 @@
+From 2ac78f288a7c1f8b363cf0c86fc196aeeb8e8264 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:20:23 +0000
+Subject: [PATCH 151/304] networkd: Read main configuration file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 3aee8ca..bd5f1f0 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -26,12 +26,15 @@
+ #include <systemd/sd-event.h>
+
+ #include "bus.h"
++#include "config.h"
+ #include "daemon.h"
+ #include "logging.h"
+
+ struct nw_daemon {
+ int nrefs;
+
++ struct nw_config* config;
++
+ // Event Loop
+ sd_event* loop;
+
+@@ -105,6 +108,11 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
++ // Read configuration file
++ r = nw_config_read(&daemon->config, "/etc/network/settings");
++ if (r)
++ return r;
++
+ // Setup the event loop
+ r = nw_daemon_setup_loop(daemon);
+ if (r)
+@@ -145,6 +153,8 @@ ERROR:
+ }
+
+ static void nw_daemon_free(struct nw_daemon* daemon) {
++ if (daemon->config)
++ nw_config_unref(daemon->config);
+ if (daemon->bus)
+ sd_bus_unref(daemon->bus);
+ if (daemon->loop)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,41 @@
+From 5c1452a43a6a8d3d3596fa8be38741e62852ce5c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:29:45 +0000
+Subject: [PATCH 152/304] networkd: Set configuration path from build scripts
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 ++-
+ src/networkd/daemon.c | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 1b18f62..3cc7ce9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -315,7 +315,8 @@ dist_networkd_SOURCES = \
+ src/networkd/string.h
+
+ networkd_CPPFLAGS = \
+- $(AM_CPPFLAGS)
++ $(AM_CPPFLAGS) \
++ -DCONFIG_DIR="\"$(configdir)\""
+
+ networkd_CFLAGS = \
+ $(AM_CFLAGS) \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index bd5f1f0..9077c54 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -109,7 +109,7 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
+ // Read configuration file
+- r = nw_config_read(&daemon->config, "/etc/network/settings");
++ r = nw_config_read(&daemon->config, CONFIG_DIR "/settings");
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,192 @@
+From e4eebc6e6a0091729831fd1361d0a9fd0d205114 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 03:42:57 +0000
+Subject: [PATCH 153/304] networkd: Add scaffolding for zones
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++-
+ src/networkd/config.h | 2 ++
+ src/networkd/string.h | 3 ++
+ src/networkd/zone.c | 83 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/zone.h | 35 ++++++++++++++++++
+ 5 files changed, 126 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/zone.c
+ create mode 100644 src/networkd/zone.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 3cc7ce9..5bb9c4d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -312,7 +312,9 @@ dist_networkd_SOURCES = \
+ src/networkd/daemon.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+- src/networkd/string.h
++ src/networkd/string.h \
++ src/networkd/zone.c \
++ src/networkd/zone.h
+
+ networkd_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 90e73f4..3474ae1 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_CONFIG_H
+ #define NETWORKD_CONFIG_H
+
++#include <stdio.h>
++
+ #define NETWORK_CONFIG_KEY_MAX_LENGTH 128
+ #define NETWORK_CONFIG_VALUE_MAX_LENGTH 2048
+
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 3ac4846..52b5add 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -21,7 +21,10 @@
+ #ifndef NETWORKD_STRING_H
+ #define NETWORKD_STRING_H
+
++#include <errno.h>
+ #include <stdarg.h>
++#include <stdio.h>
++#include <string.h>
+
+ static inline int __nw_string_vformat(char* s, const size_t length,
+ const char* format, va_list args) {
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+new file mode 100644
+index 0000000..5066c4b
+--- /dev/null
++++ b/src/networkd/zone.c
+@@ -0,0 +1,83 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++
++#include "config.h"
++#include "string.h"
++#include "zone.h"
++
++struct nw_zone {
++ int nrefs;
++
++ char name[NETWORK_ZONE_NAME_MAX_LENGTH];
++
++ // Configuration
++ struct nw_config *config;
++};
++
++static void nw_zone_free(struct nw_zone* zone) {
++ if (zone->config)
++ nw_config_unref(zone->config);
++
++ free(zone);
++}
++
++int nw_zone_create(struct nw_zone** zone, const char* name) {
++ int r;
++
++ // Allocate a new object
++ struct nw_zone* z = calloc(1, sizeof(*z));
++ if (!z)
++ return 1;
++
++ // Initialize reference counter
++ z->nrefs = 1;
++
++ // Store the name
++ r = nw_string_set(z->name, name);
++ if (r)
++ goto ERROR;
++
++ *zone = z;
++ return 0;
++
++ERROR:
++ nw_zone_free(z);
++ return r;
++}
++
++struct nw_zone* nw_zone_ref(struct nw_zone* zone) {
++ zone->nrefs++;
++
++ return zone;
++}
++
++struct nw_zone* nw_zone_unref(struct nw_zone* zone) {
++ if (--zone->nrefs > 0)
++ return zone;
++
++ nw_zone_free(zone);
++ return NULL;
++}
++
++const char* nw_zone_name(struct nw_zone* zone) {
++ return zone->name;
++}
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+new file mode 100644
+index 0000000..78287ab
+--- /dev/null
++++ b/src/networkd/zone.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_ZONE_H
++#define NETWORKD_ZONE_H
++
++#define NETWORK_ZONE_NAME_MAX_LENGTH 16
++
++struct nw_zone;
++
++int nw_zone_create(struct nw_zone** zone, const char* name);
++
++struct nw_zone* nw_zone_ref(struct nw_zone* zone);
++struct nw_zone* nw_zone_unref(struct nw_zone* zone);
++
++const char* nw_zone_name(struct nw_zone* zone);
++
++#endif /* NETWORKD_ZONE_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,82 @@
+From 063eb4d841e8fc4625d889a9d5f8ac832bd60f0d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 04:05:07 +0000
+Subject: [PATCH 154/304] networkd: Install a dbus service file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++++
+ configure.ac | 15 +++++++++++++++
+ src/networkd/org.ipfire.network1.service | 5 +++++
+ 3 files changed, 24 insertions(+)
+ create mode 100644 src/networkd/org.ipfire.network1.service
+
+diff --git a/Makefile.am b/Makefile.am
+index 5bb9c4d..5cfeefa 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -62,6 +62,7 @@ INSTALL_EXEC_HOOKS =
+ UNINSTALL_EXEC_HOOKS =
+ noinst_DATA =
+ sbin_PROGRAMS =
++dist_dbussystembusdir_DATA =
+
+ AM_CPPFLAGS = \
+ $(OUR_CPPFLAGS) \
+@@ -330,6 +331,9 @@ networkd_LDFLAGS = \
+ networkd_LDADD = \
+ $(SYSTEMD_LIBS)
+
++dist_dbussystembusdir_DATA += \
++ src/networkd/org.ipfire.network1.service
++
+ # ------------------------------------------------------------------------------
+
+ util_PROGRAMS = \
+diff --git a/configure.ac b/configure.ac
+index b820794..8634a39 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -133,6 +133,20 @@ AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-man-pages],
+ AS_IF([test "x$enable_manpages" != xno], [have_manpages=yes])
+ AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"])
+
++# ------------------------------------------------------------------------------
++
++AC_ARG_WITH([dbussystembusdir], AS_HELP_STRING([--with-dbussystembusdir=DIR],
++ [path to D-Bus system bus services directory]), [with_dbussystembusdir=${withval}])
++if test -z "${with_dbussystembusdir}"; then
++ AC_MSG_CHECKING([D-Bus system bus services dir])
++ with_dbussystembusdir="$($PKG_CONFIG --variable=system_bus_services_dir dbus-1)"
++ if test -z "${with_dbussystembusdir}"; then
++ AC_MSG_ERROR([D-Bus system bus services directory is required])
++ fi
++ AC_MSG_RESULT([${with_dbussystembusdir}])
++fi
++AC_SUBST(dbussystembusdir, [${with_dbussystembusdir}])
++
+ # ------------------------------------------------------------------------------
+ AC_ARG_WITH([systemdsystemunitdir],
+ AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
+@@ -176,6 +190,7 @@ AC_MSG_RESULT([
+
+ prefix: $prefix
+
++ dbussystembusdir: ${dbussystembusdir}
+ systemdsystemunitdir: $systemdsystemunitdir
+ udevdir: $udevdir
+
+diff --git a/src/networkd/org.ipfire.network1.service b/src/networkd/org.ipfire.network1.service
+new file mode 100644
+index 0000000..fdeda66
+--- /dev/null
++++ b/src/networkd/org.ipfire.network1.service
+@@ -0,0 +1,5 @@
++[D-BUS Service]
++Name=org.ipfire.network1
++Exec=/bin/false
++User=root
++SystemdService=dbus-org.ipfire.network1.service
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,87 @@
+From d5fab986b9ac9a692b26e5f5d49a51dce85ccf59 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 18:47:30 +0000
+Subject: [PATCH 155/304] networkd: Install a dbus policy
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 8 ++++++--
+ configure.ac | 12 ++++++++++++
+ src/networkd/org.ipfire.network1.conf | 16 ++++++++++++++++
+ 3 files changed, 34 insertions(+), 2 deletions(-)
+ create mode 100644 src/networkd/org.ipfire.network1.conf
+
+diff --git a/Makefile.am b/Makefile.am
+index 5cfeefa..eeda5ba 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -62,7 +62,8 @@ INSTALL_EXEC_HOOKS =
+ UNINSTALL_EXEC_HOOKS =
+ noinst_DATA =
+ sbin_PROGRAMS =
+-dist_dbussystembusdir_DATA =
++dist_dbuspolicy_DATA =
++dist_dbussystembus_DATA =
+
+ AM_CPPFLAGS = \
+ $(OUR_CPPFLAGS) \
+@@ -331,7 +332,10 @@ networkd_LDFLAGS = \
+ networkd_LDADD = \
+ $(SYSTEMD_LIBS)
+
+-dist_dbussystembusdir_DATA += \
++dist_dbuspolicy_DATA += \
++ src/networkd/org.ipfire.network1.conf
++
++dist_dbussystembus_DATA += \
+ src/networkd/org.ipfire.network1.service
+
+ # ------------------------------------------------------------------------------
+diff --git a/configure.ac b/configure.ac
+index 8634a39..e612ff8 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -135,6 +135,18 @@ AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"])
+
+ # ------------------------------------------------------------------------------
+
++AC_ARG_WITH(
++ [dbuspolicydir],
++ AS_HELP_STRING(
++ [--with-dbuspolicydir=arg],
++ [directory for D-Bus policies (default: ${dbusdatadir|datarootdir}/dbus-1/system.d)]
++ ),
++ [dbuspolicydir="$withval"],
++ [PKG_CHECK_VAR([dbusdatadir], [dbus-1], [datadir],, [dbusdatadir="${datarootdir}"])
++ dbuspolicydir="${dbusdatadir}/dbus-1/system.d"]
++)
++AC_SUBST(dbuspolicydir)
++
+ AC_ARG_WITH([dbussystembusdir], AS_HELP_STRING([--with-dbussystembusdir=DIR],
+ [path to D-Bus system bus services directory]), [with_dbussystembusdir=${withval}])
+ if test -z "${with_dbussystembusdir}"; then
+diff --git a/src/networkd/org.ipfire.network1.conf b/src/networkd/org.ipfire.network1.conf
+new file mode 100644
+index 0000000..96e8e15
+--- /dev/null
++++ b/src/networkd/org.ipfire.network1.conf
+@@ -0,0 +1,16 @@
++<?xml version="1.0"?> <!--*-nxml-*-->
++<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
++ "https://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
++
++<busconfig>
++ <policy user="network">
++ <allow own="org.ipfire.network1"/>
++ <allow send_destination="org.ipfire.network1"/>
++ <allow receive_sender="org.ipfire.network1"/>
++ </policy>
++
++ <policy context="default">
++ <allow send_destination="org.ipfire.network1"/>
++ <allow receive_sender="org.ipfire.network1"/>
++ </policy>
++</busconfig>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,53 @@
+From 9ca8914ef2c65a669aa05e37f2dbe232f54ecbf1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 18:51:34 +0000
+Subject: [PATCH 156/304] configure: Tidy up dbus path detection
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index e612ff8..ba5967d 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -147,17 +147,16 @@ AC_ARG_WITH(
+ )
+ AC_SUBST(dbuspolicydir)
+
+-AC_ARG_WITH([dbussystembusdir], AS_HELP_STRING([--with-dbussystembusdir=DIR],
+- [path to D-Bus system bus services directory]), [with_dbussystembusdir=${withval}])
+-if test -z "${with_dbussystembusdir}"; then
+- AC_MSG_CHECKING([D-Bus system bus services dir])
+- with_dbussystembusdir="$($PKG_CONFIG --variable=system_bus_services_dir dbus-1)"
+- if test -z "${with_dbussystembusdir}"; then
+- AC_MSG_ERROR([D-Bus system bus services directory is required])
+- fi
+- AC_MSG_RESULT([${with_dbussystembusdir}])
+-fi
+-AC_SUBST(dbussystembusdir, [${with_dbussystembusdir}])
++AC_ARG_WITH(
++ [dbussystembusdir],
++ AS_HELP_STRING(
++ [--with-dbussystembusdir=arg],
++ [path to D-Bus system bus services directory]
++ ),
++ [dbussystembusdir="$withval"],
++ [PKG_CHECK_VAR([dbussystembusdir], [dbus-1], [system_bus_services_dir],, [dbussystembusdir="${datarootdir}"])]
++)
++AC_SUBST(dbuspolicydir)
+
+ # ------------------------------------------------------------------------------
+ AC_ARG_WITH([systemdsystemunitdir],
+@@ -202,6 +201,7 @@ AC_MSG_RESULT([
+
+ prefix: $prefix
+
++ dbuspolicydir: ${dbuspolicydir}
+ dbussystembusdir: ${dbussystembusdir}
+ systemdsystemunitdir: $systemdsystemunitdir
+ udevdir: $udevdir
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,77 @@
+From a465e68bd8bf11f7cd41089941ad558dbda34f19 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 18:54:54 +0000
+Subject: [PATCH 157/304] configure: Drop non-sensical CFLAGS and add more
+ warnings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ configure.ac | 42 ++++++++++++++----------------------------
+ 1 file changed, 14 insertions(+), 28 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index ba5967d..9560838 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -28,6 +28,8 @@ AC_INIT([network],
+
+ AC_CONFIG_AUX_DIR([build-aux])
+
++AC_USE_SYSTEM_EXTENSIONS
++AC_SYS_LARGEFILE
+ AC_PREFIX_DEFAULT([/usr])
+
+ AM_INIT_AUTOMAKE([
+@@ -65,37 +67,21 @@ AC_PROG_CC_C99
+ AC_PROG_CC_C_O
+ AC_PROG_GCC_TRADITIONAL
+
+-AC_USE_SYSTEM_EXTENSIONS
+-
+ CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
+- -pipe \
+ -Wall \
+- -Wextra \
+- -Wno-inline \
+- -Wundef \
+- "-Wformat=2 -Wformat-security -Wformat-nonliteral" \
+- -Wno-unused-parameter \
+- -Wno-unused-result \
+- -fno-strict-aliasing \
+- -ffunction-sections \
+- -fdata-sections \
+- -fstack-protector-all \
+- --param=ssp-buffer-size=4])
+-AC_SUBST([OUR_CFLAGS], $with_cflags)
++ -Wchar-subscripts \
++ -Wformat-security \
++ -Wmissing-declarations \
++ -Wmissing-prototypes \
++ -Wnested-externs \
++ -Wpointer-arith \
++ -Wshadow \
++ -Wsign-compare \
++ -Wstrict-prototypes \
++ -Wtype-limits \
++])
+
+-AS_CASE([$CFLAGS], [*-O[[12345g\ ]]*],
+- [CC_CHECK_FLAGS_APPEND([with_cppflags], [CPPFLAGS], [\
+- -Wp,-D_FORTIFY_SOURCE=2])],
+- [AC_MSG_RESULT([skipping -D_FORTIFY_SOURCE, optimization not enabled])])
+-AC_SUBST([OUR_CPPFLAGS], $with_cppflags)
+-
+-CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\
+- -Wl,--as-needed \
+- -Wl,--no-undefined \
+- -Wl,--gc-sections \
+- -Wl,-z,relro \
+- -Wl,-z,now])
+-AC_SUBST([OUR_LDFLAGS], $with_ldflags)
++AC_SUBST([OUR_CFLAGS], $with_cflags)
+
+ # ------------------------------------------------------------------------------
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From ac17b7ef5c2dc801dc9eb179927096bc18e7bc58 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 18:56:17 +0000
+Subject: [PATCH 158/304] libnetwork: Fix prototype of network_version()
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libnetwork/network/libnetwork.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/libnetwork/network/libnetwork.h b/src/libnetwork/network/libnetwork.h
+index 2919fc9..e69fd04 100644
+--- a/src/libnetwork/network/libnetwork.h
++++ b/src/libnetwork/network/libnetwork.h
+@@ -36,7 +36,7 @@ void network_set_log_fn(struct network_ctx* ctx,
+ int network_get_log_priority(struct network_ctx* ctx);
+ void network_set_log_priority(struct network_ctx* ctx, int priority);
+
+-const char* network_version();
++const char* network_version(void);
+
+ #ifdef NETWORK_PRIVATE
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,104 @@
+From 8a88982f6823b376f2eace8c718dc930072de667 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 19:00:51 +0000
+Subject: [PATCH 159/304] networkd: Move systemd notifications into daemon
+ object
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 19 ++++++++++++++++---
+ src/networkd/main.c | 24 ++++--------------------
+ 2 files changed, 20 insertions(+), 23 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 9077c54..b93877e 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -23,6 +23,7 @@
+ #include <string.h>
+
+ #include <systemd/sd-bus.h>
++#include <systemd/sd-daemon.h>
+ #include <systemd/sd-event.h>
+
+ #include "bus.h"
+@@ -183,14 +184,26 @@ struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon) {
+ int nw_daemon_run(struct nw_daemon* daemon) {
+ int r;
+
++ // We are now ready to process any requests
++ sd_notify(0, "READY=1\n" "STATUS=Processing requests...");
++
+ // Launch the event loop
+ r = sd_event_loop(daemon->loop);
+- if (r) {
+- ERROR("Could not run the event loop: %m\n");
+- return r;
++ if (r < 0) {
++ ERROR("Could not run the event loop: %s\n", strerror(-r));
++ goto ERROR;
+ }
+
++
++ // Let systemd know that we are shutting down
++ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
++
+ return 0;
++
++ERROR:
++ sd_notifyf(0, "ERRNO=%i", -r);
++
++ return 1;
+ }
+
+ int nw_daemon_reload(struct nw_daemon* daemon) {
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 80123ad..51a9bba 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -18,10 +18,7 @@
+ # #
+ #############################################################################*/
+
+-#include <errno.h>
+-#include <stdlib.h>
+-
+-#include <systemd/sd-daemon.h>
++#include <stddef.h>
+
+ #include "daemon.h"
+
+@@ -34,27 +31,14 @@ int main(int argc, char** argv) {
+ // Create the daemon
+ r = nw_daemon_create(&daemon);
+ if (r)
+- goto ERROR;
+-
+- // We are now ready to process any requests
+- sd_notify(0, "READY=1\n" "STATUS=Processing requests...");
++ return r;
+
+ // Run the daemon
+ r = nw_daemon_run(daemon);
+- if (r)
+- goto ERROR;
+-
+- // Let systemd know that we are shutting down
+- sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
+-
+- goto CLEANUP;
+-
+-ERROR:
+- sd_notifyf(0, "ERRNO=%i", errno);
+
+-CLEANUP:
++ // Cleanup
+ if (daemon)
+ nw_daemon_unref(daemon);
+
+- return EXIT_FAILURE;
++ return r;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,45 @@
+From fbe46265eecc9064df8819d9bd08e5fcb0ea985c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 19:03:41 +0000
+Subject: [PATCH 160/304] man: Fix incorrect name on IPsec man page
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ man/network-vpn-ipsec.txt | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/man/network-vpn-ipsec.txt b/man/network-vpn-ipsec.txt
+index 25347a8..d60d41d 100644
+--- a/man/network-vpn-ipsec.txt
++++ b/man/network-vpn-ipsec.txt
+@@ -1,7 +1,7 @@
+-= network-vpn-security-policies(8)
++= network-vpn-ipsec(8)
+
+ == NAME
+-network-ipsec - Configure IPsec VPN connections
++network-vpn-ipsec - Configure IPsec VPN connections
+
+ == SYNOPSIS
+ [verse]
+@@ -27,7 +27,7 @@ The following commands are understood:
+ For all other commands, the name of the IPsec VPN connection needs to be passed first:
+
+ 'NAME show'::
+- Shows the configuration of the IPsec VPN connection
++ Shows the configuration of the IPsec VPN connection
+
+ 'NAME authentication mode'::
+ Set the authentication mode out of the following available modes:
+@@ -58,7 +58,7 @@ include::include-description.txt[]
+ Specify the subnets of the local system which should be made available to the remote peer.
+
+ 'NAME mode [transport|tunnel]'::
+- Set the mode of the IPsec VPN connection.
++ Set the mode of the IPsec VPN connection.
+
+ 'NAME peer PEER'::
+ Set the peer to which the IPsec VPN connection should be etablished.
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From bf98bd657165f87eafae1442a6396239d74cd88e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 19:26:35 +0000
+Subject: [PATCH 161/304] networkd: Install some simple PolicyKit policy
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++++
+ configure.ac | 13 +++++++++++++
+ src/networkd/org.ipfire.network1.policy | 19 +++++++++++++++++++
+ 3 files changed, 36 insertions(+)
+ create mode 100644 src/networkd/org.ipfire.network1.policy
+
+diff --git a/Makefile.am b/Makefile.am
+index eeda5ba..4802de3 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -64,6 +64,7 @@ noinst_DATA =
+ sbin_PROGRAMS =
+ dist_dbuspolicy_DATA =
+ dist_dbussystembus_DATA =
++dist_polkitpolicy_DATA =
+
+ AM_CPPFLAGS = \
+ $(OUR_CPPFLAGS) \
+@@ -338,6 +339,9 @@ dist_dbuspolicy_DATA += \
+ dist_dbussystembus_DATA += \
+ src/networkd/org.ipfire.network1.service
+
++dist_polkitpolicy_DATA += \
++ src/networkd/org.ipfire.network1.policy
++
+ # ------------------------------------------------------------------------------
+
+ util_PROGRAMS = \
+diff --git a/configure.ac b/configure.ac
+index 9560838..16f0724 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -144,6 +144,18 @@ AC_ARG_WITH(
+ )
+ AC_SUBST(dbuspolicydir)
+
++AC_ARG_WITH(
++ [polkitpolicydir],
++ AS_HELP_STRING(
++ [--with-polkitpolicydir=arg],
++ [directory for PolicyKit policies]
++ ),
++ [polkitpolicydir="$withval"],
++ [PKG_CHECK_VAR([polkitpolicydir], [polkit], [actiondir])
++ polkitpolicydir="${datadir}/polkit-1/actions"]
++)
++AC_SUBST(polkitpolicydir)
++
+ # ------------------------------------------------------------------------------
+ AC_ARG_WITH([systemdsystemunitdir],
+ AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
+@@ -189,6 +201,7 @@ AC_MSG_RESULT([
+
+ dbuspolicydir: ${dbuspolicydir}
+ dbussystembusdir: ${dbussystembusdir}
++ polkitpolicydir: ${polkitpolicydir}
+ systemdsystemunitdir: $systemdsystemunitdir
+ udevdir: $udevdir
+
+diff --git a/src/networkd/org.ipfire.network1.policy b/src/networkd/org.ipfire.network1.policy
+new file mode 100644
+index 0000000..46318f1
+--- /dev/null
++++ b/src/networkd/org.ipfire.network1.policy
+@@ -0,0 +1,19 @@
++<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
++<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
++ "https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
++
++<policyconfig>
++ <vendor>The IPFire Project</vendor>
++ <vendor_url>https://www.ipfire.org</vendor_url>
++
++ <action id="org.ipfire.network1.reload">
++ <description gettext-domain="systemd">Reload Network Settings</description>
++ <message gettext-domain="network">Authentication is required to reload network settings.</message>
++ <defaults>
++ <allow_any>auth_admin</allow_any>
++ <allow_inactive>auth_admin</allow_inactive>
++ <allow_active>auth_admin_keep</allow_active>
++ </defaults>
++ <annotate key="org.freedesktop.policykit.owner">unix-user:network</annotate>
++ </action>
++</policyconfig>
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,51 @@
+From bf42846c2ac47b5ecb8eea5cba618b130a6401ab Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 30 Jan 2023 20:55:42 +0000
+Subject: [PATCH 162/304] networkd: Call function when we are connected to dbus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 95862d2..36ae7c9 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -28,10 +28,18 @@
+ #include "daemon.h"
+ #include "logging.h"
+
+-static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* ret_error) {
++static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+ return 1;
+ }
+
++static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ DEBUG("Connected to D-Bus\n");
++
++ return 0;
++}
++
+ static const sd_bus_vtable daemon_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
+@@ -127,5 +135,13 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+ return 1;
+ }
+
++ // Request receiving a connect signal
++ r = sd_bus_match_signal_async(bus, NULL, "org.freedesktop.DBus.Local",
++ NULL, "org.freedesktop.DBus.Local", "Connected", nw_bus_on_connect, NULL, NULL);
++ if (r < 0) {
++ ERROR("Could not request match on Connected signal: %s\n", strerror(-r));
++ return 1;
++ }
++
+ return 0;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,116 @@
+From 5d968c0188b8181db6f392f925d7875e12e7e21b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 15:26:34 +0000
+Subject: [PATCH 163/304] networkd: Install a systemd service file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ .gitignore | 1 +
+ Makefile.am | 12 ++++++++-
+ src/networkd/networkd.service.in | 45 ++++++++++++++++++++++++++++++++
+ 3 files changed, 57 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/networkd.service.in
+
+diff --git a/.gitignore b/.gitignore
+index e3bae67..9194c93 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -8,6 +8,7 @@
+ /src/inetcalc
+ /src/libnetwork/libnetwork.pc
+ /src/network.pc
++/src/networkd/networkd.service
+ /src/ppp/ip-updown
+ /src/systemd/*.service
+ /test/nitsi/test/settings
+diff --git a/Makefile.am b/Makefile.am
+index 4802de3..3a3f82c 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -65,6 +65,7 @@ sbin_PROGRAMS =
+ dist_dbuspolicy_DATA =
+ dist_dbussystembus_DATA =
+ dist_polkitpolicy_DATA =
++systemdsystemunit_DATA =
+
+ AM_CPPFLAGS = \
+ $(OUR_CPPFLAGS) \
+@@ -342,6 +343,15 @@ dist_dbussystembus_DATA += \
+ dist_polkitpolicy_DATA += \
+ src/networkd/org.ipfire.network1.policy
+
++systemdsystemunit_DATA += \
++ src/networkd/networkd.service
++
++EXTRA_DIST += \
++ src/networkd/networkd.service.in
++
++CLEANFILES += \
++ src/networkd/networkd.service
++
+ # ------------------------------------------------------------------------------
+
+ util_PROGRAMS = \
+@@ -406,7 +416,7 @@ UNINSTALL_EXEC_HOOKS += ppp-uninstall-hook
+ # ------------------------------------------------------------------------------
+
+ if HAVE_SYSTEMD
+-systemdsystemunit_DATA = \
++systemdsystemunit_DATA += \
+ src/systemd/firewall.service \
+ src/systemd/firewall-init.service \
+ src/systemd/network-init.service \
+diff --git a/src/networkd/networkd.service.in b/src/networkd/networkd.service.in
+new file mode 100644
+index 0000000..4361023
+--- /dev/null
++++ b/src/networkd/networkd.service.in
+@@ -0,0 +1,45 @@
++[Unit]
++Description=Network Configuration
++Documentation=man:networkd.service(8)
++
++ConditionCapability=CAP_NET_ADMIN
++DefaultDependencies=no
++# systemd-udevd.service can be dropped once tuntap is moved to netlink
++After=systemd-udevd.service network-pre.target systemd-sysusers.service systemd-sysctl.service
++Before=network.target multi-user.target shutdown.target
++Conflicts=shutdown.target
++Wants=network.target
++
++[Service]
++AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
++BusName=org.ipfire.network1
++CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
++DeviceAllow=char-* rw
++ExecStart=@sbindir@/networkd
++FileDescriptorStoreMax=512
++LockPersonality=yes
++MemoryDenyWriteExecute=yes
++NoNewPrivileges=yes
++ProtectProc=invisible
++ProtectClock=yes
++ProtectControlGroups=yes
++ProtectHome=yes
++ProtectKernelLogs=yes
++ProtectKernelModules=yes
++ProtectSystem=strict
++Restart=on-failure
++RestartSec=0
++RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
++RestrictNamespaces=yes
++RestrictRealtime=yes
++RestrictSUIDSGID=yes
++SystemCallArchitectures=native
++SystemCallErrorNumber=EPERM
++SystemCallFilter=@system-service
++Type=notify-reload
++User=network
++WatchdogSec=3min
++
++[Install]
++WantedBy=multi-user.target
++Alias=dbus-org.ipfire.network1.service
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From 1dc98e400cd9c17edffa8f94f02a38ca1777cef8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 15:26:57 +0000
+Subject: [PATCH 164/304] networkd: Fully implement bus handler for Reload
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 36ae7c9..56eda87 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -29,7 +29,13 @@
+ #include "logging.h"
+
+ static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+- return 1;
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Reload the daemon
++ nw_daemon_reload(daemon);
++
++ // Respond with an empty message
++ return sd_bus_reply_method_return(m, NULL);
+ }
+
+ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,28 @@
+From 1e5ff643f2e18e5133ec932d5355f110ca1bfbf4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 15:27:18 +0000
+Subject: [PATCH 165/304] networkd: Asynchronously register to the bus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 56eda87..5224aaa 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -126,9 +126,7 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+ }
+
+ // Request interface name
+- // XXX Should this be async?
+- // XXX How do we get the actual error message from dbus?
+- r = sd_bus_request_name(bus, NETWORKD_BUS_INTERFACE_NAME, 0);
++ r = sd_bus_request_name_async(bus, NULL, NETWORKD_BUS_INTERFACE_NAME, 0, NULL, NULL);
+ if (r < 0) {
+ ERROR("Could not request bus name: %s\n", strerror(-r));
+ return 1;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,71 @@
+From 91915f80f60943afec29743eda7258f5bf65e8e5 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 21:46:11 +0000
+Subject: [PATCH 166/304] networkd: config: Split flushing all entries into a
+ function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 25 ++++++++++++++++---------
+ src/networkd/config.h | 2 ++
+ 2 files changed, 18 insertions(+), 9 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index b2a23f0..a11ee1c 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -74,15 +74,8 @@ ERROR:
+ }
+
+ static void nw_config_free(struct nw_config* config) {
+- struct nw_config_entry* entry = NULL;
+-
+- while (!STAILQ_EMPTY(&config->entries)) {
+- entry = STAILQ_FIRST(&config->entries);
+- STAILQ_REMOVE_HEAD(&config->entries, nodes);
+-
+- // Free the entry
+- nw_config_entry_free(entry);
+- }
++ // Flush all entries
++ nw_config_flush(config);
+
+ free(config);
+ }
+@@ -117,6 +110,20 @@ struct nw_config* nw_config_unref(struct nw_config* config) {
+ return NULL;
+ }
+
++int nw_config_flush(struct nw_config* config) {
++ struct nw_config_entry* entry = NULL;
++
++ while (!STAILQ_EMPTY(&config->entries)) {
++ entry = STAILQ_FIRST(&config->entries);
++ STAILQ_REMOVE_HEAD(&config->entries, nodes);
++
++ // Free the entry
++ nw_config_entry_free(entry);
++ }
++
++ return 0;
++}
++
+ static int nw_config_parse(struct nw_config* config, FILE* f) {
+ // XXX TODO
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 3474ae1..3878063 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -33,6 +33,8 @@ int nw_config_create(struct nw_config** config);
+ struct nw_config* nw_config_ref(struct nw_config* config);
+ struct nw_config* nw_config_unref(struct nw_config* config);
+
++int nw_config_flush(struct nw_config* config);
++
+ int nw_config_readf(struct nw_config** config, FILE* f);
+ int nw_config_read(struct nw_config** config, const char* path);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,70 @@
+From 3239c3b1a98103e79ae7ab524fc12386a781249f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 22:03:29 +0000
+Subject: [PATCH 167/304] networkd: Change config read functions to not create
+ a new instance
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 24 ++----------------------
+ src/networkd/config.h | 4 ++--
+ 2 files changed, 4 insertions(+), 24 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index a11ee1c..0048e44 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -124,33 +124,13 @@ int nw_config_flush(struct nw_config* config) {
+ return 0;
+ }
+
+-static int nw_config_parse(struct nw_config* config, FILE* f) {
++int nw_config_readf(struct nw_config* config, FILE* f) {
+ // XXX TODO
+
+ return 0;
+ }
+
+-int nw_config_readf(struct nw_config** config, FILE* f) {
+- int r;
+-
+- // Create a new config object
+- r = nw_config_create(config);
+- if (r)
+- return r;
+-
+- // Parse the configuration
+- r = nw_config_parse(*config, f);
+- if (r)
+- goto ERROR;
+-
+- return 0;
+-
+-ERROR:
+- nw_config_free(*config);
+- return r;
+-}
+-
+-int nw_config_read(struct nw_config** config, const char* path) {
++int nw_config_read(struct nw_config* config, const char* path) {
+ FILE* f = NULL;
+ int r;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 3878063..c4f9125 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -35,8 +35,8 @@ struct nw_config* nw_config_unref(struct nw_config* config);
+
+ int nw_config_flush(struct nw_config* config);
+
+-int nw_config_readf(struct nw_config** config, FILE* f);
+-int nw_config_read(struct nw_config** config, const char* path);
++int nw_config_readf(struct nw_config* config, FILE* f);
++int nw_config_read(struct nw_config* config, const char* path);
+
+ int nw_config_writef(struct nw_config* config, FILE* f);
+ int nw_config_write(struct nw_config* config, const char* path);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,200 @@
+From b076fa8b6b7aa4b5746b8ef01469e074b6a228ae Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 22:37:06 +0000
+Subject: [PATCH 168/304] networkd: Store the path with the configuration
+ object
+
+This makes it easier to call read and write functions without
+re-composing the path again and again...
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 64 +++++++++++++++++++++++++++++++++++++------
+ src/networkd/config.h | 11 ++++----
+ src/networkd/daemon.c | 2 +-
+ 3 files changed, 61 insertions(+), 16 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 0048e44..e531ec6 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -19,6 +19,7 @@
+ #############################################################################*/
+
+ #include <errno.h>
++#include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -38,6 +39,9 @@ struct nw_config_entry {
+ struct nw_config {
+ int nrefs;
+
++ // The path to the configuration file
++ char path[PATH_MAX];
++
+ STAILQ_HEAD(entries, nw_config_entry) entries;
+ };
+
+@@ -80,7 +84,9 @@ static void nw_config_free(struct nw_config* config) {
+ free(config);
+ }
+
+-int nw_config_create(struct nw_config** config) {
++int nw_config_create(struct nw_config** config, const char* path) {
++ int r;
++
+ struct nw_config* c = calloc(1, sizeof(*c));
+ if (!c)
+ return 1;
+@@ -91,9 +97,26 @@ int nw_config_create(struct nw_config** config) {
+ // Initialise entries
+ STAILQ_INIT(&c->entries);
+
++ // Store the path
++ if (path) {
++ r = nw_string_set(c->path, path);
++ if (r)
++ goto ERROR;
++
++ // Try to read the configuration from path
++ r = nw_config_read(c);
++ if (r)
++ goto ERROR;
++ }
++
+ *config = c;
+
+ return 0;
++
++ERROR:
++ nw_config_free(c);
++
++ return r;
+ }
+
+ struct nw_config* nw_config_ref(struct nw_config* config) {
+@@ -110,6 +133,13 @@ struct nw_config* nw_config_unref(struct nw_config* config) {
+ return NULL;
+ }
+
++const char* nw_config_path(struct nw_config* config) {
++ if (*config->path)
++ return config->path;
++
++ return NULL;
++}
++
+ int nw_config_flush(struct nw_config* config) {
+ struct nw_config_entry* entry = NULL;
+
+@@ -124,20 +154,30 @@ int nw_config_flush(struct nw_config* config) {
+ return 0;
+ }
+
+-int nw_config_readf(struct nw_config* config, FILE* f) {
++static int nw_config_readf(struct nw_config* config, FILE* f) {
+ // XXX TODO
+
+ return 0;
+ }
+
+-int nw_config_read(struct nw_config* config, const char* path) {
++int nw_config_read(struct nw_config* config) {
+ FILE* f = NULL;
+ int r;
+
++ // We cannot read if path is not set
++ if (!*config->path) {
++ errno = ENOTSUP;
++ return 1;
++ }
++
+ // Open the file
+- f = fopen(path, "r");
++ f = fopen(config->path, "r");
+ if (!f) {
+- ERROR("Could not read configuration file %s: %m\n", path);
++ // Silently ignore if the file does not exist
++ if (errno == ENOENT)
++ return 0;
++
++ ERROR("Could not read configuration file %s: %m\n", config->path);
+ r = 1;
+ goto ERROR;
+ }
+@@ -152,7 +192,7 @@ ERROR:
+ return r;
+ }
+
+-int nw_config_writef(struct nw_config* config, FILE* f) {
++static int nw_config_writef(struct nw_config* config, FILE* f) {
+ struct nw_config_entry* entry = NULL;
+ int r;
+
+@@ -172,12 +212,18 @@ int nw_config_writef(struct nw_config* config, FILE* f) {
+ return 0;
+ }
+
+-int nw_config_write(struct nw_config* config, const char* path) {
++int nw_config_write(struct nw_config* config) {
+ int r;
+
+- FILE* f = fopen(path, "w");
++ // We cannot write if path is not set
++ if (!*config->path) {
++ errno = ENOTSUP;
++ return 1;
++ }
++
++ FILE* f = fopen(config->path, "w");
+ if (!f) {
+- ERROR("Failed to open %s for writing: %m\n", path);
++ ERROR("Failed to open %s for writing: %m\n", config->path);
+ r = 1;
+ goto ERROR;
+ }
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index c4f9125..5b9910b 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -28,18 +28,17 @@
+
+ struct nw_config;
+
+-int nw_config_create(struct nw_config** config);
++int nw_config_create(struct nw_config** config, const char* path);
+
+ struct nw_config* nw_config_ref(struct nw_config* config);
+ struct nw_config* nw_config_unref(struct nw_config* config);
+
+-int nw_config_flush(struct nw_config* config);
++const char* nw_config_path(struct nw_config* config);
+
+-int nw_config_readf(struct nw_config* config, FILE* f);
+-int nw_config_read(struct nw_config* config, const char* path);
++int nw_config_flush(struct nw_config* config);
+
+-int nw_config_writef(struct nw_config* config, FILE* f);
+-int nw_config_write(struct nw_config* config, const char* path);
++int nw_config_read(struct nw_config* config);
++int nw_config_write(struct nw_config* config);
+
+ int nw_config_del(struct nw_config* config, const char* key);
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index b93877e..5b097c2 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -110,7 +110,7 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
+ // Read configuration file
+- r = nw_config_read(&daemon->config, CONFIG_DIR "/settings");
++ r = nw_config_create(&daemon->config, CONFIG_DIR "/settings");
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,137 @@
+From 00b5de56ae30d4d8f0288ec160570ab6129aec79 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 22:39:34 +0000
+Subject: [PATCH 169/304] networkd: zones: Try to read configuration
+ automatically
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/string.h | 25 ++++++++++++++++++++++
+ src/networkd/zone.c | 49 +++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 74 insertions(+)
+
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 52b5add..4ae2dbf 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -26,6 +26,9 @@
+ #include <stdio.h>
+ #include <string.h>
+
++#define nw_string_vformat(s, format, ...) \
++ __nw_string_vformat(s, sizeof(s), format, __VA_ARGS__)
++
+ static inline int __nw_string_vformat(char* s, const size_t length,
+ const char* format, va_list args) {
+ // Write string to buffer
+@@ -76,4 +79,26 @@ static inline int __nw_string_set(char* s, const size_t length, const char* valu
+ return __nw_string_format(s, length, "%s", value);
+ }
+
++/*
++ Paths
++*/
++
++#define nw_path_join(s, first, second) \
++ __nw_path_join(s, sizeof(s), first, second)
++
++static inline int __nw_path_join(char* s, const size_t length,
++ const char* first, const char* second) {
++ if (!first)
++ return __nw_string_format(s, length, "%s", second);
++
++ if (!second)
++ return __nw_string_format(s, length, "%s", first);
++
++ // Remove leading slashes from second argument
++ while (*second == '/')
++ second++;
++
++ return __nw_string_format(s, length, "%s/%s", first, second);
++}
++
+ #endif /* NETWORKD_STRING_H */
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 5066c4b..4790d33 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -18,6 +18,7 @@
+ # #
+ #############################################################################*/
+
++#include <limits.h>
+ #include <stdlib.h>
+
+ #include "config.h"
+@@ -33,6 +34,32 @@ struct nw_zone {
+ struct nw_config *config;
+ };
+
++#define nw_zone_path(zone, path, format, ...) \
++ __nw_zone_path(zone, path, sizeof(path), format, __VA_ARGS__)
++
++static int __nw_zone_path(struct nw_zone* zone, char* p, const size_t length,
++ const char* format, ...) {
++ char prefix[NAME_MAX];
++ char suffix[NAME_MAX];
++ va_list args;
++ int r;
++
++ // Format the prefix
++ r = nw_string_format(prefix, "%s/zones/%s", CONFIG_DIR, zone->name);
++ if (r)
++ return r;
++
++ // Format the suffix
++ va_start(args, format);
++ r = nw_string_vformat(suffix, format, args);
++ va_end(args);
++ if (r)
++ return r;
++
++ // Join the two parts together
++ return __nw_path_join(p, length, prefix, suffix);
++}
++
+ static void nw_zone_free(struct nw_zone* zone) {
+ if (zone->config)
+ nw_config_unref(zone->config);
+@@ -40,6 +67,23 @@ static void nw_zone_free(struct nw_zone* zone) {
+ free(zone);
+ }
+
++static int nw_zone_setup(struct nw_zone* zone) {
++ char path[PATH_MAX];
++ int r;
++
++ // Compose the path to the main configuration file
++ r = nw_zone_path(zone, path, "%s", "settings");
++ if (r)
++ return r;
++
++ // Initialize the configuration
++ r = nw_config_create(&zone->config, path);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ int nw_zone_create(struct nw_zone** zone, const char* name) {
+ int r;
+
+@@ -56,6 +100,11 @@ int nw_zone_create(struct nw_zone** zone, const char* name) {
+ if (r)
+ goto ERROR;
+
++ // Setup the zone
++ r = nw_zone_setup(z);
++ if (r)
++ goto ERROR;
++
+ *zone = z;
+ return 0;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,201 @@
+From 19e74c9b5d7e9372a3ea9d27579ba8608626459d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 22:40:04 +0000
+Subject: [PATCH 170/304] networkd: Read all zones from configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 131 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 130 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 5b097c2..86731d1 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -18,9 +18,11 @@
+ # #
+ #############################################################################*/
+
++#include <dirent.h>
+ #include <errno.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <sys/queue.h>
+
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-daemon.h>
+@@ -30,6 +32,14 @@
+ #include "config.h"
+ #include "daemon.h"
+ #include "logging.h"
++#include "zone.h"
++
++struct nw_daemon_zone {
++ struct nw_zone* zone;
++
++ // Link to the other zones
++ STAILQ_ENTRY(nw_daemon_zone) nodes;
++};
+
+ struct nw_daemon {
+ int nrefs;
+@@ -41,6 +51,9 @@ struct nw_daemon {
+
+ // DBus Connection
+ sd_bus* bus;
++
++ // Zones
++ STAILQ_HEAD(zones, nw_daemon_zone) zones;
+ };
+
+ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+@@ -106,7 +119,85 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int nw_daemon_setup(struct nw_daemon* daemon) {
++static int nw_daemon_load_zone_filter(const struct dirent* path) {
++ const char* fn = path->d_name;
++
++ // Ignore everything starting with '.'
++ if (*fn == '.')
++ return 0;
++
++ // Ignore anything that isn't a directory
++ if (path->d_type != DT_DIR)
++ return 0;
++
++ return 1;
++}
++
++static int nw_daemon_add_zone(struct nw_daemon* daemon, struct nw_zone* zone) {
++ // Allocate a new entry
++ struct nw_daemon_zone* entry = calloc(1, sizeof(*entry));
++ if (!entry)
++ return 1;
++
++ // Reference the zone
++ entry->zone = nw_zone_ref(zone);
++
++ // Add it to the list
++ STAILQ_INSERT_TAIL(&daemon->zones, entry, nodes);
++
++ return 0;
++}
++
++static int nw_daemon_load_zones(struct nw_daemon* daemon) {
++ struct dirent** paths = NULL;
++ int n;
++ int r = 0;
++
++ struct nw_zone* zone = NULL;
++
++ // Scan the zones directory
++ n = scandir(CONFIG_DIR "/zones", &paths, nw_daemon_load_zone_filter, alphasort);
++ if (n < 0) {
++ ERROR("Could not load zones: %m\n");
++ return 1;
++ }
++
++ DEBUG("Found %d zone(s)\n", n);
++
++ // Load all zones
++ for (int i = 0; i < n; i++) {
++ const char* name = paths[i]->d_name;
++
++ DEBUG("Loading zone '%s'...\n", name);
++
++ // Create a new zone object
++ r = nw_zone_create(&zone, name);
++ if (r)
++ goto ERROR;
++
++ // Store the zone
++ r = nw_daemon_add_zone(daemon, zone);
++ if (r) {
++ nw_zone_unref(zone);
++ goto ERROR;
++ }
++
++ nw_zone_unref(zone);
++ }
++
++ERROR:
++ // Free paths
++ if (paths) {
++ for (int i = 0; i < n; i++) {
++ free(paths[i]);
++ }
++ free(paths);
++ }
++
++ return r;
++}
++
++static int nw_daemon_load_config(struct nw_daemon* daemon) {
+ int r;
+
+ // Read configuration file
+@@ -114,6 +205,22 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Load zones
++ r = nw_daemon_load_zones(daemon);
++ if (r)
++ return r;
++
++ return r;
++}
++
++static int nw_daemon_setup(struct nw_daemon* daemon) {
++ int r;
++
++ // Read the configuration
++ r = nw_daemon_load_config(daemon);
++ if (r)
++ return r;
++
+ // Setup the event loop
+ r = nw_daemon_setup_loop(daemon);
+ if (r)
+@@ -137,6 +244,9 @@ int nw_daemon_create(struct nw_daemon** daemon) {
+ // Initialize reference counter
+ d->nrefs = 1;
+
++ // Initialize zones
++ STAILQ_INIT(&d->zones);
++
+ // Setup the daemon
+ r = nw_daemon_setup(d);
+ if (r)
+@@ -153,7 +263,26 @@ ERROR:
+ return r;
+ }
+
++static void nw_daemon_free_zones(struct nw_daemon* daemon) {
++ struct nw_daemon_zone* entry = NULL;
++
++ while (!STAILQ_EMPTY(&daemon->zones)) {
++ entry = STAILQ_FIRST(&daemon->zones);
++
++ // Dereference the zone
++ nw_zone_unref(entry->zone);
++
++ // Remove the entry from the list
++ STAILQ_REMOVE_HEAD(&daemon->zones, nodes);
++
++ // Free the entry
++ free(entry);
++ }
++}
++
+ static void nw_daemon_free(struct nw_daemon* daemon) {
++ nw_daemon_free_zones(daemon);
++
+ if (daemon->config)
+ nw_config_unref(daemon->config);
+ if (daemon->bus)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,128 @@
+From 9806a600f4cdf68aa2c0e509e40be6ed9ad08b44 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 23:04:27 +0000
+Subject: [PATCH 171/304] networkd: bus: Create a unified function to register
+ an interface
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 52 +++++++++++++++++++++++++++++++++-------------
+ src/networkd/bus.h | 14 +++++++++++--
+ 2 files changed, 49 insertions(+), 17 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 5224aaa..eab123c 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -38,6 +38,19 @@ static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* err
+ return sd_bus_reply_method_return(m, NULL);
+ }
+
++static const sd_bus_vtable daemon_vtable[] = {
++ SD_BUS_VTABLE_START(0),
++ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
++ nw_bus_daemon_reload, SD_BUS_VTABLE_UNPRIVILEGED),
++ SD_BUS_VTABLE_END,
++};
++
++const struct nw_bus_implementation daemon_implementation = {
++ .path = "/org/ipfire/network1",
++ .interface = "org.ipfire.network1",
++ .vtables = BUS_VTABLES(daemon_vtable),
++};
++
+ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
+ struct nw_daemon* daemon = (struct nw_daemon*)data;
+
+@@ -46,13 +59,6 @@ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error)
+ return 0;
+ }
+
+-static const sd_bus_vtable daemon_vtable[] = {
+- SD_BUS_VTABLE_START(0),
+- SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
+- nw_bus_daemon_reload, SD_BUS_VTABLE_UNPRIVILEGED),
+- SD_BUS_VTABLE_END,
+-};
+-
+ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+ int r;
+
+@@ -117,16 +123,13 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+ return 1;
+ }
+
+- // Register any functions
+- r = sd_bus_add_object_vtable(bus, NULL,
+- NETWORKD_BUS_OBJECT_PATH, NETWORKD_BUS_INTERFACE_NAME, daemon_vtable, NULL);
+- if (r < 0) {
+- ERROR("Could not add object vtable: %s\n", strerror(-r));
+- return 1;
+- }
++ // Register the implementation
++ r = nw_bus_register_implementation(bus, &daemon_implementation, NULL);
++ if (r)
++ return r;
+
+ // Request interface name
+- r = sd_bus_request_name_async(bus, NULL, NETWORKD_BUS_INTERFACE_NAME, 0, NULL, NULL);
++ r = sd_bus_request_name_async(bus, NULL, "org.ipfire.network1", 0, NULL, NULL);
+ if (r < 0) {
+ ERROR("Could not request bus name: %s\n", strerror(-r));
+ return 1;
+@@ -149,3 +152,22 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+
+ return 0;
+ }
++
++int nw_bus_register_implementation(sd_bus* bus,
++ const struct nw_bus_implementation* impl, void* data) {
++ int r;
++
++ DEBUG("Registering bus object implementation for path=%s iface=%s\n",
++ impl->path, impl->interface);
++
++ for (const sd_bus_vtable** vtable = impl->vtables; vtable && *vtable; vtable++) {
++ r = sd_bus_add_object_vtable(bus, NULL, impl->path, impl->interface, *vtable, data);
++ if (r < 0) {
++ ERROR("Could not register bus path %s with interface %s: %m\n",
++ impl->path, impl->interface);
++ return 1;
++ }
++ }
++
++ return 0;
++}
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 90c1556..05846e5 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -22,8 +22,6 @@
+ #define NETWORKD_BUS_H
+
+ #define NETWORKD_BUS_DESCRIPTION "networkd"
+-#define NETWORKD_BUS_OBJECT_PATH "/org/ipfire/network1"
+-#define NETWORKD_BUS_INTERFACE_NAME "org.ipfire.network1"
+
+ #define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket"
+
+@@ -32,4 +30,16 @@
+
+ int nw_bus_connect(sd_bus* bus, sd_event* loop);
+
++struct nw_bus_implementation {
++ const char* path;
++ const char* interface;
++
++ const sd_bus_vtable** vtables;
++};
++
++#define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL })
++
++int nw_bus_register_implementation(sd_bus* bus,
++ const struct nw_bus_implementation* impl, void* data);
++
+ #endif /* NETWORKD_BUS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,99 @@
+From ed00ef209405d6da4478a8eb050e8792187a2619 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 23:19:03 +0000
+Subject: [PATCH 172/304] networkd: Add a dummy bus implementation for zones
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 +++-
+ src/networkd/zone-bus.c | 27 +++++++++++++++++++++++++++
+ src/networkd/zone-bus.h | 28 ++++++++++++++++++++++++++++
+ 3 files changed, 58 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/zone-bus.c
+ create mode 100644 src/networkd/zone-bus.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 3a3f82c..2046fef 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -318,7 +318,9 @@ dist_networkd_SOURCES = \
+ src/networkd/main.c \
+ src/networkd/string.h \
+ src/networkd/zone.c \
+- src/networkd/zone.h
++ src/networkd/zone.h \
++ src/networkd/zone-bus.c \
++ src/networkd/zone-bus.h
+
+ networkd_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+diff --git a/src/networkd/zone-bus.c b/src/networkd/zone-bus.c
+new file mode 100644
+index 0000000..97396c5
+--- /dev/null
++++ b/src/networkd/zone-bus.c
+@@ -0,0 +1,27 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include "bus.h"
++#include "zone-bus.h"
++
++const struct nw_bus_implementation zone_bus_impl = {
++ "/org/ipfire/network1/zone",
++ "org.ipfire.network1.Zone",
++};
+diff --git a/src/networkd/zone-bus.h b/src/networkd/zone-bus.h
+new file mode 100644
+index 0000000..0d5583e
+--- /dev/null
++++ b/src/networkd/zone-bus.h
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_ZONE_BUS_H
++#define NETWORKD_ZONE_BUS_H
++
++#include "bus.h"
++
++extern const struct nw_bus_implementation zone_bus_impl;
++
++#endif /* NETWORKD_ZONE_BUS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,66 @@
+From eaeca0f9f44f0c791f21add7162ee6c7eea5ee4a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Feb 2023 23:25:17 +0000
+Subject: [PATCH 173/304] networkd: Pass daemon to all functions called by the
+ bus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 4 ++--
+ src/networkd/bus.h | 4 +++-
+ src/networkd/daemon.c | 2 +-
+ 3 files changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index eab123c..e854f7b 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -59,7 +59,7 @@ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error)
+ return 0;
+ }
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop) {
++int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon) {
+ int r;
+
+ // Create a bus object
+@@ -124,7 +124,7 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop) {
+ }
+
+ // Register the implementation
+- r = nw_bus_register_implementation(bus, &daemon_implementation, NULL);
++ r = nw_bus_register_implementation(bus, &daemon_implementation, daemon);
+ if (r)
+ return r;
+
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 05846e5..55e65b8 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -28,7 +28,9 @@
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-event.h>
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop);
++#include "daemon.h"
++
++int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon);
+
+ struct nw_bus_implementation {
+ const char* path;
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 86731d1..a478e60 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -227,7 +227,7 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ return r;
+
+ // Connect to the system bus
+- r = nw_bus_connect(daemon->bus, daemon->loop);
++ r = nw_bus_connect(daemon->bus, daemon->loop, daemon);
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,493 @@
+From 0c8e7342a7da002e877ed445d0223453acd4b93d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 2 Feb 2023 00:38:13 +0000
+Subject: [PATCH 174/304] networkd: Move zone list into an own object
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/daemon.c | 121 ++----------------------
+ src/networkd/daemon.h | 4 +
+ src/networkd/zones.c | 213 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/zones.h | 35 +++++++
+ 5 files changed, 263 insertions(+), 112 deletions(-)
+ create mode 100644 src/networkd/zones.c
+ create mode 100644 src/networkd/zones.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 2046fef..ae4cb85 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -317,6 +317,8 @@ dist_networkd_SOURCES = \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/string.h \
++ src/networkd/zones.c \
++ src/networkd/zones.h \
+ src/networkd/zone.c \
+ src/networkd/zone.h \
+ src/networkd/zone-bus.c \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index a478e60..588aa4e 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -18,11 +18,8 @@
+ # #
+ #############################################################################*/
+
+-#include <dirent.h>
+ #include <errno.h>
+ #include <stdlib.h>
+-#include <string.h>
+-#include <sys/queue.h>
+
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-daemon.h>
+@@ -33,13 +30,7 @@
+ #include "daemon.h"
+ #include "logging.h"
+ #include "zone.h"
+-
+-struct nw_daemon_zone {
+- struct nw_zone* zone;
+-
+- // Link to the other zones
+- STAILQ_ENTRY(nw_daemon_zone) nodes;
+-};
++#include "zones.h"
+
+ struct nw_daemon {
+ int nrefs;
+@@ -53,7 +44,7 @@ struct nw_daemon {
+ sd_bus* bus;
+
+ // Zones
+- STAILQ_HEAD(zones, nw_daemon_zone) zones;
++ struct nw_zones* zones;
+ };
+
+ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+@@ -119,84 +110,6 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int nw_daemon_load_zone_filter(const struct dirent* path) {
+- const char* fn = path->d_name;
+-
+- // Ignore everything starting with '.'
+- if (*fn == '.')
+- return 0;
+-
+- // Ignore anything that isn't a directory
+- if (path->d_type != DT_DIR)
+- return 0;
+-
+- return 1;
+-}
+-
+-static int nw_daemon_add_zone(struct nw_daemon* daemon, struct nw_zone* zone) {
+- // Allocate a new entry
+- struct nw_daemon_zone* entry = calloc(1, sizeof(*entry));
+- if (!entry)
+- return 1;
+-
+- // Reference the zone
+- entry->zone = nw_zone_ref(zone);
+-
+- // Add it to the list
+- STAILQ_INSERT_TAIL(&daemon->zones, entry, nodes);
+-
+- return 0;
+-}
+-
+-static int nw_daemon_load_zones(struct nw_daemon* daemon) {
+- struct dirent** paths = NULL;
+- int n;
+- int r = 0;
+-
+- struct nw_zone* zone = NULL;
+-
+- // Scan the zones directory
+- n = scandir(CONFIG_DIR "/zones", &paths, nw_daemon_load_zone_filter, alphasort);
+- if (n < 0) {
+- ERROR("Could not load zones: %m\n");
+- return 1;
+- }
+-
+- DEBUG("Found %d zone(s)\n", n);
+-
+- // Load all zones
+- for (int i = 0; i < n; i++) {
+- const char* name = paths[i]->d_name;
+-
+- DEBUG("Loading zone '%s'...\n", name);
+-
+- // Create a new zone object
+- r = nw_zone_create(&zone, name);
+- if (r)
+- goto ERROR;
+-
+- // Store the zone
+- r = nw_daemon_add_zone(daemon, zone);
+- if (r) {
+- nw_zone_unref(zone);
+- goto ERROR;
+- }
+-
+- nw_zone_unref(zone);
+- }
+-
+-ERROR:
+- // Free paths
+- if (paths) {
+- for (int i = 0; i < n; i++) {
+- free(paths[i]);
+- }
+- free(paths);
+- }
+-
+- return r;
+-}
+-
+ static int nw_daemon_load_config(struct nw_daemon* daemon) {
+ int r;
+
+@@ -206,7 +119,7 @@ static int nw_daemon_load_config(struct nw_daemon* daemon) {
+ return r;
+
+ // Load zones
+- r = nw_daemon_load_zones(daemon);
++ r = nw_zones_load(&daemon->zones);
+ if (r)
+ return r;
+
+@@ -244,9 +157,6 @@ int nw_daemon_create(struct nw_daemon** daemon) {
+ // Initialize reference counter
+ d->nrefs = 1;
+
+- // Initialize zones
+- STAILQ_INIT(&d->zones);
+-
+ // Setup the daemon
+ r = nw_daemon_setup(d);
+ if (r)
+@@ -263,26 +173,9 @@ ERROR:
+ return r;
+ }
+
+-static void nw_daemon_free_zones(struct nw_daemon* daemon) {
+- struct nw_daemon_zone* entry = NULL;
+-
+- while (!STAILQ_EMPTY(&daemon->zones)) {
+- entry = STAILQ_FIRST(&daemon->zones);
+-
+- // Dereference the zone
+- nw_zone_unref(entry->zone);
+-
+- // Remove the entry from the list
+- STAILQ_REMOVE_HEAD(&daemon->zones, nodes);
+-
+- // Free the entry
+- free(entry);
+- }
+-}
+-
+ static void nw_daemon_free(struct nw_daemon* daemon) {
+- nw_daemon_free_zones(daemon);
+-
++ if (daemon->zones)
++ nw_zones_unref(daemon->zones);
+ if (daemon->config)
+ nw_config_unref(daemon->config);
+ if (daemon->bus)
+@@ -342,3 +235,7 @@ int nw_daemon_reload(struct nw_daemon* daemon) {
+
+ return 0;
+ }
++
++struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon) {
++ return nw_zones_ref(daemon->zones);
++}
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 5b14ef3..bc69795 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_DAEMON_H
+ #define NETWORKD_DAEMON_H
+
++#include "zone.h"
++
+ struct nw_daemon;
+
+ int nw_daemon_create(struct nw_daemon** daemon);
+@@ -32,4 +34,6 @@ int nw_daemon_run(struct nw_daemon* daemon);
+
+ int nw_daemon_reload(struct nw_daemon* daemon);
+
++struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon);
++
+ #endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+new file mode 100644
+index 0000000..7d04267
+--- /dev/null
++++ b/src/networkd/zones.c
+@@ -0,0 +1,213 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <dirent.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/queue.h>
++
++#include "logging.h"
++#include "zone.h"
++#include "zones.h"
++
++struct nw_zones_entry {
++ struct nw_zone* zone;
++
++ // Link to the other entries
++ STAILQ_ENTRY(nw_zones_entry) nodes;
++};
++
++struct nw_zones {
++ int nrefs;
++
++ STAILQ_HEAD(entries, nw_zones_entry) entries;
++};
++
++static int nw_zones_create(struct nw_zones** zones) {
++ struct nw_zones* z = calloc(1, sizeof(*z));
++ if (!z)
++ return 1;
++
++ // Initialize the reference counter
++ z->nrefs = 1;
++
++ // Initialize entries
++ STAILQ_INIT(&z->entries);
++
++ // Reference the pointer
++ *zones = z;
++
++ return 0;
++}
++
++static void nw_zones_free(struct nw_zones* zones) {
++ struct nw_zones_entry* entry = NULL;
++
++ while (!STAILQ_EMPTY(&zones->entries)) {
++ entry = STAILQ_FIRST(&zones->entries);
++
++ // Dereference the zone
++ nw_zone_unref(entry->zone);
++
++ // Remove the entry from the list
++ STAILQ_REMOVE_HEAD(&zones->entries, nodes);
++
++ // Free the entry
++ free(entry);
++ }
++}
++
++struct nw_zones* nw_zones_ref(struct nw_zones* zones) {
++ zones->nrefs++;
++
++ return zones;
++}
++
++struct nw_zones* nw_zones_unref(struct nw_zones* zones) {
++ if (--zones->nrefs > 0)
++ return zones;
++
++ nw_zones_free(zones);
++ return NULL;
++}
++
++static int nw_zones_add_zone(struct nw_zones* zones, struct nw_zone* zone) {
++ // Allocate a new entry
++ struct nw_zones_entry* entry = calloc(1, sizeof(*entry));
++ if (!entry)
++ return 1;
++
++ // Reference the zone
++ entry->zone = nw_zone_ref(zone);
++
++ // Add it to the list
++ STAILQ_INSERT_TAIL(&zones->entries, entry, nodes);
++
++ return 0;
++}
++
++static int nw_zones_load_filter(const struct dirent* path) {
++ const char* fn = path->d_name;
++
++ // Ignore everything starting with '.'
++ if (*fn == '.')
++ return 0;
++
++ // Ignore anything that isn't a directory
++ if (path->d_type != DT_DIR)
++ return 0;
++
++ return 1;
++}
++
++static int __nw_zones_load(struct nw_zones* zones) {
++ struct dirent** paths = NULL;
++ int n;
++ int r = 0;
++
++ struct nw_zone* zone = NULL;
++
++ // Scan the zones directory
++ n = scandir(CONFIG_DIR "/zones", &paths, nw_zones_load_filter, alphasort);
++ if (n < 0) {
++ ERROR("Could not load zones: %m\n");
++ return 1;
++ }
++
++ DEBUG("Found %d zone(s)\n", n);
++
++ // Load all zones
++ for (int i = 0; i < n; i++) {
++ const char* name = paths[i]->d_name;
++
++ DEBUG("Loading zone '%s'...\n", name);
++
++ // Create a new zone object
++ r = nw_zone_create(&zone, name);
++ if (r)
++ goto ERROR;
++
++ // Store the zone
++ r = nw_zones_add_zone(zones, zone);
++ if (r) {
++ nw_zone_unref(zone);
++ goto ERROR;
++ }
++
++ nw_zone_unref(zone);
++ }
++
++ERROR:
++ // Free paths
++ if (paths) {
++ for (int i = 0; i < n; i++) {
++ free(paths[i]);
++ }
++ free(paths);
++ }
++
++ return r;
++}
++
++int nw_zones_load(struct nw_zones** zones) {
++ int r;
++
++ // Create a new zones object
++ r = nw_zones_create(zones);
++ if (r)
++ return r;
++
++ // Load all zones
++ r = __nw_zones_load(*zones);
++ if (r)
++ goto ERROR;
++
++ return 0;
++
++ERROR:
++ nw_zones_unref(*zones);
++ return r;
++}
++
++size_t nw_zones_num(struct nw_zones* zones) {
++ struct nw_zones_entry* entry = NULL;
++ size_t length = 0;
++
++ // Count all zones
++ STAILQ_FOREACH(entry, &zones->entries, nodes)
++ length++;
++
++ return length;
++}
++
++struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name) {
++ struct nw_zones_entry* entry = NULL;
++
++ STAILQ_FOREACH(entry, &zones->entries, nodes) {
++ const char* __name = nw_zone_name(entry->zone);
++
++ // If the name matches, return a reference to the zone
++ if (strcmp(name, __name) == 0)
++ return nw_zone_ref(entry->zone);
++ }
++
++ // No match found
++ return NULL;
++}
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+new file mode 100644
+index 0000000..0d34c3e
+--- /dev/null
++++ b/src/networkd/zones.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_ZONES_H
++#define NETWORKD_ZONES_H
++
++struct nw_zones;
++
++struct nw_zones* nw_zones_ref(struct nw_zones* zones);
++struct nw_zones* nw_zones_unref(struct nw_zones* zones);
++
++int nw_zones_load(struct nw_zones** zones);
++
++size_t nw_zones_num(struct nw_zones* zones);
++
++struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name);
++
++#endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,238 @@
+From ebc65f19ecbf67f9c8a739316afe4892d4f6a732 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 2 Feb 2023 00:53:21 +0000
+Subject: [PATCH 175/304] networkd: Implement enumerating zones on the bus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 18 +++++++++++++++++
+ src/networkd/bus.h | 3 +++
+ src/networkd/zone-bus.c | 29 +++++++++++++++++++++++++++
+ src/networkd/zone.c | 14 +++++++++++++
+ src/networkd/zone.h | 2 ++
+ src/networkd/zones.c | 44 +++++++++++++++++++++++++++++++++++++++++
+ src/networkd/zones.h | 2 ++
+ 7 files changed, 112 insertions(+)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index e854f7b..26876fa 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -27,6 +27,7 @@
+ #include "bus.h"
+ #include "daemon.h"
+ #include "logging.h"
++#include "zone-bus.h"
+
+ static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+ struct nw_daemon* daemon = (struct nw_daemon*)data;
+@@ -49,6 +50,7 @@ const struct nw_bus_implementation daemon_implementation = {
+ .path = "/org/ipfire/network1",
+ .interface = "org.ipfire.network1",
+ .vtables = BUS_VTABLES(daemon_vtable),
++ .children = BUS_IMPLEMENTATIONS(&zone_bus_impl),
+ };
+
+ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
+@@ -169,5 +171,21 @@ int nw_bus_register_implementation(sd_bus* bus,
+ }
+ }
+
++ // Register the node enumerator
++ if (impl->node_enumerator) {
++ r = sd_bus_add_node_enumerator(bus, NULL, impl->path, impl->node_enumerator, data);
++ if (r < 0) {
++ ERROR("Could not add the node enumerator for %s: %m\n", impl->path);
++ return 1;
++ }
++ }
++
++ // Register any child implementations
++ for (int i = 0; impl->children && impl->children[i]; i++) {
++ r = nw_bus_register_implementation(bus, impl->children[i], data);
++ if (r)
++ return r;
++ }
++
+ return 0;
+ }
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 55e65b8..6232ebb 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -37,8 +37,11 @@ struct nw_bus_implementation {
+ const char* interface;
+
+ const sd_bus_vtable** vtables;
++ sd_bus_node_enumerator_t node_enumerator;
++ const struct nw_bus_implementation** children;
+ };
+
++#define BUS_IMPLEMENTATIONS(...) ((const struct nw_bus_implementation* []) { __VA_ARGS__, NULL })
+ #define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL })
+
+ int nw_bus_register_implementation(sd_bus* bus,
+diff --git a/src/networkd/zone-bus.c b/src/networkd/zone-bus.c
+index 97396c5..ebd8789 100644
+--- a/src/networkd/zone-bus.c
++++ b/src/networkd/zone-bus.c
+@@ -19,9 +19,38 @@
+ #############################################################################*/
+
+ #include "bus.h"
++#include "daemon.h"
++#include "logging.h"
++#include "zone.h"
+ #include "zone-bus.h"
++#include "zones.h"
++
++static int nw_zone_node_enumerator(sd_bus* bus, const char* path, void* data,
++ char*** nodes, sd_bus_error* error) {
++ int r;
++
++ DEBUG("Enumerating zones...\n");
++
++ // Fetch a reference to the daemon
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Fetch zones
++ struct nw_zones* zones = nw_daemon_zones(daemon);
++
++ // Make bus paths for all zones
++ r = nw_zones_bus_paths(zones, nodes);
++ if (r)
++ goto ERROR;
++
++ERROR:
++ nw_zones_unref(zones);
++
++ return r;
++}
+
+ const struct nw_bus_implementation zone_bus_impl = {
+ "/org/ipfire/network1/zone",
+ "org.ipfire.network1.Zone",
++ //.fallback_vtables = BUS_FALLBACK_VTABLES({zone_bus_vtable, nw_zone_object_find}),
++ .node_enumerator = nw_zone_node_enumerator,
+ };
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 4790d33..f78334e 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -21,6 +21,8 @@
+ #include <limits.h>
+ #include <stdlib.h>
+
++#include <systemd/sd-bus.h>
++
+ #include "config.h"
+ #include "string.h"
+ #include "zone.h"
+@@ -130,3 +132,15 @@ struct nw_zone* nw_zone_unref(struct nw_zone* zone) {
+ const char* nw_zone_name(struct nw_zone* zone) {
+ return zone->name;
+ }
++
++char* nw_zone_bus_path(struct nw_zone* zone) {
++ char* p = NULL;
++ int r;
++
++ // Encode the bus path
++ r = sd_bus_path_encode("/org/ipfire/network1/zone", zone->name, &p);
++ if (r < 0)
++ return NULL;
++
++ return p;
++}
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 78287ab..081a720 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -32,4 +32,6 @@ struct nw_zone* nw_zone_unref(struct nw_zone* zone);
+
+ const char* nw_zone_name(struct nw_zone* zone);
+
++char* nw_zone_bus_path(struct nw_zone* zone);
++
+ #endif /* NETWORKD_ZONE_H */
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 7d04267..4f739f5 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -37,7 +37,11 @@ struct nw_zones_entry {
+ struct nw_zones {
+ int nrefs;
+
++ // Zone Entries
+ STAILQ_HEAD(entries, nw_zones_entry) entries;
++
++ // A counter of the zone entries
++ unsigned int num;
+ };
+
+ static int nw_zones_create(struct nw_zones** zones) {
+@@ -100,6 +104,9 @@ static int nw_zones_add_zone(struct nw_zones* zones, struct nw_zone* zone) {
+ // Add it to the list
+ STAILQ_INSERT_TAIL(&zones->entries, entry, nodes);
+
++ // Increment the counter
++ zones->num++;
++
+ return 0;
+ }
+
+@@ -211,3 +218,40 @@ struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name) {
+ // No match found
+ return NULL;
+ }
++
++int nw_zones_bus_paths(struct nw_zones* zones, char*** paths) {
++ struct nw_zones_entry* entry = NULL;
++ char* path = NULL;
++
++ // Allocate an array for all paths
++ char** p = calloc(zones->num + 1, sizeof(*p));
++ if (!p)
++ return 1;
++
++ unsigned int i = 0;
++
++ // Walk through all zones
++ STAILQ_FOREACH(entry, &zones->entries, nodes) {
++ // Generate the bus path
++ path = nw_zone_bus_path(entry->zone);
++ if (!path)
++ goto ERROR;
++
++ // Append the bus path to the array
++ p[i++] = path;
++ }
++
++ // Return pointer
++ *paths = p;
++
++ return 0;
++
++ERROR:
++ if (p) {
++ for (char** e = p; *e; e++)
++ free(*e);
++ free(p);
++ }
++
++ return 1;
++}
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index 0d34c3e..6e2686d 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -32,4 +32,6 @@ size_t nw_zones_num(struct nw_zones* zones);
+
+ struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name);
+
++int nw_zones_bus_paths(struct nw_zones* zones, char*** paths);
++
+ #endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,159 @@
+From bd9ffd6a19a8bf403a534998e0c2d3e2bf9b51c1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 2 Feb 2023 01:07:51 +0000
+Subject: [PATCH 176/304] networkd: Return zone when it is being accessed by
+ its path
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 12 ++++++++++++
+ src/networkd/bus.h | 8 +++++++-
+ src/networkd/daemon.c | 11 +++++++++++
+ src/networkd/daemon.h | 4 ++++
+ src/networkd/zone-bus.c | 34 +++++++++++++++++++++++++++++++++-
+ 5 files changed, 67 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 26876fa..cab2ae6 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -162,6 +162,7 @@ int nw_bus_register_implementation(sd_bus* bus,
+ DEBUG("Registering bus object implementation for path=%s iface=%s\n",
+ impl->path, impl->interface);
+
++ // Register vtables
+ for (const sd_bus_vtable** vtable = impl->vtables; vtable && *vtable; vtable++) {
+ r = sd_bus_add_object_vtable(bus, NULL, impl->path, impl->interface, *vtable, data);
+ if (r < 0) {
+@@ -171,6 +172,17 @@ int nw_bus_register_implementation(sd_bus* bus,
+ }
+ }
+
++ // Register fallback vtables
++ for (const struct nw_bus_vtable_pair* p = impl->fallback_vtables; p && p->vtable; p++) {
++ r = sd_bus_add_fallback_vtable(bus, NULL, impl->path, impl->interface,
++ p->vtable, p->object_find, data);
++ if (r < 0) {
++ ERROR("Could not register bus path %s with interface %s: %m\n",
++ impl->path, impl->interface);
++ return 1;
++ }
++ }
++
+ // Register the node enumerator
+ if (impl->node_enumerator) {
+ r = sd_bus_add_node_enumerator(bus, NULL, impl->path, impl->node_enumerator, data);
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 6232ebb..42ab2d3 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -32,15 +32,21 @@
+
+ int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon);
+
++struct nw_bus_vtable_pair {
++ const sd_bus_vtable* vtable;
++ sd_bus_object_find_t object_find;
++};
++
+ struct nw_bus_implementation {
+ const char* path;
+ const char* interface;
+-
+ const sd_bus_vtable** vtables;
++ const struct nw_bus_vtable_pair* fallback_vtables;
+ sd_bus_node_enumerator_t node_enumerator;
+ const struct nw_bus_implementation** children;
+ };
+
++#define BUS_FALLBACK_VTABLES(...) ((const struct nw_bus_vtable_pair[]) { __VA_ARGS__, {} })
+ #define BUS_IMPLEMENTATIONS(...) ((const struct nw_bus_implementation* []) { __VA_ARGS__, NULL })
+ #define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL })
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 588aa4e..5b5cbb4 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -236,6 +236,17 @@ int nw_daemon_reload(struct nw_daemon* daemon) {
+ return 0;
+ }
+
++/*
++ Zones
++*/
++
+ struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon) {
+ return nw_zones_ref(daemon->zones);
+ }
++
++struct nw_zone* nw_daemon_get_zone_by_name(struct nw_daemon* daemon, const char* name) {
++ if (!daemon->zones)
++ return NULL;
++
++ return nw_zones_get_by_name(daemon->zones, name);
++}
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index bc69795..891c297 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -34,6 +34,10 @@ int nw_daemon_run(struct nw_daemon* daemon);
+
+ int nw_daemon_reload(struct nw_daemon* daemon);
+
++/*
++ Zones
++*/
+ struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon);
++struct nw_zone* nw_daemon_get_zone_by_name(struct nw_daemon* daemon, const char* name);
+
+ #endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/zone-bus.c b/src/networkd/zone-bus.c
+index ebd8789..aafbfaf 100644
+--- a/src/networkd/zone-bus.c
++++ b/src/networkd/zone-bus.c
+@@ -48,9 +48,41 @@ ERROR:
+ return r;
+ }
+
++static int nw_zone_object_find(sd_bus* bus, const char* path, const char* interface,
++ void* data, void** found, sd_bus_error* error) {
++ char* name = NULL;
++ int r;
++
++ // Fetch a reference to the daemon
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Decode the path of the requested object
++ r = sd_bus_path_decode(path, "/org/ipfire/network1/zone", &name);
++ if (r <= 0)
++ return 0;
++
++ // Find the zone
++ struct nw_zone* zone = nw_daemon_get_zone_by_name(daemon, name);
++ if (!zone)
++ return 0;
++
++ // Match!
++ *found = zone;
++
++ nw_zone_unref(zone);
++
++ return 1;
++}
++
++static const sd_bus_vtable zone_vtable[] = {
++ SD_BUS_VTABLE_START(0),
++
++ SD_BUS_VTABLE_END
++};
++
+ const struct nw_bus_implementation zone_bus_impl = {
+ "/org/ipfire/network1/zone",
+ "org.ipfire.network1.Zone",
+- //.fallback_vtables = BUS_FALLBACK_VTABLES({zone_bus_vtable, nw_zone_object_find}),
++ .fallback_vtables = BUS_FALLBACK_VTABLES({zone_vtable, nw_zone_object_find}),
+ .node_enumerator = nw_zone_node_enumerator,
+ };
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,175 @@
+From d6edaf630588fbc6b54a315f49f252e226c15342 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 2 Feb 2023 01:23:31 +0000
+Subject: [PATCH 177/304] networkd: Split daemon bus implementation into a
+ separate file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/bus.c | 29 ++---------------------
+ src/networkd/daemon-bus.c | 49 +++++++++++++++++++++++++++++++++++++++
+ src/networkd/daemon-bus.h | 28 ++++++++++++++++++++++
+ 4 files changed, 81 insertions(+), 27 deletions(-)
+ create mode 100644 src/networkd/daemon-bus.c
+ create mode 100644 src/networkd/daemon-bus.h
+
+diff --git a/Makefile.am b/Makefile.am
+index ae4cb85..3972d8d 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -314,6 +314,8 @@ dist_networkd_SOURCES = \
+ src/networkd/config.h \
+ src/networkd/daemon.c \
+ src/networkd/daemon.h \
++ src/networkd/daemon-bus.c \
++ src/networkd/daemon-bus.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/string.h \
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index cab2ae6..258aacd 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -19,39 +19,14 @@
+ #############################################################################*/
+
+ #include <stdlib.h>
+-#include <string.h>
+
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-event.h>
+
+ #include "bus.h"
+ #include "daemon.h"
++#include "daemon-bus.h"
+ #include "logging.h"
+-#include "zone-bus.h"
+-
+-static int nw_bus_daemon_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
+-
+- // Reload the daemon
+- nw_daemon_reload(daemon);
+-
+- // Respond with an empty message
+- return sd_bus_reply_method_return(m, NULL);
+-}
+-
+-static const sd_bus_vtable daemon_vtable[] = {
+- SD_BUS_VTABLE_START(0),
+- SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
+- nw_bus_daemon_reload, SD_BUS_VTABLE_UNPRIVILEGED),
+- SD_BUS_VTABLE_END,
+-};
+-
+-const struct nw_bus_implementation daemon_implementation = {
+- .path = "/org/ipfire/network1",
+- .interface = "org.ipfire.network1",
+- .vtables = BUS_VTABLES(daemon_vtable),
+- .children = BUS_IMPLEMENTATIONS(&zone_bus_impl),
+-};
+
+ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
+ struct nw_daemon* daemon = (struct nw_daemon*)data;
+@@ -126,7 +101,7 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon) {
+ }
+
+ // Register the implementation
+- r = nw_bus_register_implementation(bus, &daemon_implementation, daemon);
++ r = nw_bus_register_implementation(bus, &daemon_bus_impl, daemon);
+ if (r)
+ return r;
+
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+new file mode 100644
+index 0000000..aac9775
+--- /dev/null
++++ b/src/networkd/daemon-bus.c
+@@ -0,0 +1,49 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <systemd/sd-bus.h>
++
++#include "bus.h"
++#include "daemon.h"
++#include "zone-bus.h"
++
++static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Reload the daemon
++ nw_daemon_reload(daemon);
++
++ // Respond with an empty message
++ return sd_bus_reply_method_return(m, NULL);
++}
++
++static const sd_bus_vtable daemon_vtable[] = {
++ SD_BUS_VTABLE_START(0),
++ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
++ nw_daemon_bus_reload, SD_BUS_VTABLE_UNPRIVILEGED),
++ SD_BUS_VTABLE_END,
++};
++
++const struct nw_bus_implementation daemon_bus_impl = {
++ .path = "/org/ipfire/network1",
++ .interface = "org.ipfire.network1",
++ .vtables = BUS_VTABLES(daemon_vtable),
++ .children = BUS_IMPLEMENTATIONS(&zone_bus_impl),
++};
+diff --git a/src/networkd/daemon-bus.h b/src/networkd/daemon-bus.h
+new file mode 100644
+index 0000000..787e92a
+--- /dev/null
++++ b/src/networkd/daemon-bus.h
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_DAEMON_BUS_H
++#define NETWORKD_DAEMON_BUS_H
++
++#include "bus.h"
++
++extern const struct nw_bus_implementation daemon_bus_impl;
++
++#endif /* NETWORKD_DAEMON_BUS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,201 @@
+From 9368a163388c9f8665dbdedaccae70ee168df714 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 4 Feb 2023 22:10:56 +0000
+Subject: [PATCH 178/304] networkd: Add a test bus property to set the MTU
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 38 +++++++++++++++++++++++++-------------
+ src/networkd/config.h | 5 +++--
+ src/networkd/zone-bus.c | 34 ++++++++++++++++++++++++++++++++++
+ src/networkd/zone.c | 14 ++++++++++++++
+ src/networkd/zone.h | 7 +++++++
+ 5 files changed, 83 insertions(+), 15 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index e531ec6..788308f 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -273,6 +273,17 @@ int nw_config_del(struct nw_config* config, const char* key) {
+ return 0;
+ }
+
++const char* nw_config_get(struct nw_config* config, const char* key) {
++ struct nw_config_entry* entry = nw_config_find(config, key);
++
++ // Return the value if found and set
++ if (entry && *entry->value)
++ return entry->value;
++
++ // Otherwise return NULL
++ return NULL;
++}
++
+ int nw_config_set(struct nw_config* config, const char* key, const char* value) {
+ struct nw_config_entry* entry = NULL;
+
+@@ -294,23 +305,24 @@ int nw_config_set(struct nw_config* config, const char* key, const char* value)
+ return nw_string_set(entry->value, value);
+ }
+
+-const char* nw_config_get(struct nw_config* config, const char* key) {
+- struct nw_config_entry* entry = nw_config_find(config, key);
+-
+- // Return the value if found and set
+- if (entry && *entry->value)
+- return entry->value;
+-
+- // Otherwise return NULL
+- return NULL;
+-}
+-
+-unsigned int nw_config_get_unsigned_int(struct nw_config* config, const char* key) {
++int nw_config_get_int(struct nw_config* config, const char* key, const int __default) {
+ const char* value = nw_config_get(config, key);
+
+ // Return zero if not set
+ if (!value)
+- return 0;
++ return __default;
+
+ return strtoul(value, NULL, 10);
+ }
++
++int nw_config_set_int(struct nw_config* config, const char* key, const int value) {
++ char __value[1024];
++ int r;
++
++ // Format the value as string
++ r = nw_string_format(__value, "%d\n", value);
++ if (r)
++ return r;
++
++ return nw_config_set(config, key, __value);
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 5b9910b..b8d8555 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -42,9 +42,10 @@ int nw_config_write(struct nw_config* config);
+
+ int nw_config_del(struct nw_config* config, const char* key);
+
++const char* nw_config_get(struct nw_config* config, const char* key);
+ int nw_config_set(struct nw_config* config, const char* key, const char* value);
+
+-const char* nw_config_get(struct nw_config* config, const char* key);
+-unsigned int nw_config_get_unsigned_int(struct nw_config* config, const char* key);
++int nw_config_get_int(struct nw_config* config, const char* key, const int __default);
++int nw_config_set_int(struct nw_config* config, const char* key, const int value);
+
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/zone-bus.c b/src/networkd/zone-bus.c
+index aafbfaf..e9c2ecc 100644
+--- a/src/networkd/zone-bus.c
++++ b/src/networkd/zone-bus.c
+@@ -18,6 +18,8 @@
+ # #
+ #############################################################################*/
+
++#include <errno.h>
++
+ #include "bus.h"
+ #include "daemon.h"
+ #include "logging.h"
+@@ -74,9 +76,41 @@ static int nw_zone_object_find(sd_bus* bus, const char* path, const char* interf
+ return 1;
+ }
+
++/*
++ MTU
++*/
++static int nw_zone_bus_get_mtu(sd_bus* bus, const char *path, const char *interface,
++ const char* property, sd_bus_message* reply, void* data, sd_bus_error *error) {
++ struct nw_zone* zone = (struct nw_zone*)data;
++
++ return sd_bus_message_append(reply, "u", nw_zone_mtu(zone));
++}
++
++static int nw_zone_bus_set_mtu(sd_bus* bus, const char* path, const char* interface,
++ const char* property, sd_bus_message* value, void* data, sd_bus_error* error) {
++ unsigned int mtu = 0;
++ int r;
++
++ struct nw_zone* zone = (struct nw_zone*)data;
++
++ // Parse the value
++ r = sd_bus_message_read(value, "u", &mtu);
++ if (r < 0)
++ return r;
++
++ if (!nw_zone_set_mtu(zone, mtu))
++ return -errno;
++
++ return 0;
++}
++
+ static const sd_bus_vtable zone_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
++ // MTU
++ SD_BUS_WRITABLE_PROPERTY("MTU", "u", nw_zone_bus_get_mtu, nw_zone_bus_set_mtu,
++ 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
++
+ SD_BUS_VTABLE_END
+ };
+
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index f78334e..c64706f 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -24,6 +24,7 @@
+ #include <systemd/sd-bus.h>
+
+ #include "config.h"
++#include "logging.h"
+ #include "string.h"
+ #include "zone.h"
+
+@@ -144,3 +145,16 @@ char* nw_zone_bus_path(struct nw_zone* zone) {
+
+ return p;
+ }
++
++/*
++ MTU
++*/
++unsigned int nw_zone_mtu(struct nw_zone* zone) {
++ return nw_config_get_int(zone->config, "MTU", NETWORK_ZONE_DEFAULT_MTU);
++}
++
++int nw_zone_set_mtu(struct nw_zone* zone, unsigned int mtu) {
++ DEBUG("Change MTU of %s to %u\n", zone->name, mtu);
++
++ return nw_config_set_int(zone->config, "MTU", mtu);
++}
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 081a720..4a00412 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -22,6 +22,7 @@
+ #define NETWORKD_ZONE_H
+
+ #define NETWORK_ZONE_NAME_MAX_LENGTH 16
++#define NETWORK_ZONE_DEFAULT_MTU 1500
+
+ struct nw_zone;
+
+@@ -34,4 +35,10 @@ const char* nw_zone_name(struct nw_zone* zone);
+
+ char* nw_zone_bus_path(struct nw_zone* zone);
+
++/*
++ MTU
++*/
++unsigned int nw_zone_mtu(struct nw_zone* zone);
++int nw_zone_set_mtu(struct nw_zone* zone, unsigned int mtu);
++
+ #endif /* NETWORKD_ZONE_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,203 @@
+From b286cd831946a3134f5355fe45901a8a4057dce1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 5 Feb 2023 11:05:23 +0000
+Subject: [PATCH 179/304] networkd: Connect to udev
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/daemon.c | 64 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/devmon.c | 27 ++++++++++++++++++
+ src/networkd/devmon.h | 28 +++++++++++++++++++
+ 4 files changed, 121 insertions(+)
+ create mode 100644 src/networkd/devmon.c
+ create mode 100644 src/networkd/devmon.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 3972d8d..2ebe622 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -316,6 +316,8 @@ dist_networkd_SOURCES = \
+ src/networkd/daemon.h \
+ src/networkd/daemon-bus.c \
+ src/networkd/daemon-bus.h \
++ src/networkd/devmon.c \
++ src/networkd/devmon.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/string.h \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 5b5cbb4..960fd96 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -23,15 +23,20 @@
+
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-daemon.h>
++#include <systemd/sd-device.h>
+ #include <systemd/sd-event.h>
+
+ #include "bus.h"
+ #include "config.h"
+ #include "daemon.h"
++#include "devmon.h"
+ #include "logging.h"
+ #include "zone.h"
+ #include "zones.h"
+
++// Increase the receive buffer to 128 MiB
++#define RCVBUF_SIZE 128 * 1024 * 1024
++
+ struct nw_daemon {
+ int nrefs;
+
+@@ -43,6 +48,9 @@ struct nw_daemon {
+ // DBus Connection
+ sd_bus* bus;
+
++ // udev Connection
++ sd_device_monitor* devmon;
++
+ // Zones
+ struct nw_zones* zones;
+ };
+@@ -126,6 +134,57 @@ static int nw_daemon_load_config(struct nw_daemon* daemon) {
+ return r;
+ }
+
++static int nw_start_device_monitor(struct nw_daemon* daemon) {
++ int r;
++
++ const char* subsystems[] = {
++ "net",
++ "ieee80211",
++ "rfkill",
++ NULL,
++ };
++
++ // Create a new connection to monitor any devices
++ r = sd_device_monitor_new(&daemon->devmon);
++ if (r < 0) {
++ ERROR("Could not inititalize the device monitor: %m\n");
++ return 1;
++ }
++
++ // Increase the receive buffer
++ r = sd_device_monitor_set_receive_buffer_size(daemon->devmon, RCVBUF_SIZE);
++ if (r < 0) {
++ ERROR("Could not increase buffer size for the device monitor: %m\n");
++ return 1;
++ }
++
++ // Filter for events for all relevant subsystems
++ for (const char** subsystem = subsystems; *subsystem; subsystem++) {
++ r = sd_device_monitor_filter_add_match_subsystem_devtype(
++ daemon->devmon, *subsystem, NULL);
++ if (r < 1) {
++ ERROR("Could not add device monitor for the %s subsystem: %m\n", *subsystem);
++ return 1;
++ }
++ }
++
++ // Attach the device monitor to the event loop
++ r = sd_device_monitor_attach_event(daemon->devmon, daemon->loop);
++ if (r < 0) {
++ ERROR("Could not attach the device monitor to the event loop: %m\n");
++ return 1;
++ }
++
++ // Start processing events...
++ r = sd_device_monitor_start(daemon->devmon, nw_devmon_handle_uevent, daemon);
++ if (r < 0) {
++ ERROR("Could not start the device monitor: %m\n");
++ return 1;
++ }
++
++ return 0;
++}
++
+ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
+@@ -144,6 +203,11 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Connect to udev
++ r = nw_start_device_monitor(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+diff --git a/src/networkd/devmon.c b/src/networkd/devmon.c
+new file mode 100644
+index 0000000..0a8c26d
+--- /dev/null
++++ b/src/networkd/devmon.c
+@@ -0,0 +1,27 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <systemd/sd-device.h>
++
++#include "devmon.h"
++
++int nw_devmon_handle_uevent(sd_device_monitor* monitor, sd_device* device, void* data) {
++ return 0;
++}
+diff --git a/src/networkd/devmon.h b/src/networkd/devmon.h
+new file mode 100644
+index 0000000..0d8970a
+--- /dev/null
++++ b/src/networkd/devmon.h
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_DEVMON_H
++#define NETWORKD_DEVMON_H
++
++#include <systemd/sd-device.h>
++
++int nw_devmon_handle_uevent(sd_device_monitor* monitor, sd_device* device, void* data);
++
++#endif /* NETWORKD_DEVMON_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,105 @@
+From eb3f6449e9319715fbb6135b1a47d18eaac2169e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 5 Feb 2023 12:06:19 +0000
+Subject: [PATCH 180/304] networkd: Change to a non-privileged user right away
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/main.c | 73 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 72 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 51a9bba..fbd6c3b 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -18,15 +18,86 @@
+ # #
+ #############################################################################*/
+
++#include <grp.h>
++#include <pwd.h>
+ #include <stddef.h>
++#include <sys/prctl.h>
++#include <sys/types.h>
++#include <unistd.h>
+
+ #include "daemon.h"
++#include "logging.h"
++
++static int drop_privileges(const char* user) {
++ struct passwd* passwd = NULL;
++ int r;
++
++ // Fetch the current user
++ uid_t current_uid = getuid();
++
++ // If we have not been launched by root, we will assume that we have already
++ // been launched with a minimal set of privileges.
++ if (current_uid > 0)
++ return 0;
++
++ DEBUG("Dropping privileges...\n");
++
++ // Fetch information about the desired user
++ passwd = getpwnam(user);
++ if (!passwd) {
++ ERROR("Could not find user %s: %m\n", user);
++ return 1;
++ }
++
++ uid_t uid = passwd->pw_uid;
++ gid_t gid = passwd->pw_gid;
++
++ // Change group
++ r = setresgid(gid, gid, gid);
++ if (r) {
++ ERROR("Could not change group to %d: %m\n", gid);
++ return 1;
++ }
++
++ // Set any supplementary groups
++ r = setgroups(0, NULL);
++ if (r) {
++ ERROR("Could not set supplementary groups: %m\n");
++ return 1;
++ }
++
++ // Do not drop any capabilities when we change to the new user
++ r = prctl(PR_SET_KEEPCAPS, 1);
++ if (r) {
++ ERROR("Could not set PR_SET_KEEPCAPS: %m\n");
++ return 1;
++ }
++
++ // Change to the new user
++ r = setresuid(uid, uid, uid);
++ if (r) {
++ ERROR("Could not change user to %d: %m\n", uid);
++ return 1;
++ }
++
++ // Reset PR_SET_KEEPCAPS
++ r = prctl(PR_SET_KEEPCAPS, 0);
++ if (r) {
++ ERROR("Could not set PR_SET_KEEPCAPS: %m\n");
++ return 1;
++ }
++
++ return 0;
++}
+
+ int main(int argc, char** argv) {
+ struct nw_daemon* daemon = NULL;
+ int r;
+
+- // XXX Drop privileges
++ // Drop privileges
++ r = drop_privileges("network");
++ if (r)
++ return r;
+
+ // Create the daemon
+ r = nw_daemon_create(&daemon);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,195 @@
+From dabf344de60d568e304a7f4d10548813ee4ce588 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 5 Feb 2023 13:17:21 +0000
+Subject: [PATCH 181/304] networkd: Drop all capabilities except a few we would
+ like to keep
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ configure.ac | 1 +
+ src/networkd/main.c | 121 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 124 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 2ebe622..df61552 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -334,12 +334,14 @@ networkd_CPPFLAGS = \
+
+ networkd_CFLAGS = \
+ $(AM_CFLAGS) \
++ $(CAP_CFLAGS) \
+ $(SYSTEMD_CFLAGS)
+
+ networkd_LDFLAGS = \
+ $(AM_LDFLAGS)
+
+ networkd_LDADD = \
++ $(CAP_LIBS) \
+ $(SYSTEMD_LIBS)
+
+ dist_dbuspolicy_DATA += \
+diff --git a/configure.ac b/configure.ac
+index 16f0724..e1baa64 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -172,6 +172,7 @@ AM_CONDITIONAL(HAVE_UDEV, [test -n "$with_udevdir"])
+
+ # ------------------------------------------------------------------------------
+
++PKG_CHECK_MODULES([CAP], [libcap])
+ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
+ PKG_CHECK_MODULES([SYSTEMD], [libsystemd])
+
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index fbd6c3b..9a0fd74 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -19,8 +19,10 @@
+ #############################################################################*/
+
+ #include <grp.h>
++#include <linux/capability.h>
+ #include <pwd.h>
+ #include <stddef.h>
++#include <sys/capability.h>
+ #include <sys/prctl.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+@@ -28,6 +30,120 @@
+ #include "daemon.h"
+ #include "logging.h"
+
++static int cap_acquire_setpcap(void) {
++ cap_flag_value_t value;
++ int r;
++
++ // Fetch current capabilities
++ cap_t caps = cap_get_proc();
++
++ // Check if CAP_SETPCAP is already enabled
++ r = cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &value);
++ if (r) {
++ ERROR("The kernel does not seem to know CAP_SETPCAP: %m\n");
++ goto ERROR;
++ }
++
++ // It CAP_SETPCAP isn't set, we will try to set it
++ if (value != CAP_SET) {
++ const cap_value_t cap = CAP_SETPCAP;
++
++ r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
++ if (r) {
++ ERROR("Could not set CAP_SETPCAP: %m\n");
++ goto ERROR;
++ }
++
++ // Store capabilities
++ r = cap_set_proc(caps);
++ if (r) {
++ ERROR("Could not acquire effective CAP_SETPCAP capability: %m\n");
++ goto ERROR;
++ }
++ }
++
++ERROR:
++ if (caps)
++ cap_free(caps);
++
++ return r;
++}
++
++static cap_flag_value_t keep_cap(const cap_value_t cap, const cap_value_t* keep_caps) {
++ for (const cap_value_t* c = keep_caps; *c; c++) {
++ if (cap == *c)
++ return CAP_SET;
++ }
++
++ return CAP_CLEAR;
++}
++
++static int drop_capabilities(void) {
++ int r;
++
++ const cap_value_t keep_caps[] = {
++ CAP_NET_ADMIN,
++ CAP_NET_BIND_SERVICE,
++ CAP_NET_BROADCAST,
++ CAP_NET_RAW,
++ 0,
++ };
++
++ // Acquire CAP_SETPCAP
++ r = cap_acquire_setpcap();
++ if (r)
++ return r;
++
++ // Fetch the current set of capabilities
++ cap_t caps = cap_get_proc();
++
++ // Drop all capabilities that we do not need
++ for (cap_value_t cap = 1; cap <= CAP_LAST_CAP; cap++) {
++ // Skip any capabilities we would like to skip
++ cap_flag_value_t flag = keep_cap(cap, keep_caps);
++
++ // Drop the capability from the bounding set
++ if (flag == CAP_CLEAR) {
++ r = prctl(PR_CAPBSET_DROP, cap);
++ if (r) {
++ ERROR("Could not drop capability from the bounding set: %m\n");
++ goto ERROR;
++ }
++ }
++
++ r = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR);
++ if (r) {
++ ERROR("Could not set capability %d: %m\n", (int)cap);
++ goto ERROR;
++ }
++
++ r = cap_set_flag(caps, CAP_PERMITTED, 1, &cap, flag);
++ if (r) {
++ ERROR("Could not set capability %d: %m\n", (int)cap);
++ goto ERROR;
++ }
++
++ r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
++ if (r) {
++ ERROR("Could not set capability %d: %m\n", (int)cap);
++ goto ERROR;
++ }
++ }
++
++ // Restore capabilities
++ r = cap_set_proc(caps);
++ if (r) {
++ ERROR("Could not restore capabilities: %m\n");
++ goto ERROR;
++ }
++
++ERROR:
++ if (caps)
++ cap_free(caps);
++
++ return r;
++}
++
+ static int drop_privileges(const char* user) {
+ struct passwd* passwd = NULL;
+ int r;
+@@ -87,6 +203,11 @@ static int drop_privileges(const char* user) {
+ return 1;
+ }
+
++ // Drop capabilities
++ r = drop_capabilities();
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,95 @@
+From e62b300ef78484b882d2da8683ebe79896523a13 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 19:46:25 +0000
+Subject: [PATCH 182/304] networkd: Connect to the kernel's netlink interface
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 50 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 960fd96..13e1257 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -25,6 +25,7 @@
+ #include <systemd/sd-daemon.h>
+ #include <systemd/sd-device.h>
+ #include <systemd/sd-event.h>
++#include <systemd/sd-netlink.h>
+
+ #include "bus.h"
+ #include "config.h"
+@@ -45,6 +46,9 @@ struct nw_daemon {
+ // Event Loop
+ sd_event* loop;
+
++ // Netlink Connection
++ sd_netlink* rtnl;
++
+ // DBus Connection
+ sd_bus* bus;
+
+@@ -185,6 +189,47 @@ static int nw_start_device_monitor(struct nw_daemon* daemon) {
+ return 0;
+ }
+
++static int _rtnl_dummy(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
++ DEBUG("_rtnl_dummy called\n");
++
++ return 0;
++}
++
++static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
++ int r;
++
++ // Connect to Netlink
++ r = sd_netlink_open(&daemon->rtnl);
++ if (r < 0) {
++ ERROR("Could not connect to the kernel's netlink interface: %m\n");
++ return 1;
++ }
++
++ // Increase the receive buffer
++ r = sd_netlink_increase_rxbuf(daemon->rtnl, RCVBUF_SIZE);
++ if (r < 0) {
++ ERROR("Could not increase receive buffer for the netlink socket: %m\n");
++ return 1;
++ }
++
++ // Connect it to the event loop
++ r = sd_netlink_attach_event(daemon->rtnl, daemon->loop, 0);
++ if (r < 0) {
++ ERROR("Could not connect the netlink socket to the event loop: %m\n");
++ return 1;
++ }
++
++ // Register callback for new interfaces
++ r = sd_netlink_add_match(daemon->rtnl, NULL, RTM_NEWLINK, _rtnl_dummy, NULL,
++ daemon, "networkd-RTM_NEWLINK");
++ if (r < 0) {
++ ERROR("Could not register RTM_NEWLINK: %m\n");
++ return 1;
++ }
++
++ return 0;
++}
++
+ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
+@@ -198,6 +243,11 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Connect to the kernel's netlink interface
++ r = nw_daemon_connect_rtnl(daemon, 0);
++ if (r)
++ return r;
++
+ // Connect to the system bus
+ r = nw_bus_connect(daemon->bus, daemon->loop, daemon);
+ if (r)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From b53b186e84560a3376da5e858e41c23bb4526847 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 19:50:47 +0000
+Subject: [PATCH 183/304] networkd: Link against libnetwork
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index df61552..80218d9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -341,6 +341,7 @@ networkd_LDFLAGS = \
+ $(AM_LDFLAGS)
+
+ networkd_LDADD = \
++ src/libnetwork.la \
+ $(CAP_LIBS) \
+ $(SYSTEMD_LIBS)
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,145 @@
+From 87a1e1e0b052f814aac355b60aedf736eef0bead Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 20:05:00 +0000
+Subject: [PATCH 184/304] networkd: Add a link object
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/link.c | 72 +++++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/link.h | 31 +++++++++++++++++++
+ 3 files changed, 105 insertions(+)
+ create mode 100644 src/networkd/link.c
+ create mode 100644 src/networkd/link.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 80218d9..e8946b7 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -318,6 +318,8 @@ dist_networkd_SOURCES = \
+ src/networkd/daemon-bus.h \
+ src/networkd/devmon.c \
+ src/networkd/devmon.h \
++ src/networkd/link.c \
++ src/networkd/link.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/string.h \
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+new file mode 100644
+index 0000000..06e3101
+--- /dev/null
++++ b/src/networkd/link.c
+@@ -0,0 +1,72 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stddef.h>
++#include <stdlib.h>
++
++#include "daemon.h"
++#include "link.h"
++
++struct nw_link {
++ struct nw_daemon* daemon;
++ int nrefs;
++
++ // Interface Index
++ int ifindex;
++};
++
++int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex) {
++ // Allocate a new object
++ struct nw_link* l = calloc(1, sizeof(*l));
++ if (!l)
++ return 1;
++
++ // Store a reference to the daemon
++ l->daemon = nw_daemon_ref(daemon);
++
++ // Initialize the reference counter
++ l->nrefs = 1;
++
++ // Store the ifindex
++ l->ifindex = ifindex;
++
++ *link = l;
++
++ return 0;
++}
++
++static void nw_link_free(struct nw_link* link) {
++ if (link->daemon)
++ nw_daemon_unref(link->daemon);
++}
++
++struct nw_link* nw_link_ref(struct nw_link* link) {
++ link->nrefs++;
++
++ return link;
++}
++
++struct nw_link* nw_link_unref(struct nw_link* link) {
++ if (--link->nrefs > 0)
++ return link;
++
++ nw_link_free(link);
++ return NULL;
++}
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+new file mode 100644
+index 0000000..cd79fde
+--- /dev/null
++++ b/src/networkd/link.h
+@@ -0,0 +1,31 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_LINK_H
++#define NETWORKD_LINK_H
++
++struct nw_link;
++
++int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex);
++
++struct nw_link* nw_link_ref(struct nw_link* link);
++struct nw_link* nw_link_unref(struct nw_link* link);
++
++#endif /* NETWORKD_LINK_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,303 @@
+From 18b8c8415c595442f796638f85a244915339c974 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 20:25:29 +0000
+Subject: [PATCH 185/304] networkd: Add a container for links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/daemon.c | 48 ++++++++++++++++++-
+ src/networkd/link.h | 2 +
+ src/networkd/links.c | 105 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/links.h | 35 ++++++++++++++
+ 5 files changed, 190 insertions(+), 2 deletions(-)
+ create mode 100644 src/networkd/links.c
+ create mode 100644 src/networkd/links.h
+
+diff --git a/Makefile.am b/Makefile.am
+index e8946b7..024bf45 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -320,6 +320,8 @@ dist_networkd_SOURCES = \
+ src/networkd/devmon.h \
+ src/networkd/link.c \
+ src/networkd/link.h \
++ src/networkd/links.c \
++ src/networkd/links.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/string.h \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 13e1257..27b118e 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -31,6 +31,7 @@
+ #include "config.h"
+ #include "daemon.h"
+ #include "devmon.h"
++#include "links.h"
+ #include "logging.h"
+ #include "zone.h"
+ #include "zones.h"
+@@ -55,6 +56,9 @@ struct nw_daemon {
+ // udev Connection
+ sd_device_monitor* devmon;
+
++ // Links
++ struct nw_links* links;
++
+ // Zones
+ struct nw_zones* zones;
+ };
+@@ -230,6 +234,28 @@ static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
+ return 0;
+ }
+
++static int nw_daemon_enumerate_links(struct nw_daemon* daemon) {
++ int r;
++
++ // Create a new links container
++ r = nw_links_create(&daemon->links, daemon);
++ if (r)
++ return r;
++
++ return nw_links_enumerate(daemon->links);
++}
++
++static int nw_daemon_enumerate(struct nw_daemon* daemon) {
++ int r;
++
++ // Links
++ r = nw_daemon_enumerate_links(daemon);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ int r;
+
+@@ -258,6 +284,11 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Enumerate everything we need to know
++ r = nw_daemon_enumerate(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+@@ -287,11 +318,19 @@ ERROR:
+ return r;
+ }
+
+-static void nw_daemon_free(struct nw_daemon* daemon) {
++static void nw_daemon_cleanup(struct nw_daemon* daemon) {
+ if (daemon->zones)
+ nw_zones_unref(daemon->zones);
++ if (daemon->links)
++ nw_links_unref(daemon->links);
+ if (daemon->config)
+ nw_config_unref(daemon->config);
++}
++
++static void nw_daemon_free(struct nw_daemon* daemon) {
++ // Cleanup common objects
++ nw_daemon_cleanup(daemon);
++
+ if (daemon->bus)
+ sd_bus_unref(daemon->bus);
+ if (daemon->loop)
+@@ -330,15 +369,20 @@ int nw_daemon_run(struct nw_daemon* daemon) {
+ goto ERROR;
+ }
+
+-
+ // Let systemd know that we are shutting down
+ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
+
++ // Cleanup everything
++ nw_daemon_cleanup(daemon);
++
+ return 0;
+
+ ERROR:
+ sd_notifyf(0, "ERRNO=%i", -r);
+
++ // Cleanup everything
++ nw_daemon_cleanup(daemon);
++
+ return 1;
+ }
+
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index cd79fde..6ffe843 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_LINK_H
+ #define NETWORKD_LINK_H
+
++#include "daemon.h"
++
+ struct nw_link;
+
+ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex);
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+new file mode 100644
+index 0000000..b81d6a5
+--- /dev/null
++++ b/src/networkd/links.c
+@@ -0,0 +1,105 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stddef.h>
++#include <stdlib.h>
++#include <sys/queue.h>
++
++#include "daemon.h"
++#include "link.h"
++#include "links.h"
++
++struct nw_links_entry {
++ struct nw_link* link;
++
++ // Link to the other entries
++ STAILQ_ENTRY(nw_links_entry) nodes;
++};
++
++struct nw_links {
++ struct nw_daemon* daemon;
++ int nrefs;
++
++ // Link Entries
++ STAILQ_HEAD(entries, nw_links_entry) entries;
++
++ // A counter of the link entries
++ unsigned int num;
++};
++
++int nw_links_create(struct nw_links** links, struct nw_daemon* daemon) {
++ struct nw_links* l = calloc(1, sizeof(*l));
++ if (!l)
++ return 1;
++
++ // Store a reference to the daemon
++ l->daemon = nw_daemon_ref(daemon);
++
++ // Initialize the reference counter
++ l->nrefs = 1;
++
++ // Initialize entries
++ STAILQ_INIT(&l->entries);
++
++ // Reference the pointer
++ *links = l;
++
++ return 0;
++}
++
++static void nw_links_free(struct nw_links* links) {
++ struct nw_links_entry* entry = NULL;
++
++ while (!STAILQ_EMPTY(&links->entries)) {
++ entry = STAILQ_FIRST(&links->entries);
++
++ // Dereference the zone
++ nw_link_unref(entry->link);
++
++ // Remove the entry from the list
++ STAILQ_REMOVE_HEAD(&links->entries, nodes);
++
++ // Free the entry
++ free(entry);
++ }
++
++ if (links->daemon)
++ nw_daemon_unref(links->daemon);
++}
++
++struct nw_links* nw_links_ref(struct nw_links* links) {
++ links->nrefs++;
++
++ return links;
++}
++
++struct nw_links* nw_links_unref(struct nw_links* links) {
++ if (--links->nrefs > 0)
++ return links;
++
++ nw_links_free(links);
++ return NULL;
++}
++
++int nw_links_enumerate(struct nw_links* links) {
++ // TODO
++
++ return 0;
++}
+diff --git a/src/networkd/links.h b/src/networkd/links.h
+new file mode 100644
+index 0000000..b2976e4
+--- /dev/null
++++ b/src/networkd/links.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_LINKS_H
++#define NETWORKD_LINKS_H
++
++#include "daemon.h"
++
++struct nw_links;
++
++int nw_links_create(struct nw_links** links, struct nw_daemon* daemon);
++
++struct nw_links* nw_links_ref(struct nw_links* links);
++struct nw_links* nw_links_unref(struct nw_links* links);
++
++int nw_links_enumerate(struct nw_links* links);
++
++#endif /* NETWORKD_LINKS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,172 @@
+From eaf649ee9bdfa7e20ba381a505e2c5b48501c242 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 20:41:02 +0000
+Subject: [PATCH 186/304] networkd: Enumerate all links on startup
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 16 +++++++++-------
+ src/networkd/daemon.h | 7 +++++++
+ src/networkd/link.c | 7 +++++++
+ src/networkd/link.h | 2 ++
+ src/networkd/links.c | 39 +++++++++++++++++++++++++++++++++++++--
+ 5 files changed, 62 insertions(+), 9 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 27b118e..88ce8e7 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -31,6 +31,7 @@
+ #include "config.h"
+ #include "daemon.h"
+ #include "devmon.h"
++#include "link.h"
+ #include "links.h"
+ #include "logging.h"
+ #include "zone.h"
+@@ -193,12 +194,6 @@ static int nw_start_device_monitor(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int _rtnl_dummy(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+- DEBUG("_rtnl_dummy called\n");
+-
+- return 0;
+-}
+-
+ static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
+ int r;
+
+@@ -224,7 +219,7 @@ static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
+ }
+
+ // Register callback for new interfaces
+- r = sd_netlink_add_match(daemon->rtnl, NULL, RTM_NEWLINK, _rtnl_dummy, NULL,
++ r = sd_netlink_add_match(daemon->rtnl, NULL, RTM_NEWLINK, nw_link_process, NULL,
+ daemon, "networkd-RTM_NEWLINK");
+ if (r < 0) {
+ ERROR("Could not register RTM_NEWLINK: %m\n");
+@@ -394,6 +389,13 @@ int nw_daemon_reload(struct nw_daemon* daemon) {
+ return 0;
+ }
+
++/*
++ Netlink
++*/
++sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon) {
++ return daemon->rtnl;
++}
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 891c297..543685f 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_DAEMON_H
+ #define NETWORKD_DAEMON_H
+
++#include <systemd/sd-netlink.h>
++
+ #include "zone.h"
+
+ struct nw_daemon;
+@@ -34,6 +36,11 @@ int nw_daemon_run(struct nw_daemon* daemon);
+
+ int nw_daemon_reload(struct nw_daemon* daemon);
+
++/*
++ Netlink
++*/
++sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon);
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 06e3101..ec0593b 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -23,6 +23,7 @@
+
+ #include "daemon.h"
+ #include "link.h"
++#include "logging.h"
+
+ struct nw_link {
+ struct nw_daemon* daemon;
+@@ -70,3 +71,9 @@ struct nw_link* nw_link_unref(struct nw_link* link) {
+ nw_link_free(link);
+ return NULL;
+ }
++
++int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
++ DEBUG("nw_link_process called\n");
++
++ return 0;
++}
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 6ffe843..6003133 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -30,4 +30,6 @@ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex)
+ struct nw_link* nw_link_ref(struct nw_link* link);
+ struct nw_link* nw_link_unref(struct nw_link* link);
+
++int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
++
+ #endif /* NETWORKD_LINK_H */
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index b81d6a5..22584f2 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -99,7 +99,42 @@ struct nw_links* nw_links_unref(struct nw_links* links) {
+ }
+
+ int nw_links_enumerate(struct nw_links* links) {
+- // TODO
++ sd_netlink_message* req = NULL;
++ sd_netlink_message* res = NULL;
++ int r;
+
+- return 0;
++ sd_netlink* rtnl = nw_daemon_get_rtnl(links->daemon);
++ if (!rtnl)
++ return 1;
++
++ r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
++ if (r < 0)
++ return 1;
++
++ r = sd_netlink_message_set_request_dump(req, 1);
++ if (r < 0)
++ return 1;
++
++ r = sd_netlink_call(rtnl, req, 0, &res);
++ if (r < 0)
++ return 1;
++
++ sd_netlink_message* m = res;
++
++ // Iterate through all replies
++ do {
++ r = nw_link_process(rtnl, m, links->daemon);
++ if (r)
++ goto ERROR;
++ } while ((m = sd_netlink_message_next(m)));
++
++ERROR:
++ if (m)
++ sd_netlink_message_unref(m);
++ if (req)
++ sd_netlink_message_unref(req);
++ if (res)
++ sd_netlink_message_unref(res);
++
++ return r;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,362 @@
+From 766f08ca103698e6578228e11f81c1d73d687d65 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 9 Feb 2023 21:38:09 +0000
+Subject: [PATCH 187/304] networkd: Create a link object for each interface
+
+We are also listening for netlink events that add and delete any
+interfaces.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 29 +++++++++++
+ src/networkd/daemon.h | 12 ++++-
+ src/networkd/link.c | 117 ++++++++++++++++++++++++++++++++++++++++--
+ src/networkd/link.h | 2 +
+ src/networkd/links.c | 54 +++++++++++++++++++
+ src/networkd/links.h | 6 +++
+ 6 files changed, 215 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 88ce8e7..dc2aed6 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -226,6 +226,14 @@ static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
+ return 1;
+ }
+
++ // Register callback for deleted interfaces
++ r = sd_netlink_add_match(daemon->rtnl, NULL, RTM_DELLINK, nw_link_process, NULL,
++ daemon, "networkd-RTM_DELLINK");
++ if (r < 0) {
++ ERROR("Could not register RTM_DELLINK: %m\n");
++ return 1;
++ }
++
+ return 0;
+ }
+
+@@ -396,6 +404,27 @@ sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon) {
+ return daemon->rtnl;
+ }
+
++/*
++ Links
++*/
++struct nw_links* nw_daemon_links(struct nw_daemon* daemon) {
++ return nw_links_ref(daemon->links);
++}
++
++void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link) {
++ if (!daemon->links)
++ return;
++
++ nw_links_drop_link(daemon->links, link);
++}
++
++struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex) {
++ if (!daemon->links)
++ return NULL;
++
++ return nw_links_get_by_ifindex(daemon->links, ifindex);
++}
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 543685f..8e534c5 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -23,10 +23,11 @@
+
+ #include <systemd/sd-netlink.h>
+
+-#include "zone.h"
+-
+ struct nw_daemon;
+
++#include "link.h"
++#include "zone.h"
++
+ int nw_daemon_create(struct nw_daemon** daemon);
+
+ struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon);
+@@ -41,6 +42,13 @@ int nw_daemon_reload(struct nw_daemon* daemon);
+ */
+ sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon);
+
++/*
++ Links
++*/
++struct nw_links* nw_daemon_links(struct nw_daemon* daemon);
++void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link);
++struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex);
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index ec0593b..b6ece82 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -21,8 +21,11 @@
+ #include <stddef.h>
+ #include <stdlib.h>
+
++#include <systemd/sd-netlink.h>
++
+ #include "daemon.h"
+ #include "link.h"
++#include "links.h"
+ #include "logging.h"
+
+ struct nw_link {
+@@ -48,12 +51,16 @@ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex)
+ // Store the ifindex
+ l->ifindex = ifindex;
+
++ DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
++
+ *link = l;
+
+ return 0;
+ }
+
+ static void nw_link_free(struct nw_link* link) {
++ DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
++
+ if (link->daemon)
+ nw_daemon_unref(link->daemon);
+ }
+@@ -72,8 +79,112 @@ struct nw_link* nw_link_unref(struct nw_link* link) {
+ return NULL;
+ }
+
+-int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+- DEBUG("nw_link_process called\n");
++int nw_link_ifindex(struct nw_link* link) {
++ return link->ifindex;
++}
+
+- return 0;
++int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
++ struct nw_links* links = NULL;
++ struct nw_link* link = NULL;
++ const char* ifname = NULL;
++ int ifindex;
++ uint16_t type;
++ int r;
++
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Fetch links
++ links = nw_daemon_links(daemon);
++ if (!links) {
++ r = 1;
++ goto ERROR;
++ }
++
++ // Check if this message could be received
++ if (sd_netlink_message_is_error(message)) {
++ r = sd_netlink_message_get_errno(message);
++ if (r < 0)
++ ERROR("Could not receive link message: %m\n");
++
++ goto IGNORE;
++ }
++
++ // Fetch the message type
++ r = sd_netlink_message_get_type(message, &type);
++ if (r < 0) {
++ ERROR("Could not fetch message type: %m\n");
++ goto IGNORE;
++ }
++
++ // Check type
++ switch (type) {
++ case RTM_NEWLINK:
++ case RTM_DELLINK:
++ break;
++
++ default:
++ ERROR("Received an unexpected message (type %u)\n", type);
++ goto IGNORE;
++ }
++
++ // Fetch the interface index
++ r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
++ if (r < 0) {
++ ERROR("Could not fetch ifindex: %m\n");
++ goto IGNORE;
++ }
++
++ // Check interface index
++ if (ifindex <= 0) {
++ ERROR("Received an invalid ifindex\n");
++ goto IGNORE;
++ }
++
++ // Fetch the interface name
++ r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
++ if (r < 0) {
++ ERROR("Received a netlink message without interface name: %m\n");
++ goto IGNORE;
++ }
++
++ // Try finding an existing link
++ link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
++
++ switch (type) {
++ case RTM_NEWLINK:
++ // If the link doesn't exist, create it
++ if (!link) {
++ r = nw_link_create(&link, daemon, ifindex);
++ if (r) {
++ ERROR("Could not create link: %m\n");
++ goto ERROR;
++ }
++ }
++
++ // TODO Import any data from the netlink message
++
++ // Add it to the list
++ r = nw_links_add_link(links, link);
++ if (r)
++ goto ERROR;
++
++ break;
++
++ case RTM_DELLINK:
++ if (link)
++ nw_links_drop_link(links, link);
++
++ break;
++ }
++
++IGNORE:
++ r = 0;
++
++ERROR:
++ if (links)
++ nw_links_unref(links);
++ if (link)
++ nw_link_unref(link);
++
++ return r;
+ }
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 6003133..e482d71 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -30,6 +30,8 @@ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex)
+ struct nw_link* nw_link_ref(struct nw_link* link);
+ struct nw_link* nw_link_unref(struct nw_link* link);
+
++int nw_link_ifindex(struct nw_link* link);
++
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+ #endif /* NETWORKD_LINK_H */
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index 22584f2..044a27d 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -23,6 +23,7 @@
+ #include <sys/queue.h>
+
+ #include "daemon.h"
++#include "logging.h"
+ #include "link.h"
+ #include "links.h"
+
+@@ -98,6 +99,49 @@ struct nw_links* nw_links_unref(struct nw_links* links) {
+ return NULL;
+ }
+
++static struct nw_links_entry* nw_links_find_link(struct nw_links* links, const int ifindex) {
++ struct nw_links_entry* entry = NULL;
++
++ STAILQ_FOREACH(entry, &links->entries, nodes) {
++ if (nw_link_ifindex(entry->link) == ifindex)
++ return entry;
++ }
++
++ // No match found
++ return NULL;
++}
++
++int nw_links_add_link(struct nw_links* links, struct nw_link* link) {
++ // Allocate a new entry
++ struct nw_links_entry* entry = calloc(1, sizeof(*entry));
++ if (!entry)
++ return 1;
++
++ // Reference the link
++ entry->link = nw_link_ref(link);
++
++ // Add it to the list
++ STAILQ_INSERT_TAIL(&links->entries, entry, nodes);
++
++ // Increment the counter
++ links->num++;
++
++ return 0;
++}
++
++void nw_links_drop_link(struct nw_links* links, struct nw_link* link) {
++ struct nw_links_entry* entry = NULL;
++
++ entry = nw_links_find_link(links, nw_link_ifindex(link));
++ if (!entry)
++ return;
++
++ DEBUG("Dropping link %d\n", nw_link_ifindex(entry->link));
++
++ STAILQ_REMOVE(&links->entries, entry, nw_links_entry, nodes);
++ links->num--;
++}
++
+ int nw_links_enumerate(struct nw_links* links) {
+ sd_netlink_message* req = NULL;
+ sd_netlink_message* res = NULL;
+@@ -138,3 +182,13 @@ ERROR:
+
+ return r;
+ }
++
++struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex) {
++ struct nw_links_entry* entry = NULL;
++
++ entry = nw_links_find_link(links, ifindex);
++ if (!entry)
++ return NULL;
++
++ return nw_link_ref(entry->link);
++}
+diff --git a/src/networkd/links.h b/src/networkd/links.h
+index b2976e4..2f2bbea 100644
+--- a/src/networkd/links.h
++++ b/src/networkd/links.h
+@@ -22,6 +22,7 @@
+ #define NETWORKD_LINKS_H
+
+ #include "daemon.h"
++#include "link.h"
+
+ struct nw_links;
+
+@@ -30,6 +31,11 @@ int nw_links_create(struct nw_links** links, struct nw_daemon* daemon);
+ struct nw_links* nw_links_ref(struct nw_links* links);
+ struct nw_links* nw_links_unref(struct nw_links* links);
+
++int nw_links_add_link(struct nw_links* links, struct nw_link* link);
++void nw_links_drop_link(struct nw_links* links, struct nw_link* link);
++
+ int nw_links_enumerate(struct nw_links* links);
+
++struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex);
++
+ #endif /* NETWORKD_LINKS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,38 @@
+From b8a1a68c48849accb658b926f3596857dba41654 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 09:53:47 +0000
+Subject: [PATCH 188/304] networkd: Only add link if we created it
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index b6ece82..32a66cd 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -159,15 +159,15 @@ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+ ERROR("Could not create link: %m\n");
+ goto ERROR;
+ }
++
++ // Add it to the list
++ r = nw_links_add_link(links, link);
++ if (r)
++ goto ERROR;
+ }
+
+ // TODO Import any data from the netlink message
+
+- // Add it to the list
+- r = nw_links_add_link(links, link);
+- if (r)
+- goto ERROR;
+-
+ break;
+
+ case RTM_DELLINK:
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,106 @@
+From f650f6cd802686e545d41d00be4a0abd67765391 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 10:03:08 +0000
+Subject: [PATCH 189/304] networkd: Import interface name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 51 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 32a66cd..40f809f 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -18,8 +18,10 @@
+ # #
+ #############################################################################*/
+
++#include <net/if.h>
+ #include <stddef.h>
+ #include <stdlib.h>
++#include <string.h>
+
+ #include <systemd/sd-netlink.h>
+
+@@ -27,6 +29,7 @@
+ #include "link.h"
+ #include "links.h"
+ #include "logging.h"
++#include "string.h"
+
+ struct nw_link {
+ struct nw_daemon* daemon;
+@@ -34,6 +37,9 @@ struct nw_link {
+
+ // Interface Index
+ int ifindex;
++
++ // Interface Name
++ char ifname[IF_NAMESIZE];
+ };
+
+ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex) {
+@@ -83,6 +89,47 @@ int nw_link_ifindex(struct nw_link* link) {
+ return link->ifindex;
+ }
+
++static int nw_link_update_ifname(struct nw_link* link, sd_netlink_message* message) {
++ const char* ifname = NULL;
++ int r;
++
++ r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname);
++ if (r < 0) {
++ ERROR("Could not read link name for link %d: %m\n", link->ifindex);
++ return 1;
++ }
++
++ // Do nothing if the name is already set
++ if (strcmp(link->ifname, ifname) == 0)
++ return 0;
++
++ // Otherwise update the name
++ r = nw_string_set(link->ifname, ifname);
++ if (r) {
++ ERROR("Could not set link name: %m\n");
++ return 1;
++ }
++
++ DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
++
++ return 0;
++}
++
++/*
++ This function is called whenever anything changes, so that we can
++ update our internal link object.
++*/
++static int nw_link_update(struct nw_link* link, sd_netlink_message* message) {
++ int r;
++
++ // Update the interface name
++ r = nw_link_update_ifname(link, message);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+ struct nw_links* links = NULL;
+ struct nw_link* link = NULL;
+@@ -166,7 +213,10 @@ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+ goto ERROR;
+ }
+
+- // TODO Import any data from the netlink message
++ // Import any data from the netlink message
++ r = nw_link_update(link, message);
++ if (r)
++ goto ERROR;
+
+ break;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,96 @@
+From f37df5ea0920add075e294db28a3ec5ed83b0fe5 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 10:13:37 +0000
+Subject: [PATCH 190/304] networkd: Read link MTU
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 40f809f..62be893 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -40,6 +40,11 @@ struct nw_link {
+
+ // Interface Name
+ char ifname[IF_NAMESIZE];
++
++ // MTU
++ uint32_t mtu;
++ uint32_t min_mtu;
++ uint32_t max_mtu;
+ };
+
+ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex) {
+@@ -115,6 +120,54 @@ static int nw_link_update_ifname(struct nw_link* link, sd_netlink_message* messa
+ return 0;
+ }
+
++static int nw_link_update_mtu(struct nw_link* link, sd_netlink_message* message) {
++ uint32_t mtu = 0;
++ uint32_t min_mtu = 0;
++ uint32_t max_mtu = 0;
++ int r;
++
++ // Read the MTU
++ r = sd_netlink_message_read_u32(message, IFLA_MTU, &mtu);
++ if (r < 0) {
++ ERROR("Could not read MTU for link %d: %m\n", link->ifindex);
++ return r;
++ }
++
++ // Read the minimum MTU
++ r = sd_netlink_message_read_u32(message, IFLA_MIN_MTU, &min_mtu);
++ if (r < 0) {
++ ERROR("Could not read the minimum MTU for link %d: %m\n", link->ifindex);
++ return r;
++ }
++
++ // Read the maximum MTU
++ r = sd_netlink_message_read_u32(message, IFLA_MAX_MTU, &max_mtu);
++ if (r < 0) {
++ ERROR("Could not read the maximum MTU for link %d: %m\n", link->ifindex);
++ return r;
++ }
++
++ // Set the maximum MTU to infinity
++ if (!max_mtu)
++ max_mtu = UINT32_MAX;
++
++ // Store min/max MTU
++ link->min_mtu = min_mtu;
++ link->max_mtu = max_mtu;
++
++ // End here, if the MTU has not been changed
++ if (link->mtu == mtu)
++ return 0;
++
++ DEBUG("Link %d: MTU has changed to %" PRIu32 " (min: %" PRIu32 ", max: %" PRIu32 ")\n",
++ link->ifindex, link->mtu, link->min_mtu, link->max_mtu);
++
++ // Store MTU
++ link->mtu = mtu;
++
++ return 0;
++}
++
+ /*
+ This function is called whenever anything changes, so that we can
+ update our internal link object.
+@@ -127,6 +180,11 @@ static int nw_link_update(struct nw_link* link, sd_netlink_message* message) {
+ if (r)
+ return r;
+
++ // Update the MTU
++ r = nw_link_update_mtu(link, message);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 82f84c5ff3079de8b57f5a10a118fb4ecab20ffb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 15:24:19 +0000
+Subject: [PATCH 191/304] networkd: config: Actually return entry instead of
+ freeing it straight away
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 788308f..ef6218e 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -72,6 +72,8 @@ static struct nw_config_entry* nw_config_entry_create(
+ // Append the new entry
+ STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
+
++ return entry;
++
+ ERROR:
+ nw_config_entry_free(entry);
+ return NULL;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,139 @@
+From 5ef56cff5e19dcda00a76a40846fe3def9e6239f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 15:24:53 +0000
+Subject: [PATCH 192/304] networkd: config: Implement reading configuration
+ files
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 46 ++++++++++++++++++++++++++++++++++++++++---
+ src/networkd/string.h | 41 ++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 84 insertions(+), 3 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index ef6218e..17d6d89 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -157,9 +157,49 @@ int nw_config_flush(struct nw_config* config) {
+ }
+
+ static int nw_config_readf(struct nw_config* config, FILE* f) {
+- // XXX TODO
++ char* line = NULL;
++ size_t length = 0;
++ int r;
+
+- return 0;
++ ssize_t bytes_read = 0;
++
++ char* key = NULL;
++ char* val = NULL;
++
++ for (;;) {
++ // Read the next line
++ bytes_read = getline(&line, &length, f);
++ if (bytes_read < 0)
++ break;
++
++ // Key starts at the beginning of the line
++ key = line;
++
++ // Value starts after '='
++ val = strchr(line, '=');
++
++ // Invalid line without a '=' character
++ if (!val)
++ continue;
++
++ // Split the string
++ *val++ = '\0';
++
++ // Strip any whitespace from value
++ r = nw_string_strip(val);
++ if (r)
++ break;
++
++ // Store the setting
++ r = nw_config_set(config, key, val);
++ if (r)
++ break;
++ }
++
++ if (line)
++ free(line);
++
++ return r;
+ }
+
+ int nw_config_read(struct nw_config* config) {
+@@ -204,7 +244,7 @@ static int nw_config_writef(struct nw_config* config, FILE* f) {
+ continue;
+
+ // Write the entry
+- r = fprintf(f, "%s=\"%s\"\n", entry->key, entry->value);
++ r = fprintf(f, "%s=%s\n", entry->key, entry->value);
+ if (r < 0) {
+ ERROR("Failed to write configuration: %m\n");
+ return r;
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 4ae2dbf..f3c5b71 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -21,6 +21,7 @@
+ #ifndef NETWORKD_STRING_H
+ #define NETWORKD_STRING_H
+
++#include <ctype.h>
+ #include <errno.h>
+ #include <stdarg.h>
+ #include <stdio.h>
+@@ -79,6 +80,46 @@ static inline int __nw_string_set(char* s, const size_t length, const char* valu
+ return __nw_string_format(s, length, "%s", value);
+ }
+
++static inline int nw_string_lstrip(char* s) {
++ char* p = s;
++
++ // Count any leading spaces
++ while (*p && isspace(*p))
++ p++;
++
++ // Move the string to the beginning of the buffer
++ while (*p)
++ *s++ = *p++;
++
++ // Terminate the string
++ *s = '\0';
++
++ return 0;
++}
++
++static inline int nw_string_rstrip(char* s) {
++ ssize_t l = strlen(s) - 1;
++
++ while (l >= 0 && isspace(s[l]))
++ s[l--] = '\0';
++
++ return 0;
++}
++
++static inline int nw_string_strip(char* s) {
++ int r;
++
++ r = nw_string_lstrip(s);
++ if (r)
++ return r;
++
++ r = nw_string_rstrip(s);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ /*
+ Paths
+ */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,159 @@
+From abeab06924b22765dbc410c563f27077f348b5e9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 16:15:46 +0000
+Subject: [PATCH 193/304] networkd: Add scaffolding for ports
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/port.c | 84 +++++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port.h | 33 ++++++++++++++++++
+ 3 files changed, 119 insertions(+)
+ create mode 100644 src/networkd/port.c
+ create mode 100644 src/networkd/port.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 024bf45..4bd3975 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -324,6 +324,8 @@ dist_networkd_SOURCES = \
+ src/networkd/links.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
++ src/networkd/port.c \
++ src/networkd/port.h \
+ src/networkd/string.h \
+ src/networkd/zones.c \
+ src/networkd/zones.h \
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+new file mode 100644
+index 0000000..019521f
+--- /dev/null
++++ b/src/networkd/port.c
+@@ -0,0 +1,84 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <net/if.h>
++#include <stdlib.h>
++
++#include "config.h"
++#include "string.h"
++#include "port.h"
++
++struct nw_port {
++ int nrefs;
++
++ char name[IF_NAMESIZE];
++
++ // Configuration
++ struct nw_config *config;
++};
++
++static void nw_port_free(struct nw_port* port) {
++ if (port->config)
++ nw_config_unref(port->config);
++
++ free(port);
++}
++
++int nw_port_create(struct nw_port** port, const char* name) {
++ int r;
++
++ // Allocate a new object
++ struct nw_port* p = calloc(1, sizeof(*p));
++ if (!p)
++ return 1;
++
++ // Initialize reference counter
++ p->nrefs = 1;
++
++ // Store the name
++ r = nw_string_set(p->name, name);
++ if (r)
++ goto ERROR;
++
++ *port = p;
++ return 0;
++
++ERROR:
++ nw_port_free(p);
++ return r;
++}
++
++struct nw_port* nw_port_ref(struct nw_port* port) {
++ port->nrefs++;
++
++ return port;
++}
++
++struct nw_port* nw_port_unref(struct nw_port* port) {
++ if (--port->nrefs > 0)
++ return port;
++
++ nw_port_free(port);
++ return NULL;
++}
++
++const char* nw_port_name(struct nw_port* port) {
++ return port->name;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+new file mode 100644
+index 0000000..cea6203
+--- /dev/null
++++ b/src/networkd/port.h
+@@ -0,0 +1,33 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_H
++#define NETWORKD_PORT_H
++
++struct nw_port;
++
++int nw_port_create(struct nw_port** port, const char* name);
++
++struct nw_port* nw_port_ref(struct nw_port* port);
++struct nw_port* nw_port_unref(struct nw_port* port);
++
++const char* nw_port_name(struct nw_port* port);
++
++#endif /* NETWORKD_PORT_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,240 @@
+From b8db2713c790000860e1c3b0d4f8e588cfa5ac04 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 10 Feb 2023 16:26:36 +0000
+Subject: [PATCH 194/304] networkd: Add port container
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/daemon.c | 23 ++++++++++
+ src/networkd/ports.c | 100 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/ports.h | 35 +++++++++++++++
+ 4 files changed, 160 insertions(+)
+ create mode 100644 src/networkd/ports.c
+ create mode 100644 src/networkd/ports.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 4bd3975..d78d61e 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -326,6 +326,8 @@ dist_networkd_SOURCES = \
+ src/networkd/main.c \
+ src/networkd/port.c \
+ src/networkd/port.h \
++ src/networkd/ports.c \
++ src/networkd/ports.h \
+ src/networkd/string.h \
+ src/networkd/zones.c \
+ src/networkd/zones.h \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index dc2aed6..aee27e3 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -34,6 +34,7 @@
+ #include "link.h"
+ #include "links.h"
+ #include "logging.h"
++#include "ports.h"
+ #include "zone.h"
+ #include "zones.h"
+
+@@ -62,6 +63,10 @@ struct nw_daemon {
+
+ // Zones
+ struct nw_zones* zones;
++
++ // Ports
++ struct nw_ports* ports;
++
+ };
+
+ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+@@ -248,6 +253,17 @@ static int nw_daemon_enumerate_links(struct nw_daemon* daemon) {
+ return nw_links_enumerate(daemon->links);
+ }
+
++static int nw_daemon_enumerate_ports(struct nw_daemon* daemon) {
++ int r;
++
++ // Create a new ports container
++ r = nw_ports_create(&daemon->ports, daemon);
++ if (r)
++ return r;
++
++ return nw_ports_enumerate(daemon->ports);
++}
++
+ static int nw_daemon_enumerate(struct nw_daemon* daemon) {
+ int r;
+
+@@ -256,6 +272,11 @@ static int nw_daemon_enumerate(struct nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Ports
++ r = nw_daemon_enumerate_ports(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+@@ -322,6 +343,8 @@ ERROR:
+ }
+
+ static void nw_daemon_cleanup(struct nw_daemon* daemon) {
++ if (daemon->ports)
++ nw_ports_unref(daemon->ports);
+ if (daemon->zones)
+ nw_zones_unref(daemon->zones);
+ if (daemon->links)
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+new file mode 100644
+index 0000000..500153a
+--- /dev/null
++++ b/src/networkd/ports.c
+@@ -0,0 +1,100 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++#include <string.h>
++#include <sys/queue.h>
++
++#include "logging.h"
++#include "port.h"
++#include "ports.h"
++
++struct nw_ports_entry {
++ struct nw_port* port;
++
++ // Link to the other entries
++ STAILQ_ENTRY(nw_ports_entry) nodes;
++};
++
++struct nw_ports {
++ struct nw_daemon* daemon;
++ int nrefs;
++
++ // Port Entries
++ STAILQ_HEAD(entries, nw_ports_entry) entries;
++
++ // A counter of the port entries
++ unsigned int num;
++};
++
++int nw_ports_create(struct nw_ports** ports, struct nw_daemon* daemon) {
++ struct nw_ports* p = calloc(1, sizeof(*p));
++ if (!p)
++ return 1;
++
++ // Store a reference to the daemon
++ p->daemon = nw_daemon_ref(daemon);
++
++ // Initialize the reference counter
++ p->nrefs = 1;
++
++ // Initialize entries
++ STAILQ_INIT(&p->entries);
++
++ // Reference the pointer
++ *ports = p;
++
++ return 0;
++}
++
++static void nw_ports_free(struct nw_ports* ports) {
++ struct nw_ports_entry* entry = NULL;
++
++ while (!STAILQ_EMPTY(&ports->entries)) {
++ entry = STAILQ_FIRST(&ports->entries);
++
++ // Dereference the port
++ nw_port_unref(entry->port);
++
++ // Remove the entry from the list
++ STAILQ_REMOVE_HEAD(&ports->entries, nodes);
++
++ // Free the entry
++ free(entry);
++ }
++}
++
++struct nw_ports* nw_ports_ref(struct nw_ports* ports) {
++ ports->nrefs++;
++
++ return ports;
++}
++
++struct nw_ports* nw_ports_unref(struct nw_ports* ports) {
++ if (--ports->nrefs > 0)
++ return ports;
++
++ nw_ports_free(ports);
++ return NULL;
++}
++
++int nw_ports_enumerate(struct nw_ports* ports) {
++ return 0; // XXX TODO
++}
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+new file mode 100644
+index 0000000..76a4a3a
+--- /dev/null
++++ b/src/networkd/ports.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORTS_H
++#define NETWORKD_PORTS_H
++
++struct nw_ports;
++
++#include "daemon.h"
++
++int nw_ports_create(struct nw_ports** ports, struct nw_daemon* daemon);
++
++struct nw_ports* nw_ports_ref(struct nw_ports* ports);
++struct nw_ports* nw_ports_unref(struct nw_ports* ports);
++
++int nw_ports_enumerate(struct nw_ports* ports);
++
++#endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,247 @@
+From c791afc87a22936e9564e449182ee6452659887e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 10:59:03 +0000
+Subject: [PATCH 195/304] networkd: Enumerate ports on startup
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/port.h | 2 ++
+ src/networkd/ports.c | 61 ++++++++++++++++++++++++++++++++++++++++++-
+ src/networkd/string.h | 8 ++++++
+ src/networkd/util.c | 61 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/util.h | 29 ++++++++++++++++++++
+ 6 files changed, 162 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/util.c
+ create mode 100644 src/networkd/util.h
+
+diff --git a/Makefile.am b/Makefile.am
+index d78d61e..505d679 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -329,6 +329,8 @@ dist_networkd_SOURCES = \
+ src/networkd/ports.c \
+ src/networkd/ports.h \
+ src/networkd/string.h \
++ src/networkd/util.c \
++ src/networkd/util.h \
+ src/networkd/zones.c \
+ src/networkd/zones.h \
+ src/networkd/zone.c \
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index cea6203..250a694 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_PORT_H
+ #define NETWORKD_PORT_H
+
++#define PORT_CONFIG_DIR CONFIG_DIR "/ports"
++
+ struct nw_port;
+
+ int nw_port_create(struct nw_port** port, const char* name);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 500153a..19a3a59 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -21,10 +21,13 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/queue.h>
++#include <sys/stat.h>
+
+ #include "logging.h"
+ #include "port.h"
+ #include "ports.h"
++#include "string.h"
++#include "util.h"
+
+ struct nw_ports_entry {
+ struct nw_port* port;
+@@ -95,6 +98,62 @@ struct nw_ports* nw_ports_unref(struct nw_ports* ports) {
+ return NULL;
+ }
+
++static int nw_ports_add_port(struct nw_ports* ports, struct nw_port* port) {
++ // Allocate a new entry
++ struct nw_ports_entry* entry = calloc(1, sizeof(*entry));
++ if (!entry)
++ return 1;
++
++ // Reference the port
++ entry->port = nw_port_ref(port);
++
++ // Add it to the list
++ STAILQ_INSERT_TAIL(&ports->entries, entry, nodes);
++
++ // Increment the counter
++ ports->num++;
++
++ return 0;
++}
++
++static int __nw_ports_enumerate(const char* path, const struct stat* s, void* data) {
++ struct nw_port* port = NULL;
++ int r;
++
++ struct nw_ports* ports = (struct nw_ports*)data;
++
++ // Skip anything that isn't a regular file
++ if (!S_ISREG(s->st_mode))
++ return 0;
++
++ // Find the basename of the file
++ const char* name = nw_path_basename(path);
++
++ // Break on invalid paths
++ if (!name)
++ return 0;
++
++ // Skip any hidden files
++ if (*name == '.')
++ return 0;
++
++ // Create a new port
++ r = nw_port_create(&port, name);
++ if (r)
++ goto ERROR;
++
++ // Add the port to the list
++ r = nw_ports_add_port(ports, port);
++ if (r)
++ goto ERROR;
++
++ERROR:
++ if (port)
++ nw_port_unref(port);
++
++ return r;
++}
++
+ int nw_ports_enumerate(struct nw_ports* ports) {
+- return 0; // XXX TODO
++ return nw_ftw(PORT_CONFIG_DIR, PORT_CONFIG_DIR "/*", __nw_ports_enumerate, ports);
+ }
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index f3c5b71..6ff44f9 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -142,4 +142,12 @@ static inline int __nw_path_join(char* s, const size_t length,
+ return __nw_string_format(s, length, "%s/%s", first, second);
+ }
+
++static inline const char* nw_path_basename(const char* path) {
++ const char* basename = strrchr(path, '/');
++ if (!basename)
++ return NULL;
++
++ return basename + 1;
++}
++
+ #endif /* NETWORKD_STRING_H */
+diff --git a/src/networkd/util.c b/src/networkd/util.c
+new file mode 100644
+index 0000000..0381ea8
+--- /dev/null
++++ b/src/networkd/util.c
+@@ -0,0 +1,61 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <errno.h>
++#include <fnmatch.h>
++#include <ftw.h>
++#include <stddef.h>
++#include <stdio.h>
++
++#include "util.h"
++
++int nw_ftw(const char* path, const char* filter,
++ int (*callback)(const char* path, const struct stat* s, void* data), void* data) {
++ /*
++ This is a nested function which allows us to pass some custom pointer to
++ the callback function as that isn't possible with nftw().
++ */
++ int __callback(const char* p, const struct stat* s, int type, struct FTW* ftw) {
++ int r;
++
++ // Filter out anything we don't want
++ if (filter) {
++ r = fnmatch(filter, p, FNM_PATHNAME);
++
++ switch (r) {
++ // Pattern didn't match
++ case FNM_NOMATCH:
++ return 0;
++
++ // Pattern matched
++ case 0:
++ break;
++
++ // Error
++ default:
++ return 1;
++ }
++ }
++
++ return callback(p, s, data);
++ }
++
++ return nftw(path, __callback, 0, 0);
++}
+diff --git a/src/networkd/util.h b/src/networkd/util.h
+new file mode 100644
+index 0000000..950afda
+--- /dev/null
++++ b/src/networkd/util.h
+@@ -0,0 +1,29 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_UTIL_H
++#define NETWORKD_UTIL_H
++
++#include <sys/stat.h>
++
++int nw_ftw(const char* path, const char* filter,
++ int (*callback)(const char* path, const struct stat* s, void* data), void* data);
++
++#endif /* NETWORKD_UTIL_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,147 @@
+From 827435c82f60ba1b7717d74faeac6cb6b85d6588 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 11:21:02 +0000
+Subject: [PATCH 196/304] networkd: Perform port setup from configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 76 +++++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port.h | 5 +++
+ 2 files changed, 81 insertions(+)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 019521f..f78489b 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -18,10 +18,12 @@
+ # #
+ #############################################################################*/
+
++#include <limits.h>
+ #include <net/if.h>
+ #include <stdlib.h>
+
+ #include "config.h"
++#include "logging.h"
+ #include "string.h"
+ #include "port.h"
+
+@@ -29,11 +31,31 @@ struct nw_port {
+ int nrefs;
+
+ char name[IF_NAMESIZE];
++ nw_port_type_t type;
+
+ // Configuration
+ struct nw_config *config;
+ };
+
++static const struct nw_port_type_map {
++ nw_port_type_t type;
++ const char* name;
++} nw_port_type_map[] = {
++ { NW_PORT_DUMMY, "dummy" },
++ { NW_PORT_UNKNOWN, NULL },
++};
++
++static nw_port_type_t nw_port_type_from_string(const char* s) {
++ const struct nw_port_type_map* map = NULL;
++
++ for (map = nw_port_type_map; *map->name; map++) {
++ if (strcmp(map->name, s) == 0)
++ return map->type;
++ }
++
++ return NW_PORT_UNKNOWN;
++}
++
+ static void nw_port_free(struct nw_port* port) {
+ if (port->config)
+ nw_config_unref(port->config);
+@@ -41,6 +63,55 @@ static void nw_port_free(struct nw_port* port) {
+ free(port);
+ }
+
++static int nw_port_setup_common(struct nw_port* port) {
++ return 0; // XXX TODO
++}
++
++static nw_port_type_t nw_port_setup_type(struct nw_port* port) {
++ const char* type = nw_config_get(port->config, "TYPE");
++ if (!type)
++ return NW_PORT_UNKNOWN;
++
++ return nw_port_type_from_string(type);
++}
++
++static int nw_port_setup(struct nw_port* port) {
++ char path[PATH_MAX];
++ int r;
++
++ // Compose the path to the main configuration file
++ r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
++ if (r)
++ return r;
++
++ // Initialize the configuration
++ r = nw_config_create(&port->config, path);
++ if (r)
++ return r;
++
++ // Determine type
++ port->type = nw_port_setup_type(port);
++ if (!port->type) {
++ ERROR("Could not determine type of port %s\n", port->name);
++ return 0;
++ }
++
++ // Perform some common initialization
++ r = nw_port_setup_common(port);
++ if (r)
++ return r;
++
++ // Call any custom initialization
++ switch (port->type) {
++ // These do not need any special initialization
++ case NW_PORT_DUMMY:
++ case NW_PORT_UNKNOWN:
++ break;
++ }
++
++ return 0;
++}
++
+ int nw_port_create(struct nw_port** port, const char* name) {
+ int r;
+
+@@ -57,6 +128,11 @@ int nw_port_create(struct nw_port** port, const char* name) {
+ if (r)
+ goto ERROR;
+
++ // Setup the port
++ r = nw_port_setup(p);
++ if (r)
++ goto ERROR;
++
+ *port = p;
+ return 0;
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 250a694..af7fbdd 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -23,6 +23,11 @@
+
+ #define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+
++typedef enum nw_port_type {
++ NW_PORT_UNKNOWN = 0,
++ NW_PORT_DUMMY,
++} nw_port_type_t;
++
+ struct nw_port;
+
+ int nw_port_create(struct nw_port** port, const char* name);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,145 @@
+From 2e7f4705a61e8435e22d49fcff49ecf8dde389ef Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 11:48:23 +0000
+Subject: [PATCH 197/304] networkd: Read Ethernet address from configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ src/networkd/address.h | 47 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port.c | 36 +++++++++++++++++++++++++++++++-
+ 3 files changed, 83 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/address.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 505d679..a27736a 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -308,6 +308,7 @@ sbin_PROGRAMS += \
+ networkd
+
+ dist_networkd_SOURCES = \
++ src/networkd/address.h \
+ src/networkd/bus.c \
+ src/networkd/bus.h \
+ src/networkd/config.c \
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+new file mode 100644
+index 0000000..1d9fbfc
+--- /dev/null
++++ b/src/networkd/address.h
+@@ -0,0 +1,47 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_ADDRESS_H
++#define NETWORKD_ADDRESS_H
++
++#include <netinet/ether.h>
++#include <string.h>
++
++typedef struct ether_addr nw_address_t;
++
++static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
++ struct ether_addr* p = ether_aton_r(s, addr);
++ if (!p)
++ return 1;
++
++ return 0;
++}
++
++static inline char* nw_address_to_string(const nw_address_t* addr) {
++ char buffer[ETH_ALEN];
++
++ char* p = ether_ntoa_r(addr, buffer);
++ if (!p)
++ return NULL;
++
++ return strdup(buffer);
++}
++
++#endif /* NETWORKD_ADDRESS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index f78489b..aff79a5 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -20,8 +20,10 @@
+
+ #include <limits.h>
+ #include <net/if.h>
++#include <stdint.h>
+ #include <stdlib.h>
+
++#include "address.h"
+ #include "config.h"
+ #include "logging.h"
+ #include "string.h"
+@@ -35,6 +37,9 @@ struct nw_port {
+
+ // Configuration
+ struct nw_config *config;
++
++ // Common attributes
++ nw_address_t address;
+ };
+
+ static const struct nw_port_type_map {
+@@ -63,8 +68,37 @@ static void nw_port_free(struct nw_port* port) {
+ free(port);
+ }
+
++static int nw_port_setup_address(struct nw_port* port) {
++ int r;
++
++ // Read ADDRESS from configuration
++ const char* s = nw_config_get(port->config, "ADDRESS");
++ if (!s) {
++ ERROR("Port %s: Address isn't set\n", port->name);
++ return 1;
++ }
++
++ // Parse the address
++ r = nw_address_from_string(&port->address, s);
++ if (r) {
++ ERROR("Port %s: Could not parse address: %m\n", port->name);
++ return r;
++ }
++
++ // XXX Do we need to check for multicast here?
++
++ return 0;
++}
++
+ static int nw_port_setup_common(struct nw_port* port) {
+- return 0; // XXX TODO
++ int r;
++
++ // Address
++ r = nw_port_setup_address(port);
++ if (r)
++ return r;
++
++ return 0;
+ }
+
+ static nw_port_type_t nw_port_setup_type(struct nw_port* port) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,93 @@
+From 9a93da54510ec2b49e0890d2b420a097316eaace Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 12:01:42 +0000
+Subject: [PATCH 198/304] networkd: Generate a random Ethernet address for
+ ports
+
+This happens when either no address was set, or it cannot be parsed.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 25 +++++++++++++++++++++++++
+ src/networkd/port.c | 14 ++++++++++++--
+ 2 files changed, 37 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index 1d9fbfc..f875b26 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -23,6 +23,9 @@
+
+ #include <netinet/ether.h>
+ #include <string.h>
++#include <sys/random.h>
++
++#include "logging.h"
+
+ typedef struct ether_addr nw_address_t;
+
+@@ -44,4 +47,26 @@ static inline char* nw_address_to_string(const nw_address_t* addr) {
+ return strdup(buffer);
+ }
+
++static inline int nw_address_generate(nw_address_t* addr) {
++ ssize_t bytes = getrandom(addr, sizeof(*addr), 0);
++ if (bytes < 0) {
++ ERROR("getrandom() failed: %m\n");
++ return 1;
++ }
++
++ // Check if we filled the entire buffer
++ if (bytes < (ssize_t)sizeof(*addr)) {
++ ERROR("Could not gather enough randomness\n");
++ return 1;
++ }
++
++ // Clear the multicast bit
++ addr->ether_addr_octet[0] &= 0xfe;
++
++ // Set the software-generated bit
++ addr->ether_addr_octet[1] |= 0x02;
++
++ return 0;
++}
++
+ #endif /* NETWORKD_ADDRESS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index aff79a5..4c32592 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -75,18 +75,28 @@ static int nw_port_setup_address(struct nw_port* port) {
+ const char* s = nw_config_get(port->config, "ADDRESS");
+ if (!s) {
+ ERROR("Port %s: Address isn't set\n", port->name);
+- return 1;
++ goto ERROR;
+ }
+
+ // Parse the address
+ r = nw_address_from_string(&port->address, s);
+ if (r) {
+ ERROR("Port %s: Could not parse address: %m\n", port->name);
+- return r;
++ goto ERROR;
+ }
+
+ // XXX Do we need to check for multicast here?
+
++ return 0;
++
++ERROR:
++ // Generate a random Ethernet address
++ r = nw_address_generate(&port->address);
++ if (r) {
++ ERROR("Could not generate a random Ethernet address: %m\n");
++ return r;
++ }
++
+ return 0;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,43 @@
+From 35fc59cd654c084cfe2550ed874d768f624b591e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 12:06:18 +0000
+Subject: [PATCH 199/304] networkd: Introduce address flags for better
+ readability
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index f875b26..0885d75 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -29,6 +29,11 @@
+
+ typedef struct ether_addr nw_address_t;
+
++enum {
++ NW_ADDRESS_MULTICAST = (1 << 0),
++ NW_ADDRESS_SOFTWAREASSIGNED = (1 << 1),
++};
++
+ static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
+ struct ether_addr* p = ether_aton_r(s, addr);
+ if (!p)
+@@ -61,10 +66,10 @@ static inline int nw_address_generate(nw_address_t* addr) {
+ }
+
+ // Clear the multicast bit
+- addr->ether_addr_octet[0] &= 0xfe;
++ addr->ether_addr_octet[0] &= ~NW_ADDRESS_MULTICAST;
+
+ // Set the software-generated bit
+- addr->ether_addr_octet[1] |= 0x02;
++ addr->ether_addr_octet[0] |= NW_ADDRESS_SOFTWAREASSIGNED;
+
+ return 0;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,46 @@
+From d81d9cf5582b9be41968c10c01bf436b066d4e34 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 12:10:15 +0000
+Subject: [PATCH 200/304] networkd: Check if Ethernet addresses from config are
+ usable
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 4 ++++
+ src/networkd/port.c | 7 ++++++-
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index 0885d75..d284a0b 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -74,4 +74,8 @@ static inline int nw_address_generate(nw_address_t* addr) {
+ return 0;
+ }
+
++static inline int nw_address_is_multicast(const nw_address_t* addr) {
++ return (addr->ether_addr_octet[0] & NW_ADDRESS_MULTICAST);
++}
++
+ #endif /* NETWORKD_ADDRESS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 4c32592..89f3181 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -85,7 +85,12 @@ static int nw_port_setup_address(struct nw_port* port) {
+ goto ERROR;
+ }
+
+- // XXX Do we need to check for multicast here?
++ // Check if this address is usable
++ r = nw_address_is_multicast(&port->address);
++ if (r) {
++ DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
++ goto ERROR;
++ }
+
+ return 0;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,347 @@
+From 7297ba7fab067d53560eaeba7e8aa992cef3308a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 12:34:41 +0000
+Subject: [PATCH 201/304] networkd: Export ports over dbus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 6 ++-
+ src/networkd/daemon-bus.c | 3 +-
+ src/networkd/daemon.c | 14 ++++++
+ src/networkd/daemon.h | 6 +++
+ src/networkd/port-bus.c | 89 +++++++++++++++++++++++++++++++++++++++
+ src/networkd/port-bus.h | 28 ++++++++++++
+ src/networkd/port.c | 14 ++++++
+ src/networkd/port.h | 2 +
+ src/networkd/ports.c | 52 +++++++++++++++++++++++
+ src/networkd/ports.h | 6 ++-
+ 10 files changed, 216 insertions(+), 4 deletions(-)
+ create mode 100644 src/networkd/port-bus.c
+ create mode 100644 src/networkd/port-bus.h
+
+diff --git a/Makefile.am b/Makefile.am
+index a27736a..893f1b8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -325,10 +325,12 @@ dist_networkd_SOURCES = \
+ src/networkd/links.h \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+- src/networkd/port.c \
+- src/networkd/port.h \
+ src/networkd/ports.c \
+ src/networkd/ports.h \
++ src/networkd/port.c \
++ src/networkd/port.h \
++ src/networkd/port-bus.c \
++ src/networkd/port-bus.h \
+ src/networkd/string.h \
+ src/networkd/util.c \
+ src/networkd/util.h \
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+index aac9775..ca0754f 100644
+--- a/src/networkd/daemon-bus.c
++++ b/src/networkd/daemon-bus.c
+@@ -22,6 +22,7 @@
+
+ #include "bus.h"
+ #include "daemon.h"
++#include "port-bus.h"
+ #include "zone-bus.h"
+
+ static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+@@ -45,5 +46,5 @@ const struct nw_bus_implementation daemon_bus_impl = {
+ .path = "/org/ipfire/network1",
+ .interface = "org.ipfire.network1",
+ .vtables = BUS_VTABLES(daemon_vtable),
+- .children = BUS_IMPLEMENTATIONS(&zone_bus_impl),
++ .children = BUS_IMPLEMENTATIONS(&port_bus_impl, &zone_bus_impl),
+ };
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index aee27e3..795a8b7 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -448,6 +448,20 @@ struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifin
+ return nw_links_get_by_ifindex(daemon->links, ifindex);
+ }
+
++/*
++ Ports
++*/
++struct nw_ports* nw_daemon_ports(struct nw_daemon* daemon) {
++ return nw_ports_ref(daemon->ports);
++}
++
++struct nw_port* nw_daemon_get_port_by_name(struct nw_daemon* daemon, const char* name) {
++ if (!daemon->ports)
++ return NULL;
++
++ return nw_ports_get_by_name(daemon->ports, name);
++}
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 8e534c5..e08537e 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -49,6 +49,12 @@ struct nw_links* nw_daemon_links(struct nw_daemon* daemon);
+ void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link);
+ struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex);
+
++/*
++ Ports
++*/
++struct nw_ports* nw_daemon_ports(struct nw_daemon* daemon);
++struct nw_port* nw_daemon_get_port_by_name(struct nw_daemon* daemon, const char* name);
++
+ /*
+ Zones
+ */
+diff --git a/src/networkd/port-bus.c b/src/networkd/port-bus.c
+new file mode 100644
+index 0000000..8c327b9
+--- /dev/null
++++ b/src/networkd/port-bus.c
+@@ -0,0 +1,89 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <errno.h>
++
++#include "bus.h"
++#include "daemon.h"
++#include "logging.h"
++#include "port.h"
++#include "port-bus.h"
++#include "ports.h"
++
++static int nw_port_node_enumerator(sd_bus* bus, const char* path, void* data,
++ char*** nodes, sd_bus_error* error) {
++ int r;
++
++ DEBUG("Enumerating ports...\n");
++
++ // Fetch a reference to the daemon
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Fetch ports
++ struct nw_ports* ports = nw_daemon_ports(daemon);
++
++ // Make bus paths for all ports
++ r = nw_ports_bus_paths(ports, nodes);
++ if (r)
++ goto ERROR;
++
++ERROR:
++ nw_ports_unref(ports);
++
++ return r;
++}
++
++static int nw_port_object_find(sd_bus* bus, const char* path, const char* interface,
++ void* data, void** found, sd_bus_error* error) {
++ char* name = NULL;
++ int r;
++
++ // Fetch a reference to the daemon
++ struct nw_daemon* daemon = (struct nw_daemon*)data;
++
++ // Decode the path of the requested object
++ r = sd_bus_path_decode(path, "/org/ipfire/network1/port", &name);
++ if (r <= 0)
++ return 0;
++
++ // Find the port
++ struct nw_port* port = nw_daemon_get_port_by_name(daemon, name);
++ if (!port)
++ return 0;
++
++ // Match!
++ *found = port;
++
++ nw_port_unref(port);
++
++ return 1;
++}
++
++static const sd_bus_vtable port_vtable[] = {
++ SD_BUS_VTABLE_START(0),
++ SD_BUS_VTABLE_END
++};
++
++const struct nw_bus_implementation port_bus_impl = {
++ "/org/ipfire/network1/port",
++ "org.ipfire.network1.Port",
++ .fallback_vtables = BUS_FALLBACK_VTABLES({port_vtable, nw_port_object_find}),
++ .node_enumerator = nw_port_node_enumerator,
++};
+diff --git a/src/networkd/port-bus.h b/src/networkd/port-bus.h
+new file mode 100644
+index 0000000..95e49a8
+--- /dev/null
++++ b/src/networkd/port-bus.h
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_BUS_H
++#define NETWORKD_PORT_BUS_H
++
++#include "bus.h"
++
++extern const struct nw_bus_implementation port_bus_impl;
++
++#endif /* NETWORKD_PORT_BUS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 89f3181..d1e3f7f 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -23,6 +23,8 @@
+ #include <stdint.h>
+ #include <stdlib.h>
+
++#include <systemd/sd-bus.h>
++
+ #include "address.h"
+ #include "config.h"
+ #include "logging.h"
+@@ -207,3 +209,15 @@ struct nw_port* nw_port_unref(struct nw_port* port) {
+ const char* nw_port_name(struct nw_port* port) {
+ return port->name;
+ }
++
++char* nw_port_bus_path(struct nw_port* port) {
++ char* p = NULL;
++ int r;
++
++ // Encode the bus path
++ r = sd_bus_path_encode("/org/ipfire/network1/port", port->name, &p);
++ if (r < 0)
++ return NULL;
++
++ return p;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index af7fbdd..e3655cf 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -37,4 +37,6 @@ struct nw_port* nw_port_unref(struct nw_port* port);
+
+ const char* nw_port_name(struct nw_port* port);
+
++char* nw_port_bus_path(struct nw_port* port);
++
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 19a3a59..24bd257 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -157,3 +157,55 @@ ERROR:
+ int nw_ports_enumerate(struct nw_ports* ports) {
+ return nw_ftw(PORT_CONFIG_DIR, PORT_CONFIG_DIR "/*", __nw_ports_enumerate, ports);
+ }
++
++struct nw_port* nw_ports_get_by_name(struct nw_ports* ports, const char* name) {
++ struct nw_ports_entry* entry = NULL;
++
++ STAILQ_FOREACH(entry, &ports->entries, nodes) {
++ const char* __name = nw_port_name(entry->port);
++
++ // If the name matches, return a reference to the zone
++ if (strcmp(name, __name) == 0)
++ return nw_port_ref(entry->port);
++ }
++
++ // No match found
++ return NULL;
++}
++
++int nw_ports_bus_paths(struct nw_ports* ports, char*** paths) {
++ struct nw_ports_entry* entry = NULL;
++ char* path = NULL;
++
++ // Allocate an array for all paths
++ char** p = calloc(ports->num + 1, sizeof(*p));
++ if (!p)
++ return 1;
++
++ unsigned int i = 0;
++
++ // Walk through all ports
++ STAILQ_FOREACH(entry, &ports->entries, nodes) {
++ // Generate the bus path
++ path = nw_port_bus_path(entry->port);
++ if (!path)
++ goto ERROR;
++
++ // Append the bus path to the array
++ p[i++] = path;
++ }
++
++ // Return pointer
++ *paths = p;
++
++ return 0;
++
++ERROR:
++ if (p) {
++ for (char** e = p; *e; e++)
++ free(*e);
++ free(p);
++ }
++
++ return 1;
++}
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+index 76a4a3a..e807e25 100644
+--- a/src/networkd/ports.h
++++ b/src/networkd/ports.h
+@@ -32,4 +32,8 @@ struct nw_ports* nw_ports_unref(struct nw_ports* ports);
+
+ int nw_ports_enumerate(struct nw_ports* ports);
+
+-#endif /* NETWORKD_ZONES_H */
++struct nw_port* nw_ports_get_by_name(struct nw_ports* ports, const char* name);
++
++int nw_ports_bus_paths(struct nw_ports* ports, char*** paths);
++
++#endif /* NETWORKD_PORTS_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 8c569e1ca29435a0caa6ecc3b2118ac2dbac4466 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 13:53:52 +0000
+Subject: [PATCH 202/304] networkd: address: Fix buffer to Ethernet address
+ strings
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index d284a0b..b7556f4 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -43,7 +43,7 @@ static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
+ }
+
+ static inline char* nw_address_to_string(const nw_address_t* addr) {
+- char buffer[ETH_ALEN];
++ char buffer[18];
+
+ char* p = ether_ntoa_r(addr, buffer);
+ if (!p)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,102 @@
+From 4a383286ff94b4bc6efdde95c26070b68b8942e3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 13:54:33 +0000
+Subject: [PATCH 203/304] networkd: ports: Export Ethernet address over dbus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-bus.c | 34 ++++++++++++++++++++++++++++++++++
+ src/networkd/port.c | 4 ++++
+ src/networkd/port.h | 4 ++++
+ 3 files changed, 42 insertions(+)
+
+diff --git a/src/networkd/port-bus.c b/src/networkd/port-bus.c
+index 8c327b9..1a48655 100644
+--- a/src/networkd/port-bus.c
++++ b/src/networkd/port-bus.c
+@@ -19,7 +19,9 @@
+ #############################################################################*/
+
+ #include <errno.h>
++#include <stdlib.h>
+
++#include "address.h"
+ #include "bus.h"
+ #include "daemon.h"
+ #include "logging.h"
+@@ -76,8 +78,40 @@ static int nw_port_object_find(sd_bus* bus, const char* path, const char* interf
+ return 1;
+ }
+
++static int nw_port_bus_get_address(sd_bus* bus, const char* path, const char* interface,
++ const char* property, sd_bus_message* reply, void* data, sd_bus_error* error) {
++ struct nw_port* port = (struct nw_port*)data;
++ int r;
++
++ // Fetch the address
++ const nw_address_t* address = nw_port_get_address(port);
++
++ // Format the address as a string
++ char* s = nw_address_to_string(address);
++ if (!s) {
++ // XXX How to handle any errors?
++ return 0;
++ }
++
++ // Append the address to the return value
++ r = sd_bus_message_append(reply, "s", s);
++ if (r)
++ goto ERROR;
++
++ERROR:
++ if (s)
++ free(s);
++
++ return r;
++}
++
+ static const sd_bus_vtable port_vtable[] = {
+ SD_BUS_VTABLE_START(0),
++
++ // Address
++ SD_BUS_PROPERTY("Address", "s", nw_port_bus_get_address,
++ 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
++
+ SD_BUS_VTABLE_END
+ };
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index d1e3f7f..e59e760 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -221,3 +221,7 @@ char* nw_port_bus_path(struct nw_port* port) {
+
+ return p;
+ }
++
++const nw_address_t* nw_port_get_address(struct nw_port* port) {
++ return &port->address;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index e3655cf..caf1433 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -30,6 +30,8 @@ typedef enum nw_port_type {
+
+ struct nw_port;
+
++#include "address.h"
++
+ int nw_port_create(struct nw_port** port, const char* name);
+
+ struct nw_port* nw_port_ref(struct nw_port* port);
+@@ -39,4 +41,6 @@ const char* nw_port_name(struct nw_port* port);
+
+ char* nw_port_bus_path(struct nw_port* port);
+
++const nw_address_t* nw_port_get_address(struct nw_port* port);
++
+ #endif /* NETWORKD_PORT_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,201 @@
+From 30d4ab67ce9936c6668d93229c8cc808c725338c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 14:10:16 +0000
+Subject: [PATCH 204/304] networkd: Add method to fetch corresponding link to
+ port
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 7 +++++++
+ src/networkd/daemon.h | 1 +
+ src/networkd/link.h | 5 +++--
+ src/networkd/links.c | 15 +++++++++++++++
+ src/networkd/links.h | 1 +
+ src/networkd/port.c | 13 ++++++++++++-
+ src/networkd/port.h | 3 ++-
+ src/networkd/ports.c | 2 +-
+ 8 files changed, 42 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 795a8b7..c4acff5 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -448,6 +448,13 @@ struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifin
+ return nw_links_get_by_ifindex(daemon->links, ifindex);
+ }
+
++struct nw_link* nw_daemon_get_link_by_name(struct nw_daemon* daemon, const char* name) {
++ if (!daemon->links)
++ return NULL;
++
++ return nw_links_get_by_name(daemon->links, name);
++}
++
+ /*
+ Ports
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index e08537e..a40dd60 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -48,6 +48,7 @@ sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon);
+ struct nw_links* nw_daemon_links(struct nw_daemon* daemon);
+ void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link);
+ struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex);
++struct nw_link* nw_daemon_get_link_by_name(struct nw_daemon* daemon, const char* name);
+
+ /*
+ Ports
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index e482d71..72ddfa9 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -21,16 +21,17 @@
+ #ifndef NETWORKD_LINK_H
+ #define NETWORKD_LINK_H
+
+-#include "daemon.h"
+-
+ struct nw_link;
+
++#include "daemon.h"
++
+ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex);
+
+ struct nw_link* nw_link_ref(struct nw_link* link);
+ struct nw_link* nw_link_unref(struct nw_link* link);
+
+ int nw_link_ifindex(struct nw_link* link);
++const char* nw_link_name(struct nw_link* link);
+
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index 044a27d..7aa83a3 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -20,6 +20,7 @@
+
+ #include <stddef.h>
+ #include <stdlib.h>
++#include <string.h>
+ #include <sys/queue.h>
+
+ #include "daemon.h"
+@@ -192,3 +193,17 @@ struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex) {
+
+ return nw_link_ref(entry->link);
+ }
++
++struct nw_link* nw_links_get_by_name(struct nw_links* links, const char* name) {
++ struct nw_links_entry* entry = NULL;
++
++ STAILQ_FOREACH(entry, &links->entries, nodes) {
++ const char* n = nw_link_name(entry->link);
++
++ if (strcmp(name, n) == 0)
++ return nw_link_ref(entry->link);
++ }
++
++ // No match found
++ return NULL;
++}
+diff --git a/src/networkd/links.h b/src/networkd/links.h
+index 2f2bbea..2b5c787 100644
+--- a/src/networkd/links.h
++++ b/src/networkd/links.h
+@@ -37,5 +37,6 @@ void nw_links_drop_link(struct nw_links* links, struct nw_link* link);
+ int nw_links_enumerate(struct nw_links* links);
+
+ struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex);
++struct nw_link* nw_links_get_by_name(struct nw_links* links, const char* name);
+
+ #endif /* NETWORKD_LINKS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index e59e760..33e75d7 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -27,11 +27,13 @@
+
+ #include "address.h"
+ #include "config.h"
++#include "link.h"
+ #include "logging.h"
+ #include "string.h"
+ #include "port.h"
+
+ struct nw_port {
++ struct nw_daemon* daemon;
+ int nrefs;
+
+ char name[IF_NAMESIZE];
+@@ -66,6 +68,8 @@ static nw_port_type_t nw_port_type_from_string(const char* s) {
+ static void nw_port_free(struct nw_port* port) {
+ if (port->config)
+ nw_config_unref(port->config);
++ if (port->daemon)
++ nw_daemon_unref(port->daemon);
+
+ free(port);
+ }
+@@ -163,7 +167,7 @@ static int nw_port_setup(struct nw_port* port) {
+ return 0;
+ }
+
+-int nw_port_create(struct nw_port** port, const char* name) {
++int nw_port_create(struct nw_port** port, struct nw_daemon* daemon, const char* name) {
+ int r;
+
+ // Allocate a new object
+@@ -171,6 +175,9 @@ int nw_port_create(struct nw_port** port, const char* name) {
+ if (!p)
+ return 1;
+
++ // Store a reference to the daemon
++ p->daemon = nw_daemon_ref(daemon);
++
+ // Initialize reference counter
+ p->nrefs = 1;
+
+@@ -222,6 +229,10 @@ char* nw_port_bus_path(struct nw_port* port) {
+ return p;
+ }
+
++static struct nw_link* nw_port_get_link(struct nw_port* port) {
++ return nw_daemon_get_link_by_name(port->daemon, port->name);
++}
++
+ const nw_address_t* nw_port_get_address(struct nw_port* port) {
+ return &port->address;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index caf1433..92d60b2 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -31,8 +31,9 @@ typedef enum nw_port_type {
+ struct nw_port;
+
+ #include "address.h"
++#include "daemon.h"
+
+-int nw_port_create(struct nw_port** port, const char* name);
++int nw_port_create(struct nw_port** port, struct nw_daemon* daemon, const char* name);
+
+ struct nw_port* nw_port_ref(struct nw_port* port);
+ struct nw_port* nw_port_unref(struct nw_port* port);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 24bd257..9cec111 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -138,7 +138,7 @@ static int __nw_ports_enumerate(const char* path, const struct stat* s, void* da
+ return 0;
+
+ // Create a new port
+- r = nw_port_create(&port, name);
++ r = nw_port_create(&port, ports->daemon, name);
+ if (r)
+ goto ERROR;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,1617 @@
+From 2361667ee3e575e7c1b3044936a7b466283d7996 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 17:44:42 +0000
+Subject: [PATCH 205/304] networkd: Use typedef to keep type names shorter
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 4 +--
+ src/networkd/bus.h | 10 +++----
+ src/networkd/config.c | 36 +++++++++++------------
+ src/networkd/config.h | 26 ++++++++---------
+ src/networkd/daemon-bus.c | 4 +--
+ src/networkd/daemon-bus.h | 2 +-
+ src/networkd/daemon.c | 60 +++++++++++++++++++--------------------
+ src/networkd/daemon.h | 34 ++++++++++++----------
+ src/networkd/link.c | 26 ++++++++---------
+ src/networkd/link.h | 12 ++++----
+ src/networkd/links.c | 26 ++++++++---------
+ src/networkd/links.h | 18 ++++++------
+ src/networkd/main.c | 2 +-
+ src/networkd/port-bus.c | 12 ++++----
+ src/networkd/port-bus.h | 2 +-
+ src/networkd/port.c | 30 ++++++++++----------
+ src/networkd/port.h | 14 ++++-----
+ src/networkd/ports.c | 26 ++++++++---------
+ src/networkd/ports.h | 14 ++++-----
+ src/networkd/zone-bus.c | 14 ++++-----
+ src/networkd/zone-bus.h | 2 +-
+ src/networkd/zone.c | 24 ++++++++--------
+ src/networkd/zone.h | 16 +++++------
+ src/networkd/zones.c | 26 ++++++++---------
+ src/networkd/zones.h | 14 ++++-----
+ 25 files changed, 229 insertions(+), 225 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 258aacd..1daa035 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -29,14 +29,14 @@
+ #include "logging.h"
+
+ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error) {
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ DEBUG("Connected to D-Bus\n");
+
+ return 0;
+ }
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon) {
++int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon) {
+ int r;
+
+ // Create a bus object
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 42ab2d3..05b4c63 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -30,27 +30,27 @@
+
+ #include "daemon.h"
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop, struct nw_daemon* daemon);
++int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon);
+
+ struct nw_bus_vtable_pair {
+ const sd_bus_vtable* vtable;
+ sd_bus_object_find_t object_find;
+ };
+
+-struct nw_bus_implementation {
++typedef struct nw_bus_implementation {
+ const char* path;
+ const char* interface;
+ const sd_bus_vtable** vtables;
+ const struct nw_bus_vtable_pair* fallback_vtables;
+ sd_bus_node_enumerator_t node_enumerator;
+ const struct nw_bus_implementation** children;
+-};
++} nw_bus_implementation;
+
+ #define BUS_FALLBACK_VTABLES(...) ((const struct nw_bus_vtable_pair[]) { __VA_ARGS__, {} })
+-#define BUS_IMPLEMENTATIONS(...) ((const struct nw_bus_implementation* []) { __VA_ARGS__, NULL })
++#define BUS_IMPLEMENTATIONS(...) ((const nw_bus_implementation* []) { __VA_ARGS__, NULL })
+ #define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL })
+
+ int nw_bus_register_implementation(sd_bus* bus,
+- const struct nw_bus_implementation* impl, void* data);
++ const nw_bus_implementation* impl, void* data);
+
+ #endif /* NETWORKD_BUS_H */
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 17d6d89..b3d5284 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -50,7 +50,7 @@ static void nw_config_entry_free(struct nw_config_entry* entry) {
+ }
+
+ static struct nw_config_entry* nw_config_entry_create(
+- struct nw_config* config, const char* key) {
++ nw_config* config, const char* key) {
+ int r;
+
+ // Check input value
+@@ -79,17 +79,17 @@ ERROR:
+ return NULL;
+ }
+
+-static void nw_config_free(struct nw_config* config) {
++static void nw_config_free(nw_config* config) {
+ // Flush all entries
+ nw_config_flush(config);
+
+ free(config);
+ }
+
+-int nw_config_create(struct nw_config** config, const char* path) {
++int nw_config_create(nw_config** config, const char* path) {
+ int r;
+
+- struct nw_config* c = calloc(1, sizeof(*c));
++ nw_config* c = calloc(1, sizeof(*c));
+ if (!c)
+ return 1;
+
+@@ -121,13 +121,13 @@ ERROR:
+ return r;
+ }
+
+-struct nw_config* nw_config_ref(struct nw_config* config) {
++nw_config* nw_config_ref(nw_config* config) {
+ config->nrefs++;
+
+ return config;
+ }
+
+-struct nw_config* nw_config_unref(struct nw_config* config) {
++nw_config* nw_config_unref(nw_config* config) {
+ if (--config->nrefs > 0)
+ return config;
+
+@@ -135,14 +135,14 @@ struct nw_config* nw_config_unref(struct nw_config* config) {
+ return NULL;
+ }
+
+-const char* nw_config_path(struct nw_config* config) {
++const char* nw_config_path(nw_config* config) {
+ if (*config->path)
+ return config->path;
+
+ return NULL;
+ }
+
+-int nw_config_flush(struct nw_config* config) {
++int nw_config_flush(nw_config* config) {
+ struct nw_config_entry* entry = NULL;
+
+ while (!STAILQ_EMPTY(&config->entries)) {
+@@ -156,7 +156,7 @@ int nw_config_flush(struct nw_config* config) {
+ return 0;
+ }
+
+-static int nw_config_readf(struct nw_config* config, FILE* f) {
++static int nw_config_readf(nw_config* config, FILE* f) {
+ char* line = NULL;
+ size_t length = 0;
+ int r;
+@@ -202,7 +202,7 @@ static int nw_config_readf(struct nw_config* config, FILE* f) {
+ return r;
+ }
+
+-int nw_config_read(struct nw_config* config) {
++int nw_config_read(nw_config* config) {
+ FILE* f = NULL;
+ int r;
+
+@@ -234,7 +234,7 @@ ERROR:
+ return r;
+ }
+
+-static int nw_config_writef(struct nw_config* config, FILE* f) {
++static int nw_config_writef(nw_config* config, FILE* f) {
+ struct nw_config_entry* entry = NULL;
+ int r;
+
+@@ -254,7 +254,7 @@ static int nw_config_writef(struct nw_config* config, FILE* f) {
+ return 0;
+ }
+
+-int nw_config_write(struct nw_config* config) {
++int nw_config_write(nw_config* config) {
+ int r;
+
+ // We cannot write if path is not set
+@@ -280,7 +280,7 @@ ERROR:
+ return r;
+ }
+
+-static struct nw_config_entry* nw_config_find(struct nw_config* config, const char* key) {
++static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
+ struct nw_config_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &config->entries, nodes) {
+@@ -296,7 +296,7 @@ static struct nw_config_entry* nw_config_find(struct nw_config* config, const ch
+ return NULL;
+ }
+
+-int nw_config_del(struct nw_config* config, const char* key) {
++int nw_config_del(nw_config* config, const char* key) {
+ struct nw_config_entry* entry = NULL;
+
+ // Find an entry matching the key
+@@ -315,7 +315,7 @@ int nw_config_del(struct nw_config* config, const char* key) {
+ return 0;
+ }
+
+-const char* nw_config_get(struct nw_config* config, const char* key) {
++const char* nw_config_get(nw_config* config, const char* key) {
+ struct nw_config_entry* entry = nw_config_find(config, key);
+
+ // Return the value if found and set
+@@ -326,7 +326,7 @@ const char* nw_config_get(struct nw_config* config, const char* key) {
+ return NULL;
+ }
+
+-int nw_config_set(struct nw_config* config, const char* key, const char* value) {
++int nw_config_set(nw_config* config, const char* key, const char* value) {
+ struct nw_config_entry* entry = NULL;
+
+ // Delete the entry if val is NULL
+@@ -347,7 +347,7 @@ int nw_config_set(struct nw_config* config, const char* key, const char* value)
+ return nw_string_set(entry->value, value);
+ }
+
+-int nw_config_get_int(struct nw_config* config, const char* key, const int __default) {
++int nw_config_get_int(nw_config* config, const char* key, const int __default) {
+ const char* value = nw_config_get(config, key);
+
+ // Return zero if not set
+@@ -357,7 +357,7 @@ int nw_config_get_int(struct nw_config* config, const char* key, const int __def
+ return strtoul(value, NULL, 10);
+ }
+
+-int nw_config_set_int(struct nw_config* config, const char* key, const int value) {
++int nw_config_set_int(nw_config* config, const char* key, const int value) {
+ char __value[1024];
+ int r;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index b8d8555..041a10e 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -26,26 +26,26 @@
+ #define NETWORK_CONFIG_KEY_MAX_LENGTH 128
+ #define NETWORK_CONFIG_VALUE_MAX_LENGTH 2048
+
+-struct nw_config;
++typedef struct nw_config nw_config;
+
+-int nw_config_create(struct nw_config** config, const char* path);
++int nw_config_create(nw_config** config, const char* path);
+
+-struct nw_config* nw_config_ref(struct nw_config* config);
+-struct nw_config* nw_config_unref(struct nw_config* config);
++nw_config* nw_config_ref(nw_config* config);
++nw_config* nw_config_unref(nw_config* config);
+
+-const char* nw_config_path(struct nw_config* config);
++const char* nw_config_path(nw_config* config);
+
+-int nw_config_flush(struct nw_config* config);
++int nw_config_flush(nw_config* config);
+
+-int nw_config_read(struct nw_config* config);
+-int nw_config_write(struct nw_config* config);
++int nw_config_read(nw_config* config);
++int nw_config_write(nw_config* config);
+
+-int nw_config_del(struct nw_config* config, const char* key);
++int nw_config_del(nw_config* config, const char* key);
+
+-const char* nw_config_get(struct nw_config* config, const char* key);
+-int nw_config_set(struct nw_config* config, const char* key, const char* value);
++const char* nw_config_get(nw_config* config, const char* key);
++int nw_config_set(nw_config* config, const char* key, const char* value);
+
+-int nw_config_get_int(struct nw_config* config, const char* key, const int __default);
+-int nw_config_set_int(struct nw_config* config, const char* key, const int value);
++int nw_config_get_int(nw_config* config, const char* key, const int __default);
++int nw_config_set_int(nw_config* config, const char* key, const int value);
+
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+index ca0754f..93a0411 100644
+--- a/src/networkd/daemon-bus.c
++++ b/src/networkd/daemon-bus.c
+@@ -26,7 +26,7 @@
+ #include "zone-bus.h"
+
+ static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Reload the daemon
+ nw_daemon_reload(daemon);
+@@ -42,7 +42,7 @@ static const sd_bus_vtable daemon_vtable[] = {
+ SD_BUS_VTABLE_END,
+ };
+
+-const struct nw_bus_implementation daemon_bus_impl = {
++const nw_bus_implementation daemon_bus_impl = {
+ .path = "/org/ipfire/network1",
+ .interface = "org.ipfire.network1",
+ .vtables = BUS_VTABLES(daemon_vtable),
+diff --git a/src/networkd/daemon-bus.h b/src/networkd/daemon-bus.h
+index 787e92a..c314963 100644
+--- a/src/networkd/daemon-bus.h
++++ b/src/networkd/daemon-bus.h
+@@ -23,6 +23,6 @@
+
+ #include "bus.h"
+
+-extern const struct nw_bus_implementation daemon_bus_impl;
++extern const nw_bus_implementation daemon_bus_impl;
+
+ #endif /* NETWORKD_DAEMON_BUS_H */
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index c4acff5..9b9bf73 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -44,7 +44,7 @@
+ struct nw_daemon {
+ int nrefs;
+
+- struct nw_config* config;
++ nw_config* config;
+
+ // Event Loop
+ sd_event* loop;
+@@ -59,13 +59,13 @@ struct nw_daemon {
+ sd_device_monitor* devmon;
+
+ // Links
+- struct nw_links* links;
++ nw_links* links;
+
+ // Zones
+- struct nw_zones* zones;
++ nw_zones* zones;
+
+ // Ports
+- struct nw_ports* ports;
++ nw_ports* ports;
+
+ };
+
+@@ -78,7 +78,7 @@ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_
+
+ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_siginfo* si,
+ void* data) {
+- struct nw_daemon* daemon = (struct nw_daemon*)daemon;
++ nw_daemon* daemon = (nw_daemon*)daemon;
+
+ DEBUG("Received signal to reload...\n");
+
+@@ -88,7 +88,7 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig
+ return 0;
+ }
+
+-static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
++static int nw_daemon_setup_loop(nw_daemon* daemon) {
+ int r;
+
+ // Fetch a reference to the default event loop
+@@ -132,7 +132,7 @@ static int nw_daemon_setup_loop(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int nw_daemon_load_config(struct nw_daemon* daemon) {
++static int nw_daemon_load_config(nw_daemon* daemon) {
+ int r;
+
+ // Read configuration file
+@@ -148,7 +148,7 @@ static int nw_daemon_load_config(struct nw_daemon* daemon) {
+ return r;
+ }
+
+-static int nw_start_device_monitor(struct nw_daemon* daemon) {
++static int nw_start_device_monitor(nw_daemon* daemon) {
+ int r;
+
+ const char* subsystems[] = {
+@@ -199,7 +199,7 @@ static int nw_start_device_monitor(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
++static int nw_daemon_connect_rtnl(nw_daemon* daemon, int fd) {
+ int r;
+
+ // Connect to Netlink
+@@ -242,7 +242,7 @@ static int nw_daemon_connect_rtnl(struct nw_daemon* daemon, int fd) {
+ return 0;
+ }
+
+-static int nw_daemon_enumerate_links(struct nw_daemon* daemon) {
++static int nw_daemon_enumerate_links(nw_daemon* daemon) {
+ int r;
+
+ // Create a new links container
+@@ -253,7 +253,7 @@ static int nw_daemon_enumerate_links(struct nw_daemon* daemon) {
+ return nw_links_enumerate(daemon->links);
+ }
+
+-static int nw_daemon_enumerate_ports(struct nw_daemon* daemon) {
++static int nw_daemon_enumerate_ports(nw_daemon* daemon) {
+ int r;
+
+ // Create a new ports container
+@@ -264,7 +264,7 @@ static int nw_daemon_enumerate_ports(struct nw_daemon* daemon) {
+ return nw_ports_enumerate(daemon->ports);
+ }
+
+-static int nw_daemon_enumerate(struct nw_daemon* daemon) {
++static int nw_daemon_enumerate(nw_daemon* daemon) {
+ int r;
+
+ // Links
+@@ -280,7 +280,7 @@ static int nw_daemon_enumerate(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static int nw_daemon_setup(struct nw_daemon* daemon) {
++static int nw_daemon_setup(nw_daemon* daemon) {
+ int r;
+
+ // Read the configuration
+@@ -316,10 +316,10 @@ static int nw_daemon_setup(struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-int nw_daemon_create(struct nw_daemon** daemon) {
++int nw_daemon_create(nw_daemon** daemon) {
+ int r;
+
+- struct nw_daemon* d = calloc(1, sizeof(*d));
++ nw_daemon* d = calloc(1, sizeof(*d));
+ if (!d)
+ return 1;
+
+@@ -342,7 +342,7 @@ ERROR:
+ return r;
+ }
+
+-static void nw_daemon_cleanup(struct nw_daemon* daemon) {
++static void nw_daemon_cleanup(nw_daemon* daemon) {
+ if (daemon->ports)
+ nw_ports_unref(daemon->ports);
+ if (daemon->zones)
+@@ -353,7 +353,7 @@ static void nw_daemon_cleanup(struct nw_daemon* daemon) {
+ nw_config_unref(daemon->config);
+ }
+
+-static void nw_daemon_free(struct nw_daemon* daemon) {
++static void nw_daemon_free(nw_daemon* daemon) {
+ // Cleanup common objects
+ nw_daemon_cleanup(daemon);
+
+@@ -365,13 +365,13 @@ static void nw_daemon_free(struct nw_daemon* daemon) {
+ free(daemon);
+ }
+
+-struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon) {
++nw_daemon* nw_daemon_ref(nw_daemon* daemon) {
+ daemon->nrefs++;
+
+ return daemon;
+ }
+
+-struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon) {
++nw_daemon* nw_daemon_unref(nw_daemon* daemon) {
+ if (--daemon->nrefs > 0)
+ return daemon;
+
+@@ -382,7 +382,7 @@ struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon) {
+ /*
+ This function contains the main loop of the daemon...
+ */
+-int nw_daemon_run(struct nw_daemon* daemon) {
++int nw_daemon_run(nw_daemon* daemon) {
+ int r;
+
+ // We are now ready to process any requests
+@@ -412,7 +412,7 @@ ERROR:
+ return 1;
+ }
+
+-int nw_daemon_reload(struct nw_daemon* daemon) {
++int nw_daemon_reload(nw_daemon* daemon) {
+ DEBUG("Reloading daemon...\n");
+
+ // XXX TODO
+@@ -423,32 +423,32 @@ int nw_daemon_reload(struct nw_daemon* daemon) {
+ /*
+ Netlink
+ */
+-sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon) {
++sd_netlink* nw_daemon_get_rtnl(nw_daemon* daemon) {
+ return daemon->rtnl;
+ }
+
+ /*
+ Links
+ */
+-struct nw_links* nw_daemon_links(struct nw_daemon* daemon) {
++nw_links* nw_daemon_links(nw_daemon* daemon) {
+ return nw_links_ref(daemon->links);
+ }
+
+-void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link) {
++void nw_daemon_drop_link(nw_daemon* daemon, nw_link* link) {
+ if (!daemon->links)
+ return;
+
+ nw_links_drop_link(daemon->links, link);
+ }
+
+-struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex) {
++nw_link* nw_daemon_get_link_by_ifindex(nw_daemon* daemon, int ifindex) {
+ if (!daemon->links)
+ return NULL;
+
+ return nw_links_get_by_ifindex(daemon->links, ifindex);
+ }
+
+-struct nw_link* nw_daemon_get_link_by_name(struct nw_daemon* daemon, const char* name) {
++nw_link* nw_daemon_get_link_by_name(nw_daemon* daemon, const char* name) {
+ if (!daemon->links)
+ return NULL;
+
+@@ -458,11 +458,11 @@ struct nw_link* nw_daemon_get_link_by_name(struct nw_daemon* daemon, const char*
+ /*
+ Ports
+ */
+-struct nw_ports* nw_daemon_ports(struct nw_daemon* daemon) {
++nw_ports* nw_daemon_ports(nw_daemon* daemon) {
+ return nw_ports_ref(daemon->ports);
+ }
+
+-struct nw_port* nw_daemon_get_port_by_name(struct nw_daemon* daemon, const char* name) {
++nw_port* nw_daemon_get_port_by_name(nw_daemon* daemon, const char* name) {
+ if (!daemon->ports)
+ return NULL;
+
+@@ -473,11 +473,11 @@ struct nw_port* nw_daemon_get_port_by_name(struct nw_daemon* daemon, const char*
+ Zones
+ */
+
+-struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon) {
++nw_zones* nw_daemon_zones(nw_daemon* daemon) {
+ return nw_zones_ref(daemon->zones);
+ }
+
+-struct nw_zone* nw_daemon_get_zone_by_name(struct nw_daemon* daemon, const char* name) {
++nw_zone* nw_daemon_get_zone_by_name(nw_daemon* daemon, const char* name) {
+ if (!daemon->zones)
+ return NULL;
+
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index a40dd60..1694599 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -23,43 +23,47 @@
+
+ #include <systemd/sd-netlink.h>
+
+-struct nw_daemon;
++typedef struct nw_daemon nw_daemon;
+
+ #include "link.h"
++#include "links.h"
++#include "port.h"
++#include "ports.h"
+ #include "zone.h"
++#include "zones.h"
+
+-int nw_daemon_create(struct nw_daemon** daemon);
++int nw_daemon_create(nw_daemon** daemon);
+
+-struct nw_daemon* nw_daemon_ref(struct nw_daemon* daemon);
+-struct nw_daemon* nw_daemon_unref(struct nw_daemon* daemon);
++nw_daemon* nw_daemon_ref(nw_daemon* daemon);
++nw_daemon* nw_daemon_unref(nw_daemon* daemon);
+
+-int nw_daemon_run(struct nw_daemon* daemon);
++int nw_daemon_run(nw_daemon* daemon);
+
+-int nw_daemon_reload(struct nw_daemon* daemon);
++int nw_daemon_reload(nw_daemon* daemon);
+
+ /*
+ Netlink
+ */
+-sd_netlink* nw_daemon_get_rtnl(struct nw_daemon* daemon);
++sd_netlink* nw_daemon_get_rtnl(nw_daemon* daemon);
+
+ /*
+ Links
+ */
+-struct nw_links* nw_daemon_links(struct nw_daemon* daemon);
+-void nw_daemon_drop_link(struct nw_daemon* daemon, struct nw_link* link);
+-struct nw_link* nw_daemon_get_link_by_ifindex(struct nw_daemon* daemon, int ifindex);
+-struct nw_link* nw_daemon_get_link_by_name(struct nw_daemon* daemon, const char* name);
++nw_links* nw_daemon_links(nw_daemon* daemon);
++void nw_daemon_drop_link(nw_daemon* daemon, nw_link* link);
++nw_link* nw_daemon_get_link_by_ifindex(nw_daemon* daemon, int ifindex);
++nw_link* nw_daemon_get_link_by_name(nw_daemon* daemon, const char* name);
+
+ /*
+ Ports
+ */
+-struct nw_ports* nw_daemon_ports(struct nw_daemon* daemon);
+-struct nw_port* nw_daemon_get_port_by_name(struct nw_daemon* daemon, const char* name);
++nw_ports* nw_daemon_ports(nw_daemon* daemon);
++nw_port* nw_daemon_get_port_by_name(nw_daemon* daemon, const char* name);
+
+ /*
+ Zones
+ */
+-struct nw_zones* nw_daemon_zones(struct nw_daemon* daemon);
+-struct nw_zone* nw_daemon_get_zone_by_name(struct nw_daemon* daemon, const char* name);
++nw_zones* nw_daemon_zones(nw_daemon* daemon);
++nw_zone* nw_daemon_get_zone_by_name(nw_daemon* daemon, const char* name);
+
+ #endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 62be893..2f95dc3 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -32,7 +32,7 @@
+ #include "string.h"
+
+ struct nw_link {
+- struct nw_daemon* daemon;
++ nw_daemon* daemon;
+ int nrefs;
+
+ // Interface Index
+@@ -47,9 +47,9 @@ struct nw_link {
+ uint32_t max_mtu;
+ };
+
+-int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex) {
++int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
+ // Allocate a new object
+- struct nw_link* l = calloc(1, sizeof(*l));
++ nw_link* l = calloc(1, sizeof(*l));
+ if (!l)
+ return 1;
+
+@@ -69,20 +69,20 @@ int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex)
+ return 0;
+ }
+
+-static void nw_link_free(struct nw_link* link) {
++static void nw_link_free(nw_link* link) {
+ DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
+
+ if (link->daemon)
+ nw_daemon_unref(link->daemon);
+ }
+
+-struct nw_link* nw_link_ref(struct nw_link* link) {
++nw_link* nw_link_ref(nw_link* link) {
+ link->nrefs++;
+
+ return link;
+ }
+
+-struct nw_link* nw_link_unref(struct nw_link* link) {
++nw_link* nw_link_unref(nw_link* link) {
+ if (--link->nrefs > 0)
+ return link;
+
+@@ -90,11 +90,11 @@ struct nw_link* nw_link_unref(struct nw_link* link) {
+ return NULL;
+ }
+
+-int nw_link_ifindex(struct nw_link* link) {
++int nw_link_ifindex(nw_link* link) {
+ return link->ifindex;
+ }
+
+-static int nw_link_update_ifname(struct nw_link* link, sd_netlink_message* message) {
++static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
+ const char* ifname = NULL;
+ int r;
+
+@@ -120,7 +120,7 @@ static int nw_link_update_ifname(struct nw_link* link, sd_netlink_message* messa
+ return 0;
+ }
+
+-static int nw_link_update_mtu(struct nw_link* link, sd_netlink_message* message) {
++static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
+ uint32_t mtu = 0;
+ uint32_t min_mtu = 0;
+ uint32_t max_mtu = 0;
+@@ -172,7 +172,7 @@ static int nw_link_update_mtu(struct nw_link* link, sd_netlink_message* message)
+ This function is called whenever anything changes, so that we can
+ update our internal link object.
+ */
+-static int nw_link_update(struct nw_link* link, sd_netlink_message* message) {
++static int nw_link_update(nw_link* link, sd_netlink_message* message) {
+ int r;
+
+ // Update the interface name
+@@ -189,14 +189,14 @@ static int nw_link_update(struct nw_link* link, sd_netlink_message* message) {
+ }
+
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+- struct nw_links* links = NULL;
+- struct nw_link* link = NULL;
++ nw_links* links = NULL;
++ nw_link* link = NULL;
+ const char* ifname = NULL;
+ int ifindex;
+ uint16_t type;
+ int r;
+
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Fetch links
+ links = nw_daemon_links(daemon);
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 72ddfa9..acb5e7b 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -21,17 +21,17 @@
+ #ifndef NETWORKD_LINK_H
+ #define NETWORKD_LINK_H
+
+-struct nw_link;
++typedef struct nw_link nw_link;
+
+ #include "daemon.h"
+
+-int nw_link_create(struct nw_link** link, struct nw_daemon* daemon, int ifindex);
++int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex);
+
+-struct nw_link* nw_link_ref(struct nw_link* link);
+-struct nw_link* nw_link_unref(struct nw_link* link);
++nw_link* nw_link_ref(nw_link* link);
++nw_link* nw_link_unref(nw_link* link);
+
+-int nw_link_ifindex(struct nw_link* link);
+-const char* nw_link_name(struct nw_link* link);
++int nw_link_ifindex(nw_link* link);
++const char* nw_link_name(nw_link* link);
+
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index 7aa83a3..9f41034 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -29,14 +29,14 @@
+ #include "links.h"
+
+ struct nw_links_entry {
+- struct nw_link* link;
++ nw_link* link;
+
+ // Link to the other entries
+ STAILQ_ENTRY(nw_links_entry) nodes;
+ };
+
+ struct nw_links {
+- struct nw_daemon* daemon;
++ nw_daemon* daemon;
+ int nrefs;
+
+ // Link Entries
+@@ -46,8 +46,8 @@ struct nw_links {
+ unsigned int num;
+ };
+
+-int nw_links_create(struct nw_links** links, struct nw_daemon* daemon) {
+- struct nw_links* l = calloc(1, sizeof(*l));
++int nw_links_create(nw_links** links, nw_daemon* daemon) {
++ nw_links* l = calloc(1, sizeof(*l));
+ if (!l)
+ return 1;
+
+@@ -66,7 +66,7 @@ int nw_links_create(struct nw_links** links, struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static void nw_links_free(struct nw_links* links) {
++static void nw_links_free(nw_links* links) {
+ struct nw_links_entry* entry = NULL;
+
+ while (!STAILQ_EMPTY(&links->entries)) {
+@@ -86,13 +86,13 @@ static void nw_links_free(struct nw_links* links) {
+ nw_daemon_unref(links->daemon);
+ }
+
+-struct nw_links* nw_links_ref(struct nw_links* links) {
++nw_links* nw_links_ref(nw_links* links) {
+ links->nrefs++;
+
+ return links;
+ }
+
+-struct nw_links* nw_links_unref(struct nw_links* links) {
++nw_links* nw_links_unref(nw_links* links) {
+ if (--links->nrefs > 0)
+ return links;
+
+@@ -100,7 +100,7 @@ struct nw_links* nw_links_unref(struct nw_links* links) {
+ return NULL;
+ }
+
+-static struct nw_links_entry* nw_links_find_link(struct nw_links* links, const int ifindex) {
++static struct nw_links_entry* nw_links_find_link(nw_links* links, const int ifindex) {
+ struct nw_links_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &links->entries, nodes) {
+@@ -112,7 +112,7 @@ static struct nw_links_entry* nw_links_find_link(struct nw_links* links, const i
+ return NULL;
+ }
+
+-int nw_links_add_link(struct nw_links* links, struct nw_link* link) {
++int nw_links_add_link(nw_links* links, struct nw_link* link) {
+ // Allocate a new entry
+ struct nw_links_entry* entry = calloc(1, sizeof(*entry));
+ if (!entry)
+@@ -130,7 +130,7 @@ int nw_links_add_link(struct nw_links* links, struct nw_link* link) {
+ return 0;
+ }
+
+-void nw_links_drop_link(struct nw_links* links, struct nw_link* link) {
++void nw_links_drop_link(nw_links* links, struct nw_link* link) {
+ struct nw_links_entry* entry = NULL;
+
+ entry = nw_links_find_link(links, nw_link_ifindex(link));
+@@ -143,7 +143,7 @@ void nw_links_drop_link(struct nw_links* links, struct nw_link* link) {
+ links->num--;
+ }
+
+-int nw_links_enumerate(struct nw_links* links) {
++int nw_links_enumerate(nw_links* links) {
+ sd_netlink_message* req = NULL;
+ sd_netlink_message* res = NULL;
+ int r;
+@@ -184,7 +184,7 @@ ERROR:
+ return r;
+ }
+
+-struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex) {
++nw_link* nw_links_get_by_ifindex(nw_links* links, int ifindex) {
+ struct nw_links_entry* entry = NULL;
+
+ entry = nw_links_find_link(links, ifindex);
+@@ -194,7 +194,7 @@ struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex) {
+ return nw_link_ref(entry->link);
+ }
+
+-struct nw_link* nw_links_get_by_name(struct nw_links* links, const char* name) {
++nw_link* nw_links_get_by_name(nw_links* links, const char* name) {
+ struct nw_links_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &links->entries, nodes) {
+diff --git a/src/networkd/links.h b/src/networkd/links.h
+index 2b5c787..21e6cfb 100644
+--- a/src/networkd/links.h
++++ b/src/networkd/links.h
+@@ -24,19 +24,19 @@
+ #include "daemon.h"
+ #include "link.h"
+
+-struct nw_links;
++typedef struct nw_links nw_links;
+
+-int nw_links_create(struct nw_links** links, struct nw_daemon* daemon);
++int nw_links_create(nw_links** links, nw_daemon* daemon);
+
+-struct nw_links* nw_links_ref(struct nw_links* links);
+-struct nw_links* nw_links_unref(struct nw_links* links);
++nw_links* nw_links_ref(nw_links* links);
++nw_links* nw_links_unref(nw_links* links);
+
+-int nw_links_add_link(struct nw_links* links, struct nw_link* link);
+-void nw_links_drop_link(struct nw_links* links, struct nw_link* link);
++int nw_links_add_link(nw_links* links, nw_link* link);
++void nw_links_drop_link(nw_links* links, nw_link* link);
+
+-int nw_links_enumerate(struct nw_links* links);
++int nw_links_enumerate(nw_links* links);
+
+-struct nw_link* nw_links_get_by_ifindex(struct nw_links* links, int ifindex);
+-struct nw_link* nw_links_get_by_name(struct nw_links* links, const char* name);
++nw_link* nw_links_get_by_ifindex(nw_links* links, int ifindex);
++nw_link* nw_links_get_by_name(nw_links* links, const char* name);
+
+ #endif /* NETWORKD_LINKS_H */
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index 9a0fd74..d3afc2a 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -212,7 +212,7 @@ static int drop_privileges(const char* user) {
+ }
+
+ int main(int argc, char** argv) {
+- struct nw_daemon* daemon = NULL;
++ nw_daemon* daemon = NULL;
+ int r;
+
+ // Drop privileges
+diff --git a/src/networkd/port-bus.c b/src/networkd/port-bus.c
+index 1a48655..996be92 100644
+--- a/src/networkd/port-bus.c
++++ b/src/networkd/port-bus.c
+@@ -36,10 +36,10 @@ static int nw_port_node_enumerator(sd_bus* bus, const char* path, void* data,
+ DEBUG("Enumerating ports...\n");
+
+ // Fetch a reference to the daemon
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Fetch ports
+- struct nw_ports* ports = nw_daemon_ports(daemon);
++ nw_ports* ports = nw_daemon_ports(daemon);
+
+ // Make bus paths for all ports
+ r = nw_ports_bus_paths(ports, nodes);
+@@ -58,7 +58,7 @@ static int nw_port_object_find(sd_bus* bus, const char* path, const char* interf
+ int r;
+
+ // Fetch a reference to the daemon
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Decode the path of the requested object
+ r = sd_bus_path_decode(path, "/org/ipfire/network1/port", &name);
+@@ -66,7 +66,7 @@ static int nw_port_object_find(sd_bus* bus, const char* path, const char* interf
+ return 0;
+
+ // Find the port
+- struct nw_port* port = nw_daemon_get_port_by_name(daemon, name);
++ nw_port* port = nw_daemon_get_port_by_name(daemon, name);
+ if (!port)
+ return 0;
+
+@@ -80,7 +80,7 @@ static int nw_port_object_find(sd_bus* bus, const char* path, const char* interf
+
+ static int nw_port_bus_get_address(sd_bus* bus, const char* path, const char* interface,
+ const char* property, sd_bus_message* reply, void* data, sd_bus_error* error) {
+- struct nw_port* port = (struct nw_port*)data;
++ nw_port* port = (nw_port*)data;
+ int r;
+
+ // Fetch the address
+@@ -115,7 +115,7 @@ static const sd_bus_vtable port_vtable[] = {
+ SD_BUS_VTABLE_END
+ };
+
+-const struct nw_bus_implementation port_bus_impl = {
++const nw_bus_implementation port_bus_impl = {
+ "/org/ipfire/network1/port",
+ "org.ipfire.network1.Port",
+ .fallback_vtables = BUS_FALLBACK_VTABLES({port_vtable, nw_port_object_find}),
+diff --git a/src/networkd/port-bus.h b/src/networkd/port-bus.h
+index 95e49a8..373c281 100644
+--- a/src/networkd/port-bus.h
++++ b/src/networkd/port-bus.h
+@@ -23,6 +23,6 @@
+
+ #include "bus.h"
+
+-extern const struct nw_bus_implementation port_bus_impl;
++extern const nw_bus_implementation port_bus_impl;
+
+ #endif /* NETWORKD_PORT_BUS_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 33e75d7..41b8b24 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -33,14 +33,14 @@
+ #include "port.h"
+
+ struct nw_port {
+- struct nw_daemon* daemon;
++ nw_daemon* daemon;
+ int nrefs;
+
+ char name[IF_NAMESIZE];
+ nw_port_type_t type;
+
+ // Configuration
+- struct nw_config *config;
++ nw_config *config;
+
+ // Common attributes
+ nw_address_t address;
+@@ -65,7 +65,7 @@ static nw_port_type_t nw_port_type_from_string(const char* s) {
+ return NW_PORT_UNKNOWN;
+ }
+
+-static void nw_port_free(struct nw_port* port) {
++static void nw_port_free(nw_port* port) {
+ if (port->config)
+ nw_config_unref(port->config);
+ if (port->daemon)
+@@ -74,7 +74,7 @@ static void nw_port_free(struct nw_port* port) {
+ free(port);
+ }
+
+-static int nw_port_setup_address(struct nw_port* port) {
++static int nw_port_setup_address(nw_port* port) {
+ int r;
+
+ // Read ADDRESS from configuration
+@@ -111,7 +111,7 @@ ERROR:
+ return 0;
+ }
+
+-static int nw_port_setup_common(struct nw_port* port) {
++static int nw_port_setup_common(nw_port* port) {
+ int r;
+
+ // Address
+@@ -122,7 +122,7 @@ static int nw_port_setup_common(struct nw_port* port) {
+ return 0;
+ }
+
+-static nw_port_type_t nw_port_setup_type(struct nw_port* port) {
++static nw_port_type_t nw_port_setup_type(nw_port* port) {
+ const char* type = nw_config_get(port->config, "TYPE");
+ if (!type)
+ return NW_PORT_UNKNOWN;
+@@ -130,7 +130,7 @@ static nw_port_type_t nw_port_setup_type(struct nw_port* port) {
+ return nw_port_type_from_string(type);
+ }
+
+-static int nw_port_setup(struct nw_port* port) {
++static int nw_port_setup(nw_port* port) {
+ char path[PATH_MAX];
+ int r;
+
+@@ -167,11 +167,11 @@ static int nw_port_setup(struct nw_port* port) {
+ return 0;
+ }
+
+-int nw_port_create(struct nw_port** port, struct nw_daemon* daemon, const char* name) {
++int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
+ int r;
+
+ // Allocate a new object
+- struct nw_port* p = calloc(1, sizeof(*p));
++ nw_port* p = calloc(1, sizeof(*p));
+ if (!p)
+ return 1;
+
+@@ -199,13 +199,13 @@ ERROR:
+ return r;
+ }
+
+-struct nw_port* nw_port_ref(struct nw_port* port) {
++nw_port* nw_port_ref(nw_port* port) {
+ port->nrefs++;
+
+ return port;
+ }
+
+-struct nw_port* nw_port_unref(struct nw_port* port) {
++nw_port* nw_port_unref(nw_port* port) {
+ if (--port->nrefs > 0)
+ return port;
+
+@@ -213,11 +213,11 @@ struct nw_port* nw_port_unref(struct nw_port* port) {
+ return NULL;
+ }
+
+-const char* nw_port_name(struct nw_port* port) {
++const char* nw_port_name(nw_port* port) {
+ return port->name;
+ }
+
+-char* nw_port_bus_path(struct nw_port* port) {
++char* nw_port_bus_path(nw_port* port) {
+ char* p = NULL;
+ int r;
+
+@@ -229,10 +229,10 @@ char* nw_port_bus_path(struct nw_port* port) {
+ return p;
+ }
+
+-static struct nw_link* nw_port_get_link(struct nw_port* port) {
++static nw_link* nw_port_get_link(nw_port* port) {
+ return nw_daemon_get_link_by_name(port->daemon, port->name);
+ }
+
+-const nw_address_t* nw_port_get_address(struct nw_port* port) {
++const nw_address_t* nw_port_get_address(nw_port* port) {
+ return &port->address;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 92d60b2..9881846 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -28,20 +28,20 @@ typedef enum nw_port_type {
+ NW_PORT_DUMMY,
+ } nw_port_type_t;
+
+-struct nw_port;
++typedef struct nw_port nw_port;
+
+ #include "address.h"
+ #include "daemon.h"
+
+-int nw_port_create(struct nw_port** port, struct nw_daemon* daemon, const char* name);
++int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name);
+
+-struct nw_port* nw_port_ref(struct nw_port* port);
+-struct nw_port* nw_port_unref(struct nw_port* port);
++nw_port* nw_port_ref(nw_port* port);
++nw_port* nw_port_unref(nw_port* port);
+
+-const char* nw_port_name(struct nw_port* port);
++const char* nw_port_name(nw_port* port);
+
+-char* nw_port_bus_path(struct nw_port* port);
++char* nw_port_bus_path(nw_port* port);
+
+-const nw_address_t* nw_port_get_address(struct nw_port* port);
++const nw_address_t* nw_port_get_address(nw_port* port);
+
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 9cec111..35ed048 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -30,14 +30,14 @@
+ #include "util.h"
+
+ struct nw_ports_entry {
+- struct nw_port* port;
++ nw_port* port;
+
+ // Link to the other entries
+ STAILQ_ENTRY(nw_ports_entry) nodes;
+ };
+
+ struct nw_ports {
+- struct nw_daemon* daemon;
++ nw_daemon* daemon;
+ int nrefs;
+
+ // Port Entries
+@@ -47,8 +47,8 @@ struct nw_ports {
+ unsigned int num;
+ };
+
+-int nw_ports_create(struct nw_ports** ports, struct nw_daemon* daemon) {
+- struct nw_ports* p = calloc(1, sizeof(*p));
++int nw_ports_create(nw_ports** ports, nw_daemon* daemon) {
++ nw_ports* p = calloc(1, sizeof(*p));
+ if (!p)
+ return 1;
+
+@@ -67,7 +67,7 @@ int nw_ports_create(struct nw_ports** ports, struct nw_daemon* daemon) {
+ return 0;
+ }
+
+-static void nw_ports_free(struct nw_ports* ports) {
++static void nw_ports_free(nw_ports* ports) {
+ struct nw_ports_entry* entry = NULL;
+
+ while (!STAILQ_EMPTY(&ports->entries)) {
+@@ -84,13 +84,13 @@ static void nw_ports_free(struct nw_ports* ports) {
+ }
+ }
+
+-struct nw_ports* nw_ports_ref(struct nw_ports* ports) {
++nw_ports* nw_ports_ref(nw_ports* ports) {
+ ports->nrefs++;
+
+ return ports;
+ }
+
+-struct nw_ports* nw_ports_unref(struct nw_ports* ports) {
++nw_ports* nw_ports_unref(nw_ports* ports) {
+ if (--ports->nrefs > 0)
+ return ports;
+
+@@ -98,7 +98,7 @@ struct nw_ports* nw_ports_unref(struct nw_ports* ports) {
+ return NULL;
+ }
+
+-static int nw_ports_add_port(struct nw_ports* ports, struct nw_port* port) {
++static int nw_ports_add_port(nw_ports* ports, nw_port* port) {
+ // Allocate a new entry
+ struct nw_ports_entry* entry = calloc(1, sizeof(*entry));
+ if (!entry)
+@@ -117,10 +117,10 @@ static int nw_ports_add_port(struct nw_ports* ports, struct nw_port* port) {
+ }
+
+ static int __nw_ports_enumerate(const char* path, const struct stat* s, void* data) {
+- struct nw_port* port = NULL;
++ nw_port* port = NULL;
+ int r;
+
+- struct nw_ports* ports = (struct nw_ports*)data;
++ nw_ports* ports = (nw_ports*)data;
+
+ // Skip anything that isn't a regular file
+ if (!S_ISREG(s->st_mode))
+@@ -154,11 +154,11 @@ ERROR:
+ return r;
+ }
+
+-int nw_ports_enumerate(struct nw_ports* ports) {
++int nw_ports_enumerate(nw_ports* ports) {
+ return nw_ftw(PORT_CONFIG_DIR, PORT_CONFIG_DIR "/*", __nw_ports_enumerate, ports);
+ }
+
+-struct nw_port* nw_ports_get_by_name(struct nw_ports* ports, const char* name) {
++nw_port* nw_ports_get_by_name(nw_ports* ports, const char* name) {
+ struct nw_ports_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &ports->entries, nodes) {
+@@ -173,7 +173,7 @@ struct nw_port* nw_ports_get_by_name(struct nw_ports* ports, const char* name) {
+ return NULL;
+ }
+
+-int nw_ports_bus_paths(struct nw_ports* ports, char*** paths) {
++int nw_ports_bus_paths(nw_ports* ports, char*** paths) {
+ struct nw_ports_entry* entry = NULL;
+ char* path = NULL;
+
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+index e807e25..f58c3a4 100644
+--- a/src/networkd/ports.h
++++ b/src/networkd/ports.h
+@@ -21,19 +21,19 @@
+ #ifndef NETWORKD_PORTS_H
+ #define NETWORKD_PORTS_H
+
+-struct nw_ports;
++typedef struct nw_ports nw_ports;
+
+ #include "daemon.h"
+
+-int nw_ports_create(struct nw_ports** ports, struct nw_daemon* daemon);
++int nw_ports_create(nw_ports** ports, nw_daemon* daemon);
+
+-struct nw_ports* nw_ports_ref(struct nw_ports* ports);
+-struct nw_ports* nw_ports_unref(struct nw_ports* ports);
++nw_ports* nw_ports_ref(nw_ports* ports);
++nw_ports* nw_ports_unref(nw_ports* ports);
+
+-int nw_ports_enumerate(struct nw_ports* ports);
++int nw_ports_enumerate(nw_ports* ports);
+
+-struct nw_port* nw_ports_get_by_name(struct nw_ports* ports, const char* name);
++struct nw_port* nw_ports_get_by_name(nw_ports* ports, const char* name);
+
+-int nw_ports_bus_paths(struct nw_ports* ports, char*** paths);
++int nw_ports_bus_paths(nw_ports* ports, char*** paths);
+
+ #endif /* NETWORKD_PORTS_H */
+diff --git a/src/networkd/zone-bus.c b/src/networkd/zone-bus.c
+index e9c2ecc..a06deb5 100644
+--- a/src/networkd/zone-bus.c
++++ b/src/networkd/zone-bus.c
+@@ -34,10 +34,10 @@ static int nw_zone_node_enumerator(sd_bus* bus, const char* path, void* data,
+ DEBUG("Enumerating zones...\n");
+
+ // Fetch a reference to the daemon
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Fetch zones
+- struct nw_zones* zones = nw_daemon_zones(daemon);
++ nw_zones* zones = nw_daemon_zones(daemon);
+
+ // Make bus paths for all zones
+ r = nw_zones_bus_paths(zones, nodes);
+@@ -56,7 +56,7 @@ static int nw_zone_object_find(sd_bus* bus, const char* path, const char* interf
+ int r;
+
+ // Fetch a reference to the daemon
+- struct nw_daemon* daemon = (struct nw_daemon*)data;
++ nw_daemon* daemon = (nw_daemon*)data;
+
+ // Decode the path of the requested object
+ r = sd_bus_path_decode(path, "/org/ipfire/network1/zone", &name);
+@@ -64,7 +64,7 @@ static int nw_zone_object_find(sd_bus* bus, const char* path, const char* interf
+ return 0;
+
+ // Find the zone
+- struct nw_zone* zone = nw_daemon_get_zone_by_name(daemon, name);
++ nw_zone* zone = nw_daemon_get_zone_by_name(daemon, name);
+ if (!zone)
+ return 0;
+
+@@ -81,7 +81,7 @@ static int nw_zone_object_find(sd_bus* bus, const char* path, const char* interf
+ */
+ static int nw_zone_bus_get_mtu(sd_bus* bus, const char *path, const char *interface,
+ const char* property, sd_bus_message* reply, void* data, sd_bus_error *error) {
+- struct nw_zone* zone = (struct nw_zone*)data;
++ nw_zone* zone = (nw_zone*)data;
+
+ return sd_bus_message_append(reply, "u", nw_zone_mtu(zone));
+ }
+@@ -91,7 +91,7 @@ static int nw_zone_bus_set_mtu(sd_bus* bus, const char* path, const char* interf
+ unsigned int mtu = 0;
+ int r;
+
+- struct nw_zone* zone = (struct nw_zone*)data;
++ nw_zone* zone = (nw_zone*)data;
+
+ // Parse the value
+ r = sd_bus_message_read(value, "u", &mtu);
+@@ -114,7 +114,7 @@ static const sd_bus_vtable zone_vtable[] = {
+ SD_BUS_VTABLE_END
+ };
+
+-const struct nw_bus_implementation zone_bus_impl = {
++const nw_bus_implementation zone_bus_impl = {
+ "/org/ipfire/network1/zone",
+ "org.ipfire.network1.Zone",
+ .fallback_vtables = BUS_FALLBACK_VTABLES({zone_vtable, nw_zone_object_find}),
+diff --git a/src/networkd/zone-bus.h b/src/networkd/zone-bus.h
+index 0d5583e..db257f7 100644
+--- a/src/networkd/zone-bus.h
++++ b/src/networkd/zone-bus.h
+@@ -23,6 +23,6 @@
+
+ #include "bus.h"
+
+-extern const struct nw_bus_implementation zone_bus_impl;
++extern const nw_bus_implementation zone_bus_impl;
+
+ #endif /* NETWORKD_ZONE_BUS_H */
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index c64706f..b6fa65d 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -34,13 +34,13 @@ struct nw_zone {
+ char name[NETWORK_ZONE_NAME_MAX_LENGTH];
+
+ // Configuration
+- struct nw_config *config;
++ nw_config *config;
+ };
+
+ #define nw_zone_path(zone, path, format, ...) \
+ __nw_zone_path(zone, path, sizeof(path), format, __VA_ARGS__)
+
+-static int __nw_zone_path(struct nw_zone* zone, char* p, const size_t length,
++static int __nw_zone_path(nw_zone* zone, char* p, const size_t length,
+ const char* format, ...) {
+ char prefix[NAME_MAX];
+ char suffix[NAME_MAX];
+@@ -63,14 +63,14 @@ static int __nw_zone_path(struct nw_zone* zone, char* p, const size_t length,
+ return __nw_path_join(p, length, prefix, suffix);
+ }
+
+-static void nw_zone_free(struct nw_zone* zone) {
++static void nw_zone_free(nw_zone* zone) {
+ if (zone->config)
+ nw_config_unref(zone->config);
+
+ free(zone);
+ }
+
+-static int nw_zone_setup(struct nw_zone* zone) {
++static int nw_zone_setup(nw_zone* zone) {
+ char path[PATH_MAX];
+ int r;
+
+@@ -87,11 +87,11 @@ static int nw_zone_setup(struct nw_zone* zone) {
+ return 0;
+ }
+
+-int nw_zone_create(struct nw_zone** zone, const char* name) {
++int nw_zone_create(nw_zone** zone, const char* name) {
+ int r;
+
+ // Allocate a new object
+- struct nw_zone* z = calloc(1, sizeof(*z));
++ nw_zone* z = calloc(1, sizeof(*z));
+ if (!z)
+ return 1;
+
+@@ -116,13 +116,13 @@ ERROR:
+ return r;
+ }
+
+-struct nw_zone* nw_zone_ref(struct nw_zone* zone) {
++nw_zone* nw_zone_ref(nw_zone* zone) {
+ zone->nrefs++;
+
+ return zone;
+ }
+
+-struct nw_zone* nw_zone_unref(struct nw_zone* zone) {
++nw_zone* nw_zone_unref(nw_zone* zone) {
+ if (--zone->nrefs > 0)
+ return zone;
+
+@@ -130,11 +130,11 @@ struct nw_zone* nw_zone_unref(struct nw_zone* zone) {
+ return NULL;
+ }
+
+-const char* nw_zone_name(struct nw_zone* zone) {
++const char* nw_zone_name(nw_zone* zone) {
+ return zone->name;
+ }
+
+-char* nw_zone_bus_path(struct nw_zone* zone) {
++char* nw_zone_bus_path(nw_zone* zone) {
+ char* p = NULL;
+ int r;
+
+@@ -149,11 +149,11 @@ char* nw_zone_bus_path(struct nw_zone* zone) {
+ /*
+ MTU
+ */
+-unsigned int nw_zone_mtu(struct nw_zone* zone) {
++unsigned int nw_zone_mtu(nw_zone* zone) {
+ return nw_config_get_int(zone->config, "MTU", NETWORK_ZONE_DEFAULT_MTU);
+ }
+
+-int nw_zone_set_mtu(struct nw_zone* zone, unsigned int mtu) {
++int nw_zone_set_mtu(nw_zone* zone, unsigned int mtu) {
+ DEBUG("Change MTU of %s to %u\n", zone->name, mtu);
+
+ return nw_config_set_int(zone->config, "MTU", mtu);
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 4a00412..1c8e6b6 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -24,21 +24,21 @@
+ #define NETWORK_ZONE_NAME_MAX_LENGTH 16
+ #define NETWORK_ZONE_DEFAULT_MTU 1500
+
+-struct nw_zone;
++typedef struct nw_zone nw_zone;
+
+-int nw_zone_create(struct nw_zone** zone, const char* name);
++int nw_zone_create(nw_zone** zone, const char* name);
+
+-struct nw_zone* nw_zone_ref(struct nw_zone* zone);
+-struct nw_zone* nw_zone_unref(struct nw_zone* zone);
++nw_zone* nw_zone_ref(nw_zone* zone);
++nw_zone* nw_zone_unref(nw_zone* zone);
+
+-const char* nw_zone_name(struct nw_zone* zone);
++const char* nw_zone_name(nw_zone* zone);
+
+-char* nw_zone_bus_path(struct nw_zone* zone);
++char* nw_zone_bus_path(nw_zone* zone);
+
+ /*
+ MTU
+ */
+-unsigned int nw_zone_mtu(struct nw_zone* zone);
+-int nw_zone_set_mtu(struct nw_zone* zone, unsigned int mtu);
++unsigned int nw_zone_mtu(nw_zone* zone);
++int nw_zone_set_mtu(nw_zone* zone, unsigned int mtu);
+
+ #endif /* NETWORKD_ZONE_H */
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 4f739f5..779bbe1 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -28,7 +28,7 @@
+ #include "zones.h"
+
+ struct nw_zones_entry {
+- struct nw_zone* zone;
++ nw_zone* zone;
+
+ // Link to the other entries
+ STAILQ_ENTRY(nw_zones_entry) nodes;
+@@ -44,8 +44,8 @@ struct nw_zones {
+ unsigned int num;
+ };
+
+-static int nw_zones_create(struct nw_zones** zones) {
+- struct nw_zones* z = calloc(1, sizeof(*z));
++static int nw_zones_create(nw_zones** zones) {
++ nw_zones* z = calloc(1, sizeof(*z));
+ if (!z)
+ return 1;
+
+@@ -61,7 +61,7 @@ static int nw_zones_create(struct nw_zones** zones) {
+ return 0;
+ }
+
+-static void nw_zones_free(struct nw_zones* zones) {
++static void nw_zones_free(nw_zones* zones) {
+ struct nw_zones_entry* entry = NULL;
+
+ while (!STAILQ_EMPTY(&zones->entries)) {
+@@ -78,13 +78,13 @@ static void nw_zones_free(struct nw_zones* zones) {
+ }
+ }
+
+-struct nw_zones* nw_zones_ref(struct nw_zones* zones) {
++nw_zones* nw_zones_ref(nw_zones* zones) {
+ zones->nrefs++;
+
+ return zones;
+ }
+
+-struct nw_zones* nw_zones_unref(struct nw_zones* zones) {
++nw_zones* nw_zones_unref(nw_zones* zones) {
+ if (--zones->nrefs > 0)
+ return zones;
+
+@@ -92,7 +92,7 @@ struct nw_zones* nw_zones_unref(struct nw_zones* zones) {
+ return NULL;
+ }
+
+-static int nw_zones_add_zone(struct nw_zones* zones, struct nw_zone* zone) {
++static int nw_zones_add_zone(nw_zones* zones, nw_zone* zone) {
+ // Allocate a new entry
+ struct nw_zones_entry* entry = calloc(1, sizeof(*entry));
+ if (!entry)
+@@ -124,12 +124,12 @@ static int nw_zones_load_filter(const struct dirent* path) {
+ return 1;
+ }
+
+-static int __nw_zones_load(struct nw_zones* zones) {
++static int __nw_zones_load(nw_zones* zones) {
+ struct dirent** paths = NULL;
+ int n;
+ int r = 0;
+
+- struct nw_zone* zone = NULL;
++ nw_zone* zone = NULL;
+
+ // Scan the zones directory
+ n = scandir(CONFIG_DIR "/zones", &paths, nw_zones_load_filter, alphasort);
+@@ -173,7 +173,7 @@ ERROR:
+ return r;
+ }
+
+-int nw_zones_load(struct nw_zones** zones) {
++int nw_zones_load(nw_zones** zones) {
+ int r;
+
+ // Create a new zones object
+@@ -193,7 +193,7 @@ ERROR:
+ return r;
+ }
+
+-size_t nw_zones_num(struct nw_zones* zones) {
++size_t nw_zones_num(nw_zones* zones) {
+ struct nw_zones_entry* entry = NULL;
+ size_t length = 0;
+
+@@ -204,7 +204,7 @@ size_t nw_zones_num(struct nw_zones* zones) {
+ return length;
+ }
+
+-struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name) {
++nw_zone* nw_zones_get_by_name(nw_zones* zones, const char* name) {
+ struct nw_zones_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &zones->entries, nodes) {
+@@ -219,7 +219,7 @@ struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name) {
+ return NULL;
+ }
+
+-int nw_zones_bus_paths(struct nw_zones* zones, char*** paths) {
++int nw_zones_bus_paths(nw_zones* zones, char*** paths) {
+ struct nw_zones_entry* entry = NULL;
+ char* path = NULL;
+
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index 6e2686d..e41c74f 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -21,17 +21,17 @@
+ #ifndef NETWORKD_ZONES_H
+ #define NETWORKD_ZONES_H
+
+-struct nw_zones;
++typedef struct nw_zones nw_zones;
+
+-struct nw_zones* nw_zones_ref(struct nw_zones* zones);
+-struct nw_zones* nw_zones_unref(struct nw_zones* zones);
++nw_zones* nw_zones_ref(nw_zones* zones);
++nw_zones* nw_zones_unref(nw_zones* zones);
+
+-int nw_zones_load(struct nw_zones** zones);
++int nw_zones_load(nw_zones** zones);
+
+-size_t nw_zones_num(struct nw_zones* zones);
++size_t nw_zones_num(nw_zones* zones);
+
+-struct nw_zone* nw_zones_get_by_name(struct nw_zones* zones, const char* name);
++nw_zone* nw_zones_get_by_name(nw_zones* zones, const char* name);
+
+-int nw_zones_bus_paths(struct nw_zones* zones, char*** paths);
++int nw_zones_bus_paths(nw_zones* zones, char*** paths);
+
+ #endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,180 @@
+From dd84704eadbf2f55f650b5879095bafad7ffec30 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 17:51:28 +0000
+Subject: [PATCH 206/304] networkd: Store a reference to the daemon in zone
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 2 +-
+ src/networkd/zone.c | 9 ++++++++-
+ src/networkd/zone.h | 4 +++-
+ src/networkd/zones.c | 18 ++++++++++++++----
+ src/networkd/zones.h | 4 +++-
+ 5 files changed, 29 insertions(+), 8 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 9b9bf73..8122f58 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -141,7 +141,7 @@ static int nw_daemon_load_config(nw_daemon* daemon) {
+ return r;
+
+ // Load zones
+- r = nw_zones_load(&daemon->zones);
++ r = nw_zones_load(&daemon->zones, daemon);
+ if (r)
+ return r;
+
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index b6fa65d..30f22d6 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -24,11 +24,13 @@
+ #include <systemd/sd-bus.h>
+
+ #include "config.h"
++#include "daemon.h"
+ #include "logging.h"
+ #include "string.h"
+ #include "zone.h"
+
+ struct nw_zone {
++ nw_daemon* daemon;
+ int nrefs;
+
+ char name[NETWORK_ZONE_NAME_MAX_LENGTH];
+@@ -66,6 +68,8 @@ static int __nw_zone_path(nw_zone* zone, char* p, const size_t length,
+ static void nw_zone_free(nw_zone* zone) {
+ if (zone->config)
+ nw_config_unref(zone->config);
++ if (zone->daemon)
++ nw_daemon_unref(zone->daemon);
+
+ free(zone);
+ }
+@@ -87,7 +91,7 @@ static int nw_zone_setup(nw_zone* zone) {
+ return 0;
+ }
+
+-int nw_zone_create(nw_zone** zone, const char* name) {
++int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) {
+ int r;
+
+ // Allocate a new object
+@@ -95,6 +99,9 @@ int nw_zone_create(nw_zone** zone, const char* name) {
+ if (!z)
+ return 1;
+
++ // Store a reference to the daemon
++ z->daemon = nw_daemon_ref(daemon);
++
+ // Initialize reference counter
+ z->nrefs = 1;
+
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 1c8e6b6..9df30ec 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -26,7 +26,9 @@
+
+ typedef struct nw_zone nw_zone;
+
+-int nw_zone_create(nw_zone** zone, const char* name);
++#include "daemon.h"
++
++int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name);
+
+ nw_zone* nw_zone_ref(nw_zone* zone);
+ nw_zone* nw_zone_unref(nw_zone* zone);
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 779bbe1..fd43f1c 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -23,6 +23,7 @@
+ #include <string.h>
+ #include <sys/queue.h>
+
++#include "daemon.h"
+ #include "logging.h"
+ #include "zone.h"
+ #include "zones.h"
+@@ -35,6 +36,7 @@ struct nw_zones_entry {
+ };
+
+ struct nw_zones {
++ nw_daemon* daemon;
+ int nrefs;
+
+ // Zone Entries
+@@ -44,11 +46,14 @@ struct nw_zones {
+ unsigned int num;
+ };
+
+-static int nw_zones_create(nw_zones** zones) {
++static int nw_zones_create(nw_zones** zones, nw_daemon* daemon) {
+ nw_zones* z = calloc(1, sizeof(*z));
+ if (!z)
+ return 1;
+
++ // Store a reference to the daemon
++ z->daemon = nw_daemon_ref(daemon);
++
+ // Initialize the reference counter
+ z->nrefs = 1;
+
+@@ -76,6 +81,11 @@ static void nw_zones_free(nw_zones* zones) {
+ // Free the entry
+ free(entry);
+ }
++
++ if (zones->daemon)
++ nw_daemon_unref(zones->daemon);
++
++ free(zones);
+ }
+
+ nw_zones* nw_zones_ref(nw_zones* zones) {
+@@ -147,7 +157,7 @@ static int __nw_zones_load(nw_zones* zones) {
+ DEBUG("Loading zone '%s'...\n", name);
+
+ // Create a new zone object
+- r = nw_zone_create(&zone, name);
++ r = nw_zone_create(&zone, zones->daemon, name);
+ if (r)
+ goto ERROR;
+
+@@ -173,11 +183,11 @@ ERROR:
+ return r;
+ }
+
+-int nw_zones_load(nw_zones** zones) {
++int nw_zones_load(nw_zones** zones, nw_daemon* daemon) {
+ int r;
+
+ // Create a new zones object
+- r = nw_zones_create(zones);
++ r = nw_zones_create(zones, daemon);
+ if (r)
+ return r;
+
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index e41c74f..330e524 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -23,10 +23,12 @@
+
+ typedef struct nw_zones nw_zones;
+
++#include "daemon.h"
++
+ nw_zones* nw_zones_ref(nw_zones* zones);
+ nw_zones* nw_zones_unref(nw_zones* zones);
+
+-int nw_zones_load(nw_zones** zones);
++int nw_zones_load(nw_zones** zones, nw_daemon* daemon);
+
+ size_t nw_zones_num(nw_zones* zones);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,231 @@
+From 7af642e14376b599183cd19091d4cab28ddc11e7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 18:00:26 +0000
+Subject: [PATCH 207/304] networkd: Refactor enumerating zones
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 21 +++++++---
+ src/networkd/zone.h | 2 +
+ src/networkd/zones.c | 97 +++++++++++++------------------------------
+ src/networkd/zones.h | 4 +-
+ 4 files changed, 50 insertions(+), 74 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 8122f58..6a6def3 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -140,11 +140,6 @@ static int nw_daemon_load_config(nw_daemon* daemon) {
+ if (r)
+ return r;
+
+- // Load zones
+- r = nw_zones_load(&daemon->zones, daemon);
+- if (r)
+- return r;
+-
+ return r;
+ }
+
+@@ -264,6 +259,17 @@ static int nw_daemon_enumerate_ports(nw_daemon* daemon) {
+ return nw_ports_enumerate(daemon->ports);
+ }
+
++static int nw_daemon_enumerate_zones(nw_daemon* daemon) {
++ int r;
++
++ // Create a new zones container
++ r = nw_zones_create(&daemon->zones, daemon);
++ if (r)
++ return r;
++
++ return nw_zones_enumerate(daemon->zones);
++}
++
+ static int nw_daemon_enumerate(nw_daemon* daemon) {
+ int r;
+
+@@ -277,6 +283,11 @@ static int nw_daemon_enumerate(nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Zones
++ r = nw_daemon_enumerate_zones(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 9df30ec..2748e6d 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_ZONE_H
+ #define NETWORKD_ZONE_H
+
++#define ZONE_CONFIG_DIR CONFIG_DIR "/zones"
++
+ #define NETWORK_ZONE_NAME_MAX_LENGTH 16
+ #define NETWORK_ZONE_DEFAULT_MTU 1500
+
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index fd43f1c..1b0ffdc 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -25,6 +25,8 @@
+
+ #include "daemon.h"
+ #include "logging.h"
++#include "string.h"
++#include "util.h"
+ #include "zone.h"
+ #include "zones.h"
+
+@@ -46,7 +48,7 @@ struct nw_zones {
+ unsigned int num;
+ };
+
+-static int nw_zones_create(nw_zones** zones, nw_daemon* daemon) {
++int nw_zones_create(nw_zones** zones, nw_daemon* daemon) {
+ nw_zones* z = calloc(1, sizeof(*z));
+ if (!z)
+ return 1;
+@@ -120,89 +122,48 @@ static int nw_zones_add_zone(nw_zones* zones, nw_zone* zone) {
+ return 0;
+ }
+
+-static int nw_zones_load_filter(const struct dirent* path) {
+- const char* fn = path->d_name;
+-
+- // Ignore everything starting with '.'
+- if (*fn == '.')
+- return 0;
+-
+- // Ignore anything that isn't a directory
+- if (path->d_type != DT_DIR)
+- return 0;
+-
+- return 1;
+-}
+-
+-static int __nw_zones_load(nw_zones* zones) {
+- struct dirent** paths = NULL;
+- int n;
+- int r = 0;
+-
++static int __nw_zones_enumerate(const char* path, const struct stat* s, void* data) {
+ nw_zone* zone = NULL;
++ int r;
+
+- // Scan the zones directory
+- n = scandir(CONFIG_DIR "/zones", &paths, nw_zones_load_filter, alphasort);
+- if (n < 0) {
+- ERROR("Could not load zones: %m\n");
+- return 1;
+- }
+-
+- DEBUG("Found %d zone(s)\n", n);
+-
+- // Load all zones
+- for (int i = 0; i < n; i++) {
+- const char* name = paths[i]->d_name;
+-
+- DEBUG("Loading zone '%s'...\n", name);
+-
+- // Create a new zone object
+- r = nw_zone_create(&zone, zones->daemon, name);
+- if (r)
+- goto ERROR;
+-
+- // Store the zone
+- r = nw_zones_add_zone(zones, zone);
+- if (r) {
+- nw_zone_unref(zone);
+- goto ERROR;
+- }
++ nw_zones* zones = (nw_zones*)data;
+
+- nw_zone_unref(zone);
+- }
++ // Skip anything that isn't a directory
++ if (!S_ISDIR(s->st_mode))
++ return 0;
+
+-ERROR:
+- // Free paths
+- if (paths) {
+- for (int i = 0; i < n; i++) {
+- free(paths[i]);
+- }
+- free(paths);
+- }
++ // Find the basename of the file
++ const char* name = nw_path_basename(path);
+
+- return r;
+-}
++ // Break on invalid paths
++ if (!name)
++ return 0;
+
+-int nw_zones_load(nw_zones** zones, nw_daemon* daemon) {
+- int r;
++ // Skip any hidden files
++ if (*name == '.')
++ return 0;
+
+- // Create a new zones object
+- r = nw_zones_create(zones, daemon);
++ // Create a new zone
++ r = nw_zone_create(&zone, zones->daemon, name);
+ if (r)
+- return r;
++ goto ERROR;
+
+- // Load all zones
+- r = __nw_zones_load(*zones);
++ // Add the zone to the list
++ r = nw_zones_add_zone(zones, zone);
+ if (r)
+ goto ERROR;
+
+- return 0;
+-
+ ERROR:
+- nw_zones_unref(*zones);
++ if (zone)
++ nw_zone_unref(zone);
++
+ return r;
+ }
+
++int nw_zones_enumerate(nw_zones* zones) {
++ return nw_ftw(ZONE_CONFIG_DIR, ZONE_CONFIG_DIR "/*", __nw_zones_enumerate, zones);
++}
++
+ size_t nw_zones_num(nw_zones* zones) {
+ struct nw_zones_entry* entry = NULL;
+ size_t length = 0;
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index 330e524..dbf7ccd 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -25,10 +25,12 @@ typedef struct nw_zones nw_zones;
+
+ #include "daemon.h"
+
++int nw_zones_create(nw_zones** zones, nw_daemon* daemon);
++
+ nw_zones* nw_zones_ref(nw_zones* zones);
+ nw_zones* nw_zones_unref(nw_zones* zones);
+
+-int nw_zones_load(nw_zones** zones, nw_daemon* daemon);
++int nw_zones_enumerate(nw_zones* zones);
+
+ size_t nw_zones_num(nw_zones* zones);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,239 @@
+From 605e975f53ed433718df5d101496474870d6439c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 11 Feb 2023 18:29:32 +0000
+Subject: [PATCH 208/304] networkd: Save configuration when the daemon exits
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 31 +++++++++++++++++++++++++++++++
+ src/networkd/daemon.h | 2 ++
+ src/networkd/main.c | 1 +
+ src/networkd/port.c | 10 ++++++++++
+ src/networkd/port.h | 2 ++
+ src/networkd/ports.c | 13 +++++++++++++
+ src/networkd/ports.h | 2 ++
+ src/networkd/zone.c | 10 ++++++++++
+ src/networkd/zone.h | 2 ++
+ src/networkd/zones.c | 13 +++++++++++++
+ src/networkd/zones.h | 2 ++
+ 11 files changed, 88 insertions(+)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 6a6def3..c67c759 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -409,6 +409,11 @@ int nw_daemon_run(nw_daemon* daemon) {
+ // Let systemd know that we are shutting down
+ sd_notify(0, "STOPPING=1\n" "STATUS=Shutting down...");
+
++ // Save the configuration
++ r = nw_daemon_save(daemon);
++ if (r)
++ goto ERROR;
++
+ // Cleanup everything
+ nw_daemon_cleanup(daemon);
+
+@@ -431,6 +436,32 @@ int nw_daemon_reload(nw_daemon* daemon) {
+ return 0;
+ }
+
++/*
++ Saves the configuration to disk
++*/
++int nw_daemon_save(nw_daemon* daemon) {
++ int r;
++
++ DEBUG("Saving configuration...\n");
++
++ // Save settings
++ r = nw_config_write(daemon->config);
++ if (r)
++ return r;
++
++ // Save ports
++ r = nw_ports_save(daemon->ports);
++ if (r)
++ return r;
++
++ // Save zones
++ r = nw_zones_save(daemon->zones);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ /*
+ Netlink
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 1694599..6f4c217 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -41,6 +41,8 @@ int nw_daemon_run(nw_daemon* daemon);
+
+ int nw_daemon_reload(nw_daemon* daemon);
+
++int nw_daemon_save(nw_daemon* daemon);
++
+ /*
+ Netlink
+ */
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index d3afc2a..c8b9a79 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -29,6 +29,7 @@
+
+ #include "daemon.h"
+ #include "logging.h"
++#include "port.h"
+
+ static int cap_acquire_setpcap(void) {
+ cap_flag_value_t value;
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 41b8b24..30bd98f 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -213,6 +213,16 @@ nw_port* nw_port_unref(nw_port* port) {
+ return NULL;
+ }
+
++int nw_port_save(nw_port* port) {
++ int r;
++
++ r = nw_config_write(port->config);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ const char* nw_port_name(nw_port* port) {
+ return port->name;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 9881846..8861046 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -38,6 +38,8 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name);
+ nw_port* nw_port_ref(nw_port* port);
+ nw_port* nw_port_unref(nw_port* port);
+
++int nw_port_save(nw_port* port);
++
+ const char* nw_port_name(nw_port* port);
+
+ char* nw_port_bus_path(nw_port* port);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 35ed048..a87ca03 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -98,6 +98,19 @@ nw_ports* nw_ports_unref(nw_ports* ports) {
+ return NULL;
+ }
+
++int nw_ports_save(nw_ports* ports) {
++ struct nw_ports_entry* entry = NULL;
++ int r;
++
++ STAILQ_FOREACH(entry, &ports->entries, nodes) {
++ r = nw_port_save(entry->port);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
+ static int nw_ports_add_port(nw_ports* ports, nw_port* port) {
+ // Allocate a new entry
+ struct nw_ports_entry* entry = calloc(1, sizeof(*entry));
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+index f58c3a4..40c9ae1 100644
+--- a/src/networkd/ports.h
++++ b/src/networkd/ports.h
+@@ -30,6 +30,8 @@ int nw_ports_create(nw_ports** ports, nw_daemon* daemon);
+ nw_ports* nw_ports_ref(nw_ports* ports);
+ nw_ports* nw_ports_unref(nw_ports* ports);
+
++int nw_ports_save(nw_ports* ports);
++
+ int nw_ports_enumerate(nw_ports* ports);
+
+ struct nw_port* nw_ports_get_by_name(nw_ports* ports, const char* name);
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 30f22d6..1e4abc3 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -137,6 +137,16 @@ nw_zone* nw_zone_unref(nw_zone* zone) {
+ return NULL;
+ }
+
++int nw_zone_save(nw_zone* zone) {
++ int r;
++
++ r = nw_config_write(zone->config);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ const char* nw_zone_name(nw_zone* zone) {
+ return zone->name;
+ }
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 2748e6d..088bb2f 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -35,6 +35,8 @@ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name);
+ nw_zone* nw_zone_ref(nw_zone* zone);
+ nw_zone* nw_zone_unref(nw_zone* zone);
+
++int nw_zone_save(nw_zone* zone);
++
+ const char* nw_zone_name(nw_zone* zone);
+
+ char* nw_zone_bus_path(nw_zone* zone);
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 1b0ffdc..ea0d8de 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -104,6 +104,19 @@ nw_zones* nw_zones_unref(nw_zones* zones) {
+ return NULL;
+ }
+
++int nw_zones_save(nw_zones* zones) {
++ struct nw_zones_entry* entry = NULL;
++ int r;
++
++ STAILQ_FOREACH(entry, &zones->entries, nodes) {
++ r = nw_zone_save(entry->zone);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
+ static int nw_zones_add_zone(nw_zones* zones, nw_zone* zone) {
+ // Allocate a new entry
+ struct nw_zones_entry* entry = calloc(1, sizeof(*entry));
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index dbf7ccd..c887bd0 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -30,6 +30,8 @@ int nw_zones_create(nw_zones** zones, nw_daemon* daemon);
+ nw_zones* nw_zones_ref(nw_zones* zones);
+ nw_zones* nw_zones_unref(nw_zones* zones);
+
++int nw_zones_save(nw_zones* zones);
++
+ int nw_zones_enumerate(nw_zones* zones);
+
+ size_t nw_zones_num(nw_zones* zones);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,69 @@
+From c65300b47197145ac899bd2e80597b4c6b44ca6d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 13 Feb 2023 15:05:41 +0000
+Subject: [PATCH 209/304] networkd: Store any flags
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 2f95dc3..78ae106 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -45,6 +45,9 @@ struct nw_link {
+ uint32_t mtu;
+ uint32_t min_mtu;
+ uint32_t max_mtu;
++
++ // Flags
++ unsigned int flags;
+ };
+
+ int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
+@@ -168,6 +171,29 @@ static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
+ return 0;
+ }
+
++static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
++ unsigned int flags = 0;
++ int r;
++
++ // Fetch flags
++ r = sd_rtnl_message_link_get_flags(message, &flags);
++ if (r < 0) {
++ return DEBUG("Could not read link flags: %m\n");
++ return 1;
++ }
++
++ // End here if there have been no changes
++ if (link->flags == flags)
++ return 0;
++
++ // XXX We should log any changes here
++
++ // Store the new flags
++ link->flags = flags;
++
++ return 0;
++}
++
+ /*
+ This function is called whenever anything changes, so that we can
+ update our internal link object.
+@@ -185,6 +211,11 @@ static int nw_link_update(nw_link* link, sd_netlink_message* message) {
+ if (r)
+ return r;
+
++ // Update flags
++ r = nw_link_update_flags(link, message);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,59 @@
+From 69ef50c7403a5e09fcdf6ee4b53f94665920277a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 13 Feb 2023 15:10:01 +0000
+Subject: [PATCH 210/304] networkd: Store operstate, too
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 78ae106..6392a90 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -48,6 +48,7 @@ struct nw_link {
+
+ // Flags
+ unsigned int flags;
++ uint8_t operstate;
+ };
+
+ int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
+@@ -173,6 +174,7 @@ static int nw_link_update_mtu(nw_link* link, sd_netlink_message* message) {
+
+ static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
+ unsigned int flags = 0;
++ uint8_t operstate = 0;
+ int r;
+
+ // Fetch flags
+@@ -182,14 +184,23 @@ static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
+ return 1;
+ }
+
++ // Fetch operstate
++ r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
++ if (r < 1) {
++ ERROR("Could not read operstate: %m\n");
++ return 1;
++ }
++
+ // End here if there have been no changes
+- if (link->flags == flags)
++ if (link->flags == flags && link->operstate == operstate)
+ return 0;
+
+ // XXX We should log any changes here
+
+- // Store the new flags
++ // Store the new flags & operstate
+ link->flags = flags;
++ link->operstate = operstate;
++
+
+ return 0;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,96 @@
+From dfd49c2cc85fcee2303a96bc2186de0e21d8da4f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 13 Feb 2023 15:14:01 +0000
+Subject: [PATCH 211/304] networkd: Add function to check whether a link has a
+ carrier
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 32 ++++++++++++++++++++++++++++++--
+ src/networkd/link.h | 2 ++
+ 2 files changed, 32 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 6392a90..80478f4 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -18,7 +18,7 @@
+ # #
+ #############################################################################*/
+
+-#include <net/if.h>
++#include <linux/if.h>
+ #include <stddef.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -39,7 +39,7 @@ struct nw_link {
+ int ifindex;
+
+ // Interface Name
+- char ifname[IF_NAMESIZE];
++ char ifname[IFNAMSIZ];
+
+ // MTU
+ uint32_t mtu;
+@@ -98,6 +98,20 @@ int nw_link_ifindex(nw_link* link) {
+ return link->ifindex;
+ }
+
++// Carrier
++
++int nw_link_has_carrier(nw_link* link) {
++ return link->operstate == IF_OPER_UP;
++}
++
++static int nw_link_carrier_gained(nw_link* link) {
++ return 0; // XXX TODO
++}
++
++static int nw_link_carrier_lost(nw_link* link) {
++ return 0; // XXX TODO
++}
++
+ static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
+ const char* ifname = NULL;
+ int r;
+@@ -197,10 +211,24 @@ static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
+
+ // XXX We should log any changes here
+
++ // Fetch current carrier state
++ const int had_carrier = nw_link_has_carrier(link);
++
+ // Store the new flags & operstate
+ link->flags = flags;
+ link->operstate = operstate;
+
++ // Notify if carrier was gained or lost
++ if (!had_carrier && nw_link_has_carrier(link)) {
++ r = nw_link_carrier_gained(link);
++ if (r < 0)
++ return r;
++
++ } else if (had_carrier && !nw_link_has_carrier(link)) {
++ r = nw_link_carrier_lost(link);
++ if (r < 0)
++ return r;
++ }
+
+ return 0;
+ }
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index acb5e7b..3d8d397 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -33,6 +33,8 @@ nw_link* nw_link_unref(nw_link* link);
+ int nw_link_ifindex(nw_link* link);
+ const char* nw_link_name(nw_link* link);
+
++int nw_link_has_carrier(nw_link* link);
++
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+ #endif /* NETWORKD_LINK_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,92 @@
+From 20375a083cbb965971b3b471b161e6475cef3c4b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 13 Feb 2023 15:34:40 +0000
+Subject: [PATCH 212/304] networkd: Add methods to check zones/ports for
+ carrier
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 13 +++++++++++++
+ src/networkd/port.h | 2 ++
+ src/networkd/zone.c | 19 +++++++++++++++++++
+ src/networkd/zone.h | 2 ++
+ 4 files changed, 36 insertions(+)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 30bd98f..c6c8781 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -246,3 +246,16 @@ static nw_link* nw_port_get_link(nw_port* port) {
+ const nw_address_t* nw_port_get_address(nw_port* port) {
+ return &port->address;
+ }
++
++int nw_port_has_carrier(nw_port* port) {
++ int has_carrier = 0;
++
++ // Fetch link
++ nw_link* link = nw_port_get_link(port);
++ if (link) {
++ has_carrier = nw_link_has_carrier(link);
++ nw_link_unref(link);
++ }
++
++ return has_carrier;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 8861046..33241ed 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -46,4 +46,6 @@ char* nw_port_bus_path(nw_port* port);
+
+ const nw_address_t* nw_port_get_address(nw_port* port);
+
++int nw_port_has_carrier(nw_port* port);
++
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 1e4abc3..8fe07c5 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -163,6 +163,25 @@ char* nw_zone_bus_path(nw_zone* zone) {
+ return p;
+ }
+
++static nw_link* nw_zone_get_link(nw_zone* zone) {
++ return nw_daemon_get_link_by_name(zone->daemon, zone->name);
++}
++
++// Carrier
++
++int nw_zone_has_carrier(nw_zone* zone) {
++ int has_carrier = 0;
++
++ // Fetch link
++ nw_link* link = nw_zone_get_link(zone);
++ if (link) {
++ has_carrier = nw_link_has_carrier(link);
++ nw_link_unref(link);
++ }
++
++ return has_carrier;
++}
++
+ /*
+ MTU
+ */
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 088bb2f..f5a2355 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -41,6 +41,8 @@ const char* nw_zone_name(nw_zone* zone);
+
+ char* nw_zone_bus_path(nw_zone* zone);
+
++int nw_zone_has_carrier(nw_zone* zone);
++
+ /*
+ MTU
+ */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,137 @@
+From 1a70a6864ca83ed91c9acfd6be5a64185ac34917 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 13 Feb 2023 16:00:43 +0000
+Subject: [PATCH 213/304] networkd: Log to journald
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ src/networkd/link.c | 2 +-
+ src/networkd/logging.c | 65 ++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/logging.h | 10 +++++--
+ 4 files changed, 74 insertions(+), 4 deletions(-)
+ create mode 100644 src/networkd/logging.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 893f1b8..eded292 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -323,6 +323,7 @@ dist_networkd_SOURCES = \
+ src/networkd/link.h \
+ src/networkd/links.c \
+ src/networkd/links.h \
++ src/networkd/logging.c \
+ src/networkd/logging.h \
+ src/networkd/main.c \
+ src/networkd/ports.c \
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 80478f4..7f49606 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -194,7 +194,7 @@ static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
+ // Fetch flags
+ r = sd_rtnl_message_link_get_flags(message, &flags);
+ if (r < 0) {
+- return DEBUG("Could not read link flags: %m\n");
++ DEBUG("Could not read link flags: %m\n");
+ return 1;
+ }
+
+diff --git a/src/networkd/logging.c b/src/networkd/logging.c
+new file mode 100644
+index 0000000..c4809e8
+--- /dev/null
++++ b/src/networkd/logging.c
+@@ -0,0 +1,65 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++#include <systemd/sd-journal.h>
++
++#include "logging.h"
++
++void nw_log(int priority, const char* file,
++ int line, const char* fn, const char* format, ...) {
++ char* buffer = NULL;
++ va_list args;
++ int r;
++
++ // Format log message
++ va_start(args, format);
++ r = vasprintf(&buffer, format, args);
++ va_end(args);
++ if (r < 0)
++ return;
++
++ // Send message to journald
++ r = sd_journal_send(
++ "MESSAGE=%s", buffer,
++ "PRIORITY=%d", priority,
++
++ // Syslog compat
++ "SYSLOG_IDENTIFIER=networkd",
++
++ // Debugging stuff
++ "ERRNO=%d", errno,
++ "CODE_FILE=%s", file,
++ "CODE_LINE=%d", line,
++ "CODE_FUNC=%s", fn,
++
++ NULL
++ );
++
++ // Fall back to standard output
++ if (r)
++ sd_journal_perror(buffer);
++
++ // Cleanup
++ free(buffer);
++}
+diff --git a/src/networkd/logging.h b/src/networkd/logging.h
+index 9d51f21..ea75ba0 100644
+--- a/src/networkd/logging.h
++++ b/src/networkd/logging.h
+@@ -21,12 +21,16 @@
+ #ifndef NETWORKD_LOGGING_H
+ #define NETWORKD_LOGGING_H
+
+-#include <stdio.h>
++#include <syslog.h>
++
++void nw_log(int priority, const char *file, int line, const char* fn,
++ const char *format, ...) __attribute__((format(printf, 5, 6)));
+
+ /*
+ This is just something simple which will work for now...
+ */
+-#define ERROR(...) fprintf(stderr, __VA_ARGS__)
+-#define DEBUG(...) printf(__VA_ARGS__)
++#define INFO(args...) nw_log(LOG_INFO, __FILE__, __LINE__, __FUNCTION__, ## args)
++#define ERROR(args...) nw_log(LOG_ERR, __FILE__, __LINE__, __FUNCTION__, ## args)
++#define DEBUG(args...) nw_log(LOG_DEBUG, __FILE__, __LINE__, __FUNCTION__, ## args)
+
+ #endif /* NETWORKD_LOGGING_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,49 @@
+From ac0188ee9bffceedc94417e586bcbe21daa96c74 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Mar 2023 15:34:32 +0000
+Subject: [PATCH 214/304] networkd: Install in /usr/lib/network
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 ++--
+ src/networkd/networkd.service.in | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index eded292..707eabe 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -61,7 +61,7 @@ INSTALL_DIRS =
+ INSTALL_EXEC_HOOKS =
+ UNINSTALL_EXEC_HOOKS =
+ noinst_DATA =
+-sbin_PROGRAMS =
++network_PROGRAMS =
+ dist_dbuspolicy_DATA =
+ dist_dbussystembus_DATA =
+ dist_polkitpolicy_DATA =
+@@ -304,7 +304,7 @@ EXTRA_DIST += \
+
+ # ------------------------------------------------------------------------------
+
+-sbin_PROGRAMS += \
++network_PROGRAMS += \
+ networkd
+
+ dist_networkd_SOURCES = \
+diff --git a/src/networkd/networkd.service.in b/src/networkd/networkd.service.in
+index 4361023..7ee8fad 100644
+--- a/src/networkd/networkd.service.in
++++ b/src/networkd/networkd.service.in
+@@ -15,7 +15,7 @@ AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET
+ BusName=org.ipfire.network1
+ CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
+ DeviceAllow=char-* rw
+-ExecStart=@sbindir@/networkd
++ExecStart=@networkdir@/networkd
+ FileDescriptorStoreMax=512
+ LockPersonality=yes
+ MemoryDenyWriteExecute=yes
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From 840738f6baf2d3024df6ae0e9a887c5b1bef3a50 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Mar 2023 15:41:17 +0000
+Subject: [PATCH 215/304] networkctl: Create some scaffolding
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ .gitignore | 1 +
+ Makefile.am | 19 +++++++++++++++++++
+ src/networkctl/main.c | 23 +++++++++++++++++++++++
+ 3 files changed, 43 insertions(+)
+ create mode 100644 src/networkctl/main.c
+
+diff --git a/.gitignore b/.gitignore
+index 9194c93..c45db75 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -3,6 +3,7 @@
+ /config.*
+ /libtool
+ /missing
++/networkctl
+ /networkd
+ /src/functions/functions
+ /src/inetcalc
+diff --git a/Makefile.am b/Makefile.am
+index 707eabe..1640060 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -379,6 +379,24 @@ CLEANFILES += \
+
+ # ------------------------------------------------------------------------------
+
++bin_PROGRAMS += \
++ networkctl
++
++dist_networkctl_SOURCES = \
++ src/networkctl/main.c
++
++networkctl_CFLAGS = \
++ $(AM_CFLAGS) \
++ $(SYSTEMD_CFLAGS)
++
++networkctl_LDFLAGS = \
++ $(AM_LDFLAGS)
++
++networkctl_LDADD = \
++ $(SYSTEMD_LIBS)
++
++# ------------------------------------------------------------------------------
++
+ util_PROGRAMS = \
+ src/utils/network-phy-list-channels \
+ src/utils/network-phy-list-ciphers \
+@@ -622,6 +640,7 @@ substitutions = \
+ '|builddir=$(abs_builddir)|' \
+ '|prefix=$(prefix)|' \
+ '|exec_prefix=$(exec_prefix)|' \
++ '|bindir=$(bindir)|' \
+ '|sbindir=$(sbindir)|' \
+ '|networkdir=$(networkdir)|' \
+ '|helpersdir=$(helpersdir)|' \
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+new file mode 100644
+index 0000000..14aafdd
+--- /dev/null
++++ b/src/networkctl/main.c
+@@ -0,0 +1,23 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++int main(int argc, char** argv) {
++ return 0;
++}
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,45 @@
+From d9b54f58d6abedf04cfa893cae479b05533f470f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Mar 2023 15:52:20 +0000
+Subject: [PATCH 216/304] networkctl: Connect to the system bus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/main.c | 22 +++++++++++++++++++++-
+ 1 file changed, 21 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 14aafdd..6ecaeb6 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -18,6 +18,26 @@
+ # #
+ #############################################################################*/
+
++#include <stdio.h>
++
++#include <systemd/sd-bus.h>
++
+ int main(int argc, char** argv) {
+- return 0;
++ sd_bus* bus = NULL;
++ int r;
++
++ // Connect to system bus
++ r = sd_bus_open_system(&bus);
++ if (r < 0) {
++ fprintf(stderr, "Could not connect to system bus: %m\n");
++ goto ERROR;
++ }
++
++ // XXX TODO Do all the work
++
++ERROR:
++ if (bus)
++ sd_bus_flush_close_unref(bus);
++
++ return r;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From bc44ba7128b38d16455899acaa145daa2eb6aade Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Mar 2023 16:13:11 +0000
+Subject: [PATCH 217/304] networkctl: Add some help and version arguments
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/main.c | 63 ++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 62 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 6ecaeb6..494fd7d 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -18,14 +18,75 @@
+ # #
+ #############################################################################*/
+
++#include <errno.h>
++#include <getopt.h>
+ #include <stdio.h>
++#include <unistd.h>
+
+ #include <systemd/sd-bus.h>
+
+-int main(int argc, char** argv) {
++static int version(void) {
++ printf("networkctl %s\n", PACKAGE_VERSION);
++
++ return 0;
++}
++
++static int help(void) {
++ printf(
++ "%s [OPTIONS...] COMMAND\n\n"
++ "Options:\n"
++ " -h --help Show help\n"
++ " --version Show version\n",
++ program_invocation_short_name
++ );
++
++ return 0;
++}
++
++static int parse_argv(int argc, char* argv[]) {
++ enum {
++ ARG_VERSION,
++ };
++
++ static const struct option options[] = {
++ { "help", no_argument, NULL, 'h' },
++ { "version", no_argument, NULL, ARG_VERSION },
++ { NULL },
++ };
++ int c;
++
++ for (;;) {
++ c = getopt_long(argc, argv, "h", options, NULL);
++ if (c < 0)
++ break;
++
++ switch (c) {
++ case 'h':
++ return help();
++
++ case ARG_VERSION:
++ return version();
++
++ case '?':
++ return -EINVAL;
++
++ default:
++ break;
++ }
++ }
++
++ return 0;
++}
++
++int main(int argc, char* argv[]) {
+ sd_bus* bus = NULL;
+ int r;
+
++ // Parse command line arguments
++ r = parse_argv(argc, argv);
++ if (r)
++ goto ERROR;
++
+ // Connect to system bus
+ r = sd_bus_open_system(&bus);
+ if (r < 0) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,172 @@
+From f3bb976c4be0fe41959d7623f4ae3269c0e95e1d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 1 Mar 2023 16:55:45 +0000
+Subject: [PATCH 218/304] networkctl: Implement a basic command dispatcher
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkctl/command.c | 58 ++++++++++++++++++++++++++++++++++++++++
+ src/networkctl/command.h | 34 +++++++++++++++++++++++
+ src/networkctl/main.c | 19 ++++++++++++-
+ 4 files changed, 112 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkctl/command.c
+ create mode 100644 src/networkctl/command.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 1640060..186af94 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -383,6 +383,8 @@ bin_PROGRAMS += \
+ networkctl
+
+ dist_networkctl_SOURCES = \
++ src/networkctl/command.c \
++ src/networkctl/command.h \
+ src/networkctl/main.c
+
+ networkctl_CFLAGS = \
+diff --git a/src/networkctl/command.c b/src/networkctl/command.c
+new file mode 100644
+index 0000000..7114efe
+--- /dev/null
++++ b/src/networkctl/command.c
+@@ -0,0 +1,58 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <errno.h>
++#include <getopt.h>
++#include <string.h>
++
++#include "command.h"
++
++static const struct command* command_find(const struct command* commands, const char* verb) {
++ for (const struct command* command = commands; command->verb; command++) {
++ if (strcmp(command->verb, verb) == 0)
++ return command;
++ }
++
++ return NULL;
++}
++
++int command_dispatch(sd_bus* bus, const struct command* commands, int argc, char* argv[]) {
++ const struct command* command = NULL;
++
++ argc -= optind;
++ argv += optind;
++ optind = 1;
++
++ if (!argc) {
++ fprintf(stderr, "Command required\n");
++ return -EINVAL;
++ }
++
++ const char* verb = argv[0];
++
++ // Find a matching command
++ command = command_find(commands, verb);
++ if (!command) {
++ fprintf(stderr, "Unknown command '%s'\n", verb);
++ return -EINVAL;
++ }
++
++ return command->callback(bus, argc, argv);
++}
+diff --git a/src/networkctl/command.h b/src/networkctl/command.h
+new file mode 100644
+index 0000000..f8f295e
+--- /dev/null
++++ b/src/networkctl/command.h
+@@ -0,0 +1,34 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKCTL_COMMAND_H
++#define NETWORKCTL_COMMAND_H
++
++#include <systemd/sd-bus.h>
++
++struct command {
++ const char* verb;
++ int flags;
++ int (*callback)(sd_bus* bus, int argc, char* argv[]);
++};
++
++int command_dispatch(sd_bus* bus, const struct command* commands, int argc, char* argv[]);
++
++#endif /* NETWORKCTL_COMMAND_H */
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 494fd7d..0cbc3e6 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -25,6 +25,22 @@
+
+ #include <systemd/sd-bus.h>
+
++#include "command.h"
++
++static int networkctl_status(sd_bus* bus, int argc, char* argv[]) {
++ printf("%s called\n", __FUNCTION__);
++ return 0;
++}
++
++static int networkctl_main(sd_bus* bus, int argc, char* argv[]) {
++ static const struct command commands[] = {
++ { "status", 0, networkctl_status },
++ { NULL },
++ };
++
++ return command_dispatch(bus, commands, argc, argv);
++}
++
+ static int version(void) {
+ printf("networkctl %s\n", PACKAGE_VERSION);
+
+@@ -94,7 +110,8 @@ int main(int argc, char* argv[]) {
+ goto ERROR;
+ }
+
+- // XXX TODO Do all the work
++ // Run a command
++ r = networkctl_main(bus, argc, argv);
+
+ ERROR:
+ if (bus)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,169 @@
+From b4faae0abf9bd02b4aa02660a85d58497ad8b07b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 11:25:56 +0000
+Subject: [PATCH 219/304] networkd: Implement ListZones bus command
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon-bus.c | 55 +++++++++++++++++++++++++++++++++++++++
+ src/networkd/daemon.c | 7 +++++
+ src/networkd/daemon.h | 1 +
+ src/networkd/zones.c | 13 +++++++++
+ src/networkd/zones.h | 4 +++
+ 5 files changed, 80 insertions(+)
+
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+index 93a0411..7620eed 100644
+--- a/src/networkd/daemon-bus.c
++++ b/src/networkd/daemon-bus.c
+@@ -18,12 +18,16 @@
+ # #
+ #############################################################################*/
+
++#include <stdlib.h>
++
+ #include <systemd/sd-bus.h>
+
+ #include "bus.h"
+ #include "daemon.h"
++#include "logging.h"
+ #include "port-bus.h"
+ #include "zone-bus.h"
++#include "zones.h"
+
+ static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* error) {
+ nw_daemon* daemon = (nw_daemon*)data;
+@@ -35,8 +39,59 @@ static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* err
+ return sd_bus_reply_method_return(m, NULL);
+ }
+
++static int __nw_daemon_bus_list_zones(nw_daemon* daemon, nw_zone* zone, void* data) {
++ sd_bus_message* reply = (sd_bus_message*)data;
++ int r;
++
++ // Fetch zone name
++ const char* name = nw_zone_name(zone);
++
++ // Fetch bus path
++ char* path = nw_zone_bus_path(zone);
++
++ r = sd_bus_message_append(reply, "(so)", name, path);
++
++ free(path);
++
++ return r;
++}
++
++static int nw_daemon_bus_list_zones(sd_bus_message* m, void* data, sd_bus_error* error) {
++ nw_daemon* daemon = (nw_daemon*)data;
++ sd_bus_message* reply = NULL;
++ int r;
++
++ // Form a reply message
++ r = sd_bus_message_new_method_return(m, &reply);
++ if (r < 0)
++ goto ERROR;
++
++ r = sd_bus_message_open_container(reply, 'a', "(so)");
++ if (r < 0)
++ goto ERROR;
++
++ r = nw_daemon_zones_walk(daemon, __nw_daemon_bus_list_zones, reply);
++ if (r < 0)
++ goto ERROR;
++
++ r = sd_bus_message_close_container(reply);
++ if (r < 0)
++ goto ERROR;
++
++ // Send the reply
++ r = sd_bus_send(NULL, reply, NULL);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++
++ return r;
++}
++
+ static const sd_bus_vtable daemon_vtable[] = {
+ SD_BUS_VTABLE_START(0),
++ SD_BUS_METHOD_WITH_ARGS("ListZones", SD_BUS_NO_ARGS, SD_BUS_RESULT("a(so)", links),
++ nw_daemon_bus_list_zones, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
+ nw_daemon_bus_reload, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END,
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index c67c759..925b207 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -519,6 +519,13 @@ nw_zones* nw_daemon_zones(nw_daemon* daemon) {
+ return nw_zones_ref(daemon->zones);
+ }
+
++int nw_daemon_zones_walk(nw_daemon* daemon, nw_zones_walk_callback callback, void* data) {
++ if (!daemon->zones)
++ return 0;
++
++ return nw_zones_walk(daemon->zones, callback, data);
++}
++
+ nw_zone* nw_daemon_get_zone_by_name(nw_daemon* daemon, const char* name) {
+ if (!daemon->zones)
+ return NULL;
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 6f4c217..ce9a660 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -66,6 +66,7 @@ nw_port* nw_daemon_get_port_by_name(nw_daemon* daemon, const char* name);
+ Zones
+ */
+ nw_zones* nw_daemon_zones(nw_daemon* daemon);
++int nw_daemon_zones_walk(nw_daemon* daemon, nw_zones_walk_callback callback, void* data);
+ nw_zone* nw_daemon_get_zone_by_name(nw_daemon* daemon, const char* name);
+
+ #endif /* NETWORKD_DAEMON_H */
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index ea0d8de..521da51 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -239,3 +239,16 @@ ERROR:
+
+ return 1;
+ }
++
++int nw_zones_walk(nw_zones* zones, nw_zones_walk_callback callback, void* data) {
++ struct nw_zones_entry* entry = NULL;
++ int r;
++
++ STAILQ_FOREACH(entry, &zones->entries, nodes) {
++ r = callback(zones->daemon, entry->zone, data);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index c887bd0..019955e 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -23,6 +23,8 @@
+
+ typedef struct nw_zones nw_zones;
+
++typedef int (*nw_zones_walk_callback)(nw_daemon* daemon, nw_zone* zone, void* data);
++
+ #include "daemon.h"
+
+ int nw_zones_create(nw_zones** zones, nw_daemon* daemon);
+@@ -40,4 +42,6 @@ nw_zone* nw_zones_get_by_name(nw_zones* zones, const char* name);
+
+ int nw_zones_bus_paths(nw_zones* zones, char*** paths);
+
++int nw_zones_walk(nw_zones* zones, nw_zones_walk_callback callback, void* data);
++
+ #endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,188 @@
+From 537fae0bc0758b2b93a80dff94918752ff01b813 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 11:26:43 +0000
+Subject: [PATCH 220/304] networkctl: Implement "zone list" command
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 4 +-
+ src/networkctl/main.c | 2 +
+ src/networkctl/zone.c | 97 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkctl/zone.h | 26 ++++++++++++
+ 4 files changed, 128 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkctl/zone.c
+ create mode 100644 src/networkctl/zone.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 186af94..80a7236 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -385,7 +385,9 @@ bin_PROGRAMS += \
+ dist_networkctl_SOURCES = \
+ src/networkctl/command.c \
+ src/networkctl/command.h \
+- src/networkctl/main.c
++ src/networkctl/main.c \
++ src/networkctl/zone.c \
++ src/networkctl/zone.h
+
+ networkctl_CFLAGS = \
+ $(AM_CFLAGS) \
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 0cbc3e6..a08256c 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -26,6 +26,7 @@
+ #include <systemd/sd-bus.h>
+
+ #include "command.h"
++#include "zone.h"
+
+ static int networkctl_status(sd_bus* bus, int argc, char* argv[]) {
+ printf("%s called\n", __FUNCTION__);
+@@ -35,6 +36,7 @@ static int networkctl_status(sd_bus* bus, int argc, char* argv[]) {
+ static int networkctl_main(sd_bus* bus, int argc, char* argv[]) {
+ static const struct command commands[] = {
+ { "status", 0, networkctl_status },
++ { "zone", 0, networkctl_zone },
+ { NULL },
+ };
+
+diff --git a/src/networkctl/zone.c b/src/networkctl/zone.c
+new file mode 100644
+index 0000000..ee1d0a2
+--- /dev/null
++++ b/src/networkctl/zone.c
+@@ -0,0 +1,97 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <systemd/sd-bus.h>
++
++#include "command.h"
++#include "zone.h"
++
++typedef int (*networkctl_zone_walk_callback)
++ (sd_bus* bus, const char* path, const char* name, void* data);
++
++static int networkctl_zone_walk(sd_bus* bus,
++ networkctl_zone_walk_callback callback, void* data) {
++ sd_bus_message* reply = NULL;
++ sd_bus_error error = SD_BUS_ERROR_NULL;
++ int r;
++
++ // Call ListZones
++ r = sd_bus_call_method(bus, "org.ipfire.network1", "/org/ipfire/network1",
++ "org.ipfire.network1", "ListZones", &error, &reply, "");
++ if (r < 0) {
++ fprintf(stderr, "ListZones call failed: %m\n");
++ goto ERROR;
++ }
++
++ const char* name = NULL;
++ const char* path = NULL;
++
++ // Open the container
++ r = sd_bus_message_enter_container(reply, 'a', "(so)");
++ if (r < 0) {
++ fprintf(stderr, "Could not open container: %m\n");
++ goto ERROR;
++ }
++
++ // Iterate over all zones
++ for (;;) {
++ r = sd_bus_message_read(reply, "(so)", &name, &path);
++ if (r < 0)
++ goto ERROR;
++
++ // Break if we reached the end of the container
++ if (r == 0)
++ break;
++
++ // Call the callback
++ r = callback(bus, path, name, data);
++ if (r)
++ goto ERROR;
++ }
++
++ // Close the container
++ sd_bus_message_exit_container(reply);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++ sd_bus_error_free(&error);
++
++ return r;
++}
++
++static int __networkctl_zone_list(sd_bus* bus, const char* path, const char* name, void* data) {
++ printf("%s\n", name);
++
++ return 0;
++}
++
++static int networkctl_zone_list(sd_bus* bus, int argc, char* argv[]) {
++ return networkctl_zone_walk(bus, __networkctl_zone_list, NULL);
++}
++
++int networkctl_zone(sd_bus* bus, int argc, char* argv[]) {
++ static const struct command commands[] = {
++ { "list", 0, networkctl_zone_list },
++ { NULL },
++ };
++
++ return command_dispatch(bus, commands, argc, argv);
++}
+diff --git a/src/networkctl/zone.h b/src/networkctl/zone.h
+new file mode 100644
+index 0000000..5eddd98
+--- /dev/null
++++ b/src/networkctl/zone.h
+@@ -0,0 +1,26 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKCTL_ZONE_H
++#define NETWORKCTL_ZONE_H
++
++int networkctl_zone(sd_bus* bus, int argc, char* argv[]);
++
++#endif /* NETWORKCTL_ZONE_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,56 @@
+From 207abe4eabad65c2d1e120f7c0fc711d17ed017a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 11:57:19 +0000
+Subject: [PATCH 221/304] networkd: Fix finding links by name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 4 ++++
+ src/networkd/link.h | 2 +-
+ src/networkd/links.c | 2 +-
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 7f49606..0fb388b 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -98,6 +98,10 @@ int nw_link_ifindex(nw_link* link) {
+ return link->ifindex;
+ }
+
++const char* nw_link_ifname(nw_link* link) {
++ return link->ifname;
++}
++
+ // Carrier
+
+ int nw_link_has_carrier(nw_link* link) {
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 3d8d397..779621b 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -31,7 +31,7 @@ nw_link* nw_link_ref(nw_link* link);
+ nw_link* nw_link_unref(nw_link* link);
+
+ int nw_link_ifindex(nw_link* link);
+-const char* nw_link_name(nw_link* link);
++const char* nw_link_ifname(nw_link* link);
+
+ int nw_link_has_carrier(nw_link* link);
+
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index 9f41034..3dd06d0 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -198,7 +198,7 @@ nw_link* nw_links_get_by_name(nw_links* links, const char* name) {
+ struct nw_links_entry* entry = NULL;
+
+ STAILQ_FOREACH(entry, &links->entries, nodes) {
+- const char* n = nw_link_name(entry->link);
++ const char* n = nw_link_ifname(entry->link);
+
+ if (strcmp(name, n) == 0)
+ return nw_link_ref(entry->link);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,81 @@
+From 02801f0d507924a4efb8c462f6fc775ab5793ebc Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 11:57:48 +0000
+Subject: [PATCH 222/304] networkd: ports: Keep a permanent reference to links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 30 ++++++++++++++++++++----------
+ 1 file changed, 20 insertions(+), 10 deletions(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index c6c8781..3c4f0b3 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -36,6 +36,9 @@ struct nw_port {
+ nw_daemon* daemon;
+ int nrefs;
+
++ // Link
++ nw_link* link;
++
+ char name[IF_NAMESIZE];
+ nw_port_type_t type;
+
+@@ -66,6 +69,8 @@ static nw_port_type_t nw_port_type_from_string(const char* s) {
+ }
+
+ static void nw_port_free(nw_port* port) {
++ if (port->link)
++ nw_link_unref(port->link);
+ if (port->config)
+ nw_config_unref(port->config);
+ if (port->daemon)
+@@ -134,6 +139,14 @@ static int nw_port_setup(nw_port* port) {
+ char path[PATH_MAX];
+ int r;
+
++ // Find the link
++ port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
++ if (port->link) {
++ DEBUG("%s: Found matching link %d\n", port->name, nw_link_ifindex(port->link));
++ } else {
++ DEBUG("%s: Could not find matching link\n", port->name);
++ }
++
+ // Compose the path to the main configuration file
+ r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
+ if (r)
+@@ -240,7 +253,10 @@ char* nw_port_bus_path(nw_port* port) {
+ }
+
+ static nw_link* nw_port_get_link(nw_port* port) {
+- return nw_daemon_get_link_by_name(port->daemon, port->name);
++ if (!port->link)
++ return NULL;
++
++ return nw_link_ref(port->link);
+ }
+
+ const nw_address_t* nw_port_get_address(nw_port* port) {
+@@ -248,14 +264,8 @@ const nw_address_t* nw_port_get_address(nw_port* port) {
+ }
+
+ int nw_port_has_carrier(nw_port* port) {
+- int has_carrier = 0;
+-
+- // Fetch link
+- nw_link* link = nw_port_get_link(port);
+- if (link) {
+- has_carrier = nw_link_has_carrier(link);
+- nw_link_unref(link);
+- }
++ if (!port->link)
++ return 0;
+
+- return has_carrier;
++ return nw_link_has_carrier(port->link);
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,89 @@
+From 4e989bd539cc0aea61345156a6401e7282357d0c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 12:02:40 +0000
+Subject: [PATCH 223/304] networkd: zones: Keep a permanent reference to links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/zone.c | 31 +++++++++++++++++++++----------
+ 1 file changed, 21 insertions(+), 10 deletions(-)
+
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 8fe07c5..4fda1e5 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -25,6 +25,7 @@
+
+ #include "config.h"
+ #include "daemon.h"
++#include "link.h"
+ #include "logging.h"
+ #include "string.h"
+ #include "zone.h"
+@@ -33,6 +34,9 @@ struct nw_zone {
+ nw_daemon* daemon;
+ int nrefs;
+
++ // Link
++ nw_link* link;
++
+ char name[NETWORK_ZONE_NAME_MAX_LENGTH];
+
+ // Configuration
+@@ -66,6 +70,8 @@ static int __nw_zone_path(nw_zone* zone, char* p, const size_t length,
+ }
+
+ static void nw_zone_free(nw_zone* zone) {
++ if (zone->link)
++ nw_link_unref(zone->link);
+ if (zone->config)
+ nw_config_unref(zone->config);
+ if (zone->daemon)
+@@ -78,6 +84,14 @@ static int nw_zone_setup(nw_zone* zone) {
+ char path[PATH_MAX];
+ int r;
+
++ // Find the link
++ zone->link = nw_daemon_get_link_by_name(zone->daemon, zone->name);
++ if (zone->link) {
++ DEBUG("%s: Found matching link %d\n", zone->name, nw_link_ifindex(zone->link));
++ } else {
++ DEBUG("%s: Could not find matching link\n", zone->name);
++ }
++
+ // Compose the path to the main configuration file
+ r = nw_zone_path(zone, path, "%s", "settings");
+ if (r)
+@@ -164,22 +178,19 @@ char* nw_zone_bus_path(nw_zone* zone) {
+ }
+
+ static nw_link* nw_zone_get_link(nw_zone* zone) {
+- return nw_daemon_get_link_by_name(zone->daemon, zone->name);
++ if (!zone->link)
++ return NULL;
++
++ return nw_link_ref(zone->link);
+ }
+
+ // Carrier
+
+ int nw_zone_has_carrier(nw_zone* zone) {
+- int has_carrier = 0;
+-
+- // Fetch link
+- nw_link* link = nw_zone_get_link(zone);
+- if (link) {
+- has_carrier = nw_link_has_carrier(link);
+- nw_link_unref(link);
+- }
++ if (!zone->link)
++ return 0;
+
+- return has_carrier;
++ return nw_link_has_carrier(zone->link);
+ }
+
+ /*
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,63 @@
+From 8b0b5c6d9e99c09341fe90b1278763fd3f70b53a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 13:04:17 +0000
+Subject: [PATCH 224/304] networkd: config: Add functions to handle boolean
+ values
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 28 ++++++++++++++++++++++++++++
+ src/networkd/config.h | 3 +++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index b3d5284..d879ace 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -368,3 +368,31 @@ int nw_config_set_int(nw_config* config, const char* key, const int value) {
+
+ return nw_config_set(config, key, __value);
+ }
++
++static const char* nw_config_true[] = {
++ "true",
++ "yes",
++ "1",
++ NULL,
++};
++
++int nw_config_get_bool(nw_config* config, const char* key) {
++ const char* value = nw_config_get(config, key);
++
++ // No value indicates false
++ if (!value)
++ return 0;
++
++ // Check if we match any known true words
++ for (const char** s = nw_config_true; *s; s++) {
++ if (strcmp(value, *s) == 0)
++ return 1;
++ }
++
++ // No match means false
++ return 0;
++}
++
++int nw_config_set_bool(nw_config* config, const char* key, const int value) {
++ return nw_config_set(config, key, value ? "true" : "false");
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 041a10e..0b25f75 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -48,4 +48,7 @@ int nw_config_set(nw_config* config, const char* key, const char* value);
+ int nw_config_get_int(nw_config* config, const char* key, const int __default);
+ int nw_config_set_int(nw_config* config, const char* key, const int value);
+
++int nw_config_get_bool(nw_config* config, const char* key);
++int nw_config_set_bool(nw_config* config, const char* key, const int value);
++
+ #endif /* NETWORKD_CONFIG_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,210 @@
+From b9769b09ea2943e4b212806c11f31f14f627b08f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 13:06:25 +0000
+Subject: [PATCH 225/304] networkd: Try to reconfigure all ports and zones on
+ startup
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 36 ++++++++++++++++++++++++++++++++++++
+ src/networkd/port.c | 4 ++++
+ src/networkd/port.h | 2 ++
+ src/networkd/ports.c | 21 +++++++++++++++++++++
+ src/networkd/ports.h | 6 ++++++
+ src/networkd/zone.c | 4 ++++
+ src/networkd/zone.h | 2 ++
+ src/networkd/zones.c | 8 ++++++++
+ src/networkd/zones.h | 2 ++
+ 9 files changed, 85 insertions(+)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 925b207..c6dad91 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -291,6 +291,37 @@ static int nw_daemon_enumerate(nw_daemon* daemon) {
+ return 0;
+ }
+
++static int __nw_daemon_reconfigure(sd_event_source* s, void* data) {
++ nw_daemon* daemon = (nw_daemon*)data;
++ int r;
++
++ DEBUG("Reconfiguring...\n");
++
++ // Reconfigure all zones
++ r = nw_zones_reconfigure(daemon->zones);
++ if (r)
++ return r;
++
++ // Reconfigure all ports
++ r = nw_ports_reconfigure(daemon->ports);
++ if (r)
++ return r;
++
++ return 0;
++}
++
++static int nw_daemon_reconfigure(nw_daemon* daemon) {
++ int r;
++
++ r = sd_event_add_defer(daemon->loop, NULL, __nw_daemon_reconfigure, daemon);
++ if (r) {
++ ERROR("Could not schedule re-configuration task: %m\n");
++ return r;
++ }
++
++ return 0;
++}
++
+ static int nw_daemon_setup(nw_daemon* daemon) {
+ int r;
+
+@@ -324,6 +355,11 @@ static int nw_daemon_setup(nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // (Re-)configure everything
++ r = nw_daemon_reconfigure(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 3c4f0b3..ec40830 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -263,6 +263,10 @@ const nw_address_t* nw_port_get_address(nw_port* port) {
+ return &port->address;
+ }
+
++int nw_port_reconfigure(nw_port* port) {
++ return 0; // XXX TODO
++}
++
+ int nw_port_has_carrier(nw_port* port) {
+ if (!port->link)
+ return 0;
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 33241ed..17c8c3c 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -46,6 +46,8 @@ char* nw_port_bus_path(nw_port* port);
+
+ const nw_address_t* nw_port_get_address(nw_port* port);
+
++int nw_port_reconfigure(nw_port* port);
++
+ int nw_port_has_carrier(nw_port* port);
+
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index a87ca03..87135d8 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -222,3 +222,24 @@ ERROR:
+
+ return 1;
+ }
++
++int nw_ports_walk(nw_ports* ports, nw_ports_walk_callback callback, void* data) {
++ struct nw_ports_entry* entry = NULL;
++ int r;
++
++ STAILQ_FOREACH(entry, &ports->entries, nodes) {
++ r = callback(ports->daemon, entry->port, data);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
++static int __nw_ports_reconfigure(nw_daemon* daemon, nw_port* port, void* data) {
++ return nw_port_reconfigure(port);
++}
++
++int nw_ports_reconfigure(nw_ports* ports) {
++ return nw_ports_walk(ports, __nw_ports_reconfigure, NULL);
++}
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+index 40c9ae1..68ae532 100644
+--- a/src/networkd/ports.h
++++ b/src/networkd/ports.h
+@@ -23,6 +23,8 @@
+
+ typedef struct nw_ports nw_ports;
+
++typedef int (*nw_ports_walk_callback)(nw_daemon* daemon, nw_port* port, void* data);
++
+ #include "daemon.h"
+
+ int nw_ports_create(nw_ports** ports, nw_daemon* daemon);
+@@ -38,4 +40,8 @@ struct nw_port* nw_ports_get_by_name(nw_ports* ports, const char* name);
+
+ int nw_ports_bus_paths(nw_ports* ports, char*** paths);
+
++int nw_ports_walk(nw_ports* ports, nw_ports_walk_callback callback, void* data);
++
++int nw_ports_reconfigure(nw_ports* ports);
++
+ #endif /* NETWORKD_PORTS_H */
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 4fda1e5..9daa0d3 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -184,6 +184,10 @@ static nw_link* nw_zone_get_link(nw_zone* zone) {
+ return nw_link_ref(zone->link);
+ }
+
++int nw_zone_reconfigure(nw_zone* zone) {
++ return 0; // XXX TODO
++}
++
+ // Carrier
+
+ int nw_zone_has_carrier(nw_zone* zone) {
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index f5a2355..591e467 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -41,6 +41,8 @@ const char* nw_zone_name(nw_zone* zone);
+
+ char* nw_zone_bus_path(nw_zone* zone);
+
++int nw_zone_reconfigure(nw_zone* zone);
++
+ int nw_zone_has_carrier(nw_zone* zone);
+
+ /*
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 521da51..84a6673 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -252,3 +252,11 @@ int nw_zones_walk(nw_zones* zones, nw_zones_walk_callback callback, void* data)
+
+ return 0;
+ }
++
++static int __nw_zones_reconfigure(nw_daemon* daemon, nw_zone* zone, void* data) {
++ return nw_zone_reconfigure(zone);
++}
++
++int nw_zones_reconfigure(nw_zones* zones) {
++ return nw_zones_walk(zones, __nw_zones_reconfigure, NULL);
++}
+diff --git a/src/networkd/zones.h b/src/networkd/zones.h
+index 019955e..ad39fd2 100644
+--- a/src/networkd/zones.h
++++ b/src/networkd/zones.h
+@@ -44,4 +44,6 @@ int nw_zones_bus_paths(nw_zones* zones, char*** paths);
+
+ int nw_zones_walk(nw_zones* zones, nw_zones_walk_callback callback, void* data);
+
++int nw_zones_reconfigure(nw_zones* zones);
++
+ #endif /* NETWORKD_ZONES_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,147 @@
+From 371b836adc35d69cdd7a4a71144ec5e0f7ef42e1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 13:33:13 +0000
+Subject: [PATCH 226/304] networkd: Implement deleting links
+
+This is a little bit rough but generally does work.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/link.h | 2 ++
+ src/networkd/port.c | 19 ++++++++++++-
+ 3 files changed, 86 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 0fb388b..949d0d8 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -41,6 +41,11 @@ struct nw_link {
+ // Interface Name
+ char ifname[IFNAMSIZ];
+
++ enum nw_link_state {
++ NW_LINK_UNKNOWN = 0,
++ NW_LINK_DESTROYED,
++ } state;
++
+ // MTU
+ uint32_t mtu;
+ uint32_t min_mtu;
+@@ -94,6 +99,16 @@ nw_link* nw_link_unref(nw_link* link) {
+ return NULL;
+ }
+
++/*
++ This is a helper function for when we pass a reference to the event loop
++ it will have to dereference the link instance later.
++*/
++static void __nw_link_unref(void* data) {
++ nw_link* link = (nw_link*)data;
++
++ nw_link_unref(link);
++}
++
+ int nw_link_ifindex(nw_link* link) {
+ return link->ifindex;
+ }
+@@ -370,3 +385,54 @@ ERROR:
+
+ return r;
+ }
++
++static int __nw_link_destroy(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
++ nw_link* link = (nw_link*)data;
++ int r;
++
++ // Check if the operation was successful
++ r = sd_netlink_message_get_errno(m);
++ if (r < 0) {
++ ERROR("Could not remove link %d: %m\n", link->ifindex);
++ // XXX We should extract the error message
++
++ return 0;
++ }
++
++ // Mark this link as destroyed
++ link->state = NW_LINK_DESTROYED;
++
++ return 0;
++}
++
++int nw_link_destroy(nw_link* link) {
++ sd_netlink_message* m = NULL;
++ int r;
++
++ sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
++ if (!rtnl)
++ return 1;
++
++ DEBUG("Destroying link %d\n", link->ifindex);
++
++ // Create a new message
++ r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, link->ifindex);
++ if (r < 0) {
++ ERROR("Could not allocate RTM_DELLINK message: %m\n");
++ goto ERROR;
++ }
++
++ // Send the message
++ r = sd_netlink_call_async(rtnl, NULL, m, __nw_link_destroy,
++ __nw_link_unref, nw_link_ref(link), -1, NULL);
++ if (r < 0) {
++ ERROR("Could not send rtnetlink message: %m\n");
++ goto ERROR;
++ }
++
++ERROR:
++ if (m)
++ sd_netlink_message_unref(m);
++
++ return r;
++}
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 779621b..58a825a 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -37,4 +37,6 @@ int nw_link_has_carrier(nw_link* link);
+
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
++int nw_link_destroy(nw_link* link);
++
+ #endif /* NETWORKD_LINK_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index ec40830..7d02305 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -263,8 +263,25 @@ const nw_address_t* nw_port_get_address(nw_port* port) {
+ return &port->address;
+ }
+
++static int nw_port_is_disabled(nw_port* port) {
++ return nw_config_get_bool(port->config, "DISABLED");
++}
++
+ int nw_port_reconfigure(nw_port* port) {
+- return 0; // XXX TODO
++ int r;
++
++ // If the port is disabled, we will try to destroy it
++ if (nw_port_is_disabled(port)) {
++ if (port->link) {
++ r = nw_link_destroy(port->link);
++ if (r)
++ return r;
++ }
++
++ return 0;
++ }
++
++ // XXX TODO
+ }
+
+ int nw_port_has_carrier(nw_port* port) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,368 @@
+From 611d4aca5d48567a7a7ddb0dd3446a97e91aad54 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 14:15:01 +0000
+Subject: [PATCH 227/304] networkd: Automatically reference/dereference links
+ to zones/ports
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 7 ++++
+ src/networkd/daemon.h | 1 +
+ src/networkd/link.c | 10 ++++++
+ src/networkd/links.c | 6 ++++
+ src/networkd/port.c | 82 +++++++++++++++++++++++++++++++++++--------
+ src/networkd/port.h | 3 ++
+ src/networkd/zone.c | 78 +++++++++++++++++++++++++++++++++-------
+ src/networkd/zone.h | 3 ++
+ 8 files changed, 164 insertions(+), 26 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index c6dad91..02ca8e2 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -540,6 +540,13 @@ nw_ports* nw_daemon_ports(nw_daemon* daemon) {
+ return nw_ports_ref(daemon->ports);
+ }
+
++int nw_daemon_ports_walk(nw_daemon* daemon, nw_ports_walk_callback callback, void* data) {
++ if (!daemon->ports)
++ return 0;
++
++ return nw_ports_walk(daemon->ports, callback, data);
++}
++
+ nw_port* nw_daemon_get_port_by_name(nw_daemon* daemon, const char* name) {
+ if (!daemon->ports)
+ return NULL;
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index ce9a660..a14d33b 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -60,6 +60,7 @@ nw_link* nw_daemon_get_link_by_name(nw_daemon* daemon, const char* name);
+ Ports
+ */
+ nw_ports* nw_daemon_ports(nw_daemon* daemon);
++int nw_daemon_ports_walk(nw_daemon* daemon, nw_ports_walk_callback callback, void* data);
+ nw_port* nw_daemon_get_port_by_name(nw_daemon* daemon, const char* name);
+
+ /*
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 949d0d8..09b9a62 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -114,6 +114,10 @@ int nw_link_ifindex(nw_link* link) {
+ }
+
+ const char* nw_link_ifname(nw_link* link) {
++ // Return NULL if name isn't set
++ if (!*link->ifname)
++ return NULL;
++
+ return link->ifname;
+ }
+
+@@ -154,6 +158,12 @@ static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
+
+ DEBUG("Link %d has been renamed to '%s'\n", link->ifindex, link->ifname);
+
++ // Assign link to ports
++ nw_daemon_ports_walk(link->daemon, __nw_port_set_link, link);
++
++ // Assign link to zones
++ nw_daemon_zones_walk(link->daemon, __nw_zone_set_link, link);
++
+ return 0;
+ }
+
+diff --git a/src/networkd/links.c b/src/networkd/links.c
+index 3dd06d0..40926f3 100644
+--- a/src/networkd/links.c
++++ b/src/networkd/links.c
+@@ -141,6 +141,12 @@ void nw_links_drop_link(nw_links* links, struct nw_link* link) {
+
+ STAILQ_REMOVE(&links->entries, entry, nw_links_entry, nodes);
+ links->num--;
++
++ // Drop link from all ports
++ nw_daemon_ports_walk(links->daemon, __nw_port_drop_link, link);
++
++ // Drop link from all zones
++ nw_daemon_zones_walk(links->daemon, __nw_zone_drop_link, link);
+ }
+
+ int nw_links_enumerate(nw_links* links) {
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 7d02305..37ab3a7 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -135,39 +135,65 @@ static nw_port_type_t nw_port_setup_type(nw_port* port) {
+ return nw_port_type_from_string(type);
+ }
+
++static int nw_port_set_link(nw_port* port, nw_link* link) {
++ // Do nothing if the same link is being re-assigned
++ if (port->link == link)
++ return 0;
++
++ // Dereference the former link if set
++ if (port->link)
++ nw_link_unref(port->link);
++
++ // Store the new link
++ if (link) {
++ port->link = nw_link_ref(link);
++
++ DEBUG("Port %s: Assigned link %d\n", port->name, nw_link_ifindex(port->link));
++
++ // Or clear the pointer if no link has been provided
++ } else {
++ port->link = NULL;
++
++ DEBUG("Port %s: Removed link\n", port->name);
++ }
++
++ return 0;
++}
++
+ static int nw_port_setup(nw_port* port) {
++ nw_link* link = NULL;
+ char path[PATH_MAX];
+ int r;
+
+ // Find the link
+- port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
+- if (port->link) {
+- DEBUG("%s: Found matching link %d\n", port->name, nw_link_ifindex(port->link));
+- } else {
+- DEBUG("%s: Could not find matching link\n", port->name);
++ link = nw_daemon_get_link_by_name(port->daemon, port->name);
++ if (link) {
++ r = nw_port_set_link(port, link);
++ if (r)
++ goto ERROR;
+ }
+
+ // Compose the path to the main configuration file
+ r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
+ if (r)
+- return r;
++ goto ERROR;
+
+ // Initialize the configuration
+ r = nw_config_create(&port->config, path);
+ if (r)
+- return r;
++ goto ERROR;
+
+ // Determine type
+ port->type = nw_port_setup_type(port);
+ if (!port->type) {
+ ERROR("Could not determine type of port %s\n", port->name);
+- return 0;
++ goto ERROR;
+ }
+
+ // Perform some common initialization
+ r = nw_port_setup_common(port);
+ if (r)
+- return r;
++ goto ERROR;
+
+ // Call any custom initialization
+ switch (port->type) {
+@@ -177,7 +203,11 @@ static int nw_port_setup(nw_port* port) {
+ break;
+ }
+
+- return 0;
++ERROR:
++ if (link)
++ nw_link_unref(link);
++
++ return r;
+ }
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
+@@ -252,11 +282,35 @@ char* nw_port_bus_path(nw_port* port) {
+ return p;
+ }
+
+-static nw_link* nw_port_get_link(nw_port* port) {
+- if (!port->link)
+- return NULL;
++int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data) {
++ nw_link* link = (nw_link*)data;
++
++ // Fetch the link name
++ const char* ifname = nw_link_ifname(link);
++ if (!ifname) {
++ ERROR("Link does not have a name set\n");
++ return 1;
++ }
++
++ // Set link if the name matches
++ if (strcmp(port->name, ifname) == 0)
++ return nw_port_set_link(port, link);
++
++ // If we have the link set but the name did not match, we will remove it
++ else if (port->link == link)
++ return nw_port_set_link(port, NULL);
++
++ return 0;
++}
++
++int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
++ nw_link* link = (nw_link*)data;
+
+- return nw_link_ref(port->link);
++ // Drop the link if it matches
++ if (port->link == link)
++ return nw_port_set_link(port, NULL);
++
++ return 0;
+ }
+
+ const nw_address_t* nw_port_get_address(nw_port* port) {
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 17c8c3c..3981c82 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -44,6 +44,9 @@ const char* nw_port_name(nw_port* port);
+
+ char* nw_port_bus_path(nw_port* port);
+
++int __nw_port_set_link(nw_daemon* daemon, nw_port* port, void* data);
++int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data);
++
+ const nw_address_t* nw_port_get_address(nw_port* port);
+
+ int nw_port_reconfigure(nw_port* port);
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 9daa0d3..00ab017 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -80,29 +80,59 @@ static void nw_zone_free(nw_zone* zone) {
+ free(zone);
+ }
+
++static int nw_zone_set_link(nw_zone* zone, nw_link* link) {
++ // Do nothing if the same link is being re-assigned
++ if (zone->link == link)
++ return 0;
++
++ // Dereference the former link if set
++ if (zone->link)
++ nw_link_unref(zone->link);
++
++ // Store the new link
++ if (link) {
++ zone->link = nw_link_ref(link);
++
++ DEBUG("Zone %s: Assigned link %d\n", zone->name, nw_link_ifindex(zone->link));
++
++ // Or clear the pointer if no link has been provided
++ } else {
++ zone->link = NULL;
++
++ DEBUG("Zone %s: Removed link\n", zone->name);
++ }
++
++ return 0;
++}
++
+ static int nw_zone_setup(nw_zone* zone) {
++ nw_link* link = NULL;
+ char path[PATH_MAX];
+ int r;
+
+ // Find the link
+- zone->link = nw_daemon_get_link_by_name(zone->daemon, zone->name);
+- if (zone->link) {
+- DEBUG("%s: Found matching link %d\n", zone->name, nw_link_ifindex(zone->link));
+- } else {
+- DEBUG("%s: Could not find matching link\n", zone->name);
++ link = nw_daemon_get_link_by_name(zone->daemon, zone->name);
++ if (link) {
++ r = nw_zone_set_link(zone, link);
++ if (r)
++ goto ERROR;
+ }
+
+ // Compose the path to the main configuration file
+ r = nw_zone_path(zone, path, "%s", "settings");
+ if (r)
+- return r;
++ goto ERROR;
+
+ // Initialize the configuration
+ r = nw_config_create(&zone->config, path);
+ if (r)
+- return r;
++ goto ERROR;
+
+- return 0;
++ERROR:
++ if (link)
++ nw_link_unref(link);
++
++ return r;
+ }
+
+ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) {
+@@ -177,11 +207,35 @@ char* nw_zone_bus_path(nw_zone* zone) {
+ return p;
+ }
+
+-static nw_link* nw_zone_get_link(nw_zone* zone) {
+- if (!zone->link)
+- return NULL;
++int __nw_zone_set_link(nw_daemon* daemon, nw_zone* zone, void* data) {
++ nw_link* link = (nw_link*)data;
++
++ // Fetch the link name
++ const char* ifname = nw_link_ifname(link);
++ if (!ifname) {
++ ERROR("Link does not have a name set\n");
++ return 1;
++ }
++
++ // Set link if the name matches
++ if (strcmp(zone->name, ifname) == 0)
++ return nw_zone_set_link(zone, link);
++
++ // If we have the link set but the name did not match, we will remove it
++ else if (zone->link == link)
++ return nw_zone_set_link(zone, NULL);
+
+- return nw_link_ref(zone->link);
++ return 0;
++}
++
++int __nw_zone_drop_link(nw_daemon* daemon, nw_zone* zone, void* data) {
++ nw_link* link = (nw_link*)data;
++
++ // Drop the link if it matches
++ if (zone->link == link)
++ return nw_zone_set_link(zone, NULL);
++
++ return 0;
+ }
+
+ int nw_zone_reconfigure(nw_zone* zone) {
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 591e467..ad348d7 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -41,6 +41,9 @@ const char* nw_zone_name(nw_zone* zone);
+
+ char* nw_zone_bus_path(nw_zone* zone);
+
++int __nw_zone_set_link(nw_daemon* daemon, nw_zone* zone, void* data);
++int __nw_zone_drop_link(nw_daemon* daemon, nw_zone* zone, void* data);
++
+ int nw_zone_reconfigure(nw_zone* zone);
+
+ int nw_zone_has_carrier(nw_zone* zone);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,46 @@
+From 96b1b84d857124d5b145859e25e3bfe7572eeb73 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 14:29:22 +0000
+Subject: [PATCH 228/304] networkd: ports: Create dummy function to create
+ links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 37ab3a7..5694024 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -321,6 +321,10 @@ static int nw_port_is_disabled(nw_port* port) {
+ return nw_config_get_bool(port->config, "DISABLED");
+ }
+
++static int nw_port_create_link(nw_port* port) {
++ return 0; // XXX TODO
++}
++
+ int nw_port_reconfigure(nw_port* port) {
+ int r;
+
+@@ -335,7 +339,16 @@ int nw_port_reconfigure(nw_port* port) {
+ return 0;
+ }
+
++ // If there is no link, we will try to create it
++ if (!port->link) {
++ r = nw_port_create_link(port);
++ if (r)
++ return r;
++ }
++
+ // XXX TODO
++
++ return 0;
+ }
+
+ int nw_port_has_carrier(nw_port* port) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,157 @@
+From 442b2fc2a34e8f6b7343d616d07c1aecb667a652 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 17:35:48 +0000
+Subject: [PATCH 229/304] networkd: daemon: Correctly store reference to bus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/bus.c | 30 +++++++++++++++++-------------
+ src/networkd/bus.h | 2 +-
+ src/networkd/daemon.c | 2 +-
+ 3 files changed, 19 insertions(+), 15 deletions(-)
+
+diff --git a/src/networkd/bus.c b/src/networkd/bus.c
+index 1daa035..8158c84 100644
+--- a/src/networkd/bus.c
++++ b/src/networkd/bus.c
+@@ -36,18 +36,19 @@ static int nw_bus_on_connect(sd_bus_message* m, void* data, sd_bus_error* error)
+ return 0;
+ }
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon) {
++int nw_bus_connect(sd_bus** bus, sd_event* loop, nw_daemon* daemon) {
++ sd_bus* b = NULL;
+ int r;
+
+ // Create a bus object
+- r = sd_bus_new(&bus);
++ r = sd_bus_new(&b);
+ if (r < 0) {
+ ERROR("Could not allocate a bus object: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Set description
+- r = sd_bus_set_description(bus, NETWORKD_BUS_DESCRIPTION);
++ r = sd_bus_set_description(b, NETWORKD_BUS_DESCRIPTION);
+ if (r < 0) {
+ ERROR("Could not set bus description: %s\n", strerror(-r));
+ return 1;
+@@ -58,21 +59,21 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon) {
+ address = DEFAULT_SYSTEM_BUS_ADDRESS;
+
+ // Set bus address
+- r = sd_bus_set_address(bus, address);
++ r = sd_bus_set_address(b, address);
+ if (r < 0) {
+ ERROR("Could not set bus address: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Set bus client
+- r = sd_bus_set_bus_client(bus, 1);
++ r = sd_bus_set_bus_client(b, 1);
+ if (r < 0) {
+ ERROR("Could not set bus client: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Request some credentials for all messages
+- r = sd_bus_negotiate_creds(bus, 1,
++ r = sd_bus_negotiate_creds(b, 1,
+ SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
+ if (r < 0) {
+ ERROR("Could not negotiate creds: %s\n", strerror(-r));
+@@ -80,53 +81,56 @@ int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon) {
+ }
+
+ // Automatically bind when the socket is available
+- r = sd_bus_set_watch_bind(bus, 1);
++ r = sd_bus_set_watch_bind(b, 1);
+ if (r < 0) {
+ ERROR("Could not watch socket: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Emit a connected signal when we are connected
+- r = sd_bus_set_connected_signal(bus, 1);
++ r = sd_bus_set_connected_signal(b, 1);
+ if (r < 0) {
+ ERROR("Could not enable sending a connect signal: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Connect to the bus
+- r = sd_bus_start(bus);
++ r = sd_bus_start(b);
+ if (r < 0) {
+ ERROR("Could not connect to bus: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Register the implementation
+- r = nw_bus_register_implementation(bus, &daemon_bus_impl, daemon);
++ r = nw_bus_register_implementation(b, &daemon_bus_impl, daemon);
+ if (r)
+ return r;
+
+ // Request interface name
+- r = sd_bus_request_name_async(bus, NULL, "org.ipfire.network1", 0, NULL, NULL);
++ r = sd_bus_request_name_async(b, NULL, "org.ipfire.network1", 0, NULL, NULL);
+ if (r < 0) {
+ ERROR("Could not request bus name: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Attach the event loop
+- r = sd_bus_attach_event(bus, loop, 0);
++ r = sd_bus_attach_event(b, loop, 0);
+ if (r < 0) {
+ ERROR("Could not attach bus to event loop: %s\n", strerror(-r));
+ return 1;
+ }
+
+ // Request receiving a connect signal
+- r = sd_bus_match_signal_async(bus, NULL, "org.freedesktop.DBus.Local",
++ r = sd_bus_match_signal_async(b, NULL, "org.freedesktop.DBus.Local",
+ NULL, "org.freedesktop.DBus.Local", "Connected", nw_bus_on_connect, NULL, NULL);
+ if (r < 0) {
+ ERROR("Could not request match on Connected signal: %s\n", strerror(-r));
+ return 1;
+ }
+
++ // Return reference
++ *bus = b;
++
+ return 0;
+ }
+
+diff --git a/src/networkd/bus.h b/src/networkd/bus.h
+index 05b4c63..29b1b4a 100644
+--- a/src/networkd/bus.h
++++ b/src/networkd/bus.h
+@@ -30,7 +30,7 @@
+
+ #include "daemon.h"
+
+-int nw_bus_connect(sd_bus* bus, sd_event* loop, nw_daemon* daemon);
++int nw_bus_connect(sd_bus** bus, sd_event* loop, nw_daemon* daemon);
+
+ struct nw_bus_vtable_pair {
+ const sd_bus_vtable* vtable;
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 02ca8e2..c8e65f4 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -341,7 +341,7 @@ static int nw_daemon_setup(nw_daemon* daemon) {
+ return r;
+
+ // Connect to the system bus
+- r = nw_bus_connect(daemon->bus, daemon->loop, daemon);
++ r = nw_bus_connect(&daemon->bus, daemon->loop, daemon);
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,667 @@
+From 15240e0819685c30a4955ae161374b5a3fc9d313 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 14 Apr 2023 17:43:12 +0000
+Subject: [PATCH 230/304] networkd: Collect stats regulary and emit them on
+ dbus
+
+This is useful for us monitoring interface throughput (e.g. in
+collecty).
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/daemon.c | 44 ++++++++
+ src/networkd/daemon.h | 6 +
+ src/networkd/link.c | 81 ++++++++++++++
+ src/networkd/link.h | 6 +
+ src/networkd/port.c | 31 +++++-
+ src/networkd/port.h | 5 +
+ src/networkd/stats-collector.c | 197 +++++++++++++++++++++++++++++++++
+ src/networkd/stats-collector.h | 37 +++++++
+ src/networkd/zone.c | 30 +++++
+ src/networkd/zone.h | 6 +
+ 11 files changed, 444 insertions(+), 1 deletion(-)
+ create mode 100644 src/networkd/stats-collector.c
+ create mode 100644 src/networkd/stats-collector.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 80a7236..22d90d4 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -332,6 +332,8 @@ dist_networkd_SOURCES = \
+ src/networkd/port.h \
+ src/networkd/port-bus.c \
+ src/networkd/port-bus.h \
++ src/networkd/stats-collector.c \
++ src/networkd/stats-collector.h \
+ src/networkd/string.h \
+ src/networkd/util.c \
+ src/networkd/util.h \
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index c8e65f4..749a70b 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -35,6 +35,7 @@
+ #include "links.h"
+ #include "logging.h"
+ #include "ports.h"
++#include "stats-collector.h"
+ #include "zone.h"
+ #include "zones.h"
+
+@@ -67,6 +68,8 @@ struct nw_daemon {
+ // Ports
+ nw_ports* ports;
+
++ // Stats Collector
++ sd_event_source* stats_collector_event;
+ };
+
+ static int __nw_daemon_terminate(sd_event_source* source, const struct signalfd_siginfo* si,
+@@ -322,6 +325,33 @@ static int nw_daemon_reconfigure(nw_daemon* daemon) {
+ return 0;
+ }
+
++static int nw_daemon_starts_stats_collector(nw_daemon* daemon) {
++ sd_event_source* s = NULL;
++ int r;
++
++ // Register the stats collector main function
++ r = sd_event_add_time_relative(daemon->loop, &s, CLOCK_MONOTONIC, 0, 0,
++ nw_stats_collector, daemon);
++ if (r < 0) {
++ ERROR("Could not start the stats collector: %m\n");
++ goto ERROR;
++ }
++
++ // Keep calling the stats collector for forever
++ r = sd_event_source_set_enabled(s, SD_EVENT_ON);
++ if (r < 0)
++ goto ERROR;
++
++ // Keep a reference to the event source
++ daemon->stats_collector_event = sd_event_source_ref(s);
++
++ERROR:
++ if (s)
++ sd_event_source_unref(s);
++
++ return r;
++}
++
+ static int nw_daemon_setup(nw_daemon* daemon) {
+ int r;
+
+@@ -360,6 +390,11 @@ static int nw_daemon_setup(nw_daemon* daemon) {
+ if (r)
+ return r;
+
++ // Start the stats collector
++ r = nw_daemon_starts_stats_collector(daemon);
++ if (r)
++ return r;
++
+ return 0;
+ }
+
+@@ -404,6 +439,8 @@ static void nw_daemon_free(nw_daemon* daemon) {
+ // Cleanup common objects
+ nw_daemon_cleanup(daemon);
+
++ if (daemon->stats_collector_event)
++ sd_event_source_unref(daemon->stats_collector_event);
+ if (daemon->bus)
+ sd_bus_unref(daemon->bus);
+ if (daemon->loop)
+@@ -498,6 +535,13 @@ int nw_daemon_save(nw_daemon* daemon) {
+ return 0;
+ }
+
++/*
++ Bus
++*/
++sd_bus* nw_daemon_get_bus(nw_daemon* daemon) {
++ return daemon->bus;
++}
++
+ /*
+ Netlink
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index a14d33b..74e19e6 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -21,6 +21,7 @@
+ #ifndef NETWORKD_DAEMON_H
+ #define NETWORKD_DAEMON_H
+
++#include <systemd/sd-bus.h>
+ #include <systemd/sd-netlink.h>
+
+ typedef struct nw_daemon nw_daemon;
+@@ -43,6 +44,11 @@ int nw_daemon_reload(nw_daemon* daemon);
+
+ int nw_daemon_save(nw_daemon* daemon);
+
++/*
++ Bus
++*/
++sd_bus* nw_daemon_get_bus(nw_daemon* daemon);
++
+ /*
+ Netlink
+ */
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 09b9a62..1edf20d 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -19,6 +19,7 @@
+ #############################################################################*/
+
+ #include <linux/if.h>
++#include <linux/if_link.h>
+ #include <stddef.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -46,6 +47,9 @@ struct nw_link {
+ NW_LINK_DESTROYED,
+ } state;
+
++ // Stats
++ struct rtnl_link_stats64 stats64;
++
+ // MTU
+ uint32_t mtu;
+ uint32_t min_mtu;
+@@ -121,6 +125,83 @@ const char* nw_link_ifname(nw_link* link) {
+ return link->ifname;
+ }
+
++// Stats
++
++const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
++ return &link->stats64;
++}
++
++static int nw_link_call_getlink(nw_link* link,
++ int (*callback)(sd_netlink* rtnl, sd_netlink_message* m, void* data)) {
++ sd_netlink_message* m = NULL;
++ int r;
++
++ sd_netlink* rtnl = nw_daemon_get_rtnl(link->daemon);
++ if (!rtnl)
++ return 1;
++
++ // Create a new message
++ r = sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, link->ifindex);
++ if (r < 0) {
++ ERROR("Could not allocate RTM_GETLINK message: %m\n");
++ goto ERROR;
++ }
++
++ // Send the message
++ r = sd_netlink_call_async(rtnl, NULL, m, callback,
++ __nw_link_unref, nw_link_ref(link), -1, NULL);
++ if (r < 0) {
++ ERROR("Could not send rtnetlink message: %m\n");
++ goto ERROR;
++ }
++
++ERROR:
++ if (m)
++ sd_netlink_message_unref(m);
++
++ return r;
++}
++
++static int __nw_link_update_stats(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
++ nw_link* link = (nw_link*)data;
++ int r;
++
++ // Fetch the stats
++ r = sd_netlink_message_read(m, IFLA_STATS64, sizeof(link->stats64), &link->stats64);
++ if (r < 0)
++ return r;
++
++ DEBUG("Link %d: Stats updated\n", link->ifindex);
++
++ // Log stats
++ DEBUG(" Packets : RX: %12llu, TX: %12llu\n",
++ link->stats64.rx_packets, link->stats64.tx_packets);
++ DEBUG(" Bytes : RX: %12llu, TX: %12llu\n",
++ link->stats64.rx_bytes, link->stats64.tx_bytes);
++ DEBUG(" Errors : RX: %12llu, TX: %12llu\n",
++ link->stats64.rx_errors, link->stats64.tx_errors);
++ DEBUG(" Dropped : RX: %12llu, TX: %12llu\n",
++ link->stats64.rx_dropped, link->stats64.rx_dropped);
++ DEBUG(" Multicast : %llu\n", link->stats64.multicast);
++ DEBUG(" Collisions : %llu\n", link->stats64.collisions);
++
++ // Notify ports that stats have been updated
++ r = nw_daemon_ports_walk(link->daemon, __nw_port_update_stats, link);
++ if (r)
++ return r;
++
++ // Notify zones that stats have been updated
++ r = nw_daemon_zones_walk(link->daemon, __nw_zone_update_stats, link);
++ if (r)
++ return r;
++
++ return 0;
++}
++
++int nw_link_update_stats(nw_link* link) {
++ return nw_link_call_getlink(link, __nw_link_update_stats);
++}
++
+ // Carrier
+
+ int nw_link_has_carrier(nw_link* link) {
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 58a825a..2bab47c 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_LINK_H
+ #define NETWORKD_LINK_H
+
++#include <linux/if_link.h>
++
+ typedef struct nw_link nw_link;
+
+ #include "daemon.h"
+@@ -33,6 +35,10 @@ nw_link* nw_link_unref(nw_link* link);
+ int nw_link_ifindex(nw_link* link);
+ const char* nw_link_ifname(nw_link* link);
+
++// Stats
++const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link);
++int nw_link_update_stats(nw_link* link);
++
+ int nw_link_has_carrier(nw_link* link);
+
+ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 5694024..7638c7b 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -29,8 +29,9 @@
+ #include "config.h"
+ #include "link.h"
+ #include "logging.h"
+-#include "string.h"
+ #include "port.h"
++#include "stats-collector.h"
++#include "string.h"
+
+ struct nw_port {
+ nw_daemon* daemon;
+@@ -357,3 +358,31 @@ int nw_port_has_carrier(nw_port* port) {
+
+ return nw_link_has_carrier(port->link);
+ }
++
++/*
++ Stats
++*/
++
++const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port) {
++ if (!port->link)
++ return NULL;
++
++ return nw_link_get_stats64(port->link);
++}
++
++int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data) {
++ nw_link* link = (nw_link*)data;
++
++ // Emit stats if link matches
++ if (port->link == link)
++ return nw_stats_collector_emit_port_stats(daemon, port);
++
++ return 0;
++}
++
++int nw_port_update_stats(nw_port* port) {
++ if (port->link)
++ return nw_link_update_stats(port->link);
++
++ return 0;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 3981c82..9dcd6c2 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -53,4 +53,9 @@ int nw_port_reconfigure(nw_port* port);
+
+ int nw_port_has_carrier(nw_port* port);
+
++// Stats
++const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port);
++int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data);
++int nw_port_update_stats(nw_port* port);
++
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/stats-collector.c b/src/networkd/stats-collector.c
+new file mode 100644
+index 0000000..c10602e
+--- /dev/null
++++ b/src/networkd/stats-collector.c
+@@ -0,0 +1,197 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++
++#include <systemd/sd-bus.h>
++#include <systemd/sd-event.h>
++
++#include "logging.h"
++#include "port.h"
++#include "stats-collector.h"
++#include "zone.h"
++
++static int __nw_stats_collector_port(nw_daemon* daemon, nw_port* port, void* data) {
++ return nw_port_update_stats(port);
++}
++
++static int __nw_stats_collector_zone(nw_daemon* daemon, nw_zone* zone, void* data) {
++ return nw_zone_update_stats(zone);
++}
++
++int nw_stats_collector(sd_event_source* s, long unsigned int usec, void* data) {
++ nw_daemon* daemon = (nw_daemon*)data;
++ int r;
++
++ DEBUG("Stats collector has been called\n");
++
++ // Schedule the next call
++ r = sd_event_source_set_time(s, usec + NW_STATS_COLLECTOR_INTERVAL);
++ if (r < 0)
++ return r;
++
++ // Ports
++ r = nw_daemon_ports_walk(daemon, __nw_stats_collector_port, NULL);
++ if (r)
++ return r;
++
++ // Zones
++ r = nw_daemon_zones_walk(daemon, __nw_stats_collector_zone, NULL);
++ if (r)
++ return r;
++
++ return 0;
++}
++
++static int nw_stats_collector_emit_stats(nw_daemon* daemon, const char* path,
++ const char* interface, const char* member, const struct rtnl_link_stats64* stats64) {
++ sd_bus_message* m = NULL;
++ int r;
++
++ sd_bus* bus = nw_daemon_get_bus(daemon);
++
++ // Allocate a new message
++ r = sd_bus_message_new_signal(bus, &m, path, interface, member);
++ if (r < 0) {
++ errno = -r;
++ ERROR("Could not allocate bus message: %m\n");
++ goto ERROR;
++ }
++
++ // Open the container
++ r = sd_bus_message_open_container(m, 'a', "{st}");
++ if (r < 0) {
++ ERROR("Could not open container: %m\n");
++ goto ERROR;
++ }
++
++ const struct stats64_entry {
++ const char* key;
++ uint64_t value;
++ } entries[] = {
++ { "rx-packets", stats64->rx_packets },
++ { "tx-packets", stats64->tx_packets },
++ { "rx-bytes", stats64->rx_bytes },
++ { "tx-bytes", stats64->tx_bytes },
++ { "rx-errors", stats64->rx_errors },
++ { "tx-errors", stats64->tx_errors },
++ { "rx-dropped", stats64->rx_dropped },
++ { "tx-dropped", stats64->tx_dropped },
++ { "multicast", stats64->multicast },
++ { "collisions", stats64->collisions },
++
++ // Detailed RX errors
++ { "rx-length-errors", stats64->rx_length_errors },
++ { "rx-over-errors", stats64->rx_over_errors },
++ { "rx-crc-errors", stats64->rx_crc_errors },
++ { "rx-frame-errors", stats64->rx_frame_errors },
++ { "rx-fifo-errors", stats64->rx_fifo_errors },
++ { "rx-missed-errors", stats64->rx_missed_errors },
++
++ // Detailed TX errors
++ { "tx-aborted-errors", stats64->tx_aborted_errors },
++ { "tx-carrier-errors", stats64->tx_carrier_errors },
++ { "tx-fifo-errors", stats64->tx_fifo_errors },
++ { "tx-heartbeat-errors", stats64->tx_heartbeat_errors },
++ { "tx-window-errors", stats64->tx_window_errors },
++
++ { NULL },
++ };
++
++ for (const struct stats64_entry* e = entries; e->key; e++) {
++ r = sd_bus_message_append(m, "{st}", e->key, e->value);
++ if (r < 0) {
++ ERROR("Could not set stat value: %m\n");
++ goto ERROR;
++ }
++ }
++
++ // Close the container
++ r = sd_bus_message_close_container(m);
++ if (r < 0) {
++ ERROR("Could not close container: %m\n");
++ goto ERROR;
++ }
++
++ // Emit the signal
++ r = sd_bus_send(bus, m, NULL);
++ if (r < 0) {
++ ERROR("Could not emit the stats signal for %s: %m\n", path);
++ goto ERROR;
++ }
++
++ERROR:
++ if (m)
++ sd_bus_message_unref(m);
++
++ return r;
++}
++
++int nw_stats_collector_emit_port_stats(nw_daemon* daemon, nw_port* port) {
++ const struct rtnl_link_stats64* stats64 = NULL;
++ char* path = NULL;
++ int r;
++
++ // Fetch the bus path
++ path = nw_port_bus_path(port);
++
++ // Fetch the stats
++ stats64 = nw_port_get_stats64(port);
++
++ // Emit the stats
++ r = nw_stats_collector_emit_stats(daemon, path,
++ "org.ipfire.network1.Port", "Stats", stats64);
++ if (r < 0) {
++ ERROR("Could not emit stats for port %s: %m\n", nw_port_name(port));
++ goto ERROR;
++ }
++
++ERROR:
++ if (path)
++ free(path);
++
++ return r;
++}
++
++int nw_stats_collector_emit_zone_stats(nw_daemon* daemon, nw_zone* zone) {
++ const struct rtnl_link_stats64* stats64 = NULL;
++ char* path = NULL;
++ int r;
++
++ // Fetch the bus path
++ path = nw_zone_bus_path(zone);
++
++ // Fetch the stats
++ stats64 = nw_zone_get_stats64(zone);
++
++ // Emit the stats
++ r = nw_stats_collector_emit_stats(daemon, path,
++ "org.ipfire.network1.Zone", "Stats", stats64);
++ if (r < 0) {
++ ERROR("Could not emit stats for zone %s: %m\n", nw_zone_name(zone));
++ goto ERROR;
++ }
++
++ERROR:
++ if (path)
++ free(path);
++
++ return r;
++}
+diff --git a/src/networkd/stats-collector.h b/src/networkd/stats-collector.h
+new file mode 100644
+index 0000000..ea11c11
+--- /dev/null
++++ b/src/networkd/stats-collector.h
+@@ -0,0 +1,37 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_STATS_COLLECTOR_H
++#define NETWORKD_STATS_COLLECTOR_H
++
++#include <systemd/sd-event.h>
++
++#include "daemon.h"
++#include "port.h"
++#include "zone.h"
++
++#define NW_STATS_COLLECTOR_INTERVAL 15 * 1000000ULL // 15 sec in µsec
++
++int nw_stats_collector(sd_event_source* s, long unsigned int usec, void* data);
++
++int nw_stats_collector_emit_port_stats(nw_daemon* daemon, nw_port* port);
++int nw_stats_collector_emit_zone_stats(nw_daemon* daemon, nw_zone* zone);
++
++#endif /* NETWORKD_STATS_COLLECTOR_H */
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 00ab017..3f47a26 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -19,6 +19,7 @@
+ #############################################################################*/
+
+ #include <limits.h>
++#include <linux/if_link.h>
+ #include <stdlib.h>
+
+ #include <systemd/sd-bus.h>
+@@ -27,6 +28,7 @@
+ #include "daemon.h"
+ #include "link.h"
+ #include "logging.h"
++#include "stats-collector.h"
+ #include "string.h"
+ #include "zone.h"
+
+@@ -263,3 +265,31 @@ int nw_zone_set_mtu(nw_zone* zone, unsigned int mtu) {
+
+ return nw_config_set_int(zone->config, "MTU", mtu);
+ }
++
++/*
++ Stats
++*/
++
++const struct rtnl_link_stats64* nw_zone_get_stats64(nw_zone* zone) {
++ if (!zone->link)
++ return NULL;
++
++ return nw_link_get_stats64(zone->link);
++}
++
++int __nw_zone_update_stats(nw_daemon* daemon, nw_zone* zone, void* data) {
++ nw_link* link = (nw_link*)data;
++
++ // Emit stats if link matches
++ if (zone->link == link)
++ return nw_stats_collector_emit_zone_stats(daemon, zone);
++
++ return 0;
++}
++
++int nw_zone_update_stats(nw_zone* zone) {
++ if (zone->link)
++ return nw_link_update_stats(zone->link);
++
++ return 0;
++}
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index ad348d7..9737b45 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -28,6 +28,8 @@
+
+ typedef struct nw_zone nw_zone;
+
++#include <linux/if_link.h>
++
+ #include "daemon.h"
+
+ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name);
+@@ -54,4 +56,8 @@ int nw_zone_has_carrier(nw_zone* zone);
+ unsigned int nw_zone_mtu(nw_zone* zone);
+ int nw_zone_set_mtu(nw_zone* zone, unsigned int mtu);
+
++const struct rtnl_link_stats64* nw_zone_get_stats64(nw_zone* zone);
++int __nw_zone_update_stats(nw_daemon* daemon, nw_zone* zone, void* data);
++int nw_zone_update_stats(nw_zone* zone);
++
+ #endif /* NETWORKD_ZONE_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 35bf96bb311d13924cd96668736b474c1576f553 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 10:45:53 +0000
+Subject: [PATCH 231/304] address: Fix output buffer size when formatting MAC
+ addresses
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index b7556f4..7937d62 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -43,7 +43,7 @@ static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
+ }
+
+ static inline char* nw_address_to_string(const nw_address_t* addr) {
+- char buffer[18];
++ char buffer[20];
+
+ char* p = ether_ntoa_r(addr, buffer);
+ if (!p)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,42 @@
+From 92c8a4fe8806d0a84f4c546be75c0f08dc6d7200 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 10:46:32 +0000
+Subject: [PATCH 232/304] config: Fail if there is garbage after intergers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index d879ace..42e0172 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -348,13 +348,23 @@ int nw_config_set(nw_config* config, const char* key, const char* value) {
+ }
+
+ int nw_config_get_int(nw_config* config, const char* key, const int __default) {
++ char* p = NULL;
++ int r;
++
+ const char* value = nw_config_get(config, key);
+
+ // Return zero if not set
+ if (!value)
+ return __default;
+
+- return strtoul(value, NULL, 10);
++ // Parse the input
++ r = strtoul(value, &p, 10);
++
++ // If we have characters following the input, we throw it away
++ if (p)
++ return __default;
++
++ return r;
+ }
+
+ int nw_config_set_int(nw_config* config, const char* key, const int value) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 2d00051763072b26fa5c1ba421afe2f0928d57ce Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 11:13:22 +0000
+Subject: [PATCH 233/304] config: Avoid adding empty line after integers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 42e0172..27cfad8 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -372,7 +372,7 @@ int nw_config_set_int(nw_config* config, const char* key, const int value) {
+ int r;
+
+ // Format the value as string
+- r = nw_string_format(__value, "%d\n", value);
++ r = nw_string_format(__value, "%d", value);
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,133 @@
+From 06bc93d3136c4bd2d75f7c50051b8b96d29dab59 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 11:15:15 +0000
+Subject: [PATCH 234/304] ports: Require type to be set at all times
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 50 ++++++++++++++++++++++++++++++--------------
+ src/networkd/port.h | 5 ++++-
+ src/networkd/ports.c | 2 +-
+ 3 files changed, 39 insertions(+), 18 deletions(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 7638c7b..aad6210 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -128,14 +128,6 @@ static int nw_port_setup_common(nw_port* port) {
+ return 0;
+ }
+
+-static nw_port_type_t nw_port_setup_type(nw_port* port) {
+- const char* type = nw_config_get(port->config, "TYPE");
+- if (!type)
+- return NW_PORT_UNKNOWN;
+-
+- return nw_port_type_from_string(type);
+-}
+-
+ static int nw_port_set_link(nw_port* port, nw_link* link) {
+ // Do nothing if the same link is being re-assigned
+ if (port->link == link)
+@@ -184,13 +176,6 @@ static int nw_port_setup(nw_port* port) {
+ if (r)
+ goto ERROR;
+
+- // Determine type
+- port->type = nw_port_setup_type(port);
+- if (!port->type) {
+- ERROR("Could not determine type of port %s\n", port->name);
+- goto ERROR;
+- }
+-
+ // Perform some common initialization
+ r = nw_port_setup_common(port);
+ if (r)
+@@ -211,7 +196,7 @@ ERROR:
+ return r;
+ }
+
+-int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
++int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
+ int r;
+
+ // Allocate a new object
+@@ -225,6 +210,9 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name) {
+ // Initialize reference counter
+ p->nrefs = 1;
+
++ // Store the type
++ p->type = type;
++
+ // Store the name
+ r = nw_string_set(p->name, name);
+ if (r)
+@@ -243,6 +231,36 @@ ERROR:
+ return r;
+ }
+
++int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
++ const char* name, const char* path) {
++ nw_config* config = NULL;
++ int r;
++
++ // Initialize the configuration
++ r = nw_config_create(&config, path);
++ if (r)
++ goto ERROR;
++
++ // Fetch the type
++ const char* type = nw_config_get(config, "TYPE");
++ if (!type) {
++ ERROR("Port configuration %s has no TYPE\n", path);
++ r = 1;
++ goto ERROR;
++ }
++
++ // Create a new port
++ r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
++ if (r)
++ goto ERROR;
++
++ERROR:
++ if (config)
++ nw_config_unref(config);
++
++ return r;
++}
++
+ nw_port* nw_port_ref(nw_port* port) {
+ port->nrefs++;
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 9dcd6c2..2f4319b 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -33,7 +33,10 @@ typedef struct nw_port nw_port;
+ #include "address.h"
+ #include "daemon.h"
+
+-int nw_port_create(nw_port** port, nw_daemon* daemon, const char* name);
++int nw_port_create(nw_port** port, nw_daemon* daemon,
++ nw_port_type_t type, const char* name);
++int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
++ const char* name, const char* path);
+
+ nw_port* nw_port_ref(nw_port* port);
+ nw_port* nw_port_unref(nw_port* port);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 87135d8..f0a3ebb 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -151,7 +151,7 @@ static int __nw_ports_enumerate(const char* path, const struct stat* s, void* da
+ return 0;
+
+ // Create a new port
+- r = nw_port_create(&port, ports->daemon, name);
++ r = nw_port_create_from_config(&port, ports->daemon, name, path);
+ if (r)
+ goto ERROR;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,287 @@
+From c7761af8c8f278f3adb7d422144e13f845089a4b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 11:16:33 +0000
+Subject: [PATCH 235/304] port: Implement reading/writing VLAN settings
+
+This is just simple test to see where the configuration could be going.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 166 +++++++++++++++++++++++++++++++++++++++++-
+ src/networkd/port.h | 13 ++++
+ src/networkd/string.h | 5 ++
+ 3 files changed, 182 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index aad6210..a240c61 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -40,21 +40,32 @@ struct nw_port {
+ // Link
+ nw_link* link;
+
+- char name[IF_NAMESIZE];
+ nw_port_type_t type;
++ char name[IF_NAMESIZE];
+
+ // Configuration
+ nw_config *config;
+
+ // Common attributes
+ nw_address_t address;
++
++ // VLAN settings
++ struct nw_port_vlan {
++ nw_port* parent;
++ int id;
++
++ // If the parent has not been read from the configuration we will
++ // save the name and try to find it later.
++ char __parent_name[IF_NAMESIZE];
++ } vlan;
+ };
+
+ static const struct nw_port_type_map {
+ nw_port_type_t type;
+ const char* name;
+ } nw_port_type_map[] = {
+- { NW_PORT_DUMMY, "dummy" },
++ { NW_PORT_DUMMY, "dummy" },
++ { NW_PORT_VLAN, "vlan" },
+ { NW_PORT_UNKNOWN, NULL },
+ };
+
+@@ -128,6 +139,28 @@ static int nw_port_setup_common(nw_port* port) {
+ return 0;
+ }
+
++static int nw_port_setup_vlan(nw_port* port) {
++ int r;
++
++ // VLAN ID
++ int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
++ if (id) {
++ r = nw_port_set_vlan_id(port, id);
++ if (r)
++ return r;
++ }
++
++ // Parent Port
++ const char* parent = nw_config_get(port->config, "VLAN_PARENT");
++ if (parent) {
++ r = nw_string_set(port->vlan.__parent_name, parent);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
+ static int nw_port_set_link(nw_port* port, nw_link* link) {
+ // Do nothing if the same link is being re-assigned
+ if (port->link == link)
+@@ -183,6 +216,12 @@ static int nw_port_setup(nw_port* port) {
+
+ // Call any custom initialization
+ switch (port->type) {
++ case NW_PORT_VLAN:
++ r = nw_port_setup_vlan(port);
++ if (r)
++ goto ERROR;
++ break;
++
+ // These do not need any special initialization
+ case NW_PORT_DUMMY:
+ case NW_PORT_UNKNOWN:
+@@ -275,14 +314,50 @@ nw_port* nw_port_unref(nw_port* port) {
+ return NULL;
+ }
+
++static int nw_port_save_vlan(nw_port* port) {
++ int r;
++
++ // VLAN ID
++ r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
++ if (r)
++ return r;
++
++ // Parent Port
++ r = nw_config_set(port->config, "VLAN_PARENT",
++ (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
++ if (r)
++ return r;
++
++ return 0;
++}
++
+ int nw_port_save(nw_port* port) {
+ int r;
+
++ switch (port->type) {
++ // VLAN
++ case NW_PORT_VLAN:
++ r = nw_port_save_vlan(port);
++ if (r)
++ goto ERROR;
++ break;
++
++ // These types do not have any special settings
++ case NW_PORT_DUMMY:
++ case NW_PORT_UNKNOWN:
++ break;
++ }
++
+ r = nw_config_write(port->config);
+ if (r)
+ return r;
+
+ return 0;
++
++ERROR:
++ ERROR("Could not save configuration for port %s: %m\n", port->name);
++
++ return 1;
+ }
+
+ const char* nw_port_name(nw_port* port) {
+@@ -404,3 +479,90 @@ int nw_port_update_stats(nw_port* port) {
+
+ return 0;
+ }
++
++static int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
++ if (port->type == type)
++ return 0;
++
++ errno = ENOTSUP;
++ return -errno;
++}
++
++/*
++ VLAN
++*/
++int nw_port_get_vlan_id(nw_port* port) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ return port->vlan.id;
++}
++
++int nw_port_set_vlan_id(nw_port* port, int id) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ // Check if the VLAN ID is within range
++ if (id < NW_VLAN_ID_MIN || id > NW_VLAN_ID_MAX)
++ return -EINVAL;
++
++ // Store the ID
++ port->vlan.id = id;
++
++ DEBUG("Port %s: Set VLAN ID to %d\n", port->name, port->vlan.id);
++
++ return 0;
++}
++
++nw_port* nw_port_get_vlan_parent(nw_port* port) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return NULL;
++
++ // Try to find a reference to the parent if none exists
++ if (!port->vlan.parent && *port->vlan.__parent_name)
++ port->vlan.parent = nw_daemon_get_port_by_name(port->daemon, port->vlan.__parent_name);
++
++ if (port->vlan.parent)
++ return nw_port_ref(port->vlan.parent);
++
++ // No port found
++ return NULL;
++}
++
++int nw_port_set_vlan_parent(nw_port* port, nw_port* parent) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ // Reset the former parent name
++ nw_string_empty(port->vlan.__parent_name);
++
++ // Dereference the former parent
++ if (port->vlan.parent) {
++ nw_port_unref(port->vlan.parent);
++ port->vlan.parent = NULL;
++ }
++
++ // Store the new parent
++ if (parent)
++ port->vlan.parent = nw_port_ref(parent);
++
++ DEBUG("Port %s: Set VLAN parent to %s\n", port->name, nw_port_name(port->vlan.parent));
++
++ return 0;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 2f4319b..d89e1e0 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -26,8 +26,14 @@
+ typedef enum nw_port_type {
+ NW_PORT_UNKNOWN = 0,
+ NW_PORT_DUMMY,
++ NW_PORT_VLAN,
+ } nw_port_type_t;
+
++// VLAN
++#define NW_VLAN_ID_INVALID 0
++#define NW_VLAN_ID_MIN 1
++#define NW_VLAN_ID_MAX 4096
++
+ typedef struct nw_port nw_port;
+
+ #include "address.h"
+@@ -61,4 +67,11 @@ const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port);
+ int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data);
+ int nw_port_update_stats(nw_port* port);
+
++// VLAN
++int nw_port_get_vlan_id(nw_port* port);
++int nw_port_set_vlan_id(nw_port* port, int id);
++
++nw_port* nw_port_get_vlan_parent(nw_port* port);
++int nw_port_set_vlan_parent(nw_port* port, nw_port* parent);
++
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 6ff44f9..5bdfc3d 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -120,6 +120,11 @@ static inline int nw_string_strip(char* s) {
+ return 0;
+ }
+
++static inline void nw_string_empty(char* s) {
++ if (s)
++ *s = '\0';
++}
++
+ /*
+ Paths
+ */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,193 @@
+From c403eb4c480316090d9ca3fd2915b1bf25ac10c0 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 15 Apr 2023 11:46:22 +0000
+Subject: [PATCH 236/304] ports: Implement destroying a port
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 12 ++++++++++
+ src/networkd/config.h | 2 ++
+ src/networkd/link.c | 2 ++
+ src/networkd/port.c | 54 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port.h | 3 +++
+ src/networkd/zone.c | 9 ++++++++
+ src/networkd/zone.h | 2 ++
+ 7 files changed, 84 insertions(+)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 27cfad8..8269ede 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -24,6 +24,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/queue.h>
++#include <unistd.h>
+
+ #include "config.h"
+ #include "logging.h"
+@@ -135,6 +136,17 @@ nw_config* nw_config_unref(nw_config* config) {
+ return NULL;
+ }
+
++int nw_config_destroy(nw_config* config) {
++ int r;
++
++ // Drop all entries
++ r = nw_config_flush(config);
++ if (r)
++ return r;
++
++ return unlink(config->path);
++}
++
+ const char* nw_config_path(nw_config* config) {
+ if (*config->path)
+ return config->path;
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 0b25f75..e17c016 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -33,6 +33,8 @@ int nw_config_create(nw_config** config, const char* path);
+ nw_config* nw_config_ref(nw_config* config);
+ nw_config* nw_config_unref(nw_config* config);
+
++int nw_config_destroy(nw_config* config);
++
+ const char* nw_config_path(nw_config* config);
+
+ int nw_config_flush(nw_config* config);
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 1edf20d..603aabe 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -308,12 +308,14 @@ static int nw_link_update_flags(nw_link* link, sd_netlink_message* message) {
+ return 1;
+ }
+
++#if 0
+ // Fetch operstate
+ r = sd_netlink_message_read_u8(message, IFLA_OPERSTATE, &operstate);
+ if (r < 1) {
+ ERROR("Could not read operstate: %m\n");
+ return 1;
+ }
++#endif
+
+ // End here if there have been no changes
+ if (link->flags == flags && link->operstate == operstate)
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index a240c61..23ffd0b 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -314,6 +314,60 @@ nw_port* nw_port_unref(nw_port* port) {
+ return NULL;
+ }
+
++int nw_port_destroy(nw_port* port) {
++ int r;
++
++ DEBUG("Destroying port %s\n", port->name);
++
++ // Destroy the physical link (if exists)
++ if (port->link) {
++ r = nw_link_destroy(port->link);
++ if (r)
++ return r;
++ }
++
++ // Dereference the port from other ports
++ r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
++ if (r)
++ return r;
++
++ // Dereference the port from other zones
++ r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
++ if (r)
++ return r;
++
++ // Destroy the configuration
++ r = nw_config_destroy(port->config);
++ if (r)
++ return r;
++
++ // Reset type
++ port->type = NW_PORT_UNKNOWN;
++
++ return 0;
++}
++
++int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
++ nw_port* dropped_port = (nw_port*)data;
++ int r;
++
++ switch (port->type) {
++ case NW_PORT_VLAN:
++ if (port->vlan.parent == dropped_port) {
++ r = nw_port_set_vlan_parent(port, NULL);
++ if (r)
++ return r;
++ }
++ break;
++
++ case NW_PORT_DUMMY:
++ case NW_PORT_UNKNOWN:
++ break;
++ }
++
++ return 0;
++}
++
+ static int nw_port_save_vlan(nw_port* port) {
+ int r;
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index d89e1e0..032d82c 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -47,6 +47,9 @@ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+ nw_port* nw_port_ref(nw_port* port);
+ nw_port* nw_port_unref(nw_port* port);
+
++int nw_port_destroy(nw_port* port);
++int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data);
++
+ int nw_port_save(nw_port* port);
+
+ const char* nw_port_name(nw_port* port);
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 3f47a26..9f5b7f8 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -183,6 +183,15 @@ nw_zone* nw_zone_unref(nw_zone* zone) {
+ return NULL;
+ }
+
++int __nw_zone_drop_port(nw_daemon* daemon, nw_zone* zone, void* data) {
++ nw_port* dropped_port = (nw_port*)data;
++
++ // XXX TODO
++ (void)dropped_port;
++
++ return 0;
++}
++
+ int nw_zone_save(nw_zone* zone) {
+ int r;
+
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 9737b45..480440f 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -37,6 +37,8 @@ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name);
+ nw_zone* nw_zone_ref(nw_zone* zone);
+ nw_zone* nw_zone_unref(nw_zone* zone);
+
++int __nw_zone_drop_port(nw_daemon* daemon, nw_zone* zone, void* data);
++
+ int nw_zone_save(nw_zone* zone);
+
+ const char* nw_zone_name(nw_zone* zone);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,156 @@
+From 9e8af30e5ff0ab717f7d878f8e9c62323ec161a1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 14:28:35 +0000
+Subject: [PATCH 237/304] ports: Create scaffolding for operations struct
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/port-dummy.c | 28 ++++++++++++++++++++++++++++
+ src/networkd/port-dummy.h | 28 ++++++++++++++++++++++++++++
+ src/networkd/port.c | 11 +++++++++++
+ src/networkd/port.h | 7 +++++++
+ 5 files changed, 76 insertions(+)
+ create mode 100644 src/networkd/port-dummy.c
+ create mode 100644 src/networkd/port-dummy.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 22d90d4..15e9fa0 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -332,6 +332,8 @@ dist_networkd_SOURCES = \
+ src/networkd/port.h \
+ src/networkd/port-bus.c \
+ src/networkd/port-bus.h \
++ src/networkd/port-dummy.c \
++ src/networkd/port-dummy.h \
+ src/networkd/stats-collector.c \
+ src/networkd/stats-collector.h \
+ src/networkd/string.h \
+diff --git a/src/networkd/port-dummy.c b/src/networkd/port-dummy.c
+new file mode 100644
+index 0000000..cc4b649
+--- /dev/null
++++ b/src/networkd/port-dummy.c
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include "port.h"
++#include "port-dummy.h"
++
++nw_port_ops_t nw_port_ops_dummy = {
++ // There is no special configuration
++ .config_read = NULL,
++ .config_write = NULL,
++};
+diff --git a/src/networkd/port-dummy.h b/src/networkd/port-dummy.h
+new file mode 100644
+index 0000000..0a29c84
+--- /dev/null
++++ b/src/networkd/port-dummy.h
+@@ -0,0 +1,28 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_DUMMY_H
++#define NETWORKD_PORT_DUMMY_H
++
++#include "port.h"
++
++extern nw_port_ops_t nw_port_ops_dummy;
++
++#endif /* NETWORKD_PORT_DUMMY_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 23ffd0b..6ba4875 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -30,6 +30,7 @@
+ #include "link.h"
+ #include "logging.h"
+ #include "port.h"
++#include "port-dummy.h"
+ #include "stats-collector.h"
+ #include "string.h"
+
+@@ -49,6 +50,9 @@ struct nw_port {
+ // Common attributes
+ nw_address_t address;
+
++ // Type Operations
++ nw_port_ops_t ops;
++
+ // VLAN settings
+ struct nw_port_vlan {
+ nw_port* parent;
+@@ -252,6 +256,13 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
+ // Store the type
+ p->type = type;
+
++ // Set operations
++ switch (p->type) {
++ case NW_PORT_DUMMY:
++ p->ops = nw_port_ops_dummy;
++ break;
++ }
++
+ // Store the name
+ r = nw_string_set(p->name, name);
+ if (r)
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 032d82c..76df7a7 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -37,8 +37,15 @@ typedef enum nw_port_type {
+ typedef struct nw_port nw_port;
+
+ #include "address.h"
++#include "config.h"
+ #include "daemon.h"
+
++typedef struct nw_port_ops {
++ // Configuration
++ int (*config_read)(nw_port* port, nw_config* config);
++ int (*config_write)(nw_port* port, nw_config* config);
++} nw_port_ops_t;
++
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ nw_port_type_t type, const char* name);
+ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,591 @@
+From ff88697584a58e86b3abdad0b5017fda382f50e9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 14:54:58 +0000
+Subject: [PATCH 238/304] ports: Move VLAN stuff into its own file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/port-vlan.c | 150 +++++++++++++++++++++++++++++
+ src/networkd/port-vlan.h | 36 +++++++
+ src/networkd/port.c | 203 ++++++---------------------------------
+ src/networkd/port.h | 51 ++++++++--
+ src/networkd/ports.h | 1 +
+ 6 files changed, 261 insertions(+), 182 deletions(-)
+ create mode 100644 src/networkd/port-vlan.c
+ create mode 100644 src/networkd/port-vlan.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 15e9fa0..aafa59c 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -334,6 +334,8 @@ dist_networkd_SOURCES = \
+ src/networkd/port-bus.h \
+ src/networkd/port-dummy.c \
+ src/networkd/port-dummy.h \
++ src/networkd/port-vlan.c \
++ src/networkd/port-vlan.h \
+ src/networkd/stats-collector.c \
+ src/networkd/stats-collector.h \
+ src/networkd/string.h \
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+new file mode 100644
+index 0000000..c9581f6
+--- /dev/null
++++ b/src/networkd/port-vlan.c
+@@ -0,0 +1,150 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include "config.h"
++#include "daemon.h"
++#include "logging.h"
++#include "port.h"
++#include "port-vlan.h"
++#include "string.h"
++
++static int nw_port_vlan_config_read(nw_port* port) {
++ int r;
++
++ // VLAN ID
++ int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
++ if (id) {
++ r = nw_port_set_vlan_id(port, id);
++ if (r)
++ return r;
++ }
++
++ // Parent Port
++ const char* parent = nw_config_get(port->config, "VLAN_PARENT");
++ if (parent) {
++ r = nw_string_set(port->vlan.__parent_name, parent);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
++static int nw_port_vlan_config_write(nw_port* port) {
++ int r;
++
++ // VLAN ID
++ r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
++ if (r)
++ return r;
++
++ // Parent Port
++ r = nw_config_set(port->config, "VLAN_PARENT",
++ (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
++ if (r)
++ return r;
++
++ return 0;
++}
++
++nw_port_ops_t nw_port_ops_vlan = {
++ // Configuration
++ .config_read = nw_port_vlan_config_read,
++ .config_write = nw_port_vlan_config_write,
++};
++
++/*
++ VLAN
++*/
++int nw_port_get_vlan_id(nw_port* port) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ return port->vlan.id;
++}
++
++int nw_port_set_vlan_id(nw_port* port, int id) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ // Check if the VLAN ID is within range
++ if (id < NW_VLAN_ID_MIN || id > NW_VLAN_ID_MAX)
++ return -EINVAL;
++
++ // Store the ID
++ port->vlan.id = id;
++
++ DEBUG("Port %s: Set VLAN ID to %d\n", port->name, port->vlan.id);
++
++ return 0;
++}
++
++nw_port* nw_port_get_vlan_parent(nw_port* port) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return NULL;
++
++ // Try to find a reference to the parent if none exists
++ if (!port->vlan.parent && *port->vlan.__parent_name)
++ port->vlan.parent = nw_daemon_get_port_by_name(port->daemon, port->vlan.__parent_name);
++
++ if (port->vlan.parent)
++ return nw_port_ref(port->vlan.parent);
++
++ // No port found
++ return NULL;
++}
++
++int nw_port_set_vlan_parent(nw_port* port, nw_port* parent) {
++ int r;
++
++ // Check type
++ r = nw_port_check_type(port, NW_PORT_VLAN);
++ if (r < 0)
++ return r;
++
++ // Reset the former parent name
++ nw_string_empty(port->vlan.__parent_name);
++
++ // Dereference the former parent
++ if (port->vlan.parent) {
++ nw_port_unref(port->vlan.parent);
++ port->vlan.parent = NULL;
++ }
++
++ // Store the new parent
++ if (parent)
++ port->vlan.parent = nw_port_ref(parent);
++
++ DEBUG("Port %s: Set VLAN parent to %s\n", port->name, nw_port_name(port->vlan.parent));
++
++ return 0;
++}
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+new file mode 100644
+index 0000000..2bacb24
+--- /dev/null
++++ b/src/networkd/port-vlan.h
+@@ -0,0 +1,36 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_VLAN_H
++#define NETWORKD_PORT_VLAN_H
++
++#include "port.h"
++
++extern nw_port_ops_t nw_port_ops_vlan;
++
++// ID
++int nw_port_get_vlan_id(nw_port* port);
++int nw_port_set_vlan_id(nw_port* port, int id);
++
++// Parent Port
++nw_port* nw_port_get_vlan_parent(nw_port* port);
++int nw_port_set_vlan_parent(nw_port* port, nw_port* parent);
++
++#endif /* NETWORKD_PORT_VLAN_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 6ba4875..b3b7d66 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -31,39 +31,10 @@
+ #include "logging.h"
+ #include "port.h"
+ #include "port-dummy.h"
++#include "port-vlan.h"
+ #include "stats-collector.h"
+ #include "string.h"
+
+-struct nw_port {
+- nw_daemon* daemon;
+- int nrefs;
+-
+- // Link
+- nw_link* link;
+-
+- nw_port_type_t type;
+- char name[IF_NAMESIZE];
+-
+- // Configuration
+- nw_config *config;
+-
+- // Common attributes
+- nw_address_t address;
+-
+- // Type Operations
+- nw_port_ops_t ops;
+-
+- // VLAN settings
+- struct nw_port_vlan {
+- nw_port* parent;
+- int id;
+-
+- // If the parent has not been read from the configuration we will
+- // save the name and try to find it later.
+- char __parent_name[IF_NAMESIZE];
+- } vlan;
+-};
+-
+ static const struct nw_port_type_map {
+ nw_port_type_t type;
+ const char* name;
+@@ -143,28 +114,6 @@ static int nw_port_setup_common(nw_port* port) {
+ return 0;
+ }
+
+-static int nw_port_setup_vlan(nw_port* port) {
+- int r;
+-
+- // VLAN ID
+- int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
+- if (id) {
+- r = nw_port_set_vlan_id(port, id);
+- if (r)
+- return r;
+- }
+-
+- // Parent Port
+- const char* parent = nw_config_get(port->config, "VLAN_PARENT");
+- if (parent) {
+- r = nw_string_set(port->vlan.__parent_name, parent);
+- if (r)
+- return r;
+- }
+-
+- return 0;
+-}
+-
+ static int nw_port_set_link(nw_port* port, nw_link* link) {
+ // Do nothing if the same link is being re-assigned
+ if (port->link == link)
+@@ -219,17 +168,10 @@ static int nw_port_setup(nw_port* port) {
+ goto ERROR;
+
+ // Call any custom initialization
+- switch (port->type) {
+- case NW_PORT_VLAN:
+- r = nw_port_setup_vlan(port);
+- if (r)
+- goto ERROR;
+- break;
+-
+- // These do not need any special initialization
+- case NW_PORT_DUMMY:
+- case NW_PORT_UNKNOWN:
+- break;
++ if (port->ops.config_read) {
++ r = port->ops.config_read(port);
++ if (r)
++ goto ERROR;
+ }
+
+ ERROR:
+@@ -261,6 +203,10 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
+ case NW_PORT_DUMMY:
+ p->ops = nw_port_ops_dummy;
+ break;
++
++ case NW_PORT_VLAN:
++ p->ops = nw_port_ops_vlan;
++ break;
+ }
+
+ // Store the name
+@@ -379,40 +325,17 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ return 0;
+ }
+
+-static int nw_port_save_vlan(nw_port* port) {
+- int r;
+-
+- // VLAN ID
+- r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
+- if (r)
+- return r;
+-
+- // Parent Port
+- r = nw_config_set(port->config, "VLAN_PARENT",
+- (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
+- if (r)
+- return r;
+-
+- return 0;
+-}
+-
+ int nw_port_save(nw_port* port) {
+ int r;
+
+- switch (port->type) {
+- // VLAN
+- case NW_PORT_VLAN:
+- r = nw_port_save_vlan(port);
+- if (r)
+- goto ERROR;
+- break;
+-
+- // These types do not have any special settings
+- case NW_PORT_DUMMY:
+- case NW_PORT_UNKNOWN:
+- break;
++ // Call the custom handler
++ if (port->ops.config_write) {
++ r = port->ops.config_write(port);
++ if (r)
++ goto ERROR;
+ }
+
++ // Write the configuration
+ r = nw_config_write(port->config);
+ if (r)
+ return r;
+@@ -481,7 +404,20 @@ static int nw_port_is_disabled(nw_port* port) {
+ }
+
+ static int nw_port_create_link(nw_port* port) {
+- return 0; // XXX TODO
++ int r;
++
++ // Fail if the function isn't set
++ if (!port->ops.create_link) {
++ errno = ENOTSUP;
++ return -errno;
++ }
++
++ // Create the link
++ r = port->ops.create_link(port);
++ if (r)
++ ERROR("Could not create link %s: %m\n", port->name);
++
++ return r;
+ }
+
+ int nw_port_reconfigure(nw_port* port) {
+@@ -545,89 +481,10 @@ int nw_port_update_stats(nw_port* port) {
+ return 0;
+ }
+
+-static int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
++int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
+ if (port->type == type)
+ return 0;
+
+ errno = ENOTSUP;
+ return -errno;
+ }
+-
+-/*
+- VLAN
+-*/
+-int nw_port_get_vlan_id(nw_port* port) {
+- int r;
+-
+- // Check type
+- r = nw_port_check_type(port, NW_PORT_VLAN);
+- if (r < 0)
+- return r;
+-
+- return port->vlan.id;
+-}
+-
+-int nw_port_set_vlan_id(nw_port* port, int id) {
+- int r;
+-
+- // Check type
+- r = nw_port_check_type(port, NW_PORT_VLAN);
+- if (r < 0)
+- return r;
+-
+- // Check if the VLAN ID is within range
+- if (id < NW_VLAN_ID_MIN || id > NW_VLAN_ID_MAX)
+- return -EINVAL;
+-
+- // Store the ID
+- port->vlan.id = id;
+-
+- DEBUG("Port %s: Set VLAN ID to %d\n", port->name, port->vlan.id);
+-
+- return 0;
+-}
+-
+-nw_port* nw_port_get_vlan_parent(nw_port* port) {
+- int r;
+-
+- // Check type
+- r = nw_port_check_type(port, NW_PORT_VLAN);
+- if (r < 0)
+- return NULL;
+-
+- // Try to find a reference to the parent if none exists
+- if (!port->vlan.parent && *port->vlan.__parent_name)
+- port->vlan.parent = nw_daemon_get_port_by_name(port->daemon, port->vlan.__parent_name);
+-
+- if (port->vlan.parent)
+- return nw_port_ref(port->vlan.parent);
+-
+- // No port found
+- return NULL;
+-}
+-
+-int nw_port_set_vlan_parent(nw_port* port, nw_port* parent) {
+- int r;
+-
+- // Check type
+- r = nw_port_check_type(port, NW_PORT_VLAN);
+- if (r < 0)
+- return r;
+-
+- // Reset the former parent name
+- nw_string_empty(port->vlan.__parent_name);
+-
+- // Dereference the former parent
+- if (port->vlan.parent) {
+- nw_port_unref(port->vlan.parent);
+- port->vlan.parent = NULL;
+- }
+-
+- // Store the new parent
+- if (parent)
+- port->vlan.parent = nw_port_ref(parent);
+-
+- DEBUG("Port %s: Set VLAN parent to %s\n", port->name, nw_port_name(port->vlan.parent));
+-
+- return 0;
+-}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 76df7a7..5253d69 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -21,6 +21,10 @@
+ #ifndef NETWORKD_PORT_H
+ #define NETWORKD_PORT_H
+
++#ifndef IF_NAMESIZE
++#define IF_NAMESIZE 16
++#endif
++
+ #define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+
+ typedef enum nw_port_type {
+@@ -42,10 +46,44 @@ typedef struct nw_port nw_port;
+
+ typedef struct nw_port_ops {
+ // Configuration
+- int (*config_read)(nw_port* port, nw_config* config);
+- int (*config_write)(nw_port* port, nw_config* config);
++ int (*config_read)(nw_port* port);
++ int (*config_write)(nw_port* port);
++
++ // Link
++ int (*create_link)(nw_port* port);
++ int (*destroy_link)(nw_port* port);
+ } nw_port_ops_t;
+
++struct nw_port {
++ nw_daemon* daemon;
++ int nrefs;
++
++ // Link
++ nw_link* link;
++
++ nw_port_type_t type;
++ char name[IF_NAMESIZE];
++
++ // Configuration
++ nw_config *config;
++
++ // Common attributes
++ nw_address_t address;
++
++ // Type Operations
++ nw_port_ops_t ops;
++
++ // VLAN settings
++ struct nw_port_vlan {
++ nw_port* parent;
++ int id;
++
++ // If the parent has not been read from the configuration we will
++ // save the name and try to find it later.
++ char __parent_name[IF_NAMESIZE];
++ } vlan;
++};
++
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ nw_port_type_t type, const char* name);
+ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+@@ -72,16 +110,11 @@ int nw_port_reconfigure(nw_port* port);
+
+ int nw_port_has_carrier(nw_port* port);
+
++int nw_port_check_type(nw_port* port, const nw_port_type_t type);
++
+ // Stats
+ const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port);
+ int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data);
+ int nw_port_update_stats(nw_port* port);
+
+-// VLAN
+-int nw_port_get_vlan_id(nw_port* port);
+-int nw_port_set_vlan_id(nw_port* port, int id);
+-
+-nw_port* nw_port_get_vlan_parent(nw_port* port);
+-int nw_port_set_vlan_parent(nw_port* port, nw_port* parent);
+-
+ #endif /* NETWORKD_PORT_H */
+diff --git a/src/networkd/ports.h b/src/networkd/ports.h
+index 68ae532..4e41f11 100644
+--- a/src/networkd/ports.h
++++ b/src/networkd/ports.h
+@@ -26,6 +26,7 @@ typedef struct nw_ports nw_ports;
+ typedef int (*nw_ports_walk_callback)(nw_daemon* daemon, nw_port* port, void* data);
+
+ #include "daemon.h"
++#include "port.h"
+
+ int nw_ports_create(nw_ports** ports, nw_daemon* daemon);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,294 @@
+From 240e331b2043ae254c6468ed3ce2ec6d8caf98db Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 16:26:44 +0000
+Subject: [PATCH 239/304] ports: Implement creating links from ports
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-dummy.c | 5 +-
+ src/networkd/port-vlan.c | 20 +++++
+ src/networkd/port.c | 149 +++++++++++++++++++++++++++++++++++---
+ src/networkd/port.h | 10 ++-
+ 4 files changed, 170 insertions(+), 14 deletions(-)
+
+diff --git a/src/networkd/port-dummy.c b/src/networkd/port-dummy.c
+index cc4b649..09367d8 100644
+--- a/src/networkd/port-dummy.c
++++ b/src/networkd/port-dummy.c
+@@ -18,11 +18,8 @@
+ # #
+ #############################################################################*/
+
+-#include "port.h"
+ #include "port-dummy.h"
+
+ nw_port_ops_t nw_port_ops_dummy = {
+- // There is no special configuration
+- .config_read = NULL,
+- .config_write = NULL,
++ .kind = "dummy",
+ };
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index c9581f6..edf91d9 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -18,6 +18,8 @@
+ # #
+ #############################################################################*/
+
++#include <systemd/sd-netlink.h>
++
+ #include "config.h"
+ #include "daemon.h"
+ #include "logging.h"
+@@ -64,10 +66,28 @@ static int nw_port_vlan_config_write(nw_port* port) {
+ return 0;
+ }
+
++static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
++ int r;
++
++ // Set VLAN ID
++ r = sd_netlink_message_append_u16(m, IFLA_VLAN_ID, port->vlan.id);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
++
+ nw_port_ops_t nw_port_ops_vlan = {
++ .kind = "vlan",
++
+ // Configuration
+ .config_read = nw_port_vlan_config_read,
+ .config_write = nw_port_vlan_config_write,
++
++ .get_parent_port = nw_port_get_vlan_parent,
++
++ // Link
++ .create_link = nw_port_vlan_create_link,
+ };
+
+ /*
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index b3b7d66..a7cbab6 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -72,7 +72,7 @@ static int nw_port_setup_address(nw_port* port) {
+ // Read ADDRESS from configuration
+ const char* s = nw_config_get(port->config, "ADDRESS");
+ if (!s) {
+- ERROR("Port %s: Address isn't set\n", port->name);
++ ERROR("Port %s: Address is not set\n", port->name);
+ goto ERROR;
+ }
+
+@@ -271,6 +271,16 @@ nw_port* nw_port_unref(nw_port* port) {
+ return NULL;
+ }
+
++/*
++ This is a helper function for when we pass a reference to the event loop
++ it will have to dereference the port instance later.
++*/
++static void __nw_port_unref(void* data) {
++ nw_port* port = (nw_port*)data;
++
++ nw_port_unref(port);
++}
++
+ int nw_port_destroy(nw_port* port) {
+ int r;
+
+@@ -395,6 +405,17 @@ int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data) {
+ return 0;
+ }
+
++static nw_link* nw_port_get_link(nw_port* port) {
++ // Fetch the link if not set
++ if (!port->link)
++ port->link = nw_daemon_get_link_by_name(port->daemon, port->name);
++
++ if (!port->link)
++ return NULL;
++
++ return nw_link_ref(port->link);
++}
++
+ const nw_address_t* nw_port_get_address(nw_port* port) {
+ return &port->address;
+ }
+@@ -403,19 +424,129 @@ static int nw_port_is_disabled(nw_port* port) {
+ return nw_config_get_bool(port->config, "DISABLED");
+ }
+
++static nw_link* nw_port_get_parent_link(nw_port* port) {
++ nw_port* parent = NULL;
++ nw_link* link = NULL;
++
++ // Do nothing if not implemented
++ if (!port->ops.get_parent_port)
++ goto ERROR;
++
++ // Fetch the parent
++ parent = port->ops.get_parent_port(port);
++ if (!parent)
++ goto ERROR;
++
++ // Fetch the link
++ link = nw_port_get_link(parent);
++
++ERROR:
++ if (parent)
++ nw_port_unref(parent);
++
++ return link;
++}
++
++static int __nw_port_create_link(sd_netlink* rtnl, sd_netlink_message* m, void* data) {
++ nw_port* port = (nw_port*)data;
++ int r;
++
++ // Check if the operation was successful
++ r = sd_netlink_message_get_errno(m);
++ if (r < 0) {
++ ERROR("Could not create port %s: %s\n", port->name, strerror(-r));
++ // XXX We should extract the error message
++
++ return 0;
++ }
++
++ DEBUG("Successfully created %s\n", port->name);
++
++ return 0;
++}
++
+ static int nw_port_create_link(nw_port* port) {
++ sd_netlink_message* m = NULL;
++ nw_link* link = NULL;
+ int r;
+
+- // Fail if the function isn't set
+- if (!port->ops.create_link) {
+- errno = ENOTSUP;
+- return -errno;
++ sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
++
++ // Create a new link
++ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
++ if (r < 0) {
++ ERROR("Could not create netlink message: %m\n");
++ goto ERROR;
+ }
+
+- // Create the link
+- r = port->ops.create_link(port);
+- if (r)
+- ERROR("Could not create link %s: %m\n", port->name);
++ // Set the name
++ r = sd_netlink_message_append_string(m, IFLA_IFNAME, port->name);
++ if (r < 0) {
++ ERROR("Could not set port name: %s\n", strerror(-r));
++ goto ERROR;
++ }
++
++ // XXX Set common things like MAC address, etc.
++
++ // Fetch the parent link
++ link = nw_port_get_parent_link(port);
++ if (link) {
++ r = sd_netlink_message_append_u32(m, IFLA_LINK, nw_link_ifindex(link));
++ if (r < 0)
++ goto ERROR;
++ }
++
++ // Open an IFLA_LINKINFO container
++ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
++ if (r < 0)
++ goto ERROR;
++
++ // Run the custom setup
++ if (port->ops.create_link) {
++ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, port->ops.kind);
++ if (r < 0) {
++ ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
++ goto ERROR;
++ }
++
++ r = port->ops.create_link(port, m);
++ if (r) {
++ ERROR("Could not create port %s: %m\n", port->name);
++ goto ERROR;
++ }
++
++ // Close the container
++ r = sd_netlink_message_close_container(m);
++ if (r < 0)
++ goto ERROR;
++
++ // Just set IFLA_INFO_KIND if there is no custom function
++ } else {
++ r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, port->ops.kind);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ // Close the container
++ r = sd_netlink_message_close_container(m);
++ if (r < 0)
++ goto ERROR;
++
++ // Send the message
++ r = sd_netlink_call_async(rtnl, NULL, m, __nw_port_create_link,
++ __nw_port_unref, nw_port_ref(port), -1, NULL);
++ if (r < 0) {
++ ERROR("Could not send netlink message: %s\n", strerror(-r));
++ goto ERROR;
++ }
++
++ r = 0;
++
++ERROR:
++ if (m)
++ sd_netlink_message_unref(m);
++ if (link)
++ nw_link_unref(link);
+
+ return r;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 5253d69..d44ecb6 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_PORT_H
+ #define NETWORKD_PORT_H
+
++#include <systemd/sd-netlink.h>
++
+ #ifndef IF_NAMESIZE
+ #define IF_NAMESIZE 16
+ #endif
+@@ -45,12 +47,18 @@ typedef struct nw_port nw_port;
+ #include "daemon.h"
+
+ typedef struct nw_port_ops {
++ // IFLA_INFO_KIND/IFLA_INFO_DATA
++ const char* kind;
++
+ // Configuration
+ int (*config_read)(nw_port* port);
+ int (*config_write)(nw_port* port);
+
++ // Get Parent Port
++ nw_port* (*get_parent_port)(nw_port* port);
++
+ // Link
+- int (*create_link)(nw_port* port);
++ int (*create_link)(nw_port* port, sd_netlink_message* message);
+ int (*destroy_link)(nw_port* port);
+ } nw_port_ops_t;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,219 @@
+From 89f8f6af09b8ebf897ca6f91a3abb3b3f40fcaad Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 16:42:11 +0000
+Subject: [PATCH 240/304] ports: Rename the ops struct as we will need to store
+ more things than function pointers
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-dummy.c | 2 +-
+ src/networkd/port-dummy.h | 2 +-
+ src/networkd/port-vlan.c | 17 ++++++++++-------
+ src/networkd/port-vlan.h | 2 +-
+ src/networkd/port.c | 24 ++++++++++++------------
+ src/networkd/port.h | 27 ++++++++++++++++-----------
+ 6 files changed, 41 insertions(+), 33 deletions(-)
+
+diff --git a/src/networkd/port-dummy.c b/src/networkd/port-dummy.c
+index 09367d8..d6842d0 100644
+--- a/src/networkd/port-dummy.c
++++ b/src/networkd/port-dummy.c
+@@ -20,6 +20,6 @@
+
+ #include "port-dummy.h"
+
+-nw_port_ops_t nw_port_ops_dummy = {
++nw_port_info_t nw_port_info_dummy = {
+ .kind = "dummy",
+ };
+diff --git a/src/networkd/port-dummy.h b/src/networkd/port-dummy.h
+index 0a29c84..b07cf39 100644
+--- a/src/networkd/port-dummy.h
++++ b/src/networkd/port-dummy.h
+@@ -23,6 +23,6 @@
+
+ #include "port.h"
+
+-extern nw_port_ops_t nw_port_ops_dummy;
++extern nw_port_info_t nw_port_info_dummy;
+
+ #endif /* NETWORKD_PORT_DUMMY_H */
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index edf91d9..f1cd9fb 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -77,17 +77,20 @@ static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
+ return 0;
+ }
+
+-nw_port_ops_t nw_port_ops_vlan = {
++nw_port_info_t nw_port_info_vlan = {
+ .kind = "vlan",
+
+- // Configuration
+- .config_read = nw_port_vlan_config_read,
+- .config_write = nw_port_vlan_config_write,
++ // Operations
++ .ops = {
++ // Configuration
++ .config_read = nw_port_vlan_config_read,
++ .config_write = nw_port_vlan_config_write,
+
+- .get_parent_port = nw_port_get_vlan_parent,
++ .get_parent_port = nw_port_get_vlan_parent,
+
+- // Link
+- .create_link = nw_port_vlan_create_link,
++ // Link
++ .create_link = nw_port_vlan_create_link,
++ },
+ };
+
+ /*
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index 2bacb24..c666dfb 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -23,7 +23,7 @@
+
+ #include "port.h"
+
+-extern nw_port_ops_t nw_port_ops_vlan;
++extern nw_port_info_t nw_port_info_vlan;
+
+ // ID
+ int nw_port_get_vlan_id(nw_port* port);
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index a7cbab6..061e13c 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -168,8 +168,8 @@ static int nw_port_setup(nw_port* port) {
+ goto ERROR;
+
+ // Call any custom initialization
+- if (port->ops.config_read) {
+- r = port->ops.config_read(port);
++ if (NW_PORT_OPS(port)->config_read) {
++ r = NW_PORT_OPS(port)->config_read(port);
+ if (r)
+ goto ERROR;
+ }
+@@ -201,11 +201,11 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
+ // Set operations
+ switch (p->type) {
+ case NW_PORT_DUMMY:
+- p->ops = nw_port_ops_dummy;
++ p->info = &nw_port_info_dummy;
+ break;
+
+ case NW_PORT_VLAN:
+- p->ops = nw_port_ops_vlan;
++ p->info = &nw_port_info_vlan;
+ break;
+ }
+
+@@ -339,8 +339,8 @@ int nw_port_save(nw_port* port) {
+ int r;
+
+ // Call the custom handler
+- if (port->ops.config_write) {
+- r = port->ops.config_write(port);
++ if (NW_PORT_OPS(port)->config_write) {
++ r = NW_PORT_OPS(port)->config_write(port);
+ if (r)
+ goto ERROR;
+ }
+@@ -429,11 +429,11 @@ static nw_link* nw_port_get_parent_link(nw_port* port) {
+ nw_link* link = NULL;
+
+ // Do nothing if not implemented
+- if (!port->ops.get_parent_port)
++ if (!NW_PORT_OPS(port)->get_parent_port)
+ goto ERROR;
+
+ // Fetch the parent
+- parent = port->ops.get_parent_port(port);
++ parent = NW_PORT_OPS(port)->get_parent_port(port);
+ if (!parent)
+ goto ERROR;
+
+@@ -502,14 +502,14 @@ static int nw_port_create_link(nw_port* port) {
+ goto ERROR;
+
+ // Run the custom setup
+- if (port->ops.create_link) {
+- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, port->ops.kind);
++ if (NW_PORT_OPS(port)->create_link) {
++ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_INFO(port)->kind);
+ if (r < 0) {
+ ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+- r = port->ops.create_link(port, m);
++ r = NW_PORT_OPS(port)->create_link(port, m);
+ if (r) {
+ ERROR("Could not create port %s: %m\n", port->name);
+ goto ERROR;
+@@ -522,7 +522,7 @@ static int nw_port_create_link(nw_port* port) {
+
+ // Just set IFLA_INFO_KIND if there is no custom function
+ } else {
+- r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, port->ops.kind);
++ r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_INFO(port)->kind);
+ if (r < 0)
+ goto ERROR;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index d44ecb6..b73c5fe 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -46,21 +46,26 @@ typedef struct nw_port nw_port;
+ #include "config.h"
+ #include "daemon.h"
+
+-typedef struct nw_port_ops {
++typedef struct nw_port_info {
+ // IFLA_INFO_KIND/IFLA_INFO_DATA
+ const char* kind;
+
+- // Configuration
+- int (*config_read)(nw_port* port);
+- int (*config_write)(nw_port* port);
++ struct nw_port_ops {
++ // Configuration
++ int (*config_read)(nw_port* port);
++ int (*config_write)(nw_port* port);
+
+- // Get Parent Port
+- nw_port* (*get_parent_port)(nw_port* port);
++ // Get Parent Port
++ nw_port* (*get_parent_port)(nw_port* port);
+
+- // Link
+- int (*create_link)(nw_port* port, sd_netlink_message* message);
+- int (*destroy_link)(nw_port* port);
+-} nw_port_ops_t;
++ // Link
++ int (*create_link)(nw_port* port, sd_netlink_message* message);
++ int (*destroy_link)(nw_port* port);
++ } ops;
++} nw_port_info_t;
++
++#define NW_PORT_INFO(port) (port->info)
++#define NW_PORT_OPS(port) (&NW_PORT_INFO(port)->ops)
+
+ struct nw_port {
+ nw_daemon* daemon;
+@@ -79,7 +84,7 @@ struct nw_port {
+ nw_address_t address;
+
+ // Type Operations
+- nw_port_ops_t ops;
++ nw_port_info_t* info;
+
+ // VLAN settings
+ struct nw_port_vlan {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,256 @@
+From 4c99b8ed5da20ba81845486bbcb9a841b7cd2119 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 16:50:35 +0000
+Subject: [PATCH 241/304] ports: Implement listing ports over DBus
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkctl/main.c | 2 +
+ src/networkctl/port.c | 97 +++++++++++++++++++++++++++++++++++++++
+ src/networkctl/port.h | 26 +++++++++++
+ src/networkd/daemon-bus.c | 51 ++++++++++++++++++++
+ 5 files changed, 178 insertions(+)
+ create mode 100644 src/networkctl/port.c
+ create mode 100644 src/networkctl/port.h
+
+diff --git a/Makefile.am b/Makefile.am
+index aafa59c..273302b 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -392,6 +392,8 @@ dist_networkctl_SOURCES = \
+ src/networkctl/command.c \
+ src/networkctl/command.h \
+ src/networkctl/main.c \
++ src/networkctl/port.c \
++ src/networkctl/port.h \
+ src/networkctl/zone.c \
+ src/networkctl/zone.h
+
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index a08256c..73908f8 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -26,6 +26,7 @@
+ #include <systemd/sd-bus.h>
+
+ #include "command.h"
++#include "port.h"
+ #include "zone.h"
+
+ static int networkctl_status(sd_bus* bus, int argc, char* argv[]) {
+@@ -35,6 +36,7 @@ static int networkctl_status(sd_bus* bus, int argc, char* argv[]) {
+
+ static int networkctl_main(sd_bus* bus, int argc, char* argv[]) {
+ static const struct command commands[] = {
++ { "port", 0, networkctl_port },
+ { "status", 0, networkctl_status },
+ { "zone", 0, networkctl_zone },
+ { NULL },
+diff --git a/src/networkctl/port.c b/src/networkctl/port.c
+new file mode 100644
+index 0000000..689e2f1
+--- /dev/null
++++ b/src/networkctl/port.c
+@@ -0,0 +1,97 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <systemd/sd-bus.h>
++
++#include "command.h"
++#include "port.h"
++
++typedef int (*networkctl_port_walk_callback)
++ (sd_bus* bus, const char* path, const char* name, void* data);
++
++static int networkctl_port_walk(sd_bus* bus,
++ networkctl_port_walk_callback callback, void* data) {
++ sd_bus_message* reply = NULL;
++ sd_bus_error error = SD_BUS_ERROR_NULL;
++ int r;
++
++ // Call Listports
++ r = sd_bus_call_method(bus, "org.ipfire.network1", "/org/ipfire/network1",
++ "org.ipfire.network1", "ListPorts", &error, &reply, "");
++ if (r < 0) {
++ fprintf(stderr, "Listports call failed: %m\n");
++ goto ERROR;
++ }
++
++ const char* name = NULL;
++ const char* path = NULL;
++
++ // Open the container
++ r = sd_bus_message_enter_container(reply, 'a', "(so)");
++ if (r < 0) {
++ fprintf(stderr, "Could not open container: %m\n");
++ goto ERROR;
++ }
++
++ // Iterate over all ports
++ for (;;) {
++ r = sd_bus_message_read(reply, "(so)", &name, &path);
++ if (r < 0)
++ goto ERROR;
++
++ // Break if we reached the end of the container
++ if (r == 0)
++ break;
++
++ // Call the callback
++ r = callback(bus, path, name, data);
++ if (r)
++ goto ERROR;
++ }
++
++ // Close the container
++ sd_bus_message_exit_container(reply);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++ sd_bus_error_free(&error);
++
++ return r;
++}
++
++static int __networkctl_port_list(sd_bus* bus, const char* path, const char* name, void* data) {
++ printf("%s\n", name);
++
++ return 0;
++}
++
++static int networkctl_port_list(sd_bus* bus, int argc, char* argv[]) {
++ return networkctl_port_walk(bus, __networkctl_port_list, NULL);
++}
++
++int networkctl_port(sd_bus* bus, int argc, char* argv[]) {
++ static const struct command commands[] = {
++ { "list", 0, networkctl_port_list },
++ { NULL },
++ };
++
++ return command_dispatch(bus, commands, argc, argv);
++}
+diff --git a/src/networkctl/port.h b/src/networkctl/port.h
+new file mode 100644
+index 0000000..2326ce6
+--- /dev/null
++++ b/src/networkctl/port.h
+@@ -0,0 +1,26 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKCTL_PORT_H
++#define NETWORKCTL_PORT_H
++
++int networkctl_port(sd_bus* bus, int argc, char* argv[]);
++
++#endif /* NETWORKCTL_PORT_H */
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+index 7620eed..4de8c0f 100644
+--- a/src/networkd/daemon-bus.c
++++ b/src/networkd/daemon-bus.c
+@@ -39,6 +39,55 @@ static int nw_daemon_bus_reload(sd_bus_message* m, void* data, sd_bus_error* err
+ return sd_bus_reply_method_return(m, NULL);
+ }
+
++static int __nw_daemon_bus_list_ports(nw_daemon* daemon, nw_port* port, void* data) {
++ sd_bus_message* reply = (sd_bus_message*)data;
++ int r;
++
++ // Fetch port name
++ const char* name = nw_port_name(port);
++
++ // Fetch bus path
++ char* path = nw_port_bus_path(port);
++
++ r = sd_bus_message_append(reply, "(so)", name, path);
++
++ free(path);
++
++ return r;
++}
++
++static int nw_daemon_bus_list_ports(sd_bus_message* m, void* data, sd_bus_error* error) {
++ nw_daemon* daemon = (nw_daemon*)data;
++ sd_bus_message* reply = NULL;
++ int r;
++
++ // Form a reply message
++ r = sd_bus_message_new_method_return(m, &reply);
++ if (r < 0)
++ goto ERROR;
++
++ r = sd_bus_message_open_container(reply, 'a', "(so)");
++ if (r < 0)
++ goto ERROR;
++
++ r = nw_daemon_ports_walk(daemon, __nw_daemon_bus_list_ports, reply);
++ if (r < 0)
++ goto ERROR;
++
++ r = sd_bus_message_close_container(reply);
++ if (r < 0)
++ goto ERROR;
++
++ // Send the reply
++ r = sd_bus_send(NULL, reply, NULL);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++
++ return r;
++}
++
+ static int __nw_daemon_bus_list_zones(nw_daemon* daemon, nw_zone* zone, void* data) {
+ sd_bus_message* reply = (sd_bus_message*)data;
+ int r;
+@@ -90,6 +139,8 @@ ERROR:
+
+ static const sd_bus_vtable daemon_vtable[] = {
+ SD_BUS_VTABLE_START(0),
++ SD_BUS_METHOD_WITH_ARGS("ListPorts", SD_BUS_NO_ARGS, SD_BUS_RESULT("a(so)", links),
++ nw_daemon_bus_list_ports, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListZones", SD_BUS_NO_ARGS, SD_BUS_RESULT("a(so)", links),
+ nw_daemon_bus_list_zones, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Reload", SD_BUS_NO_ARGS, SD_BUS_NO_RESULT,
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,56 @@
+From 78b41a2319c3c247a0223187edaea4c9ba98e83b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 16:52:59 +0000
+Subject: [PATCH 242/304] daemon: Fix return code handling when listing
+ ports/zones
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon-bus.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/daemon-bus.c b/src/networkd/daemon-bus.c
+index 4de8c0f..f5f7abd 100644
+--- a/src/networkd/daemon-bus.c
++++ b/src/networkd/daemon-bus.c
+@@ -49,9 +49,17 @@ static int __nw_daemon_bus_list_ports(nw_daemon* daemon, nw_port* port, void* da
+ // Fetch bus path
+ char* path = nw_port_bus_path(port);
+
++ // Append the port to the message
+ r = sd_bus_message_append(reply, "(so)", name, path);
++ if (r < 0)
++ goto ERROR;
+
+- free(path);
++ // Success
++ r = 0;
++
++ERROR:
++ if (path)
++ free(path);
+
+ return r;
+ }
+@@ -98,9 +106,17 @@ static int __nw_daemon_bus_list_zones(nw_daemon* daemon, nw_zone* zone, void* da
+ // Fetch bus path
+ char* path = nw_zone_bus_path(zone);
+
++ // Append the zone to the message
+ r = sd_bus_message_append(reply, "(so)", name, path);
++ if (r < 0)
++ goto ERROR;
+
+- free(path);
++ // Success
++ r = 0;
++
++ERROR:
++ if (path)
++ free(path);
+
+ return r;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From 895f7134d55c38f3059e88e128d5670187222e69 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 17:05:06 +0000
+Subject: [PATCH 243/304] ports: Do not expect to come back after creating
+ links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 061e13c..c87ed59 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -566,11 +566,8 @@ int nw_port_reconfigure(nw_port* port) {
+ }
+
+ // If there is no link, we will try to create it
+- if (!port->link) {
+- r = nw_port_create_link(port);
+- if (r)
+- return r;
+- }
++ if (!port->link)
++ return nw_port_create_link(port);
+
+ // XXX TODO
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,34 @@
+From 1574bc0061fa712150316d7bc77954f3264363f8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 17:09:01 +0000
+Subject: [PATCH 244/304] ports: Set the configure MAC address when creating
+ links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index c87ed59..891b831 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -486,7 +486,14 @@ static int nw_port_create_link(nw_port* port) {
+ goto ERROR;
+ }
+
+- // XXX Set common things like MAC address, etc.
++ // XXX Set common things like MTU, etc.
++
++ // Set MAC address
++ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &port->address);
++ if (r < 0) {
++ ERROR("Could not set MAC address: %s\n", strerror(-r));
++ goto ERROR;
++ }
+
+ // Fetch the parent link
+ link = nw_port_get_parent_link(port);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 575d1a3f407c29ab5b388d11ba94424adb86423a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 17:10:34 +0000
+Subject: [PATCH 245/304] ports: Show message when creating ports
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 891b831..dfce5b6 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -472,6 +472,8 @@ static int nw_port_create_link(nw_port* port) {
+
+ sd_netlink* rtnl = nw_daemon_get_rtnl(port->daemon);
+
++ DEBUG("Creating port %s...\n", port->name);
++
+ // Create a new link
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,80 @@
+From 5ddde002bc70617e45632ddc3dfdcf792a2ee745 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 17:12:52 +0000
+Subject: [PATCH 246/304] ports: Constify info struct
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-dummy.c | 2 +-
+ src/networkd/port-dummy.h | 2 +-
+ src/networkd/port-vlan.c | 2 +-
+ src/networkd/port-vlan.h | 2 +-
+ src/networkd/port.h | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/port-dummy.c b/src/networkd/port-dummy.c
+index d6842d0..e71e740 100644
+--- a/src/networkd/port-dummy.c
++++ b/src/networkd/port-dummy.c
+@@ -20,6 +20,6 @@
+
+ #include "port-dummy.h"
+
+-nw_port_info_t nw_port_info_dummy = {
++const nw_port_info_t nw_port_info_dummy = {
+ .kind = "dummy",
+ };
+diff --git a/src/networkd/port-dummy.h b/src/networkd/port-dummy.h
+index b07cf39..b74c991 100644
+--- a/src/networkd/port-dummy.h
++++ b/src/networkd/port-dummy.h
+@@ -23,6 +23,6 @@
+
+ #include "port.h"
+
+-extern nw_port_info_t nw_port_info_dummy;
++extern const nw_port_info_t nw_port_info_dummy;
+
+ #endif /* NETWORKD_PORT_DUMMY_H */
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index f1cd9fb..5e970e9 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -77,7 +77,7 @@ static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
+ return 0;
+ }
+
+-nw_port_info_t nw_port_info_vlan = {
++const nw_port_info_t nw_port_info_vlan = {
+ .kind = "vlan",
+
+ // Operations
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index c666dfb..f02c529 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -23,7 +23,7 @@
+
+ #include "port.h"
+
+-extern nw_port_info_t nw_port_info_vlan;
++extern const nw_port_info_t nw_port_info_vlan;
+
+ // ID
+ int nw_port_get_vlan_id(nw_port* port);
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index b73c5fe..8bc4db8 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -84,7 +84,7 @@ struct nw_port {
+ nw_address_t address;
+
+ // Type Operations
+- nw_port_info_t* info;
++ const nw_port_info_t* info;
+
+ // VLAN settings
+ struct nw_port_vlan {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,50 @@
+From d667fff47beb1fbdf570006b112e64500528a139 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 4 Jun 2023 17:19:19 +0000
+Subject: [PATCH 247/304] ports: Log when we created a random Ethernet address
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index dfce5b6..d5ffe42 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -67,6 +67,7 @@ static void nw_port_free(nw_port* port) {
+ }
+
+ static int nw_port_setup_address(nw_port* port) {
++ char* __address = NULL;
+ int r;
+
+ // Read ADDRESS from configuration
+@@ -100,6 +101,15 @@ ERROR:
+ return r;
+ }
+
++ // Format the generated address
++ __address = nw_address_to_string(&port->address);
++ if (__address) {
++ ERROR("Generated a random Ethernet address for %s: %s\n", port->name, __address);
++
++ // Free the address
++ free(__address);
++ }
++
+ return 0;
+ }
+
+@@ -490,7 +500,7 @@ static int nw_port_create_link(nw_port* port) {
+
+ // XXX Set common things like MTU, etc.
+
+- // Set MAC address
++ // Set Ethernet address
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &port->address);
+ if (r < 0) {
+ ERROR("Could not set MAC address: %s\n", strerror(-r));
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,309 @@
+From 95c5dca23c2fa6326cdc59736f48f20e40ed3561 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 14:51:41 +0000
+Subject: [PATCH 248/304] ports: Add the most basic supports for bonding
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/port-bonding.c | 142 ++++++++++++++++++++++++++++++++++++
+ src/networkd/port-bonding.h | 37 ++++++++++
+ src/networkd/port.c | 7 ++
+ src/networkd/port.h | 10 ++-
+ 5 files changed, 196 insertions(+), 2 deletions(-)
+ create mode 100644 src/networkd/port-bonding.c
+ create mode 100644 src/networkd/port-bonding.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 273302b..ad42f49 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -330,6 +330,8 @@ dist_networkd_SOURCES = \
+ src/networkd/ports.h \
+ src/networkd/port.c \
+ src/networkd/port.h \
++ src/networkd/port-bonding.c \
++ src/networkd/port-bonding.h \
+ src/networkd/port-bus.c \
+ src/networkd/port-bus.h \
+ src/networkd/port-dummy.c \
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+new file mode 100644
+index 0000000..6bddc43
+--- /dev/null
++++ b/src/networkd/port-bonding.c
+@@ -0,0 +1,142 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <systemd/sd-netlink.h>
++
++#include "config.h"
++#include "daemon.h"
++#include "logging.h"
++#include "port.h"
++#include "port-bonding.h"
++#include "string.h"
++
++const struct nw_port_bonding_mode {
++ const int mode;
++ const char* string;
++} nw_port_bonding_modes[] = {
++ { BOND_MODE_ROUNDROBIN, "round-robin" },
++ { BOND_MODE_ACTIVEBACKUP, "active-backup" },
++ { BOND_MODE_XOR, "xor" },
++ { BOND_MODE_BROADCAST, "broadcast" },
++ { BOND_MODE_8023AD, "802.3ad" },
++ { BOND_MODE_TLB, "tlb" },
++ { BOND_MODE_ALB, "alb" },
++ { -1, NULL },
++};
++
++static int nw_port_bonding_mode_from_string(const char* string) {
++ const struct nw_port_bonding_mode* m = NULL;
++
++ for (m = nw_port_bonding_modes; m->string; m++) {
++ if (strcmp(m->string, string) == 0)
++ return m->mode;
++ }
++
++ return -1;
++}
++
++static const char* nw_port_bonding_mode_to_string(const int mode) {
++ const struct nw_port_bonding_mode* m = NULL;
++
++ for (m = nw_port_bonding_modes; m->string; m++) {
++ if (m->mode == mode)
++ return m->string;
++ }
++
++ return NULL;
++}
++
++static int nw_port_bonding_config_read(nw_port* port) {
++ int r;
++
++ // Mode
++ const char* mode = nw_config_get(port->config, "BONDING_MODE");
++ if (mode) {
++ r = nw_port_bonding_set_mode(port, mode);
++ if (r)
++ return r;
++ }
++
++ return 0;
++}
++
++static int nw_port_bonding_config_write(nw_port* port) {
++ int r;
++
++ // Mode
++ r = nw_config_set(port->config, "BONDING_MODE",
++ nw_port_bonding_mode_to_string(port->bonding.mode));
++ if (r)
++ return r;
++
++ return 0;
++}
++
++static int nw_port_bonding_create_link(nw_port* port, sd_netlink_message* m) {
++ int r;
++
++ // Set mode
++ r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE, port->bonding.mode);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
++
++const nw_port_info_t nw_port_info_bonding = {
++ .kind = "bond",
++
++ // Operations
++ .ops = {
++ // Configuration
++ .config_read = nw_port_bonding_config_read,
++ .config_write = nw_port_bonding_config_write,
++
++ // Link
++ .create_link = nw_port_bonding_create_link,
++ },
++};
++
++const char* nw_port_bonding_get_mode(nw_port* port) {
++ return nw_port_bonding_mode_to_string(port->bonding.mode);
++}
++
++int nw_port_bonding_set_mode(nw_port* port, const char* mode) {
++ const int m = nw_port_bonding_mode_from_string(mode);
++
++ switch (m) {
++ case BOND_MODE_ROUNDROBIN:
++ case BOND_MODE_ACTIVEBACKUP:
++ case BOND_MODE_XOR:
++ case BOND_MODE_BROADCAST:
++ case BOND_MODE_8023AD:
++ case BOND_MODE_TLB:
++ case BOND_MODE_ALB:
++ port->bonding.mode = m;
++ break;
++
++ default:
++ ERROR("%s: Unsupported bonding mode '%s'\n", port->name, mode);
++ errno = ENOTSUP;
++ return 1;
++ }
++
++ return 0;
++}
+diff --git a/src/networkd/port-bonding.h b/src/networkd/port-bonding.h
+new file mode 100644
+index 0000000..5cd2c43
+--- /dev/null
++++ b/src/networkd/port-bonding.h
+@@ -0,0 +1,37 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_BONDING_H
++#define NETWORKD_PORT_BONDING_H
++
++#include <linux/if_bonding.h>
++
++#include "port.h"
++
++struct nw_port_bonding {
++ int mode;
++};
++
++extern const nw_port_info_t nw_port_info_bonding;
++
++const char* nw_port_bonding_get_mode(nw_port* port);
++int nw_port_bonding_set_mode(nw_port* port, const char* mode);
++
++#endif /* NETWORKD_PORT_BONDING_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index d5ffe42..b44cc88 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -30,6 +30,7 @@
+ #include "link.h"
+ #include "logging.h"
+ #include "port.h"
++#include "port-bonding.h"
+ #include "port-dummy.h"
+ #include "port-vlan.h"
+ #include "stats-collector.h"
+@@ -39,6 +40,7 @@ static const struct nw_port_type_map {
+ nw_port_type_t type;
+ const char* name;
+ } nw_port_type_map[] = {
++ { NW_PORT_BONDING, "bonding" },
+ { NW_PORT_DUMMY, "dummy" },
+ { NW_PORT_VLAN, "vlan" },
+ { NW_PORT_UNKNOWN, NULL },
+@@ -210,6 +212,10 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
+
+ // Set operations
+ switch (p->type) {
++ case NW_PORT_BONDING:
++ p->info = &nw_port_info_bonding;
++ break;
++
+ case NW_PORT_DUMMY:
+ p->info = &nw_port_info_dummy;
+ break;
+@@ -337,6 +343,7 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ }
+ break;
+
++ case NW_PORT_BONDING:
+ case NW_PORT_DUMMY:
+ case NW_PORT_UNKNOWN:
+ break;
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 8bc4db8..f8beed3 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -31,6 +31,7 @@
+
+ typedef enum nw_port_type {
+ NW_PORT_UNKNOWN = 0,
++ NW_PORT_BONDING,
+ NW_PORT_DUMMY,
+ NW_PORT_VLAN,
+ } nw_port_type_t;
+@@ -41,12 +42,14 @@ typedef enum nw_port_type {
+ #define NW_VLAN_ID_MAX 4096
+
+ typedef struct nw_port nw_port;
++typedef struct nw_port_info nw_port_info_t;
+
+ #include "address.h"
+ #include "config.h"
+ #include "daemon.h"
++#include "port-bonding.h"
+
+-typedef struct nw_port_info {
++struct nw_port_info {
+ // IFLA_INFO_KIND/IFLA_INFO_DATA
+ const char* kind;
+
+@@ -62,7 +65,7 @@ typedef struct nw_port_info {
+ int (*create_link)(nw_port* port, sd_netlink_message* message);
+ int (*destroy_link)(nw_port* port);
+ } ops;
+-} nw_port_info_t;
++};
+
+ #define NW_PORT_INFO(port) (port->info)
+ #define NW_PORT_OPS(port) (&NW_PORT_INFO(port)->ops)
+@@ -86,6 +89,9 @@ struct nw_port {
+ // Type Operations
+ const nw_port_info_t* info;
+
++ // Bonding Settings
++ struct nw_port_bonding bonding;
++
+ // VLAN settings
+ struct nw_port_vlan {
+ nw_port* parent;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,64 @@
+From 4d6ac814d5a46ca08a6f3cd6a1431a17cd65bc3e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 14:54:20 +0000
+Subject: [PATCH 249/304] ports: Move VLAN settings into its own header file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.h | 11 +++++++++++
+ src/networkd/port.h | 10 ++--------
+ 2 files changed, 13 insertions(+), 8 deletions(-)
+
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index f02c529..cffc178 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -23,6 +23,17 @@
+
+ #include "port.h"
+
++struct nw_port_vlan {
++ nw_port* parent;
++
++ // The VLAN ID
++ int id;
++
++ // If the parent has not been read from the configuration we will
++ // save the name and try to find it later.
++ char __parent_name[IF_NAMESIZE];
++};
++
+ extern const nw_port_info_t nw_port_info_vlan;
+
+ // ID
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index f8beed3..21e2a12 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -48,6 +48,7 @@ typedef struct nw_port_info nw_port_info_t;
+ #include "config.h"
+ #include "daemon.h"
+ #include "port-bonding.h"
++#include "port-vlan.h"
+
+ struct nw_port_info {
+ // IFLA_INFO_KIND/IFLA_INFO_DATA
+@@ -93,14 +94,7 @@ struct nw_port {
+ struct nw_port_bonding bonding;
+
+ // VLAN settings
+- struct nw_port_vlan {
+- nw_port* parent;
+- int id;
+-
+- // If the parent has not been read from the configuration we will
+- // save the name and try to find it later.
+- char __parent_name[IF_NAMESIZE];
+- } vlan;
++ struct nw_port_vlan vlan;
+ };
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 0744719e820e526593c5c7c0b4b86ce756509333 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 14:55:00 +0000
+Subject: [PATCH 250/304] networkctl: Fix typo in bus method name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/port.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/networkctl/port.c b/src/networkctl/port.c
+index 689e2f1..3a886ff 100644
+--- a/src/networkctl/port.c
++++ b/src/networkctl/port.c
+@@ -36,7 +36,7 @@ static int networkctl_port_walk(sd_bus* bus,
+ r = sd_bus_call_method(bus, "org.ipfire.network1", "/org/ipfire/network1",
+ "org.ipfire.network1", "ListPorts", &error, &reply, "");
+ if (r < 0) {
+- fprintf(stderr, "Listports call failed: %m\n");
++ fprintf(stderr, "ListPorts call failed: %m\n");
+ goto ERROR;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,163 @@
+From 5a5c346c5dac343ec8242adcd04f1fbe542640ff Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 16:47:14 +0000
+Subject: [PATCH 251/304] ports: Implement a function the generally fetches the
+ parent port
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/json.h | 89 +++++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port.c | 17 +++++----
+ src/networkd/port.h | 2 +
+ 3 files changed, 101 insertions(+), 7 deletions(-)
+ create mode 100644 src/networkd/json.h
+
+diff --git a/src/networkd/json.h b/src/networkd/json.h
+new file mode 100644
+index 0000000..33e237a
+--- /dev/null
++++ b/src/networkd/json.h
+@@ -0,0 +1,89 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_JSON_H
++#define NETWORKD_JSON_H
++
++#include <errno.h>
++
++#include <json.h>
++
++// Give some sane names to the reference count functions
++#define json_object_ref json_object_get
++#define json_object_unref json_object_put
++
++static inline int __json_object_add(struct json_object* o,
++ const char* key, struct json_object* value) {
++ int r;
++
++ // Add the object
++ r = json_object_object_add(o, key, value);
++ if (r < 0) {
++ if (value)
++ json_object_unref(value);
++ }
++
++ return r;
++}
++
++static inline int json_object_add_string(struct json_object* o,
++ const char* key, const char* value) {
++ struct json_object* element = NULL;
++
++ // Create a JSON object from the string
++ element = json_object_new_string(value);
++ if (!element)
++ return -errno;
++
++ return __json_object_add(o, key, element);
++}
++
++static inline int json_object_add_int64(struct json_object* o,
++ const char* key, const int64_t value) {
++ struct json_object* element = NULL;
++
++ // Create a JSON object
++ element = json_object_new_int64(value);
++ if (!element)
++ return -errno;
++
++ return __json_object_add(o, key, element);
++}
++
++static inline int json_to_string(struct json_object* o, char** s, size_t* l) {
++ // Format JSON to string
++ const char* buffer = json_object_to_json_string_ext(o,
++ JSON_C_TO_STRING_PRETTY|JSON_C_TO_STRING_PRETTY_TAB);
++ if (!buffer)
++ return -errno;
++
++ // Copy the string to the heap
++ *s = strdup(buffer);
++ if (!*s)
++ return -errno;
++
++ // If requested, store the length of the string
++ if (l)
++ *l = strlen(*s);
++
++ return 0;
++}
++
++#endif /* NETWORKD_JSON_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index b44cc88..c9440d4 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -441,23 +441,26 @@ static int nw_port_is_disabled(nw_port* port) {
+ return nw_config_get_bool(port->config, "DISABLED");
+ }
+
++nw_port* nw_port_get_parent_port(nw_port* port) {
++ if (!NW_PORT_OPS(port)->get_parent_port)
++ return NULL;
++
++ return NW_PORT_OPS(port)->get_parent_port(port);
++}
++
+ static nw_link* nw_port_get_parent_link(nw_port* port) {
+ nw_port* parent = NULL;
+ nw_link* link = NULL;
+
+- // Do nothing if not implemented
+- if (!NW_PORT_OPS(port)->get_parent_port)
+- goto ERROR;
+-
+ // Fetch the parent
+- parent = NW_PORT_OPS(port)->get_parent_port(port);
++ parent = nw_port_get_parent_port(port);
+ if (!parent)
+- goto ERROR;
++ return NULL;
+
+ // Fetch the link
+ link = nw_port_get_link(parent);
+
+-ERROR:
++ // Cleanup
+ if (parent)
+ nw_port_unref(parent);
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 21e2a12..38d19d1 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -119,6 +119,8 @@ int __nw_port_drop_link(nw_daemon* daemon, nw_port* port, void* data);
+
+ const nw_address_t* nw_port_get_address(nw_port* port);
+
++nw_port* nw_port_get_parent_port(nw_port* port);
++
+ int nw_port_reconfigure(nw_port* port);
+
+ int nw_port_has_carrier(nw_port* port);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 8923434ceb4264fea6044564ca7254bd28cf106e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 16:47:41 +0000
+Subject: [PATCH 252/304] config: Compare truthiness case-insensitively
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 8269ede..78448df 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -407,7 +407,7 @@ int nw_config_get_bool(nw_config* config, const char* key) {
+
+ // Check if we match any known true words
+ for (const char** s = nw_config_true; *s; s++) {
+- if (strcmp(value, *s) == 0)
++ if (strcasecmp(value, *s) == 0)
+ return 1;
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,46 @@
+From 6384cfdf37f873154f695f6cac67fe1926b5bad8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 16:48:30 +0000
+Subject: [PATCH 253/304] configure: Depend on JSON-C
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ configure.ac | 1 +
+ 2 files changed, 3 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index ad42f49..466ad73 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -357,6 +357,7 @@ networkd_CPPFLAGS = \
+ networkd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CAP_CFLAGS) \
++ $(JSON_C_CFLAGS) \
+ $(SYSTEMD_CFLAGS)
+
+ networkd_LDFLAGS = \
+@@ -365,6 +366,7 @@ networkd_LDFLAGS = \
+ networkd_LDADD = \
+ src/libnetwork.la \
+ $(CAP_LIBS) \
++ $(JSON_C_LIBS) \
+ $(SYSTEMD_LIBS)
+
+ dist_dbuspolicy_DATA += \
+diff --git a/configure.ac b/configure.ac
+index e1baa64..7883ae9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -173,6 +173,7 @@ AM_CONDITIONAL(HAVE_UDEV, [test -n "$with_udevdir"])
+ # ------------------------------------------------------------------------------
+
+ PKG_CHECK_MODULES([CAP], [libcap])
++PKG_CHECK_MODULES([JSON_C], [json-c])
+ PKG_CHECK_MODULES([LIBNL], [libnl-3.0 libnl-genl-3.0])
+ PKG_CHECK_MODULES([SYSTEMD], [libsystemd])
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,302 @@
+From e9b0614e1d653a733c76a3246fd800566e3e1561 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 5 Jun 2023 16:48:43 +0000
+Subject: [PATCH 254/304] ports: Add bus method to export port information as
+ JSON
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 1 +
+ src/networkd/port-bonding.c | 16 +++++++++++
+ src/networkd/port-bus.c | 46 +++++++++++++++++++++++++++++++
+ src/networkd/port-vlan.c | 28 +++++++++++++++++++
+ src/networkd/port.c | 55 +++++++++++++++++++++++++++++++++++++
+ src/networkd/port.h | 9 ++++++
+ 6 files changed, 155 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 466ad73..f4c22b4 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -319,6 +319,7 @@ dist_networkd_SOURCES = \
+ src/networkd/daemon-bus.h \
+ src/networkd/devmon.c \
+ src/networkd/devmon.h \
++ src/networkd/json.h \
+ src/networkd/link.c \
+ src/networkd/link.h \
+ src/networkd/links.c \
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index 6bddc43..bbc3fb7 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -100,6 +100,19 @@ static int nw_port_bonding_create_link(nw_port* port, sd_netlink_message* m) {
+ return 0;
+ }
+
++static int nw_port_bonding_to_json(nw_port* port, struct json_object* o) {
++ int r;
++
++ // Add mode
++ r = json_object_add_string(o, "BondingMode",
++ nw_port_bonding_mode_to_string(port->bonding.mode));
++ if (r < 0)
++ goto ERROR;
++
++ERROR:
++ return r;
++}
++
+ const nw_port_info_t nw_port_info_bonding = {
+ .kind = "bond",
+
+@@ -111,6 +124,9 @@ const nw_port_info_t nw_port_info_bonding = {
+
+ // Link
+ .create_link = nw_port_bonding_create_link,
++
++ // JSON
++ .to_json = nw_port_bonding_to_json,
+ },
+ };
+
+diff --git a/src/networkd/port-bus.c b/src/networkd/port-bus.c
+index 996be92..41f8ec4 100644
+--- a/src/networkd/port-bus.c
++++ b/src/networkd/port-bus.c
+@@ -20,10 +20,12 @@
+
+ #include <errno.h>
+ #include <stdlib.h>
++#include <systemd/sd-bus.h>
+
+ #include "address.h"
+ #include "bus.h"
+ #include "daemon.h"
++#include "json.h"
+ #include "logging.h"
+ #include "port.h"
+ #include "port-bus.h"
+@@ -105,6 +107,46 @@ ERROR:
+ return r;
+ }
+
++static int nw_port_bus_describe(sd_bus_message* message, void* data,
++ sd_bus_error* error) {
++ sd_bus_message* reply = NULL;
++ struct json_object* json = NULL;
++ char* text = NULL;
++ int r;
++
++ nw_port* port = (nw_port*)data;
++
++ // Export all data to JSON
++ r = nw_port_to_json(port, &json);
++ if (r < 0)
++ goto ERROR;
++
++ // Convert JSON to string
++ r = json_to_string(json, &text, NULL);
++ if (r < 0)
++ goto ERROR;
++
++ // Create a reply message
++ r = sd_bus_message_new_method_return(message, &reply);
++ if (r < 0)
++ goto ERROR;
++
++ r = sd_bus_message_append(reply, "s", text);
++ if (r < 0)
++ goto ERROR;
++
++ // Send the reply
++ r = sd_bus_send(NULL, reply, NULL);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++ if (text)
++ free(text);
++
++ return r;
++}
++
+ static const sd_bus_vtable port_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+@@ -112,6 +154,10 @@ static const sd_bus_vtable port_vtable[] = {
+ SD_BUS_PROPERTY("Address", "s", nw_port_bus_get_address,
+ 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
++ // Operations
++ SD_BUS_METHOD_WITH_ARGS("Describe", SD_BUS_NO_ARGS, SD_BUS_RESULT("s", json),
++ nw_port_bus_describe, SD_BUS_VTABLE_UNPRIVILEGED),
++
+ SD_BUS_VTABLE_END
+ };
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 5e970e9..ac4a5b4 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -22,6 +22,7 @@
+
+ #include "config.h"
+ #include "daemon.h"
++#include "json.h"
+ #include "logging.h"
+ #include "port.h"
+ #include "port-vlan.h"
+@@ -77,6 +78,30 @@ static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
+ return 0;
+ }
+
++static int nw_port_vlan_to_json(nw_port* port, struct json_object* o) {
++ nw_port* parent = NULL;
++ int r;
++
++ // Add the VLAN ID
++ r = json_object_add_int64(o, "VLANId", port->vlan.id);
++ if (r < 0)
++ goto ERROR;
++
++ // Fetch the parent
++ parent = nw_port_get_parent_port(port);
++ if (parent) {
++ r = json_object_add_string(o, "VLANParentPort", nw_port_name(parent));
++ if (r < 0)
++ goto ERROR;
++ }
++
++ERROR:
++ if (parent)
++ nw_port_unref(parent);
++
++ return r;
++}
++
+ const nw_port_info_t nw_port_info_vlan = {
+ .kind = "vlan",
+
+@@ -90,6 +115,9 @@ const nw_port_info_t nw_port_info_vlan = {
+
+ // Link
+ .create_link = nw_port_vlan_create_link,
++
++ // JSON
++ .to_json = nw_port_vlan_to_json,
+ },
+ };
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index c9440d4..a7fb826 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -27,6 +27,7 @@
+
+ #include "address.h"
+ #include "config.h"
++#include "json.h"
+ #include "link.h"
+ #include "logging.h"
+ #include "port.h"
+@@ -645,3 +646,57 @@ int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
+ errno = ENOTSUP;
+ return -errno;
+ }
++
++/*
++ JSON
++*/
++int nw_port_to_json(nw_port* port, struct json_object** object) {
++ char* address = NULL;
++ int r;
++
++ // Create a new JSON object
++ struct json_object* o = json_object_new_object();
++ if (!o) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Add name
++ r = json_object_add_string(o, "Name", port->name);
++ if (r < 0)
++ goto ERROR;
++
++ // Add Type
++ r = json_object_add_string(o, "Type", NW_PORT_INFO(port)->kind);
++ if (r < 0)
++ goto ERROR;
++
++ // Add address
++ address = nw_address_to_string(&port->address);
++ if (address) {
++ r = json_object_add_string(o, "Address", address);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ // Call custom stuff
++ if (NW_PORT_OPS(port)->to_json) {
++ r = NW_PORT_OPS(port)->to_json(port, o);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ // Success
++ r = 0;
++
++ // Return a reference to the created object
++ *object = json_object_ref(o);
++
++ERROR:
++ if (address)
++ free(address);
++ if (o)
++ json_object_unref(o);
++
++ return r;
++}
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 38d19d1..e16b957 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -21,6 +21,8 @@
+ #ifndef NETWORKD_PORT_H
+ #define NETWORKD_PORT_H
+
++#include <json.h>
++
+ #include <systemd/sd-netlink.h>
+
+ #ifndef IF_NAMESIZE
+@@ -47,6 +49,7 @@ typedef struct nw_port_info nw_port_info_t;
+ #include "address.h"
+ #include "config.h"
+ #include "daemon.h"
++#include "json.h"
+ #include "port-bonding.h"
+ #include "port-vlan.h"
+
+@@ -65,6 +68,9 @@ struct nw_port_info {
+ // Link
+ int (*create_link)(nw_port* port, sd_netlink_message* message);
+ int (*destroy_link)(nw_port* port);
++
++ // JSON
++ int (*to_json)(nw_port* port, struct json_object* object);
+ } ops;
+ };
+
+@@ -132,4 +138,7 @@ const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port);
+ int __nw_port_update_stats(nw_daemon* daemon, nw_port* port, void* data);
+ int nw_port_update_stats(nw_port* port);
+
++// JSON
++int nw_port_to_json(nw_port* port, struct json_object** object);
++
+ #endif /* NETWORKD_PORT_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,52 @@
+From d4028bc3f6b422a45f8f2ad41df2e59152474b6e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 6 Jun 2023 10:24:08 +0000
+Subject: [PATCH 255/304] networkctl: Fix parsing commands
+
+We used to send the argument that was last parsed to the next function
+which probably isn't very useful.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/command.c | 6 +-----
+ src/networkctl/main.c | 2 +-
+ 2 files changed, 2 insertions(+), 6 deletions(-)
+
+diff --git a/src/networkctl/command.c b/src/networkctl/command.c
+index 7114efe..99202dd 100644
+--- a/src/networkctl/command.c
++++ b/src/networkctl/command.c
+@@ -36,10 +36,6 @@ static const struct command* command_find(const struct command* commands, const
+ int command_dispatch(sd_bus* bus, const struct command* commands, int argc, char* argv[]) {
+ const struct command* command = NULL;
+
+- argc -= optind;
+- argv += optind;
+- optind = 1;
+-
+ if (!argc) {
+ fprintf(stderr, "Command required\n");
+ return -EINVAL;
+@@ -54,5 +50,5 @@ int command_dispatch(sd_bus* bus, const struct command* commands, int argc, char
+ return -EINVAL;
+ }
+
+- return command->callback(bus, argc, argv);
++ return command->callback(bus, argc - 1, argv + 1);
+ }
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 73908f8..0ba7284 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -115,7 +115,7 @@ int main(int argc, char* argv[]) {
+ }
+
+ // Run a command
+- r = networkctl_main(bus, argc, argv);
++ r = networkctl_main(bus, argc - 1, argv + 1);
+
+ ERROR:
+ if (bus)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,94 @@
+From d566048705bb24b12d46fa4ed7cf9352286d8086 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 6 Jun 2023 10:24:45 +0000
+Subject: [PATCH 256/304] networkctl: Implement "dump" command for ports which
+ shows the JSON
+
+This is just for debugging purposes.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/port.c | 51 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 51 insertions(+)
+
+diff --git a/src/networkctl/port.c b/src/networkctl/port.c
+index 3a886ff..67bc003 100644
+--- a/src/networkctl/port.c
++++ b/src/networkctl/port.c
+@@ -18,8 +18,11 @@
+ # #
+ #############################################################################*/
+
++#include <limits.h>
++
+ #include <systemd/sd-bus.h>
+
++#include "../networkd/string.h"
+ #include "command.h"
+ #include "port.h"
+
+@@ -77,6 +80,53 @@ ERROR:
+ return r;
+ }
+
++// Dump
++
++static int networkctl_port_dump(sd_bus* bus, int argc, char* argv[]) {
++ sd_bus_message* reply = NULL;
++ sd_bus_error error = SD_BUS_ERROR_NULL;
++ char path[PATH_MAX];
++ const char* text = NULL;
++ int r;
++
++ if (argc < 1) {
++ fprintf(stderr, "Port required\n");
++ return -EINVAL;
++ }
++
++ // Make port path
++ r = nw_string_format(path, "/org/ipfire/network1/port/%s", argv[0]);
++ if (r < 0)
++ goto ERROR;
++
++ // Call Describe
++ r = sd_bus_call_method(bus, "org.ipfire.network1", path,
++ "org.ipfire.network1.Port", "Describe", &error, &reply, "");
++ if (r < 0) {
++ fprintf(stderr, "Describe() call failed: %m\n");
++ goto ERROR;
++ }
++
++ // Read the text
++ r = sd_bus_message_read(reply, "s", &text);
++ if (r < 0) {
++ fprintf(stderr, "Could not parse bus message: %s\n", strerror(-r));
++ goto ERROR;
++ }
++
++ // Print the text
++ if (text)
++ printf("%s\n", text);
++
++ERROR:
++ if (reply)
++ sd_bus_message_unref(reply);
++
++ return r;
++}
++
++// List
++
+ static int __networkctl_port_list(sd_bus* bus, const char* path, const char* name, void* data) {
+ printf("%s\n", name);
+
+@@ -89,6 +139,7 @@ static int networkctl_port_list(sd_bus* bus, int argc, char* argv[]) {
+
+ int networkctl_port(sd_bus* bus, int argc, char* argv[]) {
+ static const struct command commands[] = {
++ { "dump", 0, networkctl_port_dump },
+ { "list", 0, networkctl_port_list },
+ { NULL },
+ };
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,70 @@
+From f9975100fdb9b9d66d9f21436e7973f9824bbcde Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 7 Jun 2023 13:16:58 +0000
+Subject: [PATCH 257/304] string: Add macros to easily define string table
+ lookups
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/string.h | 45 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 5bdfc3d..d94e270 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -125,6 +125,51 @@ static inline void nw_string_empty(char* s) {
+ *s = '\0';
+ }
+
++/*
++ Tables
++*/
++
++struct nw_string_table {
++ const int id;
++ const char* string;
++};
++
++static inline const char* nw_string_table_lookup_string(
++ const struct nw_string_table* table, const int id) {
++ const struct nw_string_table* entry = NULL;
++
++ for (entry = table; entry->string; entry++)
++ if (entry->id == id)
++ return entry->string;
++
++ return NULL;
++}
++
++static inline int nw_string_table_lookup_id(
++ const struct nw_string_table* table, const char* string) {
++ const struct nw_string_table* entry = NULL;
++
++ for (entry = table; entry->string; entry++)
++ if (strcmp(entry->string, string) == 0)
++ return entry->id;
++
++ return -1;
++}
++
++#define NW_STRING_TABLE_LOOKUP_ID(type, table, method) \
++ __attribute__((unused)) static type method(const char* s) { \
++ return nw_string_table_lookup_id(table, s); \
++ }
++
++#define NW_STRING_TABLE_LOOKUP_STRING(type, table, method) \
++ __attribute__((unused)) static const char* method(const type id) { \
++ return nw_string_table_lookup_string(table, id); \
++ }
++
++#define NW_STRING_TABLE_LOOKUP(type, table) \
++ NW_STRING_TABLE_LOOKUP_ID(type, table, table ## _from_string) \
++ NW_STRING_TABLE_LOOKUP_STRING(type, table, table ## _to_string)
++
+ /*
+ Paths
+ */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,156 @@
+From d872e169c7d38b741b0fd01796202a19357d622c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 7 Jun 2023 13:17:36 +0000
+Subject: [PATCH 258/304] ports: VLAN: Implement choosing a protocol
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.c | 49 ++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port-vlan.h | 14 ++++++++++++
+ 2 files changed, 63 insertions(+)
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index ac4a5b4..6e13ba9 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -18,6 +18,8 @@
+ # #
+ #############################################################################*/
+
++#include <linux/if_link.h>
++
+ #include <systemd/sd-netlink.h>
+
+ #include "config.h"
+@@ -28,6 +30,14 @@
+ #include "port-vlan.h"
+ #include "string.h"
+
++const struct nw_string_table nw_port_vlan_proto[] = {
++ { NW_VLAN_PROTO_8021Q, "802.1Q" },
++ { NW_VLAN_PROTO_8021ad, "802.1ad" },
++ { -1, NULL },
++};
++
++NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
++
+ static int nw_port_vlan_config_read(nw_port* port) {
+ int r;
+
+@@ -39,6 +49,14 @@ static int nw_port_vlan_config_read(nw_port* port) {
+ return r;
+ }
+
++ // VLAN Protocol
++ const char* proto = nw_config_get(port->config, "VLAN_PROTO");
++ if (proto) {
++ r = nw_port_set_vlan_proto(port, nw_port_vlan_proto_from_string(proto));
++ if (r)
++ return r;
++ }
++
+ // Parent Port
+ const char* parent = nw_config_get(port->config, "VLAN_PARENT");
+ if (parent) {
+@@ -58,6 +76,12 @@ static int nw_port_vlan_config_write(nw_port* port) {
+ if (r)
+ return r;
+
++ // VLAN Protocol
++ r = nw_config_set(port->config, "VLAN_PROTO",
++ nw_port_vlan_proto_to_string(port->vlan.proto));
++ if (r)
++ return r;
++
+ // Parent Port
+ r = nw_config_set(port->config, "VLAN_PARENT",
+ (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
+@@ -75,6 +99,11 @@ static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
+ if (r < 0)
+ return r;
+
++ // Set VLAN protocol
++ r = sd_netlink_message_append_u16(m, IFLA_VLAN_PROTOCOL, htobe16(port->vlan.proto));
++ if (r < 0)
++ return r;
++
+ return 0;
+ }
+
+@@ -87,6 +116,12 @@ static int nw_port_vlan_to_json(nw_port* port, struct json_object* o) {
+ if (r < 0)
+ goto ERROR;
+
++ // Add the VLAN Protocol
++ r = json_object_add_string(o, "VLANProtocol",
++ nw_port_vlan_proto_to_string(port->vlan.proto));
++ if (r < 0)
++ goto ERROR;
++
+ // Fetch the parent
+ parent = nw_port_get_parent_port(port);
+ if (parent) {
+@@ -155,6 +190,20 @@ int nw_port_set_vlan_id(nw_port* port, int id) {
+ return 0;
+ }
+
++int nw_port_set_vlan_proto(nw_port* port, const nw_port_vlan_proto_t proto) {
++ switch (proto) {
++ case NW_VLAN_PROTO_8021Q:
++ case NW_VLAN_PROTO_8021ad:
++ port->vlan.proto = proto;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
+ nw_port* nw_port_get_vlan_parent(nw_port* port) {
+ int r;
+
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index cffc178..17d3134 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -21,14 +21,25 @@
+ #ifndef NETWORKD_PORT_VLAN_H
+ #define NETWORKD_PORT_VLAN_H
+
++#include <arpa/inet.h>
++#include <linux/if_ether.h>
++
+ #include "port.h"
+
++typedef enum nw_port_vlan_proto {
++ NW_VLAN_PROTO_8021Q = ETH_P_8021Q,
++ NW_VLAN_PROTO_8021ad = ETH_P_8021AD,
++} nw_port_vlan_proto_t;
++
+ struct nw_port_vlan {
+ nw_port* parent;
+
+ // The VLAN ID
+ int id;
+
++ // Protocol
++ nw_port_vlan_proto_t proto;
++
+ // If the parent has not been read from the configuration we will
+ // save the name and try to find it later.
+ char __parent_name[IF_NAMESIZE];
+@@ -40,6 +51,9 @@ extern const nw_port_info_t nw_port_info_vlan;
+ int nw_port_get_vlan_id(nw_port* port);
+ int nw_port_set_vlan_id(nw_port* port, int id);
+
++// Protocol
++int nw_port_set_vlan_proto(nw_port* port, const nw_port_vlan_proto_t proto);
++
+ // Parent Port
+ nw_port* nw_port_get_vlan_parent(nw_port* port);
+ int nw_port_set_vlan_parent(nw_port* port, nw_port* parent);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,716 @@
+From 082d81a3b4115422a0d58fe8c3cf504350c76bfe Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:04:38 +0000
+Subject: [PATCH 259/304] config: Extend the parser to easier read/write
+ configs
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/address.h | 6 +-
+ src/networkd/config.c | 197 +++++++++++++++++++++++++++++++++++-
+ src/networkd/config.h | 38 +++++++
+ src/networkd/port-bonding.c | 34 ++++---
+ src/networkd/port-vlan.c | 57 ++++-------
+ src/networkd/port.c | 113 ++++++---------------
+ src/networkd/port.h | 5 +-
+ 7 files changed, 313 insertions(+), 137 deletions(-)
+
+diff --git a/src/networkd/address.h b/src/networkd/address.h
+index 7937d62..afcbca1 100644
+--- a/src/networkd/address.h
++++ b/src/networkd/address.h
+@@ -21,6 +21,7 @@
+ #ifndef NETWORKD_ADDRESS_H
+ #define NETWORKD_ADDRESS_H
+
++#include <errno.h>
+ #include <netinet/ether.h>
+ #include <string.h>
+ #include <sys/random.h>
+@@ -35,9 +36,12 @@ enum {
+ };
+
+ static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
++ if (!s)
++ return -EINVAL;
++
+ struct ether_addr* p = ether_aton_r(s, addr);
+ if (!p)
+- return 1;
++ return -errno;
+
+ return 0;
+ }
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 78448df..6f22da2 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -26,6 +26,7 @@
+ #include <sys/queue.h>
+ #include <unistd.h>
+
++#include "address.h"
+ #include "config.h"
+ #include "logging.h"
+ #include "string.h"
+@@ -37,19 +38,37 @@ struct nw_config_entry {
+ char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
+ };
+
++struct nw_config_option {
++ STAILQ_ENTRY(nw_config_option) nodes;
++
++ const char* key;
++ void* data;
++
++ // Callbacks
++ nw_config_option_read_callback_t read_callback;
++ nw_config_option_write_callback_t write_callback;
++};
++
+ struct nw_config {
+ int nrefs;
+
+ // The path to the configuration file
+ char path[PATH_MAX];
+
+- STAILQ_HEAD(entries, nw_config_entry) entries;
++ STAILQ_HEAD(config_entries, nw_config_entry) entries;
++
++ // Options
++ STAILQ_HEAD(parser_entries, nw_config_option) options;
+ };
+
+ static void nw_config_entry_free(struct nw_config_entry* entry) {
+ free(entry);
+ }
+
++static void nw_config_option_free(struct nw_config_option* option) {
++ free(option);
++}
++
+ static struct nw_config_entry* nw_config_entry_create(
+ nw_config* config, const char* key) {
+ int r;
+@@ -81,9 +100,20 @@ ERROR:
+ }
+
+ static void nw_config_free(nw_config* config) {
++ struct nw_config_option* option = NULL;
++
+ // Flush all entries
+ nw_config_flush(config);
+
++ // Free all options
++ while (!STAILQ_EMPTY(&config->options)) {
++ option = STAILQ_FIRST(&config->options);
++ STAILQ_REMOVE_HEAD(&config->options, nodes);
++
++ // Free the options
++ nw_config_option_free(option);
++ }
++
+ free(config);
+ }
+
+@@ -100,6 +130,9 @@ int nw_config_create(nw_config** config, const char* path) {
+ // Initialise entries
+ STAILQ_INIT(&c->entries);
+
++ // Initialise options
++ STAILQ_INIT(&c->options);
++
+ // Store the path
+ if (path) {
+ r = nw_string_set(c->path, path);
+@@ -147,6 +180,33 @@ int nw_config_destroy(nw_config* config) {
+ return unlink(config->path);
+ }
+
++int nw_config_copy(nw_config* config, nw_config** copy) {
++ struct nw_config_entry* entry = NULL;
++ nw_config* c = NULL;
++ int r;
++
++ // Create a new configuration
++ r = nw_config_create(&c, NULL);
++ if (r)
++ return r;
++
++ // Copy everything
++ STAILQ_FOREACH(entry, &config->entries, nodes) {
++ r = nw_config_set(c, entry->key, entry->value);
++ if (r)
++ goto ERROR;
++ }
++
++ *copy = c;
++ return 0;
++
++ERROR:
++ if (c)
++ nw_config_unref(c);
++
++ return r;
++}
++
+ const char* nw_config_path(nw_config* config) {
+ if (*config->path)
+ return config->path;
+@@ -341,6 +401,9 @@ const char* nw_config_get(nw_config* config, const char* key) {
+ int nw_config_set(nw_config* config, const char* key, const char* value) {
+ struct nw_config_entry* entry = NULL;
+
++ // Log the change
++ DEBUG("%p: Setting %s = %s\n", config, key, value);
++
+ // Delete the entry if val is NULL
+ if (!value)
+ return nw_config_del(config, key);
+@@ -418,3 +481,135 @@ int nw_config_get_bool(nw_config* config, const char* key) {
+ int nw_config_set_bool(nw_config* config, const char* key, const int value) {
+ return nw_config_set(config, key, value ? "true" : "false");
+ }
++
++/*
++ Options
++*/
++
++int nw_config_options_read(nw_config* config) {
++ struct nw_config_option* option = NULL;
++ int r;
++
++ STAILQ_FOREACH(option, &config->options, nodes) {
++ r = option->read_callback(config, option->key, option->data);
++ if (r < 0)
++ return r;
++ }
++
++ return 0;
++}
++
++int nw_config_options_write(nw_config* config) {
++ struct nw_config_option* option = NULL;
++ int r;
++
++ STAILQ_FOREACH(option, &config->options, nodes) {
++ r = option->write_callback(config, option->key, option->data);
++ if (r < 0)
++ return r;
++ }
++
++ return 0;
++}
++
++int nw_config_option_add(nw_config* config, const char* key, void* data,
++ nw_config_option_read_callback_t read_callback,
++ nw_config_option_write_callback_t write_callback) {
++ // Check input
++ if (!key || !data || !read_callback || !write_callback)
++ return -EINVAL;
++
++ // Allocate a new option
++ struct nw_config_option* option = calloc(1, sizeof(*option));
++ if (!option)
++ return -errno;
++
++ // Set key
++ option->key = key;
++
++ // Set data
++ option->data = data;
++
++ // Set callbacks
++ option->read_callback = read_callback;
++ option->write_callback = write_callback;
++
++ // Append the new option
++ STAILQ_INSERT_TAIL(&config->options, option, nodes);
++
++ return 0;
++}
++
++int nw_config_read_int(nw_config* config, const char* key, void* data) {
++ int* p = (int*)data;
++
++ // Fetch the value
++ *p = nw_config_get_int(config, key, -1);
++
++ return 0;
++}
++
++int nw_config_write_int(nw_config* config, const char* key, const void* data) {
++ return 0;
++}
++
++// String
++
++int nw_config_read_string(nw_config* config, const char* key, void* data) {
++ const char** p = (const char**)data;
++
++ // Fetch the value
++ const char* value = nw_config_get(config, key);
++ if (value)
++ *p = value;
++
++ return 0;
++}
++
++int nw_config_write_string(nw_config* config, const char* key, const void* data) {
++ const char** value = (const char**)data;
++
++ return nw_config_set(config, key, *value);
++}
++
++// Address
++
++int nw_config_read_address(nw_config* config, const char* key, void* data) {
++ nw_address_t* address = (nw_address_t*)data;
++ int r;
++
++ // Fetch the value
++ const char* value = nw_config_get(config, key);
++ if (!value)
++ return -EINVAL;
++
++ r = nw_address_from_string(address, value);
++ if (r < 0)
++ ERROR("Could not parse address: %s\n", value);
++
++ return r;
++}
++
++int nw_config_write_address(nw_config* config, const char* key, const void* data) {
++ const nw_address_t* address = (nw_address_t*)data;
++ int r;
++
++ // Format the address to string
++ char* value = nw_address_to_string(address);
++ if (!value)
++ return -errno;
++
++ // Store the value
++ r = nw_config_set(config, key, value);
++ if (r < 0)
++ goto ERROR;
++
++ // Success
++ r = 0;
++
++ERROR:
++ if (value)
++ free(value);
++
++ return r;
++}
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index e17c016..b5417e4 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -34,6 +34,7 @@ nw_config* nw_config_ref(nw_config* config);
+ nw_config* nw_config_unref(nw_config* config);
+
+ int nw_config_destroy(nw_config* config);
++int nw_config_copy(nw_config* config, nw_config** copy);
+
+ const char* nw_config_path(nw_config* config);
+
+@@ -53,4 +54,41 @@ int nw_config_set_int(nw_config* config, const char* key, const int value);
+ int nw_config_get_bool(nw_config* config, const char* key);
+ int nw_config_set_bool(nw_config* config, const char* key, const int value);
+
++/*
++ Options
++*/
++
++int nw_config_options_read(nw_config* config);
++int nw_config_options_write(nw_config* config);
++
++typedef int (*nw_config_option_read_callback_t)
++ (nw_config* config, const char* key, void* data);
++typedef int (*nw_config_option_write_callback_t)
++ (nw_config* config, const char* key, const void* data);
++
++int nw_config_option_add(nw_config* config, const char* key, void* value,
++ nw_config_option_read_callback_t read_callback,
++ nw_config_option_write_callback_t write_callback);
++
++#define NW_CONFIG_OPTION(config, key, data, read_callback, write_callback) \
++ nw_config_option_add(config, key, data, read_callback, write_callback)
++
++#define NW_CONFIG_OPTION_STRING(config, key, data) \
++ nw_config_option_add(config, key, data, nw_config_read_string, nw_config_write_string)
++
++int nw_config_read_string(nw_config* config, const char* key, void* data);
++int nw_config_write_string(nw_config* config, const char* key, const void* data);
++
++#define NW_CONFIG_OPTION_INT(config, key, data) \
++ nw_config_option_add(config, key, data, nw_config_read_int, nw_config_write_int)
++
++int nw_config_read_int(nw_config* config, const char* key, void* data);
++int nw_config_write_int(nw_config* config, const char* key, const void* data);
++
++#define NW_CONFIG_OPTION_ADDRESS(config, key, data) \
++ nw_config_option_add(config, key, data, nw_config_read_address, nw_config_write_address)
++
++int nw_config_read_address(nw_config* config, const char* key, void* data);
++int nw_config_write_address(nw_config* config, const char* key, const void* data);
++
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index bbc3fb7..a2e0fc3 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -63,27 +63,32 @@ static const char* nw_port_bonding_mode_to_string(const int mode) {
+ return NULL;
+ }
+
+-static int nw_port_bonding_config_read(nw_port* port) {
+- int r;
+-
+- // Mode
+- const char* mode = nw_config_get(port->config, "BONDING_MODE");
+- if (mode) {
+- r = nw_port_bonding_set_mode(port, mode);
+- if (r)
+- return r;
++static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* data) {
++ int* mode = (int*)data;
++
++ const char* value = nw_config_get(config, key);
++ if (value) {
++ *mode = nw_port_bonding_mode_from_string(value);
++ if (!*mode)
++ return -errno;
+ }
+
+ return 0;
+ }
+
+-static int nw_port_bonding_config_write(nw_port* port) {
++static int nw_port_bonding_write_mode(nw_config* config, const char* key, const void* data) {
++ const int* mode = (int*)data;
++
++ return nw_config_set(config, key, nw_port_bonding_mode_to_string(*mode));
++}
++
++static int nw_port_bonding_setup(nw_port* port) {
+ int r;
+
+ // Mode
+- r = nw_config_set(port->config, "BONDING_MODE",
+- nw_port_bonding_mode_to_string(port->bonding.mode));
+- if (r)
++ r = NW_CONFIG_OPTION(port->config, "BONDING_MODE", &port->bonding.mode,
++ nw_port_bonding_read_mode, nw_port_bonding_write_mode);
++ if (r < 0)
+ return r;
+
+ return 0;
+@@ -119,8 +124,7 @@ const nw_port_info_t nw_port_info_bonding = {
+ // Operations
+ .ops = {
+ // Configuration
+- .config_read = nw_port_bonding_config_read,
+- .config_write = nw_port_bonding_config_write,
++ .setup = nw_port_bonding_setup,
+
+ // Link
+ .create_link = nw_port_bonding_create_link,
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 6e13ba9..2d89a09 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -38,54 +38,42 @@ const struct nw_string_table nw_port_vlan_proto[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
+
+-static int nw_port_vlan_config_read(nw_port* port) {
+- int r;
+-
+- // VLAN ID
+- int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
+- if (id) {
+- r = nw_port_set_vlan_id(port, id);
+- if (r)
+- return r;
++static int nw_port_vlan_read_proto(nw_config* config, const char* key, void* data) {
++ nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
++
++ const char* value = nw_config_get(config, key);
++ if (value) {
++ *proto = nw_port_vlan_proto_from_string(data);
++ if (!*proto)
++ return -errno;
+ }
+
+- // VLAN Protocol
+- const char* proto = nw_config_get(port->config, "VLAN_PROTO");
+- if (proto) {
+- r = nw_port_set_vlan_proto(port, nw_port_vlan_proto_from_string(proto));
+- if (r)
+- return r;
+- }
++ return 0;
++}
+
+- // Parent Port
+- const char* parent = nw_config_get(port->config, "VLAN_PARENT");
+- if (parent) {
+- r = nw_string_set(port->vlan.__parent_name, parent);
+- if (r)
+- return r;
+- }
++static int nw_port_vlan_write_proto(nw_config* config, const char* key, const void* data) {
++ const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
+
+- return 0;
++ return nw_config_set(config, key, nw_port_vlan_proto_to_string(*proto));
+ }
+
+-static int nw_port_vlan_config_write(nw_port* port) {
++static int nw_port_vlan_setup(nw_port* port) {
+ int r;
+
+ // VLAN ID
+- r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
+- if (r)
++ r = NW_CONFIG_OPTION_INT(port->config, "VLAN_ID", &port->vlan.id);
++ if (r < 0)
+ return r;
+
+ // VLAN Protocol
+- r = nw_config_set(port->config, "VLAN_PROTO",
+- nw_port_vlan_proto_to_string(port->vlan.proto));
+- if (r)
++ r = NW_CONFIG_OPTION(port->config, "VLAN_PROTO", &port->vlan.proto,
++ nw_port_vlan_read_proto, nw_port_vlan_write_proto);
++ if (r < 0)
+ return r;
+
+ // Parent Port
+- r = nw_config_set(port->config, "VLAN_PARENT",
+- (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
+- if (r)
++ r = NW_CONFIG_OPTION_STRING(port->config, "VLAN_PARENT", &port->vlan.__parent_name);
++ if (r < 0)
+ return r;
+
+ return 0;
+@@ -143,8 +131,7 @@ const nw_port_info_t nw_port_info_vlan = {
+ // Operations
+ .ops = {
+ // Configuration
+- .config_read = nw_port_vlan_config_read,
+- .config_write = nw_port_vlan_config_write,
++ .setup = nw_port_vlan_setup,
+
+ .get_parent_port = nw_port_get_vlan_parent,
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index a7fb826..cab8fc5 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -69,64 +69,6 @@ static void nw_port_free(nw_port* port) {
+ free(port);
+ }
+
+-static int nw_port_setup_address(nw_port* port) {
+- char* __address = NULL;
+- int r;
+-
+- // Read ADDRESS from configuration
+- const char* s = nw_config_get(port->config, "ADDRESS");
+- if (!s) {
+- ERROR("Port %s: Address is not set\n", port->name);
+- goto ERROR;
+- }
+-
+- // Parse the address
+- r = nw_address_from_string(&port->address, s);
+- if (r) {
+- ERROR("Port %s: Could not parse address: %m\n", port->name);
+- goto ERROR;
+- }
+-
+- // Check if this address is usable
+- r = nw_address_is_multicast(&port->address);
+- if (r) {
+- DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
+- goto ERROR;
+- }
+-
+- return 0;
+-
+-ERROR:
+- // Generate a random Ethernet address
+- r = nw_address_generate(&port->address);
+- if (r) {
+- ERROR("Could not generate a random Ethernet address: %m\n");
+- return r;
+- }
+-
+- // Format the generated address
+- __address = nw_address_to_string(&port->address);
+- if (__address) {
+- ERROR("Generated a random Ethernet address for %s: %s\n", port->name, __address);
+-
+- // Free the address
+- free(__address);
+- }
+-
+- return 0;
+-}
+-
+-static int nw_port_setup_common(nw_port* port) {
+- int r;
+-
+- // Address
+- r = nw_port_setup_address(port);
+- if (r)
+- return r;
+-
+- return 0;
+-}
+-
+ static int nw_port_set_link(nw_port* port, nw_link* link) {
+ // Do nothing if the same link is being re-assigned
+ if (port->link == link)
+@@ -154,7 +96,6 @@ static int nw_port_set_link(nw_port* port, nw_link* link) {
+
+ static int nw_port_setup(nw_port* port) {
+ nw_link* link = NULL;
+- char path[PATH_MAX];
+ int r;
+
+ // Find the link
+@@ -165,28 +106,32 @@ static int nw_port_setup(nw_port* port) {
+ goto ERROR;
+ }
+
+- // Compose the path to the main configuration file
+- r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
+- if (r)
+- goto ERROR;
+-
+- // Initialize the configuration
+- r = nw_config_create(&port->config, path);
+- if (r)
++ // Generate a random Ethernet address
++ r = nw_address_generate(&port->address);
++ if (r < 0) {
++ ERROR("Could not generate an Ethernet address: %s\n", strerror(-r));
+ goto ERROR;
++ }
+
+- // Perform some common initialization
+- r = nw_port_setup_common(port);
+- if (r)
++ // Setup options
++ r = NW_CONFIG_OPTION_ADDRESS(port->config, "ADDRESS", &port->address);
++ if (r < 0)
+ goto ERROR;
+
+ // Call any custom initialization
+- if (NW_PORT_OPS(port)->config_read) {
+- r = NW_PORT_OPS(port)->config_read(port);
+- if (r)
++ if (NW_PORT_OPS(port)->setup) {
++ r = NW_PORT_OPS(port)->setup(port);
++ if (r < 0)
+ goto ERROR;
+ }
+
++ // Parse the configuration
++ r = nw_config_options_read(port->config);
++ if (r < 0) {
++ ERROR("Could not read configuration for port %s: %s\n", port->name, strerror(-r));
++ goto ERROR;
++ }
++
+ ERROR:
+ if (link)
+ nw_link_unref(link);
+@@ -194,7 +139,8 @@ ERROR:
+ return r;
+ }
+
+-int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
++int nw_port_create(nw_port** port, nw_daemon* daemon,
++ nw_port_type_t type, const char* name, nw_config* config) {
+ int r;
+
+ // Allocate a new object
+@@ -231,6 +177,11 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
+ if (r)
+ goto ERROR;
+
++ // Copy the configuration
++ r = nw_config_copy(config, &p->config);
++ if (r)
++ goto ERROR;
++
+ // Setup the port
+ r = nw_port_setup(p);
+ if (r)
+@@ -263,7 +214,7 @@ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+ }
+
+ // Create a new port
+- r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
++ r = nw_port_create(port, daemon, nw_port_type_from_string(type), name, config);
+ if (r)
+ goto ERROR;
+
+@@ -356,12 +307,10 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ int nw_port_save(nw_port* port) {
+ int r;
+
+- // Call the custom handler
+- if (NW_PORT_OPS(port)->config_write) {
+- r = NW_PORT_OPS(port)->config_write(port);
+- if (r)
+- goto ERROR;
+- }
++ // Write out the configuration
++ r = nw_config_options_write(port->config);
++ if (r < 0)
++ goto ERROR;
+
+ // Write the configuration
+ r = nw_config_write(port->config);
+@@ -371,7 +320,7 @@ int nw_port_save(nw_port* port) {
+ return 0;
+
+ ERROR:
+- ERROR("Could not save configuration for port %s: %m\n", port->name);
++ ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
+
+ return 1;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index e16b957..ef68a89 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -59,8 +59,7 @@ struct nw_port_info {
+
+ struct nw_port_ops {
+ // Configuration
+- int (*config_read)(nw_port* port);
+- int (*config_write)(nw_port* port);
++ int (*setup)(nw_port* port);
+
+ // Get Parent Port
+ nw_port* (*get_parent_port)(nw_port* port);
+@@ -104,7 +103,7 @@ struct nw_port {
+ };
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+- nw_port_type_t type, const char* name);
++ nw_port_type_t type, const char* name, nw_config* config);
+ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+ const char* name, const char* path);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,93 @@
+From 582536b21276f11b040eef80e185635b312556da Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:12:00 +0000
+Subject: [PATCH 260/304] ports: bonding: Convert mode to string table
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-bonding.c | 41 ++++++++-----------------------------
+ src/networkd/port-bonding.h | 12 ++++++++++-
+ 2 files changed, 20 insertions(+), 33 deletions(-)
+
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index a2e0fc3..e765791 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -27,41 +27,18 @@
+ #include "port-bonding.h"
+ #include "string.h"
+
+-const struct nw_port_bonding_mode {
+- const int mode;
+- const char* string;
+-} nw_port_bonding_modes[] = {
+- { BOND_MODE_ROUNDROBIN, "round-robin" },
+- { BOND_MODE_ACTIVEBACKUP, "active-backup" },
+- { BOND_MODE_XOR, "xor" },
+- { BOND_MODE_BROADCAST, "broadcast" },
+- { BOND_MODE_8023AD, "802.3ad" },
+- { BOND_MODE_TLB, "tlb" },
+- { BOND_MODE_ALB, "alb" },
++const struct nw_string_table nw_port_bonding_mode[] = {
++ { NW_BONDING_MODE_ROUNDROBIN, "round-robin" },
++ { NW_BONDING_MODE_ACTIVEBACKUP, "active-backup" },
++ { NW_BONDING_MODE_XOR, "xor" },
++ { NW_BONDING_MODE_BROADCAST, "broadcast" },
++ { NW_BONDING_MODE_8023AD, "802.3ad" },
++ { NW_BONDING_MODE_TLB, "tlb" },
++ { NW_BONDING_MODE_ALB, "alb" },
+ { -1, NULL },
+ };
+
+-static int nw_port_bonding_mode_from_string(const char* string) {
+- const struct nw_port_bonding_mode* m = NULL;
+-
+- for (m = nw_port_bonding_modes; m->string; m++) {
+- if (strcmp(m->string, string) == 0)
+- return m->mode;
+- }
+-
+- return -1;
+-}
+-
+-static const char* nw_port_bonding_mode_to_string(const int mode) {
+- const struct nw_port_bonding_mode* m = NULL;
+-
+- for (m = nw_port_bonding_modes; m->string; m++) {
+- if (m->mode == mode)
+- return m->string;
+- }
+-
+- return NULL;
+-}
++NW_STRING_TABLE_LOOKUP(nw_port_bonding_mode_t, nw_port_bonding_mode)
+
+ static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* data) {
+ int* mode = (int*)data;
+diff --git a/src/networkd/port-bonding.h b/src/networkd/port-bonding.h
+index 5cd2c43..e21c251 100644
+--- a/src/networkd/port-bonding.h
++++ b/src/networkd/port-bonding.h
+@@ -25,8 +25,18 @@
+
+ #include "port.h"
+
++typedef enum nw_port_bonding_mode {
++ NW_BONDING_MODE_ROUNDROBIN = BOND_MODE_ROUNDROBIN,
++ NW_BONDING_MODE_ACTIVEBACKUP = BOND_MODE_ACTIVEBACKUP,
++ NW_BONDING_MODE_XOR = BOND_MODE_XOR,
++ NW_BONDING_MODE_BROADCAST = BOND_MODE_BROADCAST,
++ NW_BONDING_MODE_8023AD = BOND_MODE_8023AD,
++ NW_BONDING_MODE_TLB = BOND_MODE_TLB,
++ NW_BONDING_MODE_ALB = BOND_MODE_ALB,
++} nw_port_bonding_mode_t;
++
+ struct nw_port_bonding {
+- int mode;
++ nw_port_bonding_mode_t mode;
+ };
+
+ extern const nw_port_info_t nw_port_info_bonding;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,221 @@
+From cc54472edbcc3a4325b9bad6eff1532bcd42c891 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:17:24 +0000
+Subject: [PATCH 261/304] config: Rename "data" to "value" as it holds a
+ reference to it
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 64 ++++++++++++++++++++-----------------------
+ src/networkd/config.h | 28 +++++++++----------
+ 2 files changed, 43 insertions(+), 49 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 6f22da2..b142d17 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -42,7 +42,7 @@ struct nw_config_option {
+ STAILQ_ENTRY(nw_config_option) nodes;
+
+ const char* key;
+- void* data;
++ void* value;
+
+ // Callbacks
+ nw_config_option_read_callback_t read_callback;
+@@ -491,7 +491,7 @@ int nw_config_options_read(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->read_callback(config, option->key, option->data);
++ r = option->read_callback(config, option->key, option->value);
+ if (r < 0)
+ return r;
+ }
+@@ -504,7 +504,7 @@ int nw_config_options_write(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->write_callback(config, option->key, option->data);
++ r = option->write_callback(config, option->key, option->value);
+ if (r < 0)
+ return r;
+ }
+@@ -512,11 +512,11 @@ int nw_config_options_write(nw_config* config) {
+ return 0;
+ }
+
+-int nw_config_option_add(nw_config* config, const char* key, void* data,
++int nw_config_option_add(nw_config* config, const char* key, void* value,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback) {
+ // Check input
+- if (!key || !data || !read_callback || !write_callback)
++ if (!key || !value || !read_callback || !write_callback)
+ return -EINVAL;
+
+ // Allocate a new option
+@@ -527,8 +527,8 @@ int nw_config_option_add(nw_config* config, const char* key, void* data,
+ // Set key
+ option->key = key;
+
+- // Set data
+- option->data = data;
++ // Set value
++ option->value = value;
+
+ // Set callbacks
+ option->read_callback = read_callback;
+@@ -540,67 +540,61 @@ int nw_config_option_add(nw_config* config, const char* key, void* data,
+ return 0;
+ }
+
+-int nw_config_read_int(nw_config* config, const char* key, void* data) {
+- int* p = (int*)data;
+-
++int nw_config_read_int(nw_config* config, const char* key, void* value) {
+ // Fetch the value
+- *p = nw_config_get_int(config, key, -1);
++ *(int*)value = nw_config_get_int(config, key, -1);
+
+ return 0;
+ }
+
+-int nw_config_write_int(nw_config* config, const char* key, const void* data) {
++int nw_config_write_int(nw_config* config, const char* key, const void* value) {
+ return 0;
+ }
+
+ // String
+
+-int nw_config_read_string(nw_config* config, const char* key, void* data) {
+- const char** p = (const char**)data;
+-
++int nw_config_read_string(nw_config* config, const char* key, void* value) {
+ // Fetch the value
+- const char* value = nw_config_get(config, key);
+- if (value)
+- *p = value;
++ const char* p = nw_config_get(config, key);
++ if (p)
++ *(const char**)value = p;
+
+ return 0;
+ }
+
+-int nw_config_write_string(nw_config* config, const char* key, const void* data) {
+- const char** value = (const char**)data;
+-
+- return nw_config_set(config, key, *value);
++int nw_config_write_string(nw_config* config, const char* key, const void* value) {
++ return nw_config_set(config, key, *(const char**)value);
+ }
+
+ // Address
+
+-int nw_config_read_address(nw_config* config, const char* key, void* data) {
+- nw_address_t* address = (nw_address_t*)data;
++int nw_config_read_address(nw_config* config, const char* key, void* value) {
++ nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+ // Fetch the value
+- const char* value = nw_config_get(config, key);
+- if (!value)
++ const char* p = nw_config_get(config, key);
++ if (!p)
+ return -EINVAL;
+
+- r = nw_address_from_string(address, value);
++ r = nw_address_from_string(address, p);
+ if (r < 0)
+- ERROR("Could not parse address: %s\n", value);
++ ERROR("Could not parse address: %s\n", p);
+
+ return r;
+ }
+
+-int nw_config_write_address(nw_config* config, const char* key, const void* data) {
+- const nw_address_t* address = (nw_address_t*)data;
++int nw_config_write_address(nw_config* config, const char* key, const void* value) {
++ const nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+ // Format the address to string
+- char* value = nw_address_to_string(address);
+- if (!value)
++ char* p = nw_address_to_string(address);
++ if (!p)
+ return -errno;
+
+ // Store the value
+- r = nw_config_set(config, key, value);
++ r = nw_config_set(config, key, p);
+ if (r < 0)
+ goto ERROR;
+
+@@ -608,8 +602,8 @@ int nw_config_write_address(nw_config* config, const char* key, const void* data
+ r = 0;
+
+ ERROR:
+- if (value)
+- free(value);
++ if (p)
++ free(p);
+
+ return r;
+ }
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index b5417e4..7185038 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -62,9 +62,9 @@ int nw_config_options_read(nw_config* config);
+ int nw_config_options_write(nw_config* config);
+
+ typedef int (*nw_config_option_read_callback_t)
+- (nw_config* config, const char* key, void* data);
++ (nw_config* config, const char* key, void* value);
+ typedef int (*nw_config_option_write_callback_t)
+- (nw_config* config, const char* key, const void* data);
++ (nw_config* config, const char* key, const void* value);
+
+ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ nw_config_option_read_callback_t read_callback,
+@@ -73,22 +73,22 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ #define NW_CONFIG_OPTION(config, key, data, read_callback, write_callback) \
+ nw_config_option_add(config, key, data, read_callback, write_callback)
+
+-#define NW_CONFIG_OPTION_STRING(config, key, data) \
+- nw_config_option_add(config, key, data, nw_config_read_string, nw_config_write_string)
++#define NW_CONFIG_OPTION_STRING(config, key, value) \
++ nw_config_option_add(config, key, value, nw_config_read_string, nw_config_write_string)
+
+-int nw_config_read_string(nw_config* config, const char* key, void* data);
+-int nw_config_write_string(nw_config* config, const char* key, const void* data);
++int nw_config_read_string(nw_config* config, const char* key, void* value);
++int nw_config_write_string(nw_config* config, const char* key, const void* value);
+
+-#define NW_CONFIG_OPTION_INT(config, key, data) \
+- nw_config_option_add(config, key, data, nw_config_read_int, nw_config_write_int)
++#define NW_CONFIG_OPTION_INT(config, key, value) \
++ nw_config_option_add(config, key, value, nw_config_read_int, nw_config_write_int)
+
+-int nw_config_read_int(nw_config* config, const char* key, void* data);
+-int nw_config_write_int(nw_config* config, const char* key, const void* data);
++int nw_config_read_int(nw_config* config, const char* key, void* value);
++int nw_config_write_int(nw_config* config, const char* key, const void* value);
+
+-#define NW_CONFIG_OPTION_ADDRESS(config, key, data) \
+- nw_config_option_add(config, key, data, nw_config_read_address, nw_config_write_address)
++#define NW_CONFIG_OPTION_ADDRESS(config, key, value) \
++ nw_config_option_add(config, key, value, nw_config_read_address, nw_config_write_address)
+
+-int nw_config_read_address(nw_config* config, const char* key, void* data);
+-int nw_config_write_address(nw_config* config, const char* key, const void* data);
++int nw_config_read_address(nw_config* config, const char* key, void* value);
++int nw_config_write_address(nw_config* config, const char* key, const void* value);
+
+ #endif /* NETWORKD_CONFIG_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,255 @@
+From 3eeb38b7ad3a343645127f11fea281ed2a22e80a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:23:10 +0000
+Subject: [PATCH 262/304] config: Add data pointer to callbacks
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 23 ++++++++++++++---------
+ src/networkd/config.h | 28 ++++++++++++++--------------
+ src/networkd/port-bonding.c | 18 ++++++++++--------
+ src/networkd/port-vlan.c | 18 ++++++++++--------
+ 4 files changed, 48 insertions(+), 39 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index b142d17..39e9881 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -47,6 +47,7 @@ struct nw_config_option {
+ // Callbacks
+ nw_config_option_read_callback_t read_callback;
+ nw_config_option_write_callback_t write_callback;
++ void* data;
+ };
+
+ struct nw_config {
+@@ -491,7 +492,7 @@ int nw_config_options_read(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->read_callback(config, option->key, option->value);
++ r = option->read_callback(config, option->key, option->value, option->data);
+ if (r < 0)
+ return r;
+ }
+@@ -504,7 +505,7 @@ int nw_config_options_write(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->write_callback(config, option->key, option->value);
++ r = option->write_callback(config, option->key, option->value, option->data);
+ if (r < 0)
+ return r;
+ }
+@@ -514,7 +515,7 @@ int nw_config_options_write(nw_config* config) {
+
+ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ nw_config_option_read_callback_t read_callback,
+- nw_config_option_write_callback_t write_callback) {
++ nw_config_option_write_callback_t write_callback, void* data) {
+ // Check input
+ if (!key || !value || !read_callback || !write_callback)
+ return -EINVAL;
+@@ -533,6 +534,7 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ // Set callbacks
+ option->read_callback = read_callback;
+ option->write_callback = write_callback;
++ option->data = data;
+
+ // Append the new option
+ STAILQ_INSERT_TAIL(&config->options, option, nodes);
+@@ -540,20 +542,21 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ return 0;
+ }
+
+-int nw_config_read_int(nw_config* config, const char* key, void* value) {
++int nw_config_read_int(nw_config* config, const char* key, void* value, void* data) {
+ // Fetch the value
+ *(int*)value = nw_config_get_int(config, key, -1);
+
+ return 0;
+ }
+
+-int nw_config_write_int(nw_config* config, const char* key, const void* value) {
++int nw_config_write_int(nw_config* config,
++ const char* key, const void* value, void* data) {
+ return 0;
+ }
+
+ // String
+
+-int nw_config_read_string(nw_config* config, const char* key, void* value) {
++int nw_config_read_string(nw_config* config, const char* key, void* value, void* data) {
+ // Fetch the value
+ const char* p = nw_config_get(config, key);
+ if (p)
+@@ -562,13 +565,14 @@ int nw_config_read_string(nw_config* config, const char* key, void* value) {
+ return 0;
+ }
+
+-int nw_config_write_string(nw_config* config, const char* key, const void* value) {
++int nw_config_write_string(nw_config* config,
++ const char* key, const void* value, void* data) {
+ return nw_config_set(config, key, *(const char**)value);
+ }
+
+ // Address
+
+-int nw_config_read_address(nw_config* config, const char* key, void* value) {
++int nw_config_read_address(nw_config* config, const char* key, void* value, void* data) {
+ nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+@@ -584,7 +588,8 @@ int nw_config_read_address(nw_config* config, const char* key, void* value) {
+ return r;
+ }
+
+-int nw_config_write_address(nw_config* config, const char* key, const void* value) {
++int nw_config_write_address(nw_config* config,
++ const char* key, const void* value, void* data) {
+ const nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 7185038..d6b8db8 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -62,33 +62,33 @@ int nw_config_options_read(nw_config* config);
+ int nw_config_options_write(nw_config* config);
+
+ typedef int (*nw_config_option_read_callback_t)
+- (nw_config* config, const char* key, void* value);
++ (nw_config* config, const char* key, void* value, void* data);
+ typedef int (*nw_config_option_write_callback_t)
+- (nw_config* config, const char* key, const void* value);
++ (nw_config* config, const char* key, const void* value, void* data);
+
+ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ nw_config_option_read_callback_t read_callback,
+- nw_config_option_write_callback_t write_callback);
++ nw_config_option_write_callback_t write_callback, void* data);
+
+-#define NW_CONFIG_OPTION(config, key, data, read_callback, write_callback) \
+- nw_config_option_add(config, key, data, read_callback, write_callback)
++#define NW_CONFIG_OPTION(config, key, value, read_callback, write_callback, data) \
++ nw_config_option_add(config, key, value, read_callback, write_callback, data)
+
+ #define NW_CONFIG_OPTION_STRING(config, key, value) \
+- nw_config_option_add(config, key, value, nw_config_read_string, nw_config_write_string)
++ nw_config_option_add(config, key, value, nw_config_read_string, nw_config_write_string, NULL)
+
+-int nw_config_read_string(nw_config* config, const char* key, void* value);
+-int nw_config_write_string(nw_config* config, const char* key, const void* value);
++int nw_config_read_string(nw_config* config, const char* key, void* value, void* data);
++int nw_config_write_string(nw_config* config, const char* key, const void* value, void* data);
+
+ #define NW_CONFIG_OPTION_INT(config, key, value) \
+- nw_config_option_add(config, key, value, nw_config_read_int, nw_config_write_int)
++ nw_config_option_add(config, key, value, nw_config_read_int, nw_config_write_int, NULL)
+
+-int nw_config_read_int(nw_config* config, const char* key, void* value);
+-int nw_config_write_int(nw_config* config, const char* key, const void* value);
++int nw_config_read_int(nw_config* config, const char* key, void* value, void* data);
++int nw_config_write_int(nw_config* config, const char* key, const void* value, void* data);
+
+ #define NW_CONFIG_OPTION_ADDRESS(config, key, value) \
+- nw_config_option_add(config, key, value, nw_config_read_address, nw_config_write_address)
++ nw_config_option_add(config, key, value, nw_config_read_address, nw_config_write_address, NULL)
+
+-int nw_config_read_address(nw_config* config, const char* key, void* value);
+-int nw_config_write_address(nw_config* config, const char* key, const void* value);
++int nw_config_read_address(nw_config* config, const char* key, void* value, void* data);
++int nw_config_write_address(nw_config* config, const char* key, const void* value, void* data);
+
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index e765791..7bd54e1 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -40,12 +40,13 @@ const struct nw_string_table nw_port_bonding_mode[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_port_bonding_mode_t, nw_port_bonding_mode)
+
+-static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* data) {
+- int* mode = (int*)data;
++static int nw_port_bonding_read_mode(nw_config* config,
++ const char* key, void* value, void* data) {
++ int* mode = (int*)value;
+
+- const char* value = nw_config_get(config, key);
+- if (value) {
+- *mode = nw_port_bonding_mode_from_string(value);
++ const char* p = nw_config_get(config, key);
++ if (p) {
++ *mode = nw_port_bonding_mode_from_string(p);
+ if (!*mode)
+ return -errno;
+ }
+@@ -53,8 +54,9 @@ static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* d
+ return 0;
+ }
+
+-static int nw_port_bonding_write_mode(nw_config* config, const char* key, const void* data) {
+- const int* mode = (int*)data;
++static int nw_port_bonding_write_mode(nw_config* config,
++ const char* key, const void* value, void* data) {
++ const int* mode = (int*)value;
+
+ return nw_config_set(config, key, nw_port_bonding_mode_to_string(*mode));
+ }
+@@ -64,7 +66,7 @@ static int nw_port_bonding_setup(nw_port* port) {
+
+ // Mode
+ r = NW_CONFIG_OPTION(port->config, "BONDING_MODE", &port->bonding.mode,
+- nw_port_bonding_read_mode, nw_port_bonding_write_mode);
++ nw_port_bonding_read_mode, nw_port_bonding_write_mode, NULL);
+ if (r < 0)
+ return r;
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 2d89a09..e3f93d7 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -38,12 +38,13 @@ const struct nw_string_table nw_port_vlan_proto[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
+
+-static int nw_port_vlan_read_proto(nw_config* config, const char* key, void* data) {
+- nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
++static int nw_port_vlan_read_proto(nw_config* config,
++ const char* key, void* value, void* data) {
++ nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)value;
+
+- const char* value = nw_config_get(config, key);
+- if (value) {
+- *proto = nw_port_vlan_proto_from_string(data);
++ const char* p = nw_config_get(config, key);
++ if (p) {
++ *proto = nw_port_vlan_proto_from_string(p);
+ if (!*proto)
+ return -errno;
+ }
+@@ -51,8 +52,9 @@ static int nw_port_vlan_read_proto(nw_config* config, const char* key, void* dat
+ return 0;
+ }
+
+-static int nw_port_vlan_write_proto(nw_config* config, const char* key, const void* data) {
+- const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
++static int nw_port_vlan_write_proto(nw_config* config,
++ const char* key, const void* value, void* data) {
++ const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)value;
+
+ return nw_config_set(config, key, nw_port_vlan_proto_to_string(*proto));
+ }
+@@ -67,7 +69,7 @@ static int nw_port_vlan_setup(nw_port* port) {
+
+ // VLAN Protocol
+ r = NW_CONFIG_OPTION(port->config, "VLAN_PROTO", &port->vlan.proto,
+- nw_port_vlan_read_proto, nw_port_vlan_write_proto);
++ nw_port_vlan_read_proto, nw_port_vlan_write_proto, NULL);
+ if (r < 0)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,75 @@
+From 644eb1c88dc179c3efdeb6af32c5d153d5483957 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:30:41 +0000
+Subject: [PATCH 263/304] string: Define an own type for string tables
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-bonding.c | 2 +-
+ src/networkd/port-vlan.c | 2 +-
+ src/networkd/string.h | 12 ++++++------
+ 3 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index 7bd54e1..b1d2c18 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -27,7 +27,7 @@
+ #include "port-bonding.h"
+ #include "string.h"
+
+-const struct nw_string_table nw_port_bonding_mode[] = {
++const nw_string_table_t nw_port_bonding_mode[] = {
+ { NW_BONDING_MODE_ROUNDROBIN, "round-robin" },
+ { NW_BONDING_MODE_ACTIVEBACKUP, "active-backup" },
+ { NW_BONDING_MODE_XOR, "xor" },
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index e3f93d7..792fd28 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -30,7 +30,7 @@
+ #include "port-vlan.h"
+ #include "string.h"
+
+-const struct nw_string_table nw_port_vlan_proto[] = {
++const nw_string_table_t nw_port_vlan_proto[] = {
+ { NW_VLAN_PROTO_8021Q, "802.1Q" },
+ { NW_VLAN_PROTO_8021ad, "802.1ad" },
+ { -1, NULL },
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index d94e270..270ed6b 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -129,14 +129,14 @@ static inline void nw_string_empty(char* s) {
+ Tables
+ */
+
+-struct nw_string_table {
++typedef struct nw_string_table {
+ const int id;
+ const char* string;
+-};
++} nw_string_table_t;
+
+ static inline const char* nw_string_table_lookup_string(
+- const struct nw_string_table* table, const int id) {
+- const struct nw_string_table* entry = NULL;
++ const nw_string_table_t* table, const int id) {
++ const nw_string_table_t* entry = NULL;
+
+ for (entry = table; entry->string; entry++)
+ if (entry->id == id)
+@@ -146,8 +146,8 @@ static inline const char* nw_string_table_lookup_string(
+ }
+
+ static inline int nw_string_table_lookup_id(
+- const struct nw_string_table* table, const char* string) {
+- const struct nw_string_table* entry = NULL;
++ const nw_string_table_t* table, const char* string) {
++ const nw_string_table_t* entry = NULL;
+
+ for (entry = table; entry->string; entry++)
+ if (strcmp(entry->string, string) == 0)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,184 @@
+From 7442668a42febaf6ad533c6d7647ad7847cb4775 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:46:20 +0000
+Subject: [PATCH 264/304] config: Implement option that looks up string tables
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 37 +++++++++++++++++++++++++++++++++++++
+ src/networkd/config.h | 14 ++++++++++++++
+ src/networkd/port-bonding.c | 25 ++-----------------------
+ src/networkd/port-vlan.c | 25 ++-----------------------
+ 4 files changed, 55 insertions(+), 46 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 39e9881..ee5e8b8 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -570,6 +570,43 @@ int nw_config_write_string(nw_config* config,
+ return nw_config_set(config, key, *(const char**)value);
+ }
+
++// String Table
++
++int nw_config_read_string_table(nw_config* config, const char* key, void* value, void* data) {
++ const char* s = NULL;
++ int* v = (int*)value;
++
++ const nw_string_table_t* table = (nw_string_table_t*)data;
++
++ // Fetch the string
++ s = nw_config_get(config, key);
++ if (!s)
++ return -errno;
++
++ // Lookup the string in the table
++ *v = nw_string_table_lookup_id(table, s);
++
++ // If the result is negative, nothing was found
++ if (*v < 0)
++ return -EINVAL;
++
++ return 0;
++}
++
++int nw_config_write_string_table(nw_config* config,
++ const char* key, const void* value, void* data) {
++ int* v = (int*)value;
++
++ const nw_string_table_t* table = (nw_string_table_t*)data;
++
++ // Lookup the string
++ const char* s = nw_string_table_lookup_string(table, *v);
++ if (!s)
++ return -errno;
++
++ return nw_config_set(config, key, s);
++}
++
+ // Address
+
+ int nw_config_read_address(nw_config* config, const char* key, void* value, void* data) {
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index d6b8db8..63e9d18 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -73,18 +73,32 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ #define NW_CONFIG_OPTION(config, key, value, read_callback, write_callback, data) \
+ nw_config_option_add(config, key, value, read_callback, write_callback, data)
+
++// String
++
+ #define NW_CONFIG_OPTION_STRING(config, key, value) \
+ nw_config_option_add(config, key, value, nw_config_read_string, nw_config_write_string, NULL)
+
+ int nw_config_read_string(nw_config* config, const char* key, void* value, void* data);
+ int nw_config_write_string(nw_config* config, const char* key, const void* value, void* data);
+
++// String Table
++
++#define NW_CONFIG_OPTION_STRING_TABLE(config, key, value, table) \
++ nw_config_option_add(config, key, value, nw_config_read_string_table, nw_config_write_string_table, (void*)table)
++
++int nw_config_read_string_table(nw_config* config, const char* key, void* value, void* data);
++int nw_config_write_string_table(nw_config* config, const char* key, const void* value, void* data);
++
++// Integer
++
+ #define NW_CONFIG_OPTION_INT(config, key, value) \
+ nw_config_option_add(config, key, value, nw_config_read_int, nw_config_write_int, NULL)
+
+ int nw_config_read_int(nw_config* config, const char* key, void* value, void* data);
+ int nw_config_write_int(nw_config* config, const char* key, const void* value, void* data);
+
++// Address
++
+ #define NW_CONFIG_OPTION_ADDRESS(config, key, value) \
+ nw_config_option_add(config, key, value, nw_config_read_address, nw_config_write_address, NULL)
+
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index b1d2c18..4e4c8a7 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -40,33 +40,12 @@ const nw_string_table_t nw_port_bonding_mode[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_port_bonding_mode_t, nw_port_bonding_mode)
+
+-static int nw_port_bonding_read_mode(nw_config* config,
+- const char* key, void* value, void* data) {
+- int* mode = (int*)value;
+-
+- const char* p = nw_config_get(config, key);
+- if (p) {
+- *mode = nw_port_bonding_mode_from_string(p);
+- if (!*mode)
+- return -errno;
+- }
+-
+- return 0;
+-}
+-
+-static int nw_port_bonding_write_mode(nw_config* config,
+- const char* key, const void* value, void* data) {
+- const int* mode = (int*)value;
+-
+- return nw_config_set(config, key, nw_port_bonding_mode_to_string(*mode));
+-}
+-
+ static int nw_port_bonding_setup(nw_port* port) {
+ int r;
+
+ // Mode
+- r = NW_CONFIG_OPTION(port->config, "BONDING_MODE", &port->bonding.mode,
+- nw_port_bonding_read_mode, nw_port_bonding_write_mode, NULL);
++ r = NW_CONFIG_OPTION_STRING_TABLE(port->config,
++ "BONDING_MODE", &port->bonding.mode, nw_port_bonding_mode);
+ if (r < 0)
+ return r;
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 792fd28..8f5372e 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -38,27 +38,6 @@ const nw_string_table_t nw_port_vlan_proto[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
+
+-static int nw_port_vlan_read_proto(nw_config* config,
+- const char* key, void* value, void* data) {
+- nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)value;
+-
+- const char* p = nw_config_get(config, key);
+- if (p) {
+- *proto = nw_port_vlan_proto_from_string(p);
+- if (!*proto)
+- return -errno;
+- }
+-
+- return 0;
+-}
+-
+-static int nw_port_vlan_write_proto(nw_config* config,
+- const char* key, const void* value, void* data) {
+- const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)value;
+-
+- return nw_config_set(config, key, nw_port_vlan_proto_to_string(*proto));
+-}
+-
+ static int nw_port_vlan_setup(nw_port* port) {
+ int r;
+
+@@ -68,8 +47,8 @@ static int nw_port_vlan_setup(nw_port* port) {
+ return r;
+
+ // VLAN Protocol
+- r = NW_CONFIG_OPTION(port->config, "VLAN_PROTO", &port->vlan.proto,
+- nw_port_vlan_read_proto, nw_port_vlan_write_proto, NULL);
++ r = NW_CONFIG_OPTION_STRING_TABLE(port->config,
++ "VLAN_PROTO", &port->vlan.proto, nw_port_vlan_proto);
+ if (r < 0)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,32 @@
+From d200865e663a1a15b43d7462cbcddf7c13dc6997 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:48:20 +0000
+Subject: [PATCH 265/304] ports: Store the parent name
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 8f5372e..45abebe 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -209,9 +209,13 @@ int nw_port_set_vlan_parent(nw_port* port, nw_port* parent) {
+ }
+
+ // Store the new parent
+- if (parent)
++ if (parent) {
+ port->vlan.parent = nw_port_ref(parent);
+
++ // Store the name
++ nw_string_set(port->vlan.__parent_name, nw_port_name(parent));
++ }
++
+ DEBUG("Port %s: Set VLAN parent to %s\n", port->name, nw_port_name(port->vlan.parent));
+
+ return 0;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,49 @@
+From 4db7e54816eb20319d2f90336a029386b0cd3e91 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 15:49:27 +0000
+Subject: [PATCH 266/304] ports: VLAN: Make all constants uppercase
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.c | 4 ++--
+ src/networkd/port-vlan.h | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 45abebe..56b0274 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -32,7 +32,7 @@
+
+ const nw_string_table_t nw_port_vlan_proto[] = {
+ { NW_VLAN_PROTO_8021Q, "802.1Q" },
+- { NW_VLAN_PROTO_8021ad, "802.1ad" },
++ { NW_VLAN_PROTO_8021AD, "802.1ad" },
+ { -1, NULL },
+ };
+
+@@ -161,7 +161,7 @@ int nw_port_set_vlan_id(nw_port* port, int id) {
+ int nw_port_set_vlan_proto(nw_port* port, const nw_port_vlan_proto_t proto) {
+ switch (proto) {
+ case NW_VLAN_PROTO_8021Q:
+- case NW_VLAN_PROTO_8021ad:
++ case NW_VLAN_PROTO_8021AD:
+ port->vlan.proto = proto;
+ break;
+
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index 17d3134..b4df562 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -28,7 +28,7 @@
+
+ typedef enum nw_port_vlan_proto {
+ NW_VLAN_PROTO_8021Q = ETH_P_8021Q,
+- NW_VLAN_PROTO_8021ad = ETH_P_8021AD,
++ NW_VLAN_PROTO_8021AD = ETH_P_8021AD,
+ } nw_port_vlan_proto_t;
+
+ struct nw_port_vlan {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,440 @@
+From c464b5d1e146402f153f98a5e5d04b76b4d74e10 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:19:05 +0000
+Subject: [PATCH 267/304] ports: Unify type
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-bonding.c | 17 ++++------
+ src/networkd/port-bonding.h | 2 +-
+ src/networkd/port-dummy.c | 2 +-
+ src/networkd/port-dummy.h | 2 +-
+ src/networkd/port-vlan.c | 19 +++++------
+ src/networkd/port-vlan.h | 2 +-
+ src/networkd/port.c | 68 ++++++++++++++++---------------------
+ src/networkd/port.h | 66 +++++++++++++++++------------------
+ 8 files changed, 80 insertions(+), 98 deletions(-)
+
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index 4e4c8a7..ad02b5d 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -76,20 +76,17 @@ ERROR:
+ return r;
+ }
+
+-const nw_port_info_t nw_port_info_bonding = {
++const nw_port_type_t nw_port_type_bonding = {
+ .kind = "bond",
+
+- // Operations
+- .ops = {
+- // Configuration
+- .setup = nw_port_bonding_setup,
++ // Configuration
++ .setup = nw_port_bonding_setup,
+
+- // Link
+- .create_link = nw_port_bonding_create_link,
++ // Link
++ .create_link = nw_port_bonding_create_link,
+
+- // JSON
+- .to_json = nw_port_bonding_to_json,
+- },
++ // JSON
++ .to_json = nw_port_bonding_to_json,
+ };
+
+ const char* nw_port_bonding_get_mode(nw_port* port) {
+diff --git a/src/networkd/port-bonding.h b/src/networkd/port-bonding.h
+index e21c251..e5c8c32 100644
+--- a/src/networkd/port-bonding.h
++++ b/src/networkd/port-bonding.h
+@@ -39,7 +39,7 @@ struct nw_port_bonding {
+ nw_port_bonding_mode_t mode;
+ };
+
+-extern const nw_port_info_t nw_port_info_bonding;
++extern const nw_port_type_t nw_port_type_bonding;
+
+ const char* nw_port_bonding_get_mode(nw_port* port);
+ int nw_port_bonding_set_mode(nw_port* port, const char* mode);
+diff --git a/src/networkd/port-dummy.c b/src/networkd/port-dummy.c
+index e71e740..8a44008 100644
+--- a/src/networkd/port-dummy.c
++++ b/src/networkd/port-dummy.c
+@@ -20,6 +20,6 @@
+
+ #include "port-dummy.h"
+
+-const nw_port_info_t nw_port_info_dummy = {
++const nw_port_type_t nw_port_type_dummy = {
+ .kind = "dummy",
+ };
+diff --git a/src/networkd/port-dummy.h b/src/networkd/port-dummy.h
+index b74c991..34a7265 100644
+--- a/src/networkd/port-dummy.h
++++ b/src/networkd/port-dummy.h
+@@ -23,6 +23,6 @@
+
+ #include "port.h"
+
+-extern const nw_port_info_t nw_port_info_dummy;
++extern const nw_port_type_t nw_port_type_dummy;
+
+ #endif /* NETWORKD_PORT_DUMMY_H */
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 56b0274..06047ab 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -106,22 +106,19 @@ ERROR:
+ return r;
+ }
+
+-const nw_port_info_t nw_port_info_vlan = {
++const nw_port_type_t nw_port_type_vlan = {
+ .kind = "vlan",
+
+- // Operations
+- .ops = {
+- // Configuration
+- .setup = nw_port_vlan_setup,
++ // Configuration
++ .setup = nw_port_vlan_setup,
+
+- .get_parent_port = nw_port_get_vlan_parent,
++ .get_parent_port = nw_port_get_vlan_parent,
+
+- // Link
+- .create_link = nw_port_vlan_create_link,
++ // Link
++ .create_link = nw_port_vlan_create_link,
+
+- // JSON
+- .to_json = nw_port_vlan_to_json,
+- },
++ // JSON
++ .to_json = nw_port_vlan_to_json,
+ };
+
+ /*
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index b4df562..6ecf6a4 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -45,7 +45,7 @@ struct nw_port_vlan {
+ char __parent_name[IF_NAMESIZE];
+ };
+
+-extern const nw_port_info_t nw_port_info_vlan;
++extern const nw_port_type_t nw_port_type_vlan;
+
+ // ID
+ int nw_port_get_vlan_id(nw_port* port);
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index cab8fc5..c4d6ffe 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -37,26 +37,14 @@
+ #include "stats-collector.h"
+ #include "string.h"
+
+-static const struct nw_port_type_map {
+- nw_port_type_t type;
+- const char* name;
+-} nw_port_type_map[] = {
++static const nw_string_table_t nw_port_type_id[] = {
+ { NW_PORT_BONDING, "bonding" },
+ { NW_PORT_DUMMY, "dummy" },
+ { NW_PORT_VLAN, "vlan" },
+- { NW_PORT_UNKNOWN, NULL },
++ { -1, NULL },
+ };
+
+-static nw_port_type_t nw_port_type_from_string(const char* s) {
+- const struct nw_port_type_map* map = NULL;
+-
+- for (map = nw_port_type_map; *map->name; map++) {
+- if (strcmp(map->name, s) == 0)
+- return map->type;
+- }
+-
+- return NW_PORT_UNKNOWN;
+-}
++NW_STRING_TABLE_LOOKUP(nw_port_type_id_t, nw_port_type_id)
+
+ static void nw_port_free(nw_port* port) {
+ if (port->link)
+@@ -119,8 +107,8 @@ static int nw_port_setup(nw_port* port) {
+ goto ERROR;
+
+ // Call any custom initialization
+- if (NW_PORT_OPS(port)->setup) {
+- r = NW_PORT_OPS(port)->setup(port);
++ if (NW_PORT_TYPE(port)->setup) {
++ r = NW_PORT_TYPE(port)->setup(port);
+ if (r < 0)
+ goto ERROR;
+ }
+@@ -140,7 +128,7 @@ ERROR:
+ }
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+- nw_port_type_t type, const char* name, nw_config* config) {
++ const nw_port_type_id_t type, const char* name, nw_config* config) {
+ int r;
+
+ // Allocate a new object
+@@ -154,21 +142,18 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ // Initialize reference counter
+ p->nrefs = 1;
+
+- // Store the type
+- p->type = type;
+-
+ // Set operations
+- switch (p->type) {
++ switch (type) {
+ case NW_PORT_BONDING:
+- p->info = &nw_port_info_bonding;
++ p->type = &nw_port_type_bonding;
+ break;
+
+ case NW_PORT_DUMMY:
+- p->info = &nw_port_info_dummy;
++ p->type = &nw_port_type_dummy;
+ break;
+
+ case NW_PORT_VLAN:
+- p->info = &nw_port_info_vlan;
++ p->type = &nw_port_type_vlan;
+ break;
+ }
+
+@@ -214,7 +199,7 @@ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+ }
+
+ // Create a new port
+- r = nw_port_create(port, daemon, nw_port_type_from_string(type), name, config);
++ r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config);
+ if (r)
+ goto ERROR;
+
+@@ -286,7 +271,7 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ nw_port* dropped_port = (nw_port*)data;
+ int r;
+
+- switch (port->type) {
++ switch (port->type->id) {
+ case NW_PORT_VLAN:
+ if (port->vlan.parent == dropped_port) {
+ r = nw_port_set_vlan_parent(port, NULL);
+@@ -392,10 +377,10 @@ static int nw_port_is_disabled(nw_port* port) {
+ }
+
+ nw_port* nw_port_get_parent_port(nw_port* port) {
+- if (!NW_PORT_OPS(port)->get_parent_port)
++ if (!NW_PORT_TYPE(port)->get_parent_port)
+ return NULL;
+
+- return NW_PORT_OPS(port)->get_parent_port(port);
++ return NW_PORT_TYPE(port)->get_parent_port(port);
+ }
+
+ static nw_link* nw_port_get_parent_link(nw_port* port) {
+@@ -444,6 +429,13 @@ static int nw_port_create_link(nw_port* port) {
+
+ DEBUG("Creating port %s...\n", port->name);
+
++ // Check the kind
++ if (!NW_PORT_TYPE(port)->kind) {
++ ERROR("Port type has no kind\n");
++ r = -ENOTSUP;
++ goto ERROR;
++ }
++
+ // Create a new link
+ r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0) {
+@@ -481,14 +473,14 @@ static int nw_port_create_link(nw_port* port) {
+ goto ERROR;
+
+ // Run the custom setup
+- if (NW_PORT_OPS(port)->create_link) {
+- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_INFO(port)->kind);
++ if (NW_PORT_TYPE(port)->create_link) {
++ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, NW_PORT_TYPE(port)->kind);
+ if (r < 0) {
+ ERROR("Could not open IFLA_INFO_DATA container: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+- r = NW_PORT_OPS(port)->create_link(port, m);
++ r = NW_PORT_TYPE(port)->create_link(port, m);
+ if (r) {
+ ERROR("Could not create port %s: %m\n", port->name);
+ goto ERROR;
+@@ -501,7 +493,7 @@ static int nw_port_create_link(nw_port* port) {
+
+ // Just set IFLA_INFO_KIND if there is no custom function
+ } else {
+- r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_INFO(port)->kind);
++ r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, NW_PORT_TYPE(port)->kind);
+ if (r < 0)
+ goto ERROR;
+ }
+@@ -588,8 +580,8 @@ int nw_port_update_stats(nw_port* port) {
+ return 0;
+ }
+
+-int nw_port_check_type(nw_port* port, const nw_port_type_t type) {
+- if (port->type == type)
++int nw_port_check_type(nw_port* port, const nw_port_type_id_t type) {
++ if (port->type->id == type)
+ return 0;
+
+ errno = ENOTSUP;
+@@ -616,7 +608,7 @@ int nw_port_to_json(nw_port* port, struct json_object** object) {
+ goto ERROR;
+
+ // Add Type
+- r = json_object_add_string(o, "Type", NW_PORT_INFO(port)->kind);
++ r = json_object_add_string(o, "Type", nw_port_type_id_to_string(port->type->id));
+ if (r < 0)
+ goto ERROR;
+
+@@ -629,8 +621,8 @@ int nw_port_to_json(nw_port* port, struct json_object** object) {
+ }
+
+ // Call custom stuff
+- if (NW_PORT_OPS(port)->to_json) {
+- r = NW_PORT_OPS(port)->to_json(port, o);
++ if (NW_PORT_TYPE(port)->to_json) {
++ r = NW_PORT_TYPE(port)->to_json(port, o);
+ if (r < 0)
+ goto ERROR;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index ef68a89..c9add71 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -31,50 +31,49 @@
+
+ #define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+
+-typedef enum nw_port_type {
+- NW_PORT_UNKNOWN = 0,
+- NW_PORT_BONDING,
+- NW_PORT_DUMMY,
+- NW_PORT_VLAN,
+-} nw_port_type_t;
+-
+ // VLAN
+ #define NW_VLAN_ID_INVALID 0
+ #define NW_VLAN_ID_MIN 1
+ #define NW_VLAN_ID_MAX 4096
+
+ typedef struct nw_port nw_port;
+-typedef struct nw_port_info nw_port_info_t;
+
+-#include "address.h"
+-#include "config.h"
+-#include "daemon.h"
+-#include "json.h"
+-#include "port-bonding.h"
+-#include "port-vlan.h"
++typedef enum nw_port_type_id {
++ NW_PORT_UNKNOWN = 0,
++ NW_PORT_BONDING,
++ NW_PORT_DUMMY,
++ NW_PORT_VLAN,
++} nw_port_type_id_t;
++
++typedef struct nw_port_type {
++ // Type ID
++ nw_port_type_id_t id;
+
+-struct nw_port_info {
+ // IFLA_INFO_KIND/IFLA_INFO_DATA
+ const char* kind;
+
+- struct nw_port_ops {
+- // Configuration
+- int (*setup)(nw_port* port);
++ // Configuration
++ int (*setup)(nw_port* port);
+
+- // Get Parent Port
+- nw_port* (*get_parent_port)(nw_port* port);
++ // Get Parent Port
++ nw_port* (*get_parent_port)(nw_port* port);
+
+- // Link
+- int (*create_link)(nw_port* port, sd_netlink_message* message);
+- int (*destroy_link)(nw_port* port);
++ // Link
++ int (*create_link)(nw_port* port, sd_netlink_message* message);
++ int (*destroy_link)(nw_port* port);
+
+- // JSON
+- int (*to_json)(nw_port* port, struct json_object* object);
+- } ops;
+-};
++ // JSON
++ int (*to_json)(nw_port* port, struct json_object* object);
++} nw_port_type_t;
++
++#include "address.h"
++#include "config.h"
++#include "daemon.h"
++#include "json.h"
++#include "port-bonding.h"
++#include "port-vlan.h"
+
+-#define NW_PORT_INFO(port) (port->info)
+-#define NW_PORT_OPS(port) (&NW_PORT_INFO(port)->ops)
++#define NW_PORT_TYPE(port) (port->type)
+
+ struct nw_port {
+ nw_daemon* daemon;
+@@ -83,7 +82,7 @@ struct nw_port {
+ // Link
+ nw_link* link;
+
+- nw_port_type_t type;
++ const nw_port_type_t* type;
+ char name[IF_NAMESIZE];
+
+ // Configuration
+@@ -92,9 +91,6 @@ struct nw_port {
+ // Common attributes
+ nw_address_t address;
+
+- // Type Operations
+- const nw_port_info_t* info;
+-
+ // Bonding Settings
+ struct nw_port_bonding bonding;
+
+@@ -103,7 +99,7 @@ struct nw_port {
+ };
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+- nw_port_type_t type, const char* name, nw_config* config);
++ const nw_port_type_id_t type, const char* name, nw_config* config);
+ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+ const char* name, const char* path);
+
+@@ -130,7 +126,7 @@ int nw_port_reconfigure(nw_port* port);
+
+ int nw_port_has_carrier(nw_port* port);
+
+-int nw_port_check_type(nw_port* port, const nw_port_type_t type);
++int nw_port_check_type(nw_port* port, const nw_port_type_id_t type);
+
+ // Stats
+ const struct rtnl_link_stats64* nw_port_get_stats64(nw_port* port);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,46 @@
+From 81ab55ae98a4280265fc7b3bee2c3770d621c152 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:22:11 +0000
+Subject: [PATCH 268/304] ports: Move VLAN constants to VLAN header
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.h | 5 +++++
+ src/networkd/port.h | 5 -----
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index 6ecf6a4..e17f7e7 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -31,6 +31,11 @@ typedef enum nw_port_vlan_proto {
+ NW_VLAN_PROTO_8021AD = ETH_P_8021AD,
+ } nw_port_vlan_proto_t;
+
++// VLAN ID
++#define NW_VLAN_ID_INVALID 0
++#define NW_VLAN_ID_MIN 1
++#define NW_VLAN_ID_MAX 4096
++
+ struct nw_port_vlan {
+ nw_port* parent;
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index c9add71..15b8bc1 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -31,11 +31,6 @@
+
+ #define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+
+-// VLAN
+-#define NW_VLAN_ID_INVALID 0
+-#define NW_VLAN_ID_MIN 1
+-#define NW_VLAN_ID_MAX 4096
+-
+ typedef struct nw_port nw_port;
+
+ typedef enum nw_port_type_id {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,48 @@
+From b249255d5ca7cdff8ea97e0a0cdd39f1bf0e6e63 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:26:13 +0000
+Subject: [PATCH 269/304] ports: Drop UNKNOWN type
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 4 ----
+ src/networkd/port.h | 1 -
+ 2 files changed, 5 deletions(-)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index c4d6ffe..c14d439 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -261,9 +261,6 @@ int nw_port_destroy(nw_port* port) {
+ if (r)
+ return r;
+
+- // Reset type
+- port->type = NW_PORT_UNKNOWN;
+-
+ return 0;
+ }
+
+@@ -282,7 +279,6 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+
+ case NW_PORT_BONDING:
+ case NW_PORT_DUMMY:
+- case NW_PORT_UNKNOWN:
+ break;
+ }
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 15b8bc1..b96b13e 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -34,7 +34,6 @@
+ typedef struct nw_port nw_port;
+
+ typedef enum nw_port_type_id {
+- NW_PORT_UNKNOWN = 0,
+ NW_PORT_BONDING,
+ NW_PORT_DUMMY,
+ NW_PORT_VLAN,
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,74 @@
+From ea7dc1bbab6162c319ce738161af813f2d1b7241 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:35:33 +0000
+Subject: [PATCH 270/304] ports: Implement scaffolding for configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.c | 30 ++++++++++++++++++++++++++++++
+ src/networkd/port.h | 1 +
+ 2 files changed, 31 insertions(+)
+
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index c14d439..fb5d418 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -127,6 +127,19 @@ ERROR:
+ return r;
+ }
+
++static int nw_port_validate(nw_port* port) {
++ int r = 0;
++
++ // Validate the port configuration
++ if (NW_PORT_TYPE(port)->validate) {
++ r = NW_PORT_TYPE(port)->validate(port);
++ if (r < 0)
++ ERROR("Could not check configuration for %s: %s\n", port->name, strerror(-r));
++ }
++
++ return r;
++}
++
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ const nw_port_type_id_t type, const char* name, nw_config* config) {
+ int r;
+@@ -172,6 +185,23 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ if (r)
+ goto ERROR;
+
++ // Validate the configuration
++ r = nw_port_validate(p);
++ switch (r) {
++ // Configuration is valid
++ case 0:
++ break;
++
++ // Configuration is invalid
++ case 1:
++ ERROR("%s: Invalid configuration\n", p->name);
++ goto ERROR;
++
++ // Error
++ default:
++ goto ERROR;
++ }
++
+ *port = p;
+ return 0;
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index b96b13e..7c2e436 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -48,6 +48,7 @@ typedef struct nw_port_type {
+
+ // Configuration
+ int (*setup)(nw_port* port);
++ int (*validate)(nw_port* port);
+
+ // Get Parent Port
+ nw_port* (*get_parent_port)(nw_port* port);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,53 @@
+From 749b1a85fdbf4ec0ec0ac3b3fe07cdeffd72460c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:41:13 +0000
+Subject: [PATCH 271/304] ports: VLAN: Validate configuration
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 06047ab..25a59ee 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -60,6 +60,27 @@ static int nw_port_vlan_setup(nw_port* port) {
+ return 0;
+ }
+
++static int nw_port_vlan_validate(nw_port* port) {
++ // Check if the VLAN ID is within range
++ if (port->vlan.id < NW_VLAN_ID_MIN || port->vlan.id > NW_VLAN_ID_MAX) {
++ ERROR("%s: Invalid VLAN ID %d\n", port->name, port->vlan.id);
++ return 1;
++ }
++
++ // Validate protocol
++ switch (port->vlan.proto) {
++ case NW_VLAN_PROTO_8021Q:
++ case NW_VLAN_PROTO_8021AD:
++ break;
++
++ default:
++ ERROR("%p: Invalid VLAN protocol\n", port->name);
++ return 1;
++ }
++
++ return 0;
++}
++
+ static int nw_port_vlan_create_link(nw_port* port, sd_netlink_message* m) {
+ int r;
+
+@@ -111,6 +132,7 @@ const nw_port_type_t nw_port_type_vlan = {
+
+ // Configuration
+ .setup = nw_port_vlan_setup,
++ .validate = nw_port_vlan_validate,
+
+ .get_parent_port = nw_port_get_vlan_parent,
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,40 @@
+From ac7659bf6a5f84f473424c6976e4ccd6c2e72979 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 16:46:12 +0000
+Subject: [PATCH 272/304] daemon: Don't crash when a port could not be loaded
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/ports.c | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index f0a3ebb..65545e8 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -152,8 +152,20 @@ static int __nw_ports_enumerate(const char* path, const struct stat* s, void* da
+
+ // Create a new port
+ r = nw_port_create_from_config(&port, ports->daemon, name, path);
+- if (r)
+- goto ERROR;
++ switch (r) {
++ // All okay
++ case 0:
++ break;
++
++ // Invalid configuration
++ case 1:
++ ERROR("Could not open port %s\n", name);
++ r = 0;
++ goto ERROR;
++
++ default:
++ goto ERROR;
++ }
+
+ // Add the port to the list
+ r = nw_ports_add_port(ports, port);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,33 @@
+From 45a3d5aaca4e1d64132d7b4190209bb743efaa68 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 8 Jun 2023 17:05:54 +0000
+Subject: [PATCH 273/304] string: Have all functions return negative values on
+ error
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/string.h | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/src/networkd/string.h b/src/networkd/string.h
+index 270ed6b..a36b023 100644
+--- a/src/networkd/string.h
++++ b/src/networkd/string.h
+@@ -37,12 +37,11 @@ static inline int __nw_string_vformat(char* s, const size_t length,
+
+ // Catch any errors
+ if (required < 0)
+- return 1;
++ return required;
+
+ // Check if the entire string could be written
+ if ((size_t)required >= length) {
+- errno = ENOMEM;
+- return 1;
++ return -ENOMEM;
+ }
+
+ // Success
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,132 @@
+From 3138451ba526d9ada4e74cffccd10c5807c9bc8e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 04:58:39 +0000
+Subject: [PATCH 274/304] networkd: Parse command line arguments
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 45 ++++++++++++++++++++++++++++++++++++++++++-
+ src/networkd/daemon.h | 2 +-
+ src/networkd/main.c | 2 +-
+ 3 files changed, 46 insertions(+), 3 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 749a70b..31fda8e 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -19,6 +19,8 @@
+ #############################################################################*/
+
+ #include <errno.h>
++#include <getopt.h>
++#include <limits.h>
+ #include <stdlib.h>
+
+ #include <systemd/sd-bus.h>
+@@ -36,6 +38,7 @@
+ #include "logging.h"
+ #include "ports.h"
+ #include "stats-collector.h"
++#include "string.h"
+ #include "zone.h"
+ #include "zones.h"
+
+@@ -45,6 +48,7 @@
+ struct nw_daemon {
+ int nrefs;
+
++ char config_path[PATH_MAX];
+ nw_config* config;
+
+ // Event Loop
+@@ -91,6 +95,40 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig
+ return 0;
+ }
+
++static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) {
++ enum {
++ ARG_CONFIG,
++ };
++ int r;
++
++ static const struct option options[] = {
++ { "config", required_argument, NULL, ARG_CONFIG },
++ { NULL },
++ };
++ int c;
++
++ for (;;) {
++ c = getopt_long(argc, argv, "", options, NULL);
++ if (c < 0)
++ break;
++
++ switch (c) {
++ case ARG_CONFIG:
++ r = nw_string_set(daemon->config_path, optarg);
++ if (r < 0)
++ return r;
++
++ break;
++
++ // Abort on any unrecognised option
++ default:
++ return -EINVAL;
++ }
++ }
++
++ return 0;
++}
++
+ static int nw_daemon_setup_loop(nw_daemon* daemon) {
+ int r;
+
+@@ -398,7 +436,7 @@ static int nw_daemon_setup(nw_daemon* daemon) {
+ return 0;
+ }
+
+-int nw_daemon_create(nw_daemon** daemon) {
++int nw_daemon_create(nw_daemon** daemon, int argc, char* argv[]) {
+ int r;
+
+ nw_daemon* d = calloc(1, sizeof(*d));
+@@ -408,6 +446,11 @@ int nw_daemon_create(nw_daemon** daemon) {
+ // Initialize reference counter
+ d->nrefs = 1;
+
++ // Parse command line arguments
++ r = nw_daemon_parse_argv(d, argc, argv);
++ if (r)
++ goto ERROR;
++
+ // Setup the daemon
+ r = nw_daemon_setup(d);
+ if (r)
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 74e19e6..8653af3 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -33,7 +33,7 @@ typedef struct nw_daemon nw_daemon;
+ #include "zone.h"
+ #include "zones.h"
+
+-int nw_daemon_create(nw_daemon** daemon);
++int nw_daemon_create(nw_daemon** daemon, int argc, char* argv[]);
+
+ nw_daemon* nw_daemon_ref(nw_daemon* daemon);
+ nw_daemon* nw_daemon_unref(nw_daemon* daemon);
+diff --git a/src/networkd/main.c b/src/networkd/main.c
+index c8b9a79..f5f09f5 100644
+--- a/src/networkd/main.c
++++ b/src/networkd/main.c
+@@ -222,7 +222,7 @@ int main(int argc, char** argv) {
+ return r;
+
+ // Create the daemon
+- r = nw_daemon_create(&daemon);
++ r = nw_daemon_create(&daemon, argc, argv);
+ if (r)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,163 @@
+From 5a925d5f91e32245446ba568da2549b08913c0c7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 05:28:51 +0000
+Subject: [PATCH 275/304] networkd: Open config directory and keep a handle to
+ it
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 2 +-
+ src/networkd/config.h | 1 +
+ src/networkd/daemon.c | 71 ++++++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 66 insertions(+), 8 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index ee5e8b8..b7f3f72 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -229,7 +229,7 @@ int nw_config_flush(nw_config* config) {
+ return 0;
+ }
+
+-static int nw_config_readf(nw_config* config, FILE* f) {
++int nw_config_readf(nw_config* config, FILE* f) {
+ char* line = NULL;
+ size_t length = 0;
+ int r;
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 63e9d18..d532da3 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -40,6 +40,7 @@ const char* nw_config_path(nw_config* config);
+
+ int nw_config_flush(nw_config* config);
+
++int nw_config_readf(nw_config* config, FILE* f);
+ int nw_config_read(nw_config* config);
+ int nw_config_write(nw_config* config);
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index 31fda8e..e645bee 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -18,7 +18,9 @@
+ # #
+ #############################################################################*/
+
++#include <dirent.h>
+ #include <errno.h>
++#include <fcntl.h>
+ #include <getopt.h>
+ #include <limits.h>
+ #include <stdlib.h>
+@@ -48,7 +50,7 @@
+ struct nw_daemon {
+ int nrefs;
+
+- char config_path[PATH_MAX];
++ DIR* config_dir;
+ nw_config* config;
+
+ // Event Loop
+@@ -95,6 +97,43 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig
+ return 0;
+ }
+
++/*
++ Configuration
++*/
++
++static int nw_daemon_config_open(nw_daemon* daemon, const char* path) {
++ daemon->config_dir = opendir(path);
++ if (!daemon->config_dir) {
++ ERROR("Could not open %s: %m\n", path);
++ return -errno;
++ }
++
++ return 0;
++}
++
++static FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) {
++ int r;
++
++ // If no configuration path has been opened yet, we will open something
++ if (!daemon->config_dir) {
++ r = nw_daemon_config_open(daemon, CONFIG_DIR);
++ if (r < 0) {
++ errno = -r;
++ return NULL;
++ }
++ }
++
++ // Open the file
++ int fd = openat(dirfd(daemon->config_dir), path, 0);
++ if (fd < 0) {
++ ERROR("Could not open configuration file %s: %m\n", path);
++ return NULL;
++ }
++
++ // Return a file handle
++ return fdopen(fd, mode);
++}
++
+ static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) {
+ enum {
+ ARG_CONFIG,
+@@ -114,10 +153,9 @@ static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) {
+
+ switch (c) {
+ case ARG_CONFIG:
+- r = nw_string_set(daemon->config_path, optarg);
++ r = nw_daemon_config_open(daemon, optarg);
+ if (r < 0)
+ return r;
+-
+ break;
+
+ // Abort on any unrecognised option
+@@ -174,12 +212,29 @@ static int nw_daemon_setup_loop(nw_daemon* daemon) {
+ }
+
+ static int nw_daemon_load_config(nw_daemon* daemon) {
++ FILE* f = NULL;
+ int r;
+
+- // Read configuration file
+- r = nw_config_create(&daemon->config, CONFIG_DIR "/settings");
+- if (r)
+- return r;
++ // Open the configuration file
++ f = nw_daemon_config_fopen(daemon, "settings", "r");
++ if (!f) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Create configuration
++ r = nw_config_create(&daemon->config, NULL);
++ if (r < 0)
++ goto ERROR;
++
++ // Parse configuration
++ r = nw_config_readf(daemon->config, f);
++ if (r < 0)
++ goto ERROR;
++
++ERROR:
++ if (f)
++ fclose(f);
+
+ return r;
+ }
+@@ -482,6 +537,8 @@ static void nw_daemon_free(nw_daemon* daemon) {
+ // Cleanup common objects
+ nw_daemon_cleanup(daemon);
+
++ if (daemon->config_dir)
++ closedir(daemon->config_dir);
+ if (daemon->stats_collector_event)
+ sd_event_source_unref(daemon->stats_collector_event);
+ if (daemon->bus)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,99 @@
+From f6e3fd80ad4d86681ad2949567b6763c41530f16 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 05:37:57 +0000
+Subject: [PATCH 276/304] networkd: Hold a file descriptor instead of DIR*
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/daemon.c | 34 ++++++++++++++++------------------
+ 1 file changed, 16 insertions(+), 18 deletions(-)
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index e645bee..f0ef4aa 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -18,12 +18,12 @@
+ # #
+ #############################################################################*/
+
+-#include <dirent.h>
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <getopt.h>
+ #include <limits.h>
+ #include <stdlib.h>
++#include <unistd.h>
+
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-daemon.h>
+@@ -50,7 +50,8 @@
+ struct nw_daemon {
+ int nrefs;
+
+- DIR* config_dir;
++ // Configuration
++ int configfd;
+ nw_config* config;
+
+ // Event Loop
+@@ -102,8 +103,9 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig
+ */
+
+ static int nw_daemon_config_open(nw_daemon* daemon, const char* path) {
+- daemon->config_dir = opendir(path);
+- if (!daemon->config_dir) {
++ // Open the directory
++ daemon->configfd = open(path, O_DIRECTORY);
++ if (daemon->configfd < 0) {
+ ERROR("Could not open %s: %m\n", path);
+ return -errno;
+ }
+@@ -112,19 +114,8 @@ static int nw_daemon_config_open(nw_daemon* daemon, const char* path) {
+ }
+
+ static FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) {
+- int r;
+-
+- // If no configuration path has been opened yet, we will open something
+- if (!daemon->config_dir) {
+- r = nw_daemon_config_open(daemon, CONFIG_DIR);
+- if (r < 0) {
+- errno = -r;
+- return NULL;
+- }
+- }
+-
+ // Open the file
+- int fd = openat(dirfd(daemon->config_dir), path, 0);
++ int fd = openat(daemon->configfd, path, 0);
+ if (fd < 0) {
+ ERROR("Could not open configuration file %s: %m\n", path);
+ return NULL;
+@@ -215,6 +206,13 @@ static int nw_daemon_load_config(nw_daemon* daemon) {
+ FILE* f = NULL;
+ int r;
+
++ // If no configuration path has been opened yet, we will open something
++ if (!daemon->configfd) {
++ r = nw_daemon_config_open(daemon, CONFIG_DIR);
++ if (r < 0)
++ goto ERROR;
++ }
++
+ // Open the configuration file
+ f = nw_daemon_config_fopen(daemon, "settings", "r");
+ if (!f) {
+@@ -537,8 +535,8 @@ static void nw_daemon_free(nw_daemon* daemon) {
+ // Cleanup common objects
+ nw_daemon_cleanup(daemon);
+
+- if (daemon->config_dir)
+- closedir(daemon->config_dir);
++ if (daemon->configfd > 0)
++ close(daemon->configfd);
+ if (daemon->stats_collector_event)
+ sd_event_source_unref(daemon->stats_collector_event);
+ if (daemon->bus)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,197 @@
+From c920185ee99d0f143946925aba0668785225f6f4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 07:46:40 +0000
+Subject: [PATCH 277/304] networkd: Add a simple test environment
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 24 ++++-
+ test/networkd/00_launch.t/config/settings | 0
+ test/networkd/00_launch.t/test.sh | 4 +
+ test/networkd/test.sh | 108 ++++++++++++++++++++++
+ 4 files changed, 132 insertions(+), 4 deletions(-)
+ create mode 100644 test/networkd/00_launch.t/config/settings
+ create mode 100644 test/networkd/00_launch.t/test.sh
+ create mode 100644 test/networkd/test.sh
+
+diff --git a/Makefile.am b/Makefile.am
+index f4c22b4..fe1e4d1 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -59,6 +59,7 @@ DISTCLEANFILES =
+ EXTRA_DIST =
+ INSTALL_DIRS =
+ INSTALL_EXEC_HOOKS =
++TESTS =
+ UNINSTALL_EXEC_HOOKS =
+ noinst_DATA =
+ network_PROGRAMS =
+@@ -690,12 +691,10 @@ TESTS_ENVIRONMENT = \
+
+ dist_check_DATA = \
+ test/constants.sh \
+- test/test-functions
++ test/test-functions \
++ test/networkd/test.sh
+
+ dist_check_SCRIPTS = \
+- $(TESTS)
+-
+-TESTS = \
+ test/load-library \
+ test/functions/ip/ip_detect_protocol \
+ test/functions/ip/ip_get_prefix \
+@@ -706,6 +705,23 @@ TESTS = \
+ test/functions/ip/ip_protocol_is_supported \
+ test/functions/ip/ip_split_prefix
+
++TESTS += $(dist_check_SCRIPTS)
++
++TEST_EXTENSIONS = .t
++
++NETWORKD_TESTS = \
++ test/networkd/00_launch.t
++
++TESTS += $(NETWORKD_TESTS)
++
++EXTRA_DIST += \
++ test/networkd/test.sh \
++ $(NETWORKD_TESTS)
++
++# Run all networkd tests in their own namespaces
++T_LOG_COMPILER = unshare --net --ipc --uts --user --cgroup --time --pid --fork \
++ $(SHELL) test/networkd/test.sh
++
+ # - NITSI tests ----------------------------------------------------------------
+
+ # Files for the virtual environment
+diff --git a/test/networkd/00_launch.t/config/settings b/test/networkd/00_launch.t/config/settings
+new file mode 100644
+index 0000000..e69de29
+diff --git a/test/networkd/00_launch.t/test.sh b/test/networkd/00_launch.t/test.sh
+new file mode 100644
+index 0000000..f3d7bbc
+--- /dev/null
++++ b/test/networkd/00_launch.t/test.sh
+@@ -0,0 +1,4 @@
++#!/bin/bash
++
++# Simply run networkctl to check whether it works
++./networkctl --version
+diff --git a/test/networkd/test.sh b/test/networkd/test.sh
+new file mode 100644
+index 0000000..2af7479
+--- /dev/null
++++ b/test/networkd/test.sh
+@@ -0,0 +1,108 @@
++#!/bin/bash
++###############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++###############################################################################
++
++# Break if anything fails
++set -e
++
++# Turn on job control
++set -o monitor
++
++run_script() {
++ local script="${1}"
++ shift
++
++ if [ -f "${script}" ]; then
++ echo "Launching ${script}..."
++
++ # Launch the script in a separate shell and echo every command
++ if ! ${SHELL} -xe "${script}"; then
++ echo "${script} failed" >&2
++ return 1
++ fi
++ fi
++
++ return 0
++}
++
++# Launches networkd in the background
++launch_networkd() {
++ echo "Launching networkd..."
++
++ # Launch it!
++ coproc networkd { ./networkd "$@"; }
++
++ echo "networkd launched as PID ${networkd_PID}"
++}
++
++terminate_networkd() {
++ if [ -n "${networkd_PID}" ]; then
++ # Send SIGTERM
++ kill -TERM "${networkd_PID}"
++
++ # Wait until networkd has finished
++ echo "Waiting for networkd to terminate..."
++ wait "${networkd_PID}"
++
++ echo "networkd has terminated"
++ fi
++}
++
++# Make sure networkd has been terminated when this script exits
++trap "terminate_networkd" EXIT
++
++main() {
++ local test="${1}"
++ shift
++
++ echo "Running ${test}..."
++
++ # Check if the test exists
++ if [ ! -d "${test}" ]; then
++ echo "Test '${test}' does not exist" >&2
++ return 2
++ fi
++
++ # Run prepare script
++ if ! run_script "${test}/prepare.sh"; then
++ return 1
++ fi
++
++ # Launch networkd
++ launch_networkd --config="${test}/config"
++
++ # Run test script
++ if ! run_script "${test}/test.sh"; then
++ return 1
++ fi
++
++ # Terminate networkd
++ terminate_networkd
++
++ # Run cleanup script
++ if ! run_script "${test}/cleanup.sh"; then
++ return 1
++ fi
++
++ return 0
++}
++
++# Call main()
++main "$@" || exit $?
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,48 @@
+From 9ddb7dad28a2122317b0551884640afdf14deadb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 07:49:54 +0000
+Subject: [PATCH 278/304] test: Run "ip -d link" to show the status of the
+ environment
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ test/networkd/test.sh | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/test/networkd/test.sh b/test/networkd/test.sh
+index 2af7479..a22aeb3 100644
+--- a/test/networkd/test.sh
++++ b/test/networkd/test.sh
+@@ -42,6 +42,19 @@ run_script() {
+ return 0
+ }
+
++dump_command() {
++ echo "Output of $@"
++
++ # Run the command
++ $@ 2>&1
++
++ echo "EOF"
++}
++
++dump_status() {
++ dump_command "ip -d link"
++}
++
+ # Launches networkd in the background
+ launch_networkd() {
+ echo "Launching networkd..."
+@@ -54,6 +67,9 @@ launch_networkd() {
+
+ terminate_networkd() {
+ if [ -n "${networkd_PID}" ]; then
++ # Collect some status information
++ dump_status
++
+ # Send SIGTERM
+ kill -TERM "${networkd_PID}"
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,64 @@
+From dc2a5320b848576772c261b76b23b87715d118a7 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 07:51:50 +0000
+Subject: [PATCH 279/304] networkctl: Terminate after showing help or version
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/main.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/src/networkctl/main.c b/src/networkctl/main.c
+index 0ba7284..fde77b8 100644
+--- a/src/networkctl/main.c
++++ b/src/networkctl/main.c
+@@ -21,6 +21,7 @@
+ #include <errno.h>
+ #include <getopt.h>
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <unistd.h>
+
+ #include <systemd/sd-bus.h>
+@@ -45,13 +46,13 @@ static int networkctl_main(sd_bus* bus, int argc, char* argv[]) {
+ return command_dispatch(bus, commands, argc, argv);
+ }
+
+-static int version(void) {
++static void version(void) {
+ printf("networkctl %s\n", PACKAGE_VERSION);
+
+- return 0;
++ exit(0);
+ }
+
+-static int help(void) {
++static void help(void) {
+ printf(
+ "%s [OPTIONS...] COMMAND\n\n"
+ "Options:\n"
+@@ -60,7 +61,7 @@ static int help(void) {
+ program_invocation_short_name
+ );
+
+- return 0;
++ exit(0);
+ }
+
+ static int parse_argv(int argc, char* argv[]) {
+@@ -82,10 +83,10 @@ static int parse_argv(int argc, char* argv[]) {
+
+ switch (c) {
+ case 'h':
+- return help();
++ help();
+
+ case ARG_VERSION:
+- return version();
++ version();
+
+ case '?':
+ return -EINVAL;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,84 @@
+From f1e43a02719530f865fd7818b60b706c551d143c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 08:20:41 +0000
+Subject: [PATCH 280/304] test: Be less patient if networkd does not want to
+ terminate
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ test/networkd/test.sh | 43 +++++++++++++++++++++++++++++++------------
+ 1 file changed, 31 insertions(+), 12 deletions(-)
+
+diff --git a/test/networkd/test.sh b/test/networkd/test.sh
+index a22aeb3..7690978 100644
+--- a/test/networkd/test.sh
++++ b/test/networkd/test.sh
+@@ -63,27 +63,43 @@ launch_networkd() {
+ coproc networkd { ./networkd "$@"; }
+
+ echo "networkd launched as PID ${networkd_PID}"
++
++ sleep 1
+ }
+
+ terminate_networkd() {
+- if [ -n "${networkd_PID}" ]; then
+- # Collect some status information
+- dump_status
+-
+- # Send SIGTERM
+- kill -TERM "${networkd_PID}"
++ local seconds=0
+
+- # Wait until networkd has finished
+- echo "Waiting for networkd to terminate..."
+- wait "${networkd_PID}"
++ if [ -n "${networkd_PID}" ]; then
++ while [ -n "${networkd_PID}" ] && kill -0 "${networkd_PID}"; do
++ case "${seconds}" in
++ # Send SIGTERM in the beginning
++ 0)
++ echo "Sending SIGTERM to networkd"
++ kill -TERM "${networkd_PID}"
++ ;;
++
++ # After 5 seconds, send SIGKILL
++ 5)
++ echo "Sending SIGKILL to networkd"
++ kill -KILL "${networkd_PID}"
++
++ # It is an error, if we have to kill networkd
++ exit 1
++ ;;
++ esac
++
++ # Wait for a moment
++ sleep 1
++
++ # Increment seconds
++ (( seconds++ ))
++ done
+
+ echo "networkd has terminated"
+ fi
+ }
+
+-# Make sure networkd has been terminated when this script exits
+-trap "terminate_networkd" EXIT
+-
+ main() {
+ local test="${1}"
+ shift
+@@ -112,6 +128,9 @@ main() {
+ # Terminate networkd
+ terminate_networkd
+
++ # Collect some status information
++ dump_status
++
+ # Run cleanup script
+ if ! run_script "${test}/cleanup.sh"; then
+ return 1
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From 119cc5be2dc054f619226a27ffd855e849330adf Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 09:27:51 +0000
+Subject: [PATCH 281/304] test: Run networkd as root in its own namespace
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index fe1e4d1..08e04a1 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -720,7 +720,7 @@ EXTRA_DIST += \
+
+ # Run all networkd tests in their own namespaces
+ T_LOG_COMPILER = unshare --net --ipc --uts --user --cgroup --time --pid --fork \
+- $(SHELL) test/networkd/test.sh
++ --map-root-user --keep-caps $(SHELL) test/networkd/test.sh
+
+ # - NITSI tests ----------------------------------------------------------------
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,636 @@
+From 8edf3da1c28feb4e2af80786c5e41e52f07b0f73 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 09:29:01 +0000
+Subject: [PATCH 282/304] ports: Refactor enumerating ports
+
+This entails a little rewrite how we deal with where configuration files
+are stored.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 117 ++++++++++--------------------------------
+ src/networkd/config.h | 11 ++--
+ src/networkd/daemon.c | 23 ++++++---
+ src/networkd/daemon.h | 9 ++++
+ src/networkd/port.c | 64 +++++++++++++++++------
+ src/networkd/port.h | 3 +-
+ src/networkd/ports.c | 80 +++++++++++++++++------------
+ src/networkd/zone.c | 27 ++++++++--
+ 8 files changed, 172 insertions(+), 162 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index b7f3f72..3d444c4 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -53,9 +53,6 @@ struct nw_config_option {
+ struct nw_config {
+ int nrefs;
+
+- // The path to the configuration file
+- char path[PATH_MAX];
+-
+ STAILQ_HEAD(config_entries, nw_config_entry) entries;
+
+ // Options
+@@ -118,7 +115,7 @@ static void nw_config_free(nw_config* config) {
+ free(config);
+ }
+
+-int nw_config_create(nw_config** config, const char* path) {
++int nw_config_create(nw_config** config, FILE* f) {
+ int r;
+
+ nw_config* c = calloc(1, sizeof(*c));
+@@ -134,15 +131,10 @@ int nw_config_create(nw_config** config, const char* path) {
+ // Initialise options
+ STAILQ_INIT(&c->options);
+
+- // Store the path
+- if (path) {
+- r = nw_string_set(c->path, path);
+- if (r)
+- goto ERROR;
+-
+- // Try to read the configuration from path
+- r = nw_config_read(c);
+- if (r)
++ // Read configuration
++ if (f) {
++ r = nw_config_read(c, f);
++ if (r < 0)
+ goto ERROR;
+ }
+
+@@ -156,6 +148,25 @@ ERROR:
+ return r;
+ }
+
++int nw_config_open(nw_config** config, const char* path) {
++ FILE* f = NULL;
++ int r;
++
++ // Open path
++ f = fopen(path, "r");
++ if (!f)
++ return -errno;
++
++ // Create a new configuration
++ r = nw_config_create(config, f);
++
++ERROR:
++ if (f)
++ fclose(f);
++
++ return r;
++}
++
+ nw_config* nw_config_ref(nw_config* config) {
+ config->nrefs++;
+
+@@ -170,17 +181,6 @@ nw_config* nw_config_unref(nw_config* config) {
+ return NULL;
+ }
+
+-int nw_config_destroy(nw_config* config) {
+- int r;
+-
+- // Drop all entries
+- r = nw_config_flush(config);
+- if (r)
+- return r;
+-
+- return unlink(config->path);
+-}
+-
+ int nw_config_copy(nw_config* config, nw_config** copy) {
+ struct nw_config_entry* entry = NULL;
+ nw_config* c = NULL;
+@@ -208,13 +208,6 @@ ERROR:
+ return r;
+ }
+
+-const char* nw_config_path(nw_config* config) {
+- if (*config->path)
+- return config->path;
+-
+- return NULL;
+-}
+-
+ int nw_config_flush(nw_config* config) {
+ struct nw_config_entry* entry = NULL;
+
+@@ -229,7 +222,7 @@ int nw_config_flush(nw_config* config) {
+ return 0;
+ }
+
+-int nw_config_readf(nw_config* config, FILE* f) {
++int nw_config_read(nw_config* config, FILE* f) {
+ char* line = NULL;
+ size_t length = 0;
+ int r;
+@@ -275,39 +268,7 @@ int nw_config_readf(nw_config* config, FILE* f) {
+ return r;
+ }
+
+-int nw_config_read(nw_config* config) {
+- FILE* f = NULL;
+- int r;
+-
+- // We cannot read if path is not set
+- if (!*config->path) {
+- errno = ENOTSUP;
+- return 1;
+- }
+-
+- // Open the file
+- f = fopen(config->path, "r");
+- if (!f) {
+- // Silently ignore if the file does not exist
+- if (errno == ENOENT)
+- return 0;
+-
+- ERROR("Could not read configuration file %s: %m\n", config->path);
+- r = 1;
+- goto ERROR;
+- }
+-
+- // Read from file
+- r = nw_config_readf(config, f);
+-
+-ERROR:
+- if (f)
+- fclose(f);
+-
+- return r;
+-}
+-
+-static int nw_config_writef(nw_config* config, FILE* f) {
++int nw_config_write(nw_config* config, FILE* f) {
+ struct nw_config_entry* entry = NULL;
+ int r;
+
+@@ -327,32 +288,6 @@ static int nw_config_writef(nw_config* config, FILE* f) {
+ return 0;
+ }
+
+-int nw_config_write(nw_config* config) {
+- int r;
+-
+- // We cannot write if path is not set
+- if (!*config->path) {
+- errno = ENOTSUP;
+- return 1;
+- }
+-
+- FILE* f = fopen(config->path, "w");
+- if (!f) {
+- ERROR("Failed to open %s for writing: %m\n", config->path);
+- r = 1;
+- goto ERROR;
+- }
+-
+- // Write configuration
+- r = nw_config_writef(config, f);
+-
+-ERROR:
+- if (f)
+- fclose(f);
+-
+- return r;
+-}
+-
+ static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
+ struct nw_config_entry* entry = NULL;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index d532da3..b25d05e 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -28,21 +28,18 @@
+
+ typedef struct nw_config nw_config;
+
+-int nw_config_create(nw_config** config, const char* path);
++int nw_config_create(nw_config** config, FILE* f);
++int nw_config_open(nw_config** config, const char* path);
+
+ nw_config* nw_config_ref(nw_config* config);
+ nw_config* nw_config_unref(nw_config* config);
+
+-int nw_config_destroy(nw_config* config);
+ int nw_config_copy(nw_config* config, nw_config** copy);
+
+-const char* nw_config_path(nw_config* config);
+-
+ int nw_config_flush(nw_config* config);
+
+-int nw_config_readf(nw_config* config, FILE* f);
+-int nw_config_read(nw_config* config);
+-int nw_config_write(nw_config* config);
++int nw_config_read(nw_config* config, FILE* f);
++int nw_config_write(nw_config* config, FILE* f);
+
+ int nw_config_del(nw_config* config, const char* key);
+
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index f0ef4aa..a62e343 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -113,7 +113,7 @@ static int nw_daemon_config_open(nw_daemon* daemon, const char* path) {
+ return 0;
+ }
+
+-static FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) {
++FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) {
+ // Open the file
+ int fd = openat(daemon->configfd, path, 0);
+ if (fd < 0) {
+@@ -125,6 +125,16 @@ static FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const c
+ return fdopen(fd, mode);
+ }
+
++DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path) {
++ int fd = openat(daemon->configfd, path, O_DIRECTORY);
++ if (fd < 0) {
++ ERROR("Could not open configuration directory %s: %m\n", path);
++ return NULL;
++ }
++
++ return fdopendir(fd);
++}
++
+ static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) {
+ enum {
+ ARG_CONFIG,
+@@ -221,12 +231,7 @@ static int nw_daemon_load_config(nw_daemon* daemon) {
+ }
+
+ // Create configuration
+- r = nw_config_create(&daemon->config, NULL);
+- if (r < 0)
+- goto ERROR;
+-
+- // Parse configuration
+- r = nw_config_readf(daemon->config, f);
++ r = nw_config_create(&daemon->config, f);
+ if (r < 0)
+ goto ERROR;
+
+@@ -615,10 +620,12 @@ int nw_daemon_save(nw_daemon* daemon) {
+
+ DEBUG("Saving configuration...\n");
+
++#if 0
+ // Save settings
+- r = nw_config_write(daemon->config);
++ r = nw_config_write(daemon->config, f);
+ if (r)
+ return r;
++#endif
+
+ // Save ports
+ r = nw_ports_save(daemon->ports);
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index 8653af3..b03086c 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -21,6 +21,9 @@
+ #ifndef NETWORKD_DAEMON_H
+ #define NETWORKD_DAEMON_H
+
++#include <dirent.h>
++#include <stdio.h>
++
+ #include <systemd/sd-bus.h>
+ #include <systemd/sd-netlink.h>
+
+@@ -44,6 +47,12 @@ int nw_daemon_reload(nw_daemon* daemon);
+
+ int nw_daemon_save(nw_daemon* daemon);
+
++/*
++ Configuration
++*/
++FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode);
++DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path);
++
+ /*
+ Bus
+ */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index fb5d418..7d654e3 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -147,7 +147,7 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ // Allocate a new object
+ nw_port* p = calloc(1, sizeof(*p));
+ if (!p)
+- return 1;
++ return -errno;
+
+ // Store a reference to the daemon
+ p->daemon = nw_daemon_ref(daemon);
+@@ -172,17 +172,17 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+
+ // Store the name
+ r = nw_string_set(p->name, name);
+- if (r)
++ if (r < 0)
+ goto ERROR;
+
+ // Copy the configuration
+ r = nw_config_copy(config, &p->config);
+- if (r)
++ if (r < 0)
+ goto ERROR;
+
+ // Setup the port
+ r = nw_port_setup(p);
+- if (r)
++ if (r < 0)
+ goto ERROR;
+
+ // Validate the configuration
+@@ -210,32 +210,47 @@ ERROR:
+ return r;
+ }
+
+-int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+- const char* name, const char* path) {
++int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) {
+ nw_config* config = NULL;
++ FILE* f = NULL;
++ char path[PATH_MAX];
+ int r;
+
++ // Make path
++ r = nw_string_format(path, "ports/%s", name);
++ if (r < 0)
++ goto ERROR;
++
++ // Open the configuration file
++ f = nw_daemon_config_fopen(daemon, path, "r");
++ if (!f) {
++ r = -errno;
++ goto ERROR;
++ }
++
+ // Initialize the configuration
+- r = nw_config_create(&config, path);
+- if (r)
++ r = nw_config_create(&config, f);
++ if (r < 0)
+ goto ERROR;
+
+ // Fetch the type
+ const char* type = nw_config_get(config, "TYPE");
+ if (!type) {
+ ERROR("Port configuration %s has no TYPE\n", path);
+- r = 1;
++ r = -ENOTSUP;
+ goto ERROR;
+ }
+
+ // Create a new port
+ r = nw_port_create(port, daemon, nw_port_type_id_from_string(type), name, config);
+- if (r)
++ if (r < 0)
+ goto ERROR;
+
+ ERROR:
+ if (config)
+ nw_config_unref(config);
++ if (f)
++ fclose(f);
+
+ return r;
+ }
+@@ -316,24 +331,39 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ }
+
+ int nw_port_save(nw_port* port) {
++ char path[PATH_MAX];
++ FILE* f = NULL;
+ int r;
+
++ // Compose path
++ r = nw_string_format(path, "ports/%s", port->name);
++ if (r < 0)
++ return r;
++
++ // Open file
++ f = nw_daemon_config_fopen(port->daemon, path, "w");
++ if (!f) {
++ r = -errno;
++ goto ERROR;
++ }
++
+ // Write out the configuration
+ r = nw_config_options_write(port->config);
+ if (r < 0)
+ goto ERROR;
+
+ // Write the configuration
+- r = nw_config_write(port->config);
+- if (r)
+- return r;
+-
+- return 0;
++ r = nw_config_write(port->config, f);
++ if (r < 0)
++ goto ERROR;
+
+ ERROR:
+- ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
++ if (f)
++ fclose(f);
++ if (r)
++ ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
+
+- return 1;
++ return r;
+ }
+
+ const char* nw_port_name(nw_port* port) {
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 7c2e436..efa2fdb 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -95,8 +95,7 @@ struct nw_port {
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ const nw_port_type_id_t type, const char* name, nw_config* config);
+-int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
+- const char* name, const char* path);
++int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name);
+
+ nw_port* nw_port_ref(nw_port* port);
+ nw_port* nw_port_unref(nw_port* port);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 65545e8..761e564 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -18,6 +18,7 @@
+ # #
+ #############################################################################*/
+
++#include <dirent.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/queue.h>
+@@ -129,43 +130,14 @@ static int nw_ports_add_port(nw_ports* ports, nw_port* port) {
+ return 0;
+ }
+
+-static int __nw_ports_enumerate(const char* path, const struct stat* s, void* data) {
++static int nw_ports_enumerate_port(nw_ports* ports, const char* name) {
+ nw_port* port = NULL;
+ int r;
+
+- nw_ports* ports = (nw_ports*)data;
+-
+- // Skip anything that isn't a regular file
+- if (!S_ISREG(s->st_mode))
+- return 0;
+-
+- // Find the basename of the file
+- const char* name = nw_path_basename(path);
+-
+- // Break on invalid paths
+- if (!name)
+- return 0;
+-
+- // Skip any hidden files
+- if (*name == '.')
+- return 0;
+-
+ // Create a new port
+- r = nw_port_create_from_config(&port, ports->daemon, name, path);
+- switch (r) {
+- // All okay
+- case 0:
+- break;
+-
+- // Invalid configuration
+- case 1:
+- ERROR("Could not open port %s\n", name);
+- r = 0;
+- goto ERROR;
+-
+- default:
+- goto ERROR;
+- }
++ r = nw_port_open(&port, ports->daemon, name);
++ if (r < 0 || r == 1)
++ goto ERROR;
+
+ // Add the port to the list
+ r = nw_ports_add_port(ports, port);
+@@ -180,7 +152,47 @@ ERROR:
+ }
+
+ int nw_ports_enumerate(nw_ports* ports) {
+- return nw_ftw(PORT_CONFIG_DIR, PORT_CONFIG_DIR "/*", __nw_ports_enumerate, ports);
++ DIR* d = NULL;
++ struct dirent* entry = NULL;
++ int r;
++
++ // Open the ports directory
++ d = nw_daemon_config_opendir(ports->daemon, "ports");
++ if (!d) {
++ switch (errno) {
++ case ENOENT:
++ return 0;
++
++ default:
++ return -errno;
++ }
++ }
++
++ for (;;) {
++ // Read the next entry
++ entry = readdir(d);
++ if (!entry)
++ break;
++
++ // Skip anything that is not a regular file
++ if (entry->d_type != DT_REG)
++ continue;
++
++ // Skip hidden files
++ if (entry->d_name[0] == '.')
++ continue;
++
++ // Enumerate the port
++ r = nw_ports_enumerate_port(ports, entry->d_name);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ERROR:
++ if (d)
++ closedir(d);
++
++ return r;
+ }
+
+ nw_port* nw_ports_get_by_name(nw_ports* ports, const char* name) {
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 9f5b7f8..cc5fdaf 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -120,6 +120,7 @@ static int nw_zone_setup(nw_zone* zone) {
+ goto ERROR;
+ }
+
++#if 0
+ // Compose the path to the main configuration file
+ r = nw_zone_path(zone, path, "%s", "settings");
+ if (r)
+@@ -129,6 +130,7 @@ static int nw_zone_setup(nw_zone* zone) {
+ r = nw_config_create(&zone->config, path);
+ if (r)
+ goto ERROR;
++#endif
+
+ ERROR:
+ if (link)
+@@ -193,13 +195,32 @@ int __nw_zone_drop_port(nw_daemon* daemon, nw_zone* zone, void* data) {
+ }
+
+ int nw_zone_save(nw_zone* zone) {
++ char path[PATH_MAX];
++ FILE* f = NULL;
+ int r;
+
+- r = nw_config_write(zone->config);
++ // Compose path
++ r = nw_string_format(path, "zones/%s/settings", zone->name);
++ if (r < 0)
++ goto ERROR;
++
++ // Open file
++ f = nw_daemon_config_fopen(zone->daemon, path, "w");
++ if (!f) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Write the configuration
++ r = nw_config_write(zone->config, f);
+ if (r)
+- return r;
++ goto ERROR;
+
+- return 0;
++ERROR:
++ if (f)
++ fclose(f);
++
++ return r;
+ }
+
+ const char* nw_zone_name(nw_zone* zone) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,67 @@
+From b2134e9ee25fe3aedddc9f6ef812881724122a81 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 09:38:00 +0000
+Subject: [PATCH 283/304] tests: Add new test that creates two dummy interfaces
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 3 ++-
+ test/networkd/01_dummy.t/config/ports/d0 | 2 ++
+ test/networkd/01_dummy.t/config/ports/d1 | 2 ++
+ test/networkd/01_dummy.t/config/settings | 0
+ test/networkd/01_dummy.t/test.sh | 7 +++++++
+ 5 files changed, 13 insertions(+), 1 deletion(-)
+ create mode 100644 test/networkd/01_dummy.t/config/ports/d0
+ create mode 100644 test/networkd/01_dummy.t/config/ports/d1
+ create mode 100644 test/networkd/01_dummy.t/config/settings
+ create mode 100644 test/networkd/01_dummy.t/test.sh
+
+diff --git a/Makefile.am b/Makefile.am
+index 08e04a1..2df9185 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -710,7 +710,8 @@ TESTS += $(dist_check_SCRIPTS)
+ TEST_EXTENSIONS = .t
+
+ NETWORKD_TESTS = \
+- test/networkd/00_launch.t
++ test/networkd/00_launch.t \
++ test/networkd/01_dummy.t
+
+ TESTS += $(NETWORKD_TESTS)
+
+diff --git a/test/networkd/01_dummy.t/config/ports/d0 b/test/networkd/01_dummy.t/config/ports/d0
+new file mode 100644
+index 0000000..36a350b
+--- /dev/null
++++ b/test/networkd/01_dummy.t/config/ports/d0
+@@ -0,0 +1,2 @@
++TYPE=dummy
++ADDRESS=00:11:22:33:44:55
+diff --git a/test/networkd/01_dummy.t/config/ports/d1 b/test/networkd/01_dummy.t/config/ports/d1
+new file mode 100644
+index 0000000..d040863
+--- /dev/null
++++ b/test/networkd/01_dummy.t/config/ports/d1
+@@ -0,0 +1,2 @@
++TYPE=dummy
++ADDRESS=00:55:44:33:22:11
+diff --git a/test/networkd/01_dummy.t/config/settings b/test/networkd/01_dummy.t/config/settings
+new file mode 100644
+index 0000000..e69de29
+diff --git a/test/networkd/01_dummy.t/test.sh b/test/networkd/01_dummy.t/test.sh
+new file mode 100644
+index 0000000..fff0d69
+--- /dev/null
++++ b/test/networkd/01_dummy.t/test.sh
+@@ -0,0 +1,7 @@
++#!/bin/bash
++
++# Dump status of d0
++./networkctl port dump d0
++
++# Dump status of d1
++./networkctl port dump d1
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,47 @@
+From 5d4157c6655aea0673a60894420a9d5a0fa63dd6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 09:38:35 +0000
+Subject: [PATCH 284/304] tests: Always dump the environment
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ test/networkd/test.sh | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/test/networkd/test.sh b/test/networkd/test.sh
+index 7690978..9cecf0a 100644
+--- a/test/networkd/test.sh
++++ b/test/networkd/test.sh
+@@ -64,6 +64,9 @@ launch_networkd() {
+
+ echo "networkd launched as PID ${networkd_PID}"
+
++ # Wait until networkd is initialized
++ # XXX Calling sleep(8) is very racy and should be replaced by something that
++ # waits until networkd has connected to dbus
+ sleep 1
+ }
+
+@@ -100,6 +103,9 @@ terminate_networkd() {
+ fi
+ }
+
++# Collect some status information
++trap dump_status EXIT
++
+ main() {
+ local test="${1}"
+ shift
+@@ -128,9 +134,6 @@ main() {
+ # Terminate networkd
+ terminate_networkd
+
+- # Collect some status information
+- dump_status
+-
+ # Run cleanup script
+ if ! run_script "${test}/cleanup.sh"; then
+ return 1
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From fcc334a2e325a5d08b7abecb0eb67d7c944b4f35 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 10:00:06 +0000
+Subject: [PATCH 285/304] test: Collect more information from test environment
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ test/networkd/test.sh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/test/networkd/test.sh b/test/networkd/test.sh
+index 9cecf0a..3eda034 100644
+--- a/test/networkd/test.sh
++++ b/test/networkd/test.sh
+@@ -52,6 +52,8 @@ dump_command() {
+ }
+
+ dump_status() {
++ dump_command "printenv"
++ dump_command "ps aux"
+ dump_command "ip -d link"
+ }
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,38 @@
+From 5f9c43922b2b9e52ad61e5b8203854881da793a4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 10:17:58 +0000
+Subject: [PATCH 286/304] ports: bonding: Use correct enum for mode
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-bonding.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c
+index ad02b5d..6064957 100644
+--- a/src/networkd/port-bonding.c
++++ b/src/networkd/port-bonding.c
+@@ -97,13 +97,13 @@ int nw_port_bonding_set_mode(nw_port* port, const char* mode) {
+ const int m = nw_port_bonding_mode_from_string(mode);
+
+ switch (m) {
+- case BOND_MODE_ROUNDROBIN:
+- case BOND_MODE_ACTIVEBACKUP:
+- case BOND_MODE_XOR:
+- case BOND_MODE_BROADCAST:
+- case BOND_MODE_8023AD:
+- case BOND_MODE_TLB:
+- case BOND_MODE_ALB:
++ case NW_BONDING_MODE_ROUNDROBIN:
++ case NW_BONDING_MODE_ACTIVEBACKUP:
++ case NW_BONDING_MODE_XOR:
++ case NW_BONDING_MODE_BROADCAST:
++ case NW_BONDING_MODE_8023AD:
++ case NW_BONDING_MODE_TLB:
++ case NW_BONDING_MODE_ALB:
+ port->bonding.mode = m;
+ break;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From e5c50dd3c189c96bead686cc728121d6f22dcd49 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 10:37:00 +0000
+Subject: [PATCH 287/304] networkd: json: Include string.h
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/json.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/networkd/json.h b/src/networkd/json.h
+index 33e237a..19ced9a 100644
+--- a/src/networkd/json.h
++++ b/src/networkd/json.h
+@@ -22,6 +22,7 @@
+ #define NETWORKD_JSON_H
+
+ #include <errno.h>
++#include <string.h>
+
+ #include <json.h>
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,228 @@
+From 67d3fef1d79e933b8af64a39f89fef4b1f8b31cb Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 10:37:47 +0000
+Subject: [PATCH 288/304] ports: Add support for VETH
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkd/port-veth.c | 81 ++++++++++++++++++++++++++++++++++++++++
+ src/networkd/port-veth.h | 35 +++++++++++++++++
+ src/networkd/port.c | 6 +++
+ src/networkd/port.h | 5 +++
+ 5 files changed, 129 insertions(+)
+ create mode 100644 src/networkd/port-veth.c
+ create mode 100644 src/networkd/port-veth.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 2df9185..caab99e 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -338,6 +338,8 @@ dist_networkd_SOURCES = \
+ src/networkd/port-bus.h \
+ src/networkd/port-dummy.c \
+ src/networkd/port-dummy.h \
++ src/networkd/port-veth.c \
++ src/networkd/port-veth.h \
+ src/networkd/port-vlan.c \
+ src/networkd/port-vlan.h \
+ src/networkd/stats-collector.c \
+diff --git a/src/networkd/port-veth.c b/src/networkd/port-veth.c
+new file mode 100644
+index 0000000..44b1a0d
+--- /dev/null
++++ b/src/networkd/port-veth.c
+@@ -0,0 +1,81 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <linux/veth.h>
++
++#include "json.h"
++#include "port.h"
++#include "port-veth.h"
++
++static int nw_port_veth_setup(nw_port* port) {
++ int r;
++
++ // Peer
++ r = NW_CONFIG_OPTION_STRING(port->config, "VETH_PEER", &port->veth.peer);
++ if (r < 0)
++ return 1;
++
++ return 0;
++}
++
++static int nw_port_veth_create_link(nw_port* port, sd_netlink_message* m) {
++ int r;
++
++ // Open the container
++ r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
++ if (r < 0)
++ return r;
++
++ // Set VETH Peer
++ r = sd_netlink_message_append_string(m, IFLA_VLAN_ID, port->veth.peer);
++ if (r < 0)
++ return r;
++
++ // Close the container
++ r = sd_netlink_message_close_container(m);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
++
++static int nw_port_veth_to_json(nw_port* port, struct json_object* o) {
++ int r;
++
++ // Add the VETH Peer
++ r = json_object_add_string(o, "VETHPeer", port->veth.peer);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
++
++const nw_port_type_t nw_port_type_veth = {
++ .kind = "veth",
++
++ // Configuration
++ .setup = nw_port_veth_setup,
++
++ // Link
++ .create_link = nw_port_veth_create_link,
++
++ // JSON
++ .to_json = nw_port_veth_to_json,
++};
+diff --git a/src/networkd/port-veth.h b/src/networkd/port-veth.h
+new file mode 100644
+index 0000000..aa4a03b
+--- /dev/null
++++ b/src/networkd/port-veth.h
+@@ -0,0 +1,35 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_VETH_H
++#define NETWORKD_PORT_VETH_H
++
++#include "port.h"
++
++// Maximum length of the peer name
++#define NW_VETH_PEER_MAX 64
++
++struct nw_port_veth {
++ char peer[NW_VETH_PEER_MAX];
++};
++
++extern const nw_port_type_t nw_port_type_veth;
++
++#endif /* NETWORKD_PORT_VETH_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 7d654e3..fb62520 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -33,6 +33,7 @@
+ #include "port.h"
+ #include "port-bonding.h"
+ #include "port-dummy.h"
++#include "port-veth.h"
+ #include "port-vlan.h"
+ #include "stats-collector.h"
+ #include "string.h"
+@@ -40,6 +41,7 @@
+ static const nw_string_table_t nw_port_type_id[] = {
+ { NW_PORT_BONDING, "bonding" },
+ { NW_PORT_DUMMY, "dummy" },
++ { NW_PORT_VETH, "veth", },
+ { NW_PORT_VLAN, "vlan" },
+ { -1, NULL },
+ };
+@@ -165,6 +167,9 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ p->type = &nw_port_type_dummy;
+ break;
+
++ case NW_PORT_VETH:
++ p->type = &nw_port_type_veth;
++
+ case NW_PORT_VLAN:
+ p->type = &nw_port_type_vlan;
+ break;
+@@ -324,6 +329,7 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+
+ case NW_PORT_BONDING:
+ case NW_PORT_DUMMY:
++ case NW_PORT_VETH:
+ break;
+ }
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index efa2fdb..5c0a2a1 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -36,6 +36,7 @@ typedef struct nw_port nw_port;
+ typedef enum nw_port_type_id {
+ NW_PORT_BONDING,
+ NW_PORT_DUMMY,
++ NW_PORT_VETH,
+ NW_PORT_VLAN,
+ } nw_port_type_id_t;
+
+@@ -66,6 +67,7 @@ typedef struct nw_port_type {
+ #include "daemon.h"
+ #include "json.h"
+ #include "port-bonding.h"
++#include "port-veth.h"
+ #include "port-vlan.h"
+
+ #define NW_PORT_TYPE(port) (port->type)
+@@ -89,6 +91,9 @@ struct nw_port {
+ // Bonding Settings
+ struct nw_port_bonding bonding;
+
++ // VETH Settings
++ struct nw_port_veth veth;
++
+ // VLAN settings
+ struct nw_port_vlan vlan;
+ };
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,264 @@
+From e633147dcf3bc46e0686e106cb013dfccf30b1c6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 11:19:30 +0000
+Subject: [PATCH 289/304] config: Add string buffer type
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 45 +++++++++++++++++++++++++++---------
+ src/networkd/config.h | 49 +++++++++++++++++++++++++++-------------
+ src/networkd/port-veth.c | 2 +-
+ src/networkd/port-vlan.c | 3 ++-
+ 4 files changed, 70 insertions(+), 29 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index 3d444c4..c6281cb 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -43,6 +43,7 @@ struct nw_config_option {
+
+ const char* key;
+ void* value;
++ size_t length;
+
+ // Callbacks
+ nw_config_option_read_callback_t read_callback;
+@@ -427,7 +428,8 @@ int nw_config_options_read(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->read_callback(config, option->key, option->value, option->data);
++ r = option->read_callback(config,
++ option->key, option->value, option->length, option->data);
+ if (r < 0)
+ return r;
+ }
+@@ -440,7 +442,8 @@ int nw_config_options_write(nw_config* config) {
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+- r = option->write_callback(config, option->key, option->value, option->data);
++ r = option->write_callback(config,
++ option->key, option->value, option->length, option->data);
+ if (r < 0)
+ return r;
+ }
+@@ -448,7 +451,8 @@ int nw_config_options_write(nw_config* config) {
+ return 0;
+ }
+
+-int nw_config_option_add(nw_config* config, const char* key, void* value,
++int nw_config_option_add(nw_config* config,
++ const char* key, void* value, const size_t length,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback, void* data) {
+ // Check input
+@@ -465,6 +469,7 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+
+ // Set value
+ option->value = value;
++ option->length = length;
+
+ // Set callbacks
+ option->read_callback = read_callback;
+@@ -477,7 +482,8 @@ int nw_config_option_add(nw_config* config, const char* key, void* value,
+ return 0;
+ }
+
+-int nw_config_read_int(nw_config* config, const char* key, void* value, void* data) {
++int nw_config_read_int(nw_config* config,
++ const char* key, void* value, const size_t length, void* data) {
+ // Fetch the value
+ *(int*)value = nw_config_get_int(config, key, -1);
+
+@@ -485,13 +491,14 @@ int nw_config_read_int(nw_config* config, const char* key, void* value, void* da
+ }
+
+ int nw_config_write_int(nw_config* config,
+- const char* key, const void* value, void* data) {
++ const char* key, const void* value, const size_t length, void* data) {
+ return 0;
+ }
+
+ // String
+
+-int nw_config_read_string(nw_config* config, const char* key, void* value, void* data) {
++int nw_config_read_string(nw_config* config,
++ const char* key, void* value, const size_t length, void* data) {
+ // Fetch the value
+ const char* p = nw_config_get(config, key);
+ if (p)
+@@ -501,13 +508,28 @@ int nw_config_read_string(nw_config* config, const char* key, void* value, void*
+ }
+
+ int nw_config_write_string(nw_config* config,
+- const char* key, const void* value, void* data) {
++ const char* key, const void* value, const size_t length, void* data) {
+ return nw_config_set(config, key, *(const char**)value);
+ }
+
++// String Buffer
++
++int nw_config_read_string_buffer(nw_config* config,
++ const char* key, void* value, const size_t length, void* data) {
++ char* string = (char*)value;
++
++ // Fetch the value
++ const char* p = nw_config_get(config, key);
++ if (p)
++ return __nw_string_set(string, length, p);
++
++ return 0;
++}
++
+ // String Table
+
+-int nw_config_read_string_table(nw_config* config, const char* key, void* value, void* data) {
++int nw_config_read_string_table(nw_config* config,
++ const char* key, void* value, const size_t length, void* data) {
+ const char* s = NULL;
+ int* v = (int*)value;
+
+@@ -529,7 +551,7 @@ int nw_config_read_string_table(nw_config* config, const char* key, void* value,
+ }
+
+ int nw_config_write_string_table(nw_config* config,
+- const char* key, const void* value, void* data) {
++ const char* key, const void* value, const size_t length, void* data) {
+ int* v = (int*)value;
+
+ const nw_string_table_t* table = (nw_string_table_t*)data;
+@@ -544,7 +566,8 @@ int nw_config_write_string_table(nw_config* config,
+
+ // Address
+
+-int nw_config_read_address(nw_config* config, const char* key, void* value, void* data) {
++int nw_config_read_address(nw_config* config,
++ const char* key, void* value, const size_t length, void* data) {
+ nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+@@ -561,7 +584,7 @@ int nw_config_read_address(nw_config* config, const char* key, void* value, void
+ }
+
+ int nw_config_write_address(nw_config* config,
+- const char* key, const void* value, void* data) {
++ const char* key, const void* value, const size_t length, void* data) {
+ const nw_address_t* address = (nw_address_t*)value;
+ int r;
+
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index b25d05e..4b8bc01 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -60,47 +60,64 @@ int nw_config_options_read(nw_config* config);
+ int nw_config_options_write(nw_config* config);
+
+ typedef int (*nw_config_option_read_callback_t)
+- (nw_config* config, const char* key, void* value, void* data);
++ (nw_config* config, const char* key, void* value, const size_t length, void* data);
+ typedef int (*nw_config_option_write_callback_t)
+- (nw_config* config, const char* key, const void* value, void* data);
++ (nw_config* config, const char* key, const void* value, const size_t length, void* data);
+
+-int nw_config_option_add(nw_config* config, const char* key, void* value,
++int nw_config_option_add(nw_config* config, const char* key, void* value, const size_t length,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback, void* data);
+
+-#define NW_CONFIG_OPTION(config, key, value, read_callback, write_callback, data) \
+- nw_config_option_add(config, key, value, read_callback, write_callback, data)
++#define NW_CONFIG_OPTION(config, key, value, length, read_callback, write_callback, data) \
++ nw_config_option_add(config, key, value, length, read_callback, write_callback, data)
+
+ // String
+
+ #define NW_CONFIG_OPTION_STRING(config, key, value) \
+ nw_config_option_add(config, key, value, nw_config_read_string, nw_config_write_string, NULL)
+
+-int nw_config_read_string(nw_config* config, const char* key, void* value, void* data);
+-int nw_config_write_string(nw_config* config, const char* key, const void* value, void* data);
++int nw_config_read_string(nw_config* config,
++ const char* key, void* value, const size_t length, void* data);
++int nw_config_write_string(nw_config* config,
++ const char* key, const void* value, const size_t length, void* data);
++
++#define NW_CONFIG_OPTION_STRING_BUFFER(config, key, value) \
++ nw_config_option_add(config, key, value, sizeof(value), \
++ nw_config_read_string_buffer, nw_config_write_string_buffer, NULL)
++
++int nw_config_read_string_buffer(nw_config* config,
++ const char* key, void* value, const size_t length, void* data);
++#define nw_config_write_string_buffer nw_config_write_string
+
+ // String Table
+
+ #define NW_CONFIG_OPTION_STRING_TABLE(config, key, value, table) \
+- nw_config_option_add(config, key, value, nw_config_read_string_table, nw_config_write_string_table, (void*)table)
++ nw_config_option_add(config, key, value, 0, \
++ nw_config_read_string_table, nw_config_write_string_table, (void*)table)
+
+-int nw_config_read_string_table(nw_config* config, const char* key, void* value, void* data);
+-int nw_config_write_string_table(nw_config* config, const char* key, const void* value, void* data);
++int nw_config_read_string_table(nw_config* config,
++ const char* key, void* value, const size_t length, void* data);
++int nw_config_write_string_table(nw_config* config,
++ const char* key, const void* value, const size_t length, void* data);
+
+ // Integer
+
+ #define NW_CONFIG_OPTION_INT(config, key, value) \
+- nw_config_option_add(config, key, value, nw_config_read_int, nw_config_write_int, NULL)
++ nw_config_option_add(config, key, value, 0, nw_config_read_int, nw_config_write_int, NULL)
+
+-int nw_config_read_int(nw_config* config, const char* key, void* value, void* data);
+-int nw_config_write_int(nw_config* config, const char* key, const void* value, void* data);
++int nw_config_read_int(nw_config* config,
++ const char* key, void* value, const size_t length, void* data);
++int nw_config_write_int(nw_config* config,
++ const char* key, const void* value, const size_t length, void* data);
+
+ // Address
+
+ #define NW_CONFIG_OPTION_ADDRESS(config, key, value) \
+- nw_config_option_add(config, key, value, nw_config_read_address, nw_config_write_address, NULL)
++ nw_config_option_add(config, key, value, 0, nw_config_read_address, nw_config_write_address, NULL)
+
+-int nw_config_read_address(nw_config* config, const char* key, void* value, void* data);
+-int nw_config_write_address(nw_config* config, const char* key, const void* value, void* data);
++int nw_config_read_address(nw_config* config,
++ const char* key, void* value, const size_t length, void* data);
++int nw_config_write_address(nw_config* config,
++ const char* key, const void* value, const size_t length, void* data);
+
+ #endif /* NETWORKD_CONFIG_H */
+diff --git a/src/networkd/port-veth.c b/src/networkd/port-veth.c
+index 44b1a0d..029ef50 100644
+--- a/src/networkd/port-veth.c
++++ b/src/networkd/port-veth.c
+@@ -28,7 +28,7 @@ static int nw_port_veth_setup(nw_port* port) {
+ int r;
+
+ // Peer
+- r = NW_CONFIG_OPTION_STRING(port->config, "VETH_PEER", &port->veth.peer);
++ r = NW_CONFIG_OPTION_STRING_BUFFER(port->config, "VETH_PEER", port->veth.peer);
+ if (r < 0)
+ return 1;
+
+diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c
+index 25a59ee..c759f71 100644
+--- a/src/networkd/port-vlan.c
++++ b/src/networkd/port-vlan.c
+@@ -53,7 +53,8 @@ static int nw_port_vlan_setup(nw_port* port) {
+ return r;
+
+ // Parent Port
+- r = NW_CONFIG_OPTION_STRING(port->config, "VLAN_PARENT", &port->vlan.__parent_name);
++ r = NW_CONFIG_OPTION_STRING_BUFFER(port->config,
++ "VLAN_PARENT", port->vlan.__parent_name);
+ if (r < 0)
+ return r;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,176 @@
+From aa7cc0927e2ea5cde54685e01290be8c0afdc31c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 12:30:33 +0000
+Subject: [PATCH 290/304] networkctl: Add color functions
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +
+ src/networkctl/terminal.c | 51 ++++++++++++++++++++++++
+ src/networkctl/terminal.h | 83 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 136 insertions(+)
+ create mode 100644 src/networkctl/terminal.c
+ create mode 100644 src/networkctl/terminal.h
+
+diff --git a/Makefile.am b/Makefile.am
+index caab99e..95fff11 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -402,6 +402,8 @@ dist_networkctl_SOURCES = \
+ src/networkctl/main.c \
+ src/networkctl/port.c \
+ src/networkctl/port.h \
++ src/networkctl/terminal.c \
++ src/networkctl/terminal.h \
+ src/networkctl/zone.c \
+ src/networkctl/zone.h
+
+diff --git a/src/networkctl/terminal.c b/src/networkctl/terminal.c
+new file mode 100644
+index 0000000..de7cd8d
+--- /dev/null
++++ b/src/networkctl/terminal.c
+@@ -0,0 +1,51 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include <stdlib.h>
++#include <unistd.h>
++
++#include "terminal.h"
++
++// Cache the color mode
++static color_mode_t __color_mode = COLORS_UNKNOWN;
++
++static color_mode_t detect_color_mode(void) {
++ const char* s = NULL;
++
++ // Check for NO_COLOR and if found turn off colours
++ s = secure_getenv("NO_COLOR");
++ if (s)
++ return COLORS_OFF;
++
++ // Disable colours if this isn't an interactive terminal
++ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO))
++ return COLORS_OFF;
++
++ // Otherwise we enable colours
++ return COLORS_ON;
++}
++
++color_mode_t color_mode() {
++ if (__color_mode == COLORS_UNKNOWN) {
++ __color_mode = detect_color_mode();
++ }
++
++ return __color_mode;
++}
+diff --git a/src/networkctl/terminal.h b/src/networkctl/terminal.h
+new file mode 100644
+index 0000000..b7bffec
+--- /dev/null
++++ b/src/networkctl/terminal.h
+@@ -0,0 +1,83 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKCTL_TERMINAL_H
++#define NETWORKCTL_TERMINAL_H
++
++typedef enum color_mode {
++ COLORS_UNKNOWN = 0,
++ COLORS_OFF,
++ COLORS_ON,
++} color_mode_t;
++
++// Reset
++#define COLOR_RESET "\x1B[0m"
++
++// Highlight
++#define COLOR_HIGHLIGHT "\x1B[0;1;39m"
++
++// Regular Colors
++#define COLOR_BLACK "\x1B[0;30m"
++#define COLOR_RED "\x1B[0;31m"
++#define COLOR_GREEN "\x1B[0;32m"
++#define COLOR_YELLOW "\x1B[0;33m"
++#define COLOR_BLUE "\x1B[0;34m"
++#define COLOR_MAGENTA "\x1B[0;35m"
++#define COLOR_CYAN "\x1B[0;36m"
++#define COLOR_WHITE "\x1B[0;37m"
++
++#define COLOR_BRIGHT_BLACK "\x1B[0;90m"
++#define COLOR_BRIGHT_RED "\x1B[0;91m"
++#define COLOR_BRIGHT_GREEN "\x1B[0;92m"
++#define COLOR_BRIGHT_YELLOW "\x1B[0;93m"
++#define COLOR_BRIGHT_BLUE "\x1B[0;94m"
++#define COLOR_BRIGHT_MAGENTA "\x1B[0;95m"
++#define COLOR_BRIGHT_CYAN "\x1B[0;96m"
++#define COLOR_BRIGHT_WHITE "\x1B[0;97m"
++
++#define COLOR_HIGHLIGHT_BLACK "\x1B[0;1;30m"
++#define COLOR_HIGHLIGHT_RED "\x1B[0;1;31m"
++#define COLOR_HIGHLIGHT_GREEN "\x1B[0;1;32m"
++#define COLOR_HIGHLIGHT_YELLOW "\x1B[0;1;33m"
++#define COLOR_HIGHLIGHT_BLUE "\x1B[0;1;34m"
++#define COLOR_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
++#define COLOR_HIGHLIGHT_CYAN "\x1B[0;1;36m"
++#define COLOR_HIGHLIGHT_WHITE "\x1B[0;1;37m"
++
++// Returns the color mode
++color_mode_t color_mode(void);
++
++#define COLOR_FUNC(name, color) \
++ static inline const char* color_##name(void) { \
++ return (color_mode() == COLORS_ON) ? COLOR_ ## color : ""; \
++ }
++
++COLOR_FUNC(reset, RESET)
++COLOR_FUNC(highlight, HIGHLIGHT)
++COLOR_FUNC(black, BLACK)
++COLOR_FUNC(red, RED)
++COLOR_FUNC(green, GREEN)
++COLOR_FUNC(yellow, YELLOW)
++COLOR_FUNC(blue, BLUE)
++COLOR_FUNC(magenta, MAGENTA)
++COLOR_FUNC(cyan, CYAN)
++COLOR_FUNC(white, WHITE)
++
++#endif /* NETWORKCTL_TERMINAL_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,106 @@
+From 8dc40ed861b5ee93f176caf792fc818f681fd9b4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 12:40:55 +0000
+Subject: [PATCH 291/304] networkctl: Move describe into an own function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkctl/port.c | 48 ++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 36 insertions(+), 12 deletions(-)
+
+diff --git a/src/networkctl/port.c b/src/networkctl/port.c
+index 67bc003..b6a95c5 100644
+--- a/src/networkctl/port.c
++++ b/src/networkctl/port.c
+@@ -19,6 +19,7 @@
+ #############################################################################*/
+
+ #include <limits.h>
++#include <stdlib.h>
+
+ #include <systemd/sd-bus.h>
+
+@@ -80,22 +81,19 @@ ERROR:
+ return r;
+ }
+
+-// Dump
+-
+-static int networkctl_port_dump(sd_bus* bus, int argc, char* argv[]) {
++static int networkctl_port_describe(sd_bus* bus, const char* name, char** text) {
+ sd_bus_message* reply = NULL;
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ char path[PATH_MAX];
+- const char* text = NULL;
++ const char* t = NULL;
+ int r;
+
+- if (argc < 1) {
+- fprintf(stderr, "Port required\n");
++ // Check input
++ if (!name || !text)
+ return -EINVAL;
+- }
+
+ // Make port path
+- r = nw_string_format(path, "/org/ipfire/network1/port/%s", argv[0]);
++ r = nw_string_format(path, "/org/ipfire/network1/port/%s", name);
+ if (r < 0)
+ goto ERROR;
+
+@@ -108,15 +106,16 @@ static int networkctl_port_dump(sd_bus* bus, int argc, char* argv[]) {
+ }
+
+ // Read the text
+- r = sd_bus_message_read(reply, "s", &text);
++ r = sd_bus_message_read(reply, "s", &t);
+ if (r < 0) {
+ fprintf(stderr, "Could not parse bus message: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+- // Print the text
+- if (text)
+- printf("%s\n", text);
++ // Copy text to heap
++ *text = strdup(t);
++ if (!*text)
++ r = -errno;
+
+ ERROR:
+ if (reply)
+@@ -125,6 +124,31 @@ ERROR:
+ return r;
+ }
+
++// Dump
++
++static int networkctl_port_dump(sd_bus* bus, int argc, char* argv[]) {
++ char* text = NULL;
++ int r;
++
++ if (argc < 1) {
++ fprintf(stderr, "Port required\n");
++ return -EINVAL;
++ }
++
++ // Describe the port
++ r = networkctl_port_describe(bus, argv[0], &text);
++ if (r < 0)
++ return r;
++
++ // Print the text
++ printf("%s\n", text);
++
++ if (text)
++ free(text);
++
++ return 0;
++}
++
+ // List
+
+ static int __networkctl_port_list(sd_bus* bus, const char* path, const char* name, void* data) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,145 @@
+From 3363ba36e33d5fc9c455825aeeabf4b615cde3c1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 14:14:42 +0000
+Subject: [PATCH 292/304] networkctl: Implement scaffolding to show ports
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkctl/port.c | 68 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/json.h | 9 ++++++
+ 3 files changed, 79 insertions(+)
+
+diff --git a/Makefile.am b/Makefile.am
+index 95fff11..2632ca7 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -409,12 +409,14 @@ dist_networkctl_SOURCES = \
+
+ networkctl_CFLAGS = \
+ $(AM_CFLAGS) \
++ $(JSON_C_CFLAGS) \
+ $(SYSTEMD_CFLAGS)
+
+ networkctl_LDFLAGS = \
+ $(AM_LDFLAGS)
+
+ networkctl_LDADD = \
++ $(JSON_C_LIBS) \
+ $(SYSTEMD_LIBS)
+
+ # ------------------------------------------------------------------------------
+diff --git a/src/networkctl/port.c b/src/networkctl/port.c
+index b6a95c5..439177d 100644
+--- a/src/networkctl/port.c
++++ b/src/networkctl/port.c
+@@ -23,9 +23,11 @@
+
+ #include <systemd/sd-bus.h>
+
++#include "../networkd/json.h"
+ #include "../networkd/string.h"
+ #include "command.h"
+ #include "port.h"
++#include "terminal.h"
+
+ typedef int (*networkctl_port_walk_callback)
+ (sd_bus* bus, const char* path, const char* name, void* data);
+@@ -161,10 +163,76 @@ static int networkctl_port_list(sd_bus* bus, int argc, char* argv[]) {
+ return networkctl_port_walk(bus, __networkctl_port_list, NULL);
+ }
+
++// Show
++
++#define SHOW_LINE " %-12s : %s\n"
++
++static int __networkctl_port_show(sd_bus* bus, const char* path, const char* name, void* data) {
++ struct json_object* object = NULL;
++ enum json_tokener_error json_error;
++ char* describe = NULL;
++ int r;
++
++ // Describe this port
++ r = networkctl_port_describe(bus, name, &describe);
++ if (r < 0)
++ goto ERROR;
++
++ // Parse JSON
++ object = json_tokener_parse_verbose(describe, &json_error);
++ if (!object) {
++ fprintf(stderr, "Could not parse port %s: %s\n",
++ name, json_tokener_error_desc(json_error));
++ return -EINVAL;
++ }
++
++ // Show headline
++ printf("Port %s%s%s\n", color_highlight(), name, color_reset());
++
++ // Show type
++ const char* type = json_object_fetch_string(object, "Type");
++ if (type)
++ printf(SHOW_LINE, "Type", type);
++
++ // Show address
++ const char* address = json_object_fetch_string(object, "Address");
++ if (address)
++ printf(SHOW_LINE, "Address", address);
++
++ // Show an empty line at the end
++ printf("\n");
++
++ // Success!
++ r = 0;
++
++ERROR:
++ if (describe)
++ free(describe);
++ if (object)
++ json_object_unref(object);
++
++ return r;
++}
++
++static int networkctl_port_show(sd_bus* bus, int argc, char* argv[]) {
++ switch (argc) {
++ case 0:
++ return networkctl_port_walk(bus, __networkctl_port_show, NULL);
++
++ case 1:
++ return __networkctl_port_show(bus, NULL, argv[0], NULL);
++
++ default:
++ fprintf(stderr, "Too many arguments\n");
++ return 1;
++ }
++}
++
+ int networkctl_port(sd_bus* bus, int argc, char* argv[]) {
+ static const struct command commands[] = {
+ { "dump", 0, networkctl_port_dump },
+ { "list", 0, networkctl_port_list },
++ { "show", 0, networkctl_port_show },
+ { NULL },
+ };
+
+diff --git a/src/networkd/json.h b/src/networkd/json.h
+index 19ced9a..6c2f66f 100644
+--- a/src/networkd/json.h
++++ b/src/networkd/json.h
+@@ -87,4 +87,13 @@ static inline int json_to_string(struct json_object* o, char** s, size_t* l) {
+ return 0;
+ }
+
++static inline const char* json_object_fetch_string(
++ struct json_object* o, const char* key) {
++ struct json_object* e = json_object_object_get(o, key);
++ if (!e)
++ return NULL;
++
++ return json_object_get_string(e);
++}
++
+ #endif /* NETWORKD_JSON_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,82 @@
+From 69467266f8b3d8a925604b3ffdeda060c5d409cd Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 14:19:04 +0000
+Subject: [PATCH 293/304] ports: Add link stuff to JSON output
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 14 ++++++++++++++
+ src/networkd/link.h | 4 ++++
+ src/networkd/port.c | 7 +++++++
+ 3 files changed, 25 insertions(+)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 603aabe..60abd46 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -27,6 +27,7 @@
+ #include <systemd/sd-netlink.h>
+
+ #include "daemon.h"
++#include "json.h"
+ #include "link.h"
+ #include "links.h"
+ #include "logging.h"
+@@ -529,3 +530,16 @@ ERROR:
+
+ return r;
+ }
++
++// JSON
++
++int nw_link_to_json(nw_link* link, struct json_object* o) {
++ int r;
++
++ // Add ifindex
++ r = json_object_add_int64(o, "LinkIndex", link->ifindex);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 2bab47c..36c7d9d 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -26,6 +26,7 @@
+ typedef struct nw_link nw_link;
+
+ #include "daemon.h"
++#include "json.h"
+
+ int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex);
+
+@@ -45,4 +46,7 @@ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+ int nw_link_destroy(nw_link* link);
+
++// JSON
++int nw_link_to_json(nw_link* link, struct json_object* o);
++
+ #endif /* NETWORKD_LINK_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index fb62520..84fceee 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -682,6 +682,13 @@ int nw_port_to_json(nw_port* port, struct json_object** object) {
+ goto ERROR;
+ }
+
++ // Add link stuff
++ if (port->link) {
++ r = nw_link_to_json(port->link, o);
++ if (r < 0)
++ goto ERROR;
++ }
++
+ // Call custom stuff
+ if (NW_PORT_TYPE(port)->to_json) {
+ r = NW_PORT_TYPE(port)->to_json(port, o);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,185 @@
+From cf75e1dbc997b8a785e5f4d75e6a3d0efd594cf5 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 14:44:06 +0000
+Subject: [PATCH 294/304] link: Add device stuff to JSON output
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 113 ++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 105 insertions(+), 8 deletions(-)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 60abd46..23918cb 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -24,6 +24,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+
++#include <systemd/sd-device.h>
+ #include <systemd/sd-netlink.h>
+
+ #include "daemon.h"
+@@ -48,6 +49,9 @@ struct nw_link {
+ NW_LINK_DESTROYED,
+ } state;
+
++ // Device
++ struct sd_device* device;
++
+ // Stats
+ struct rtnl_link_stats64 stats64;
+
+@@ -61,11 +65,35 @@ struct nw_link {
+ uint8_t operstate;
+ };
+
++static int nw_link_setup_device(nw_link* link) {
++ int r;
++
++ // Fetch sd-device
++ r = sd_device_new_from_ifindex(&link->device, link->ifindex);
++ if (r < 0) {
++ ERROR("Could not fetch sd-device for link %d: %s\n", link->ifindex, strerror(-r));
++ return r;
++ }
++
++ return 0;
++}
++
++static void nw_link_free(nw_link* link) {
++ DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
++
++ if (link->device)
++ sd_device_unref(link->device);
++ if (link->daemon)
++ nw_daemon_unref(link->daemon);
++}
++
+ int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
++ int r;
++
+ // Allocate a new object
+ nw_link* l = calloc(1, sizeof(*l));
+ if (!l)
+- return 1;
++ return -errno;
+
+ // Store a reference to the daemon
+ l->daemon = nw_daemon_ref(daemon);
+@@ -78,16 +106,16 @@ int nw_link_create(nw_link** link, nw_daemon* daemon, int ifindex) {
+
+ DEBUG("New link allocated (ifindex = %d)\n", l->ifindex);
+
+- *link = l;
++ r = nw_link_setup_device(l);
++ if (r < 0)
++ goto ERROR;
+
++ *link = l;
+ return 0;
+-}
+-
+-static void nw_link_free(nw_link* link) {
+- DEBUG("Freeing link (ifindex = %d)\n", link->ifindex);
+
+- if (link->daemon)
+- nw_daemon_unref(link->daemon);
++ERROR:
++ nw_link_free(l);
++ return r;
+ }
+
+ nw_link* nw_link_ref(nw_link* link) {
+@@ -126,6 +154,19 @@ const char* nw_link_ifname(nw_link* link) {
+ return link->ifname;
+ }
+
++static int nw_link_get_sd_device(nw_link* link, struct sd_device** device) {
++ int r;
++
++ // Fetch sd-device
++ r = sd_device_new_from_ifindex(device, link->ifindex);
++ if (r < 0) {
++ ERROR("Could not fetch sd-device for link %d: %s\n", link->ifindex, strerror(-r));
++ return r;
++ }
++
++ return 0;
++}
++
+ // Stats
+
+ const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
+@@ -533,6 +574,57 @@ ERROR:
+
+ // JSON
+
++static int nw_link_device_to_json(nw_link* link, struct json_object* o) {
++ const char* driver = NULL;
++ const char* vendor = NULL;
++ const char* model = NULL;
++ int r;
++
++ // Fetch driver
++ r = sd_device_get_driver(link->device, &driver);
++ if (r < 0 && r != -ENOENT)
++ return r;
++
++ // Add driver
++ if (driver) {
++ r = json_object_add_string(o, "Driver", driver);
++ if (r < 0)
++ return r;
++ }
++
++ // Fetch vendor
++ r = sd_device_get_property_value(link->device, "ID_VENDOR_FROM_DATABASE", &vendor);
++ if (r < 0) {
++ r = sd_device_get_property_value(link->device, "ID_VENDOR", &vendor);
++ if (r < 0 && r != -ENOENT)
++ return r;
++ }
++
++ // Add vendor
++ if (vendor) {
++ r = json_object_add_string(o, "Vendor", vendor);
++ if (r < 0)
++ return r;
++ }
++
++ // Fetch model
++ r = sd_device_get_property_value(link->device, "ID_MODEL_FROM_DATABASE", &model);
++ if (r < 0) {
++ r = sd_device_get_property_value(link->device, "ID_MODEL", &model);
++ if (r < 0 && r != -ENOENT)
++ return r;
++ }
++
++ // Add model
++ if (model) {
++ r = json_object_add_string(o, "Model", model);
++ if (r < 0)
++ return r;
++ }
++
++ return 0;
++}
++
+ int nw_link_to_json(nw_link* link, struct json_object* o) {
+ int r;
+
+@@ -541,5 +633,10 @@ int nw_link_to_json(nw_link* link, struct json_object* o) {
+ if (r < 0)
+ return r;
+
++ // Add sd-device stuff
++ r = nw_link_device_to_json(link, o);
++ if (r < 0)
++ return r;
++
+ return 0;
+ }
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,246 @@
+From f4f98a384fd5cb95b04be989ec488b2ecfc1960f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 9 Jun 2023 14:59:54 +0000
+Subject: [PATCH 295/304] ports: Add scaffolding for physical Ethernet
+ interfaces
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 ++
+ src/networkd/link.c | 13 --------
+ src/networkd/port-ethernet.c | 63 ++++++++++++++++++++++++++++++++++++
+ src/networkd/port-ethernet.h | 34 +++++++++++++++++++
+ src/networkd/port.c | 16 ++++++---
+ src/networkd/port.h | 5 +++
+ 6 files changed, 116 insertions(+), 17 deletions(-)
+ create mode 100644 src/networkd/port-ethernet.c
+ create mode 100644 src/networkd/port-ethernet.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 2632ca7..8c0d0c0 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -338,6 +338,8 @@ dist_networkd_SOURCES = \
+ src/networkd/port-bus.h \
+ src/networkd/port-dummy.c \
+ src/networkd/port-dummy.h \
++ src/networkd/port-ethernet.c \
++ src/networkd/port-ethernet.h \
+ src/networkd/port-veth.c \
+ src/networkd/port-veth.h \
+ src/networkd/port-vlan.c \
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 23918cb..f172208 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -154,19 +154,6 @@ const char* nw_link_ifname(nw_link* link) {
+ return link->ifname;
+ }
+
+-static int nw_link_get_sd_device(nw_link* link, struct sd_device** device) {
+- int r;
+-
+- // Fetch sd-device
+- r = sd_device_new_from_ifindex(device, link->ifindex);
+- if (r < 0) {
+- ERROR("Could not fetch sd-device for link %d: %s\n", link->ifindex, strerror(-r));
+- return r;
+- }
+-
+- return 0;
+-}
+-
+ // Stats
+
+ const struct rtnl_link_stats64* nw_link_get_stats64(nw_link* link) {
+diff --git a/src/networkd/port-ethernet.c b/src/networkd/port-ethernet.c
+new file mode 100644
+index 0000000..a48b45e
+--- /dev/null
++++ b/src/networkd/port-ethernet.c
+@@ -0,0 +1,63 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#include "config.h"
++#include "json.h"
++#include "port.h"
++#include "port-ethernet.h"
++
++static int nw_port_ethernet_setup(nw_port* port) {
++ int r;
++
++ // Permanent Address
++ r = NW_CONFIG_OPTION_ADDRESS(port->config,
++ "PERMANENT_ADDRESS", &port->ethernet.permanent_address);
++ if (r < 0)
++ return r;
++
++ return 0;
++}
++
++static int nw_port_ethernet_to_json(nw_port* port, struct json_object* o) {
++ char* address = NULL;
++ int r = 0;
++
++ // Permanent Address
++ address = nw_address_to_string(&port->ethernet.permanent_address);
++ if (address) {
++ r = json_object_add_string(o, "PermanentAddress", address);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ERROR:
++ if (address)
++ free(address);
++
++ return r;
++}
++
++const nw_port_type_t nw_port_type_ethernet = {
++ // Configuration
++ .setup = nw_port_ethernet_setup,
++
++ // JSON
++ .to_json = nw_port_ethernet_to_json,
++};
+diff --git a/src/networkd/port-ethernet.h b/src/networkd/port-ethernet.h
+new file mode 100644
+index 0000000..50dade2
+--- /dev/null
++++ b/src/networkd/port-ethernet.h
+@@ -0,0 +1,34 @@
++/*#############################################################################
++# #
++# IPFire.org - A linux based firewall #
++# Copyright (C) 2023 IPFire Network Development Team #
++# #
++# 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, either version 3 of the License, or #
++# (at your option) any later version. #
++# #
++# 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 <http://www.gnu.org/licenses/>. #
++# #
++#############################################################################*/
++
++#ifndef NETWORKD_PORT_ETHERNET_H
++#define NETWORKD_PORT_ETHERNET_H
++
++#include "address.h"
++#include "port.h"
++
++struct nw_port_ethernet {
++ // Permanent Address
++ nw_address_t permanent_address;
++};
++
++extern const nw_port_type_t nw_port_type_ethernet;
++
++#endif /* NETWORKD_PORT_ETHERNET_H */
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index 84fceee..f547956 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -33,16 +33,18 @@
+ #include "port.h"
+ #include "port-bonding.h"
+ #include "port-dummy.h"
++#include "port-ethernet.h"
+ #include "port-veth.h"
+ #include "port-vlan.h"
+ #include "stats-collector.h"
+ #include "string.h"
+
+ static const nw_string_table_t nw_port_type_id[] = {
+- { NW_PORT_BONDING, "bonding" },
+- { NW_PORT_DUMMY, "dummy" },
+- { NW_PORT_VETH, "veth", },
+- { NW_PORT_VLAN, "vlan" },
++ { NW_PORT_BONDING, "bonding" },
++ { NW_PORT_DUMMY, "dummy" },
++ { NW_PORT_ETHERNET, "ethernet" },
++ { NW_PORT_VETH, "veth", },
++ { NW_PORT_VLAN, "vlan" },
+ { -1, NULL },
+ };
+
+@@ -167,8 +169,13 @@ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ p->type = &nw_port_type_dummy;
+ break;
+
++ case NW_PORT_ETHERNET:
++ p->type = &nw_port_type_ethernet;
++ break;
++
+ case NW_PORT_VETH:
+ p->type = &nw_port_type_veth;
++ break;
+
+ case NW_PORT_VLAN:
+ p->type = &nw_port_type_vlan;
+@@ -329,6 +336,7 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+
+ case NW_PORT_BONDING:
+ case NW_PORT_DUMMY:
++ case NW_PORT_ETHERNET:
+ case NW_PORT_VETH:
+ break;
+ }
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 5c0a2a1..f1cb3d2 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -36,6 +36,7 @@ typedef struct nw_port nw_port;
+ typedef enum nw_port_type_id {
+ NW_PORT_BONDING,
+ NW_PORT_DUMMY,
++ NW_PORT_ETHERNET,
+ NW_PORT_VETH,
+ NW_PORT_VLAN,
+ } nw_port_type_id_t;
+@@ -67,6 +68,7 @@ typedef struct nw_port_type {
+ #include "daemon.h"
+ #include "json.h"
+ #include "port-bonding.h"
++#include "port-ethernet.h"
+ #include "port-veth.h"
+ #include "port-vlan.h"
+
+@@ -91,6 +93,9 @@ struct nw_port {
+ // Bonding Settings
+ struct nw_port_bonding bonding;
+
++ // Ethernet Settings
++ struct nw_port_ethernet ethernet;
++
+ // VETH Settings
+ struct nw_port_veth veth;
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,25 @@
+From 008488e0f95c140f7df74cadd238db16321a7737 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 10 Jun 2023 11:22:46 +0000
+Subject: [PATCH 296/304] logging: Add WARNING log level
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/logging.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/networkd/logging.h b/src/networkd/logging.h
+index ea75ba0..e835064 100644
+--- a/src/networkd/logging.h
++++ b/src/networkd/logging.h
+@@ -30,6 +30,7 @@ void nw_log(int priority, const char *file, int line, const char* fn,
+ This is just something simple which will work for now...
+ */
+ #define INFO(args...) nw_log(LOG_INFO, __FILE__, __LINE__, __FUNCTION__, ## args)
++#define WARNING(args...) nw_log(LOG_WARNING, __FILE__, __LINE__, __FUNCTION__, ## args)
+ #define ERROR(args...) nw_log(LOG_ERR, __FILE__, __LINE__, __FUNCTION__, ## args)
+ #define DEBUG(args...) nw_log(LOG_DEBUG, __FILE__, __LINE__, __FUNCTION__, ## args)
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,148 @@
+From 40e2c8ca02fae3d175e8d49498a3b3b443b1c6ef Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 10 Jun 2023 11:29:22 +0000
+Subject: [PATCH 297/304] networkd: Handle any uevents for links
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/devmon.c | 67 +++++++++++++++++++++++++++++++++++++++++++
+ src/networkd/link.c | 19 ++++++++++++
+ src/networkd/link.h | 5 ++++
+ 3 files changed, 91 insertions(+)
+
+diff --git a/src/networkd/devmon.c b/src/networkd/devmon.c
+index 0a8c26d..c751985 100644
+--- a/src/networkd/devmon.c
++++ b/src/networkd/devmon.c
+@@ -20,8 +20,75 @@
+
+ #include <systemd/sd-device.h>
+
++#include "daemon.h"
+ #include "devmon.h"
++#include "logging.h"
++
++static int nw_daemon_handle_uevent_net(nw_daemon* daemon,
++ sd_device* device, sd_device_action_t action) {
++ nw_link* link = NULL;
++ int ifindex;
++ int r;
++
++ // Fetch ifindex
++ r = sd_device_get_ifindex(device, &ifindex);
++ if (r < 0) {
++ ERROR("Could not get ifindex from uevent: %s\n", strerror(-r));
++ goto ERROR;
++ }
++
++ // Fetch the link
++ link = nw_daemon_get_link_by_ifindex(daemon, ifindex);
++ if (!link) {
++ DEBUG("Could not fetch link %d, ignoring\n", ifindex);
++ r = 0;
++ goto ERROR;
++ }
++
++ // Let the link handle its uevent
++ r = nw_link_handle_uevent(link, device, action);
++
++ERROR:
++ if (link)
++ nw_link_unref(link);
++
++ return r;
++}
+
+ int nw_devmon_handle_uevent(sd_device_monitor* monitor, sd_device* device, void* data) {
++ sd_device_action_t action;
++ const char* subsystem = NULL;
++ int r;
++
++ // Fetch daemon
++ nw_daemon* daemon = (nw_daemon*)data;
++
++ // Fetch action
++ r = sd_device_get_action(device, &action);
++ if (r < 0) {
++ WARNING("Could not get uevent action, ignoring: %s\n", strerror(-r));
++ return r;
++ }
++
++ // Fetch subsystem
++ r = sd_device_get_subsystem(device, &subsystem);
++ if (r < 0) {
++ ERROR("Could not get uevent subsystem, ignoring: %s\n", strerror(-r));
++ return r;
++ }
++
++ // Handle any links
++ if (strcmp(subsystem, "net") == 0) {
++ r = nw_daemon_handle_uevent_net(daemon, device, action);
++
++ } else {
++ DEBUG("Received an uevent for an unhandled subsystem '%s', ignoring\n", subsystem);
++ return 0;
++ }
++
++ // Log if something went wrong
++ if (r < 0)
++ ERROR("Failed processing uevent: %s\n", strerror(-r));
++
+ return 0;
+ }
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index f172208..b0d9e86 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -559,6 +559,25 @@ ERROR:
+ return r;
+ }
+
++// uevent
++
++int nw_link_handle_uevent(nw_link* link, sd_device* device, sd_device_action_t action) {
++ // We need to remove or replace the stored device as it is now outdated
++ if (link->device) {
++ sd_device_unref(link->device);
++
++ // If the device has been removed, we remove its reference
++ if (action == SD_DEVICE_REMOVE)
++ link->device = NULL;
++
++ // Otherwise we just store the new one
++ else
++ link->device = sd_device_ref(device);
++ }
++
++ return 0;
++}
++
+ // JSON
+
+ static int nw_link_device_to_json(nw_link* link, struct json_object* o) {
+diff --git a/src/networkd/link.h b/src/networkd/link.h
+index 36c7d9d..c2f7b7e 100644
+--- a/src/networkd/link.h
++++ b/src/networkd/link.h
+@@ -23,6 +23,8 @@
+
+ #include <linux/if_link.h>
+
++#include <systemd/sd-device.h>
++
+ typedef struct nw_link nw_link;
+
+ #include "daemon.h"
+@@ -46,6 +48,9 @@ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data);
+
+ int nw_link_destroy(nw_link* link);
+
++// uevent
++int nw_link_handle_uevent(nw_link* link, sd_device* device, sd_device_action_t action);
++
+ // JSON
+ int nw_link_to_json(nw_link* link, struct json_object* o);
+
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,83 @@
+From 4d7437ddbcb56da5d64ae9189bdb2c8c161e4a9b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sat, 10 Jun 2023 12:04:18 +0000
+Subject: [PATCH 298/304] links: Initialize udev device when links are created
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 52 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 52 insertions(+)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index b0d9e86..8ba5da5 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -245,6 +245,53 @@ static int nw_link_carrier_lost(nw_link* link) {
+ return 0; // XXX TODO
+ }
+
++static int nw_link_initialize(nw_link* link) {
++ sd_device *device = NULL;
++ int r;
++
++ // Fetch device
++ r = sd_device_new_from_ifindex(&device, link->ifindex);
++ if (r < 0) {
++ WARNING("Could not find device for link %d: %s\n", link->ifindex, strerror(-r));
++ r = 0;
++ goto ERROR;
++ }
++
++ // Check if device is initialized
++ r = sd_device_get_is_initialized(device);
++ switch (r) {
++ // Initialized - fallthrough
++ case 0:
++ break;
++
++ case 1:
++ DEBUG("The device has not been initialized, yet\n");
++ r = 0;
++ goto ERROR;
++
++ default:
++ WARNING("Could not check whether device is initialized, ignoring: %s\n",
++ strerror(-r));
++ goto ERROR;
++ }
++
++ // XXX Check renaming?!
++
++ if (link->device)
++ sd_device_unref(link->device);
++
++ // Store the device
++ link->device = sd_device_ref(device);
++
++ DEBUG("Link %d has been initialized\n", link->ifindex);
++
++ERROR:
++ if (device)
++ sd_device_unref(device);
++
++ return r;
++}
++
+ static int nw_link_update_ifname(nw_link* link, sd_netlink_message* message) {
+ const char* ifname = NULL;
+ int r;
+@@ -476,6 +523,11 @@ int nw_link_process(sd_netlink* rtnl, sd_netlink_message* message, void* data) {
+ goto ERROR;
+ }
+
++ // Initialize the link
++ r = nw_link_initialize(link);
++ if (r < 0)
++ goto ERROR;
++
+ // Add it to the list
+ r = nw_links_add_link(links, link);
+ if (r)
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,60 @@
+From 7d01b16d6ae3502e48bf99b572be627a2d8a2eac Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 11 Jun 2023 11:07:25 +0000
+Subject: [PATCH 299/304] link: Skip uevent when the device is renaming
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/link.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/src/networkd/link.c b/src/networkd/link.c
+index 8ba5da5..cb79dd2 100644
+--- a/src/networkd/link.c
++++ b/src/networkd/link.c
+@@ -613,7 +613,42 @@ ERROR:
+
+ // uevent
+
++static int nw_link_uevent_device_is_renaming(sd_device* device) {
++ int r;
++
++ r = sd_device_get_property_value(device, "ID_RENAMING", NULL);
++ switch (r) {
++ case -ENOENT:
++ return 0;
++
++ case 0:
++ return 1;
++
++ default:
++ return r;
++ }
++}
++
+ int nw_link_handle_uevent(nw_link* link, sd_device* device, sd_device_action_t action) {
++ int r;
++
++ // Check if the device is renaming
++ r = nw_link_uevent_device_is_renaming(device);
++ switch (r) {
++ // Not renaming - Fallthrough
++ case 0:
++ break;
++
++ case 1:
++ DEBUG("Device is renaming, skipping initialization\n");
++ return 0;
++
++ default:
++ ERROR("Could not determine whether the device is being renamed: %s\n",
++ strerror(-r));
++ return r;
++ }
++
+ // We need to remove or replace the stored device as it is now outdated
+ if (link->device) {
+ sd_device_unref(link->device);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,951 @@
+From 25808f650e39dbcdf3e2d0ba42d079a9d5e43079 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 11 Jun 2023 13:02:35 +0000
+Subject: [PATCH 300/304] networkd: Implement smarter handling of the
+ configuration file hierarchy
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/config.c | 207 +++++++++++++++++++++++++++++++++++++++++-
+ src/networkd/config.h | 22 +++++
+ src/networkd/daemon.c | 72 +++++----------
+ src/networkd/daemon.h | 7 +-
+ src/networkd/port.c | 66 +++++++-------
+ src/networkd/port.h | 2 +-
+ src/networkd/ports.c | 50 +++-------
+ src/networkd/zone.c | 112 +++++++++++++----------
+ src/networkd/zone.h | 9 +-
+ src/networkd/zones.c | 37 ++++----
+ 10 files changed, 386 insertions(+), 198 deletions(-)
+
+diff --git a/src/networkd/config.c b/src/networkd/config.c
+index c6281cb..53fd8e3 100644
+--- a/src/networkd/config.c
++++ b/src/networkd/config.c
+@@ -18,12 +18,16 @@
+ # #
+ #############################################################################*/
+
++#include <dirent.h>
+ #include <errno.h>
++#include <fcntl.h>
+ #include <limits.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/queue.h>
++#include <sys/types.h>
++#include <sys/stat.h>
+ #include <unistd.h>
+
+ #include "address.h"
+@@ -161,7 +165,6 @@ int nw_config_open(nw_config** config, const char* path) {
+ // Create a new configuration
+ r = nw_config_create(config, f);
+
+-ERROR:
+ if (f)
+ fclose(f);
+
+@@ -419,6 +422,208 @@ int nw_config_set_bool(nw_config* config, const char* key, const int value) {
+ return nw_config_set(config, key, value ? "true" : "false");
+ }
+
++/*
++ Directory
++*/
++
++struct nw_configd {
++ int nrefs;
++
++ char path[PATH_MAX];
++ int fd;
++};
++
++static void nw_configd_free(nw_configd* dir) {
++ if (dir->fd >= 0)
++ close(dir->fd);
++
++ free(dir);
++}
++
++static int __nw_configd_create(nw_configd** dir, int fd, const char* path) {
++ nw_configd* d = NULL;
++ int r;
++
++ // Allocate a new object
++ d = calloc(1, sizeof(*d));
++ if (!d)
++ return -errno;
++
++ // Initialize the reference counter
++ d->nrefs = 1;
++
++ // Store the file descriptor
++ d->fd = dup(fd);
++ if (d->fd < 0) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Store path
++ if (path) {
++ r = nw_string_set(d->path, path);
++ if (r < 0)
++ goto ERROR;
++ }
++
++ *dir = d;
++ return 0;
++
++ERROR:
++ nw_configd_free(d);
++ return r;
++}
++
++int nw_configd_create(nw_configd** dir, const char* path) {
++ int fd;
++
++ // Open the directory
++ fd = open(path, O_DIRECTORY);
++ if (fd < 0) {
++ ERROR("Could not open %s: %m\n", path);
++ return -errno;
++ }
++
++ return __nw_configd_create(dir, fd, path);
++}
++
++nw_configd* nw_configd_ref(nw_configd* dir) {
++ dir->nrefs++;
++
++ return dir;
++}
++
++nw_configd* nw_configd_unref(nw_configd* dir) {
++ if (--dir->nrefs > 0)
++ return dir;
++
++ nw_configd_free(dir);
++ return NULL;
++}
++
++static int nw_configd_open(nw_configd* dir, const char* path, int flags) {
++ return openat(dir->fd, path, flags);
++}
++
++FILE* nw_configd_fopen(nw_configd* dir, const char* path, const char* mode) {
++ int fd;
++
++ // Open file
++ fd = nw_configd_open(dir, path, 0);
++ if (fd < 0)
++ return NULL;
++
++ // Return a file handle
++ return fdopen(fd, mode);
++}
++
++int nw_configd_open_config(nw_config** config, nw_configd* dir, const char* path) {
++ FILE* f = NULL;
++ int r;
++
++ // Open the file
++ f = nw_configd_fopen(dir, path, "r");
++ if (!f)
++ return -errno;
++
++ // Create configuration
++ r = nw_config_create(config, f);
++ if (r < 0)
++ goto ERROR;
++
++ERROR:
++ if (f)
++ fclose(f);
++
++ return r;
++}
++
++int nw_configd_unlink(nw_configd* dir, const char* path, int flags) {
++ return unlinkat(dir->fd, path, flags);
++}
++
++nw_configd* nw_configd_descend(nw_configd* dir, const char* path) {
++ nw_configd* d = NULL;
++ char p[PATH_MAX];
++ int fd = -1;
++ int r;
++
++ // Join paths
++ r = nw_path_join(p, dir->path, path);
++ if (r < 0)
++ goto ERROR;
++
++ // Open directory
++ fd = nw_configd_open(dir, path, O_DIRECTORY);
++ if (fd < 0) {
++ ERROR("Could not open %s: %m\n", p);
++ goto ERROR;
++ }
++
++ // Create a new config directory object
++ r = __nw_configd_create(&d, fd, p);
++ if (r < 0)
++ goto ERROR;
++
++ERROR:
++ if (fd >= 0)
++ close(fd);
++
++ return d;
++}
++
++int nw_configd_walk(nw_configd* dir, nw_configd_walk_callback callback, void* data) {
++ FILE* f = NULL;
++ DIR* d = NULL;
++ struct dirent* e = NULL;
++ int r;
++
++ // Re-open the directory
++ d = fdopendir(dir->fd);
++ if (!d) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Walk trough everything
++ for (;;) {
++ // Read the next entry
++ e = readdir(d);
++ if (!e)
++ break;
++
++ // Skip anything that is not a regular file
++ if (e->d_type != DT_REG)
++ continue;
++
++ // Skip hidden files
++ if (e->d_name[0] == '.')
++ continue;
++
++ // Open the file
++ f = nw_configd_fopen(dir, e->d_name, "r");
++ if (!f) {
++ r = -errno;
++ goto ERROR;
++ }
++
++ // Call the callback
++ r = callback(e, f, data);
++ fclose(f);
++
++ if (r < 0)
++ goto ERROR;
++ }
++
++ r = 0;
++
++ERROR:
++ if (d)
++ closedir(d);
++
++ return r;
++}
++
+ /*
+ Options
+ */
+diff --git a/src/networkd/config.h b/src/networkd/config.h
+index 4b8bc01..3e7c097 100644
+--- a/src/networkd/config.h
++++ b/src/networkd/config.h
+@@ -21,6 +21,7 @@
+ #ifndef NETWORKD_CONFIG_H
+ #define NETWORKD_CONFIG_H
+
++#include <dirent.h>
+ #include <stdio.h>
+
+ #define NETWORK_CONFIG_KEY_MAX_LENGTH 128
+@@ -52,6 +53,27 @@ int nw_config_set_int(nw_config* config, const char* key, const int value);
+ int nw_config_get_bool(nw_config* config, const char* key);
+ int nw_config_set_bool(nw_config* config, const char* key, const int value);
+
++/*
++ Directory
++*/
++
++typedef struct nw_configd nw_configd;
++
++int nw_configd_create(nw_configd** dir, const char* path);
++
++nw_configd* nw_configd_ref(nw_configd* dir);
++nw_configd* nw_configd_unref(nw_configd* dir);
++
++FILE* nw_configd_fopen(nw_configd* dir, const char* path, const char* mode);
++int nw_configd_open_config(nw_config** config, nw_configd* dir, const char* path);
++int nw_configd_unlink(nw_configd* dir, const char* path, int flags);
++
++nw_configd* nw_configd_descend(nw_configd* dir, const char* path);
++
++typedef int (*nw_configd_walk_callback)(struct dirent* entry, FILE* f, void* data);
++
++int nw_configd_walk(nw_configd* dir, nw_configd_walk_callback callback, void* data);
++
+ /*
+ Options
+ */
+diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c
+index a62e343..dfbcc15 100644
+--- a/src/networkd/daemon.c
++++ b/src/networkd/daemon.c
+@@ -51,7 +51,7 @@ struct nw_daemon {
+ int nrefs;
+
+ // Configuration
+- int configfd;
++ nw_configd* configd;
+ nw_config* config;
+
+ // Event Loop
+@@ -103,36 +103,14 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig
+ */
+
+ static int nw_daemon_config_open(nw_daemon* daemon, const char* path) {
+- // Open the directory
+- daemon->configfd = open(path, O_DIRECTORY);
+- if (daemon->configfd < 0) {
+- ERROR("Could not open %s: %m\n", path);
+- return -errno;
+- }
+-
+- return 0;
+-}
+-
+-FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) {
+- // Open the file
+- int fd = openat(daemon->configfd, path, 0);
+- if (fd < 0) {
+- ERROR("Could not open configuration file %s: %m\n", path);
+- return NULL;
+- }
+-
+- // Return a file handle
+- return fdopen(fd, mode);
+-}
++ int r;
+
+-DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path) {
+- int fd = openat(daemon->configfd, path, O_DIRECTORY);
+- if (fd < 0) {
+- ERROR("Could not open configuration directory %s: %m\n", path);
+- return NULL;
+- }
++ // Open the configuration directory
++ r = nw_configd_create(&daemon->configd, path);
++ if (r < 0)
++ return r;
+
+- return fdopendir(fd);
++ return 0;
+ }
+
+ static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) {
+@@ -213,33 +191,17 @@ static int nw_daemon_setup_loop(nw_daemon* daemon) {
+ }
+
+ static int nw_daemon_load_config(nw_daemon* daemon) {
+- FILE* f = NULL;
+ int r;
+
+ // If no configuration path has been opened yet, we will open something
+- if (!daemon->configfd) {
++ if (!daemon->configd) {
+ r = nw_daemon_config_open(daemon, CONFIG_DIR);
+ if (r < 0)
+- goto ERROR;
++ return r;
+ }
+
+ // Open the configuration file
+- f = nw_daemon_config_fopen(daemon, "settings", "r");
+- if (!f) {
+- r = -errno;
+- goto ERROR;
+- }
+-
+- // Create configuration
+- r = nw_config_create(&daemon->config, f);
+- if (r < 0)
+- goto ERROR;
+-
+-ERROR:
+- if (f)
+- fclose(f);
+-
+- return r;
++ return nw_configd_open_config(&daemon->config, daemon->configd, "settings");
+ }
+
+ static int nw_start_device_monitor(nw_daemon* daemon) {
+@@ -540,8 +502,8 @@ static void nw_daemon_free(nw_daemon* daemon) {
+ // Cleanup common objects
+ nw_daemon_cleanup(daemon);
+
+- if (daemon->configfd > 0)
+- close(daemon->configfd);
++ if (daemon->configd)
++ nw_configd_unref(daemon->configd);
+ if (daemon->stats_collector_event)
+ sd_event_source_unref(daemon->stats_collector_event);
+ if (daemon->bus)
+@@ -640,6 +602,16 @@ int nw_daemon_save(nw_daemon* daemon) {
+ return 0;
+ }
+
++nw_configd* nw_daemon_configd(nw_daemon* daemon, const char* path) {
++ if (!daemon->configd)
++ return NULL;
++
++ if (path)
++ return nw_configd_descend(daemon->configd, path);
++
++ return nw_configd_ref(daemon->configd);
++}
++
+ /*
+ Bus
+ */
+diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h
+index b03086c..2d56d79 100644
+--- a/src/networkd/daemon.h
++++ b/src/networkd/daemon.h
+@@ -29,6 +29,7 @@
+
+ typedef struct nw_daemon nw_daemon;
+
++#include "config.h"
+ #include "link.h"
+ #include "links.h"
+ #include "port.h"
+@@ -47,11 +48,7 @@ int nw_daemon_reload(nw_daemon* daemon);
+
+ int nw_daemon_save(nw_daemon* daemon);
+
+-/*
+- Configuration
+-*/
+-FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode);
+-DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path);
++nw_configd* nw_daemon_configd(nw_daemon* daemon, const char* path);
+
+ /*
+ Bus
+diff --git a/src/networkd/port.c b/src/networkd/port.c
+index f547956..141bba5 100644
+--- a/src/networkd/port.c
++++ b/src/networkd/port.c
+@@ -222,24 +222,10 @@ ERROR:
+ return r;
+ }
+
+-int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) {
++int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name, FILE* f) {
+ nw_config* config = NULL;
+- FILE* f = NULL;
+- char path[PATH_MAX];
+ int r;
+
+- // Make path
+- r = nw_string_format(path, "ports/%s", name);
+- if (r < 0)
+- goto ERROR;
+-
+- // Open the configuration file
+- f = nw_daemon_config_fopen(daemon, path, "r");
+- if (!f) {
+- r = -errno;
+- goto ERROR;
+- }
+-
+ // Initialize the configuration
+ r = nw_config_create(&config, f);
+ if (r < 0)
+@@ -248,7 +234,7 @@ int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) {
+ // Fetch the type
+ const char* type = nw_config_get(config, "TYPE");
+ if (!type) {
+- ERROR("Port configuration %s has no TYPE\n", path);
++ ERROR("Port %s has no TYPE\n", name);
+ r = -ENOTSUP;
+ goto ERROR;
+ }
+@@ -261,8 +247,6 @@ int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) {
+ ERROR:
+ if (config)
+ nw_config_unref(config);
+- if (f)
+- fclose(f);
+
+ return r;
+ }
+@@ -292,6 +276,7 @@ static void __nw_port_unref(void* data) {
+ }
+
+ int nw_port_destroy(nw_port* port) {
++ nw_configd* configd = NULL;
+ int r;
+
+ DEBUG("Destroying port %s\n", port->name);
+@@ -299,26 +284,33 @@ int nw_port_destroy(nw_port* port) {
+ // Destroy the physical link (if exists)
+ if (port->link) {
+ r = nw_link_destroy(port->link);
+- if (r)
+- return r;
++ if (r < 0)
++ goto ERROR;
+ }
+
+ // Dereference the port from other ports
+ r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port);
+- if (r)
+- return r;
++ if (r < 0)
++ goto ERROR;
+
+ // Dereference the port from other zones
+ r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port);
+- if (r)
+- return r;
++ if (r < 0)
++ goto ERROR;
+
+- // Destroy the configuration
+- r = nw_config_destroy(port->config);
+- if (r)
+- return r;
++ // Fetch the configuration directory
++ configd = nw_daemon_configd(port->daemon, "ports");
++ if (configd) {
++ r = nw_configd_unlink(configd, port->name, 0);
++ if (r < 0)
++ goto ERROR;
++ }
+
+- return 0;
++ERROR:
++ if (configd)
++ nw_configd_unref(configd);
++
++ return r;
+ }
+
+ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+@@ -345,17 +337,19 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
+ }
+
+ int nw_port_save(nw_port* port) {
+- char path[PATH_MAX];
++ nw_configd* configd = NULL;
+ FILE* f = NULL;
+ int r;
+
+- // Compose path
+- r = nw_string_format(path, "ports/%s", port->name);
+- if (r < 0)
+- return r;
++ // Fetch configuration directory
++ configd = nw_daemon_configd(port->daemon, "ports");
++ if (!configd) {
++ r = -errno;
++ goto ERROR;
++ }
+
+ // Open file
+- f = nw_daemon_config_fopen(port->daemon, path, "w");
++ f = nw_configd_fopen(configd, port->name, "w");
+ if (!f) {
+ r = -errno;
+ goto ERROR;
+@@ -372,6 +366,8 @@ int nw_port_save(nw_port* port) {
+ goto ERROR;
+
+ ERROR:
++ if (configd)
++ nw_configd_unref(configd);
+ if (f)
+ fclose(f);
+ if (r)
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index f1cb3d2..3cbe4b0 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -105,7 +105,7 @@ struct nw_port {
+
+ int nw_port_create(nw_port** port, nw_daemon* daemon,
+ const nw_port_type_id_t type, const char* name, nw_config* config);
+-int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name);
++int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name, FILE* f);
+
+ nw_port* nw_port_ref(nw_port* port);
+ nw_port* nw_port_unref(nw_port* port);
+diff --git a/src/networkd/ports.c b/src/networkd/ports.c
+index 761e564..95a13e3 100644
+--- a/src/networkd/ports.c
++++ b/src/networkd/ports.c
+@@ -130,12 +130,14 @@ static int nw_ports_add_port(nw_ports* ports, nw_port* port) {
+ return 0;
+ }
+
+-static int nw_ports_enumerate_port(nw_ports* ports, const char* name) {
++static int __nw_ports_enumerate(struct dirent* entry, FILE* f, void* data) {
+ nw_port* port = NULL;
+ int r;
+
++ nw_ports* ports = (nw_ports*)data;
++
+ // Create a new port
+- r = nw_port_open(&port, ports->daemon, name);
++ r = nw_port_open(&port, ports->daemon, entry->d_name, f);
+ if (r < 0 || r == 1)
+ goto ERROR;
+
+@@ -152,45 +154,19 @@ ERROR:
+ }
+
+ int nw_ports_enumerate(nw_ports* ports) {
+- DIR* d = NULL;
+- struct dirent* entry = NULL;
++ nw_configd* configd = NULL;
+ int r;
+
+- // Open the ports directory
+- d = nw_daemon_config_opendir(ports->daemon, "ports");
+- if (!d) {
+- switch (errno) {
+- case ENOENT:
+- return 0;
+-
+- default:
+- return -errno;
+- }
+- }
+-
+- for (;;) {
+- // Read the next entry
+- entry = readdir(d);
+- if (!entry)
+- break;
+-
+- // Skip anything that is not a regular file
+- if (entry->d_type != DT_REG)
+- continue;
++ // Fetch ports configuration directory
++ configd = nw_daemon_configd(ports->daemon, "ports");
++ if (!configd)
++ return 0;
+
+- // Skip hidden files
+- if (entry->d_name[0] == '.')
+- continue;
++ // Walk through all files
++ r = nw_configd_walk(configd, __nw_ports_enumerate, ports);
+
+- // Enumerate the port
+- r = nw_ports_enumerate_port(ports, entry->d_name);
+- if (r < 0)
+- goto ERROR;
+- }
+-
+-ERROR:
+- if (d)
+- closedir(d);
++ // Cleanup
++ nw_configd_unref(configd);
+
+ return r;
+ }
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index cc5fdaf..19d221f 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -32,6 +32,12 @@
+ #include "string.h"
+ #include "zone.h"
+
++static const nw_string_table_t nw_zone_type_id[] = {
++ { -1, NULL },
++};
++
++NW_STRING_TABLE_LOOKUP(nw_zone_type_id_t, nw_zone_type_id)
++
+ struct nw_zone {
+ nw_daemon* daemon;
+ int nrefs;
+@@ -45,32 +51,6 @@ struct nw_zone {
+ nw_config *config;
+ };
+
+-#define nw_zone_path(zone, path, format, ...) \
+- __nw_zone_path(zone, path, sizeof(path), format, __VA_ARGS__)
+-
+-static int __nw_zone_path(nw_zone* zone, char* p, const size_t length,
+- const char* format, ...) {
+- char prefix[NAME_MAX];
+- char suffix[NAME_MAX];
+- va_list args;
+- int r;
+-
+- // Format the prefix
+- r = nw_string_format(prefix, "%s/zones/%s", CONFIG_DIR, zone->name);
+- if (r)
+- return r;
+-
+- // Format the suffix
+- va_start(args, format);
+- r = nw_string_vformat(suffix, format, args);
+- va_end(args);
+- if (r)
+- return r;
+-
+- // Join the two parts together
+- return __nw_path_join(p, length, prefix, suffix);
+-}
+-
+ static void nw_zone_free(nw_zone* zone) {
+ if (zone->link)
+ nw_link_unref(zone->link);
+@@ -109,8 +89,7 @@ static int nw_zone_set_link(nw_zone* zone, nw_link* link) {
+
+ static int nw_zone_setup(nw_zone* zone) {
+ nw_link* link = NULL;
+- char path[PATH_MAX];
+- int r;
++ int r = 0;
+
+ // Find the link
+ link = nw_daemon_get_link_by_name(zone->daemon, zone->name);
+@@ -120,18 +99,6 @@ static int nw_zone_setup(nw_zone* zone) {
+ goto ERROR;
+ }
+
+-#if 0
+- // Compose the path to the main configuration file
+- r = nw_zone_path(zone, path, "%s", "settings");
+- if (r)
+- goto ERROR;
+-
+- // Initialize the configuration
+- r = nw_config_create(&zone->config, path);
+- if (r)
+- goto ERROR;
+-#endif
+-
+ ERROR:
+ if (link)
+ nw_link_unref(link);
+@@ -139,13 +106,15 @@ ERROR:
+ return r;
+ }
+
+-int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) {
++int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const nw_zone_type_id_t type,
++ const char* name, nw_config* config) {
++ nw_zone* z = NULL;
+ int r;
+
+ // Allocate a new object
+- nw_zone* z = calloc(1, sizeof(*z));
++ z = calloc(1, sizeof(*z));
+ if (!z)
+- return 1;
++ return -errno;
+
+ // Store a reference to the daemon
+ z->daemon = nw_daemon_ref(daemon);
+@@ -158,6 +127,11 @@ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) {
+ if (r)
+ goto ERROR;
+
++ // Copy the configuration
++ r = nw_config_copy(config, &z->config);
++ if (r < 0)
++ goto ERROR;
++
+ // Setup the zone
+ r = nw_zone_setup(z);
+ if (r)
+@@ -171,6 +145,35 @@ ERROR:
+ return r;
+ }
+
++int nw_zone_open(nw_zone** zone, nw_daemon* daemon, const char* name, FILE* f) {
++ nw_config* config = NULL;
++ int r;
++
++ // Initialize the configuration
++ r = nw_config_create(&config, f);
++ if (r < 0)
++ goto ERROR;
++
++ // Fetch the type
++ const char* type = nw_config_get(config, "TYPE");
++ if (!type) {
++ ERROR("Zone %s has no TYPE\n", name);
++ r = -ENOTSUP;
++ goto ERROR;
++ }
++
++ // Create a new zone
++ r = nw_zone_create(zone, daemon, nw_zone_type_id_from_string(type), name, config);
++ if (r < 0)
++ goto ERROR;
++
++ERROR:
++ if (config)
++ nw_config_unref(config);
++
++ return r;
++}
++
+ nw_zone* nw_zone_ref(nw_zone* zone) {
+ zone->nrefs++;
+
+@@ -195,30 +198,41 @@ int __nw_zone_drop_port(nw_daemon* daemon, nw_zone* zone, void* data) {
+ }
+
+ int nw_zone_save(nw_zone* zone) {
+- char path[PATH_MAX];
++ nw_configd* configd = NULL;
+ FILE* f = NULL;
+ int r;
+
+- // Compose path
+- r = nw_string_format(path, "zones/%s/settings", zone->name);
+- if (r < 0)
++ // Fetch configuration directory
++ configd = nw_daemon_configd(zone->daemon, "zones");
++ if (!configd) {
++ r = -errno;
+ goto ERROR;
++ }
+
+ // Open file
+- f = nw_daemon_config_fopen(zone->daemon, path, "w");
++ f = nw_configd_fopen(configd, zone->name, "w");
+ if (!f) {
+ r = -errno;
+ goto ERROR;
+ }
+
++ // Write out the configuration
++ r = nw_config_options_write(zone->config);
++ if (r < 0)
++ goto ERROR;
++
+ // Write the configuration
+ r = nw_config_write(zone->config, f);
+- if (r)
++ if (r < 0)
+ goto ERROR;
+
+ ERROR:
++ if (configd)
++ nw_configd_unref(configd);
+ if (f)
+ fclose(f);
++ if (r)
++ ERROR("Could not save configuration for zone %s: %s\n", zone->name, strerror(-r));
+
+ return r;
+ }
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 480440f..6ab951b 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -28,11 +28,18 @@
+
+ typedef struct nw_zone nw_zone;
+
++typedef enum nw_zone_type_id {
++ __EMPTY
++} nw_zone_type_id_t;
++
+ #include <linux/if_link.h>
+
++#include "config.h"
+ #include "daemon.h"
+
+-int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name);
++int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const nw_zone_type_id_t type,
++ const char* name, nw_config* config);
++int nw_zone_open(nw_zone** zone, nw_daemon* daemon, const char* name, FILE* f);
+
+ nw_zone* nw_zone_ref(nw_zone* zone);
+ nw_zone* nw_zone_unref(nw_zone* zone);
+diff --git a/src/networkd/zones.c b/src/networkd/zones.c
+index 84a6673..654e637 100644
+--- a/src/networkd/zones.c
++++ b/src/networkd/zones.c
+@@ -135,30 +135,15 @@ static int nw_zones_add_zone(nw_zones* zones, nw_zone* zone) {
+ return 0;
+ }
+
+-static int __nw_zones_enumerate(const char* path, const struct stat* s, void* data) {
++static int __nw_zones_enumerate(struct dirent* entry, FILE* f, void* data) {
+ nw_zone* zone = NULL;
+ int r;
+
+ nw_zones* zones = (nw_zones*)data;
+
+- // Skip anything that isn't a directory
+- if (!S_ISDIR(s->st_mode))
+- return 0;
+-
+- // Find the basename of the file
+- const char* name = nw_path_basename(path);
+-
+- // Break on invalid paths
+- if (!name)
+- return 0;
+-
+- // Skip any hidden files
+- if (*name == '.')
+- return 0;
+-
+ // Create a new zone
+- r = nw_zone_create(&zone, zones->daemon, name);
+- if (r)
++ r = nw_zone_open(&zone, zones->daemon, entry->d_name, f);
++ if (r < 0 || r == 1)
+ goto ERROR;
+
+ // Add the zone to the list
+@@ -174,7 +159,21 @@ ERROR:
+ }
+
+ int nw_zones_enumerate(nw_zones* zones) {
+- return nw_ftw(ZONE_CONFIG_DIR, ZONE_CONFIG_DIR "/*", __nw_zones_enumerate, zones);
++ nw_configd* configd = NULL;
++ int r;
++
++ // Fetch zones configuration directory
++ configd = nw_daemon_configd(zones->daemon, "zones");
++ if (!configd)
++ return 0;
++
++ // Walk through all files
++ r = nw_configd_walk(configd, __nw_zones_enumerate, zones);
++
++ // Cleanup
++ nw_configd_unref(configd);
++
++ return r;
+ }
+
+ size_t nw_zones_num(nw_zones* zones) {
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,115 @@
+From 416ba6cd142bc5006b1c977bec86fc12dfffc80c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 11 Jun 2023 13:09:41 +0000
+Subject: [PATCH 301/304] zones: Move "struct nw_zone" into header
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port-vlan.h | 2 +-
+ src/networkd/port.h | 6 +-----
+ src/networkd/zone.c | 13 -------------
+ src/networkd/zone.h | 16 +++++++++++++++-
+ 4 files changed, 17 insertions(+), 20 deletions(-)
+
+diff --git a/src/networkd/port-vlan.h b/src/networkd/port-vlan.h
+index e17f7e7..c807238 100644
+--- a/src/networkd/port-vlan.h
++++ b/src/networkd/port-vlan.h
+@@ -47,7 +47,7 @@ struct nw_port_vlan {
+
+ // If the parent has not been read from the configuration we will
+ // save the name and try to find it later.
+- char __parent_name[IF_NAMESIZE];
++ char __parent_name[IFNAMSIZ];
+ };
+
+ extern const nw_port_type_t nw_port_type_vlan;
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 3cbe4b0..422a65f 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -25,10 +25,6 @@
+
+ #include <systemd/sd-netlink.h>
+
+-#ifndef IF_NAMESIZE
+-#define IF_NAMESIZE 16
+-#endif
+-
+ #define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+
+ typedef struct nw_port nw_port;
+@@ -82,7 +78,7 @@ struct nw_port {
+ nw_link* link;
+
+ const nw_port_type_t* type;
+- char name[IF_NAMESIZE];
++ char name[IFNAMSIZ];
+
+ // Configuration
+ nw_config *config;
+diff --git a/src/networkd/zone.c b/src/networkd/zone.c
+index 19d221f..1610dc0 100644
+--- a/src/networkd/zone.c
++++ b/src/networkd/zone.c
+@@ -38,19 +38,6 @@ static const nw_string_table_t nw_zone_type_id[] = {
+
+ NW_STRING_TABLE_LOOKUP(nw_zone_type_id_t, nw_zone_type_id)
+
+-struct nw_zone {
+- nw_daemon* daemon;
+- int nrefs;
+-
+- // Link
+- nw_link* link;
+-
+- char name[NETWORK_ZONE_NAME_MAX_LENGTH];
+-
+- // Configuration
+- nw_config *config;
+-};
+-
+ static void nw_zone_free(nw_zone* zone) {
+ if (zone->link)
+ nw_link_unref(zone->link);
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 6ab951b..2d1e1dc 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -23,7 +23,6 @@
+
+ #define ZONE_CONFIG_DIR CONFIG_DIR "/zones"
+
+-#define NETWORK_ZONE_NAME_MAX_LENGTH 16
+ #define NETWORK_ZONE_DEFAULT_MTU 1500
+
+ typedef struct nw_zone nw_zone;
+@@ -32,10 +31,25 @@ typedef enum nw_zone_type_id {
+ __EMPTY
+ } nw_zone_type_id_t;
+
++#include <linux/if.h>
+ #include <linux/if_link.h>
+
+ #include "config.h"
+ #include "daemon.h"
++#include "link.h"
++
++struct nw_zone {
++ nw_daemon* daemon;
++ int nrefs;
++
++ // Link
++ nw_link* link;
++
++ char name[IFNAMSIZ];
++
++ // Configuration
++ nw_config *config;
++};
+
+ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const nw_zone_type_id_t type,
+ const char* name, nw_config* config);
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,40 @@
+From 5548c63c9425f39472e75fef86ad2b8877657997 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 11 Jun 2023 13:10:31 +0000
+Subject: [PATCH 302/304] Drop unused configuration file paths
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/port.h | 2 --
+ src/networkd/zone.h | 2 --
+ 2 files changed, 4 deletions(-)
+
+diff --git a/src/networkd/port.h b/src/networkd/port.h
+index 422a65f..9770079 100644
+--- a/src/networkd/port.h
++++ b/src/networkd/port.h
+@@ -25,8 +25,6 @@
+
+ #include <systemd/sd-netlink.h>
+
+-#define PORT_CONFIG_DIR CONFIG_DIR "/ports"
+-
+ typedef struct nw_port nw_port;
+
+ typedef enum nw_port_type_id {
+diff --git a/src/networkd/zone.h b/src/networkd/zone.h
+index 2d1e1dc..2ece268 100644
+--- a/src/networkd/zone.h
++++ b/src/networkd/zone.h
+@@ -21,8 +21,6 @@
+ #ifndef NETWORKD_ZONE_H
+ #define NETWORKD_ZONE_H
+
+-#define ZONE_CONFIG_DIR CONFIG_DIR "/zones"
+-
+ #define NETWORK_ZONE_DEFAULT_MTU 1500
+
+ typedef struct nw_zone nw_zone;
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,77 @@
+From 10edad249159db9d6ef1e2b6e7a8fac78f183fce Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 11 Jun 2023 13:11:11 +0000
+Subject: [PATCH 303/304] util: Drop nw_ftw
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/networkd/util.c | 40 ----------------------------------------
+ src/networkd/util.h | 5 -----
+ 2 files changed, 45 deletions(-)
+
+diff --git a/src/networkd/util.c b/src/networkd/util.c
+index 0381ea8..e2f5cf1 100644
+--- a/src/networkd/util.c
++++ b/src/networkd/util.c
+@@ -18,44 +18,4 @@
+ # #
+ #############################################################################*/
+
+-#include <errno.h>
+-#include <fnmatch.h>
+-#include <ftw.h>
+-#include <stddef.h>
+-#include <stdio.h>
+-
+ #include "util.h"
+-
+-int nw_ftw(const char* path, const char* filter,
+- int (*callback)(const char* path, const struct stat* s, void* data), void* data) {
+- /*
+- This is a nested function which allows us to pass some custom pointer to
+- the callback function as that isn't possible with nftw().
+- */
+- int __callback(const char* p, const struct stat* s, int type, struct FTW* ftw) {
+- int r;
+-
+- // Filter out anything we don't want
+- if (filter) {
+- r = fnmatch(filter, p, FNM_PATHNAME);
+-
+- switch (r) {
+- // Pattern didn't match
+- case FNM_NOMATCH:
+- return 0;
+-
+- // Pattern matched
+- case 0:
+- break;
+-
+- // Error
+- default:
+- return 1;
+- }
+- }
+-
+- return callback(p, s, data);
+- }
+-
+- return nftw(path, __callback, 0, 0);
+-}
+diff --git a/src/networkd/util.h b/src/networkd/util.h
+index 950afda..11317ff 100644
+--- a/src/networkd/util.h
++++ b/src/networkd/util.h
+@@ -21,9 +21,4 @@
+ #ifndef NETWORKD_UTIL_H
+ #define NETWORKD_UTIL_H
+
+-#include <sys/stat.h>
+-
+-int nw_ftw(const char* path, const char* filter,
+- int (*callback)(const char* path, const struct stat* s, void* data), void* data);
+-
+ #endif /* NETWORKD_UTIL_H */
+--
+2.39.2
+
new file mode 100644
@@ -0,0 +1,26 @@
+From b9ac9712ab5dadf45ddc3749d7c9e393bc3eddaf Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 19 Sep 2023 12:54:53 +0000
+Subject: [PATCH 304/304] Makefile: Fix typo in localstatedir
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 8c0d0c0..7ca1229 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -51,7 +51,7 @@ hooks_zonesdir = $(hooksdir)/zones
+
+ triggersdir = $(networkdir)/triggers
+
+-logdir = $(localestatedir)/log/network
++logdir = $(localstatedir)/log/network
+ utildir = $(networkdir)
+
+ CLEANFILES =
+--
+2.39.2
+
deleted file mode 100644
@@ -1,23 +0,0 @@
-commit 88e5f32944b8dc1c4b1c74028c7d46c37b2aad34
-Author: Stefan Schantl <stefan.schantl@ipfire.org>
-Date: Sun Mar 19 15:58:11 2023 +0100
-
- Makefile.am: Fix typo defining the logdir
-
- The correct value name should be "localstatedir"
-
- Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
-
-diff --git a/Makefile.am b/Makefile.am
-index 893f1b8..5dc4629 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -51,7 +51,7 @@ hooks_zonesdir = $(hooksdir)/zones
-
- triggersdir = $(networkdir)/triggers
-
--logdir = $(localestatedir)/log/network
-+logdir = $(localstatedir)/log/network
- utildir = $(networkdir)
-
- CLEANFILES =