Compare commits

...
Author SHA1 Message Date
Joachim Wiberg 226ea49e7c confd: fix annoying warning in log
This fixes the annoying libyang warning after commit da29771.

    confd[3375]: libyang[0]: Invalid argument ctx_node (lyd_find_xpath()).

This happens when the diff is used in the wrong event when it is NULL.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 21:19:41 +01:00
Joachim Wiberg cb633a78d6 confd: minor coding style fixes and refactoring
Collapse, simplify, and break up ietf_keystore_change() into multiple
functions.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 21:19:41 +01:00
Joachim Wiberg d2e55eadf8 board/common: check if partition/LABEL is available
Check if partition/LABEL is available before trying to run tune2fs and
mount commands on it.  This change masks the kernel output:

    LABEL=var: Can't lookup blockdev

commne when running `make run` on x86_64 builds.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 21:19:40 +01:00
Joachim Wiberg 70d51420c9 board/common: fix scary boot warnings, follow-up to 45efa94
In 45efa94 we introduced new mount options for the: 'aux', 'cfg', and
'var' partitions.  To save boot time, we also disabled the periodic fsck
check, which does not affect journal replay or other Ext4 safety or
integrity checks.  The latter involved calling tune2fs at runtime, but
this caused the following to appear for the 'aux' partition, which is an
ext2 for systems that need to change U-Boot settings, e.g., to set MAC
address on systems without a VPD.

   EXT4-fs: Mount option(s) incompatible with ext2

To make matters worse, the new mount option(s), e.g., 'commit=30', gave
us another set of warnings from the kernel.  This was due to the fstype
being 'auto', so the kernel objected to options not being applicable to
every filesystem it tried before ending up with extfs:

    squashfs: Unknown parameter 'commit'
    vfat: Unknown parameter 'commit'
    exfat: Unknown parameter 'commit'
    fuseblk: Unknown parameter 'commit'
    btrfs: Unknown parameter 'errors'

This commit explicitly sets aux to ext2, and the others to ext4, and it
now checks before calling tune2fs that the partition/disk image is ext4.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 21:19:40 +01:00
Joachim Wiberg 1e80b20856 board/common: utilize new finit-style logging for factory reset
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 21:19:39 +01:00
Joachim Wiberg 918353dcc7 confd: drop 'enabled' node from IPv4 autoconf container
Neither the IPv6 autonconf container, nor the recently moved DHCP client
container have an 'enabled' flag.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 14:12:10 +01:00
Joachim Wiberg d53a35b867 confd: relocate /dhcp-client to /interfaces/interface/ipv4/dhcp
Please note, this change drops not only the global enabled flag, but also the
per-interface enabled flag, converting it to a presence container.  The name
of the container is also shortened from dhcp-client -> dhcp.  A pattern that
expected to be reused also for the DHCPv6 client.

Fixes #1109

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 14:11:53 +01:00
Joachim Wiberg 331f60be64 confd: minor, silence libyang warning when built w/o containers
Silence confd[3582]: libyang[0]: Invalid argument ctx_node (lyd_find_xpath())
warning in syslog.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-02 11:38:58 +01:00
36 changed files with 577 additions and 459 deletions
@@ -99,7 +99,20 @@
{ {
"name": "wan", "name": "wan",
"type": "infix-if-type:ethernet", "type": "infix-if-type:ethernet",
"ietf-ip:ipv6": {} "ietf-ip:ipv6": {},
"ietf-ip:ipv4": {
"infix-dhcp-client:dhcp": {
"option": [
{"id": "ntp-server"},
{"id": "broadcast"},
{"id": "domain"},
{"id": "hostname"},
{"id": "dns-server"},
{"id": "router"},
{"id": "netmask"}
]
}
}
}, },
{ {
"name": "wifi0", "name": "wifi0",
@@ -221,36 +234,6 @@
}, },
"infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo=" "infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo="
}, },
"infix-dhcp-client:dhcp-client": {
"client-if": [
{
"if-name": "wan",
"option": [
{
"id": "ntp-server"
},
{
"id": "broadcast"
},
{
"id": "domain"
},
{
"id": "hostname"
},
{
"id": "dns-server"
},
{
"id": "router"
},
{
"id": "netmask"
}
]
}
]
},
"infix-dhcp-server:dhcp-server": { "infix-dhcp-server:dhcp-server": {
"option": [ "option": [
{ {
@@ -311,7 +294,7 @@
] ]
}, },
"infix-meta:meta": { "infix-meta:meta": {
"version": "1.5" "version": "1.6"
}, },
"infix-services:mdns": { "infix-services:mdns": {
"enabled": true "enabled": true
@@ -58,7 +58,20 @@
{ {
"name": "wan", "name": "wan",
"type": "infix-if-type:ethernet", "type": "infix-if-type:ethernet",
"ietf-ip:ipv6": {} "ietf-ip:ipv6": {},
"ietf-ip:ipv4": {
"infix-dhcp-client:dhcp": {
"option": [
{"id": "ntp-server"},
{"id": "broadcast"},
{"id": "domain"},
{"id": "hostname"},
{"id": "dns-server"},
{"id": "router"},
{"id": "netmask"}
]
}
}
} }
] ]
}, },
@@ -172,36 +185,6 @@
}, },
"infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo=" "infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo="
}, },
"infix-dhcp-client:dhcp-client": {
"client-if": [
{
"if-name": "wan",
"option": [
{
"id": "ntp-server"
},
{
"id": "broadcast"
},
{
"id": "domain"
},
{
"id": "hostname"
},
{
"id": "dns-server"
},
{
"id": "router"
},
{
"id": "netmask"
}
]
}
]
},
"infix-dhcp-server:dhcp-server": { "infix-dhcp-server:dhcp-server": {
"option": [ "option": [
{ {
@@ -262,7 +245,7 @@
] ]
}, },
"infix-meta:meta": { "infix-meta:meta": {
"version": "1.5" "version": "1.6"
}, },
"infix-services:mdns": { "infix-services:mdns": {
"enabled": true "enabled": true
@@ -37,7 +37,20 @@
}, },
{ {
"name": "eth0", "name": "eth0",
"type": "infix-if-type:ethernet" "type": "infix-if-type:ethernet",
"ietf-ip:ipv4": {
"infix-dhcp-client:dhcp": {
"option": [
{"id": "netmask"},
{"id": "broadcast"},
{"id": "router"},
{"id": "domain"},
{"id": "hostname"},
{"id": "dns-server"},
{"id": "ntp-server"}
]
}
}
}, },
{ {
"name": "wifi0", "name": "wifi0",
@@ -157,38 +170,8 @@
}, },
"infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo=" "infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo="
}, },
"infix-dhcp-client:dhcp-client": {
"client-if": [
{
"if-name": "eth0",
"option": [
{
"id": "netmask"
},
{
"id": "broadcast"
},
{
"id": "router"
},
{
"id": "domain"
},
{
"id": "hostname"
},
{
"id": "dns-server"
},
{
"id": "ntp-server"
}
]
}
]
},
"infix-meta:meta": { "infix-meta:meta": {
"version": "1.5" "version": "1.6"
}, },
"infix-services:mdns": { "infix-services:mdns": {
"enabled": true "enabled": true
+6 -6
View File
@@ -15,9 +15,9 @@ cfgfs /config configfs nofail,noauto 0 0
# The chosen backing storage for the overlays placed on /cfg, /etc, # The chosen backing storage for the overlays placed on /cfg, /etc,
# /home, /root, and /var, are determined dynamically by /usr/libexec/infix/mnt # /home, /root, and /var, are determined dynamically by /usr/libexec/infix/mnt
# depending on the available devices. # depending on the available devices.
mnttmp /mnt/tmp tmpfs defaults 0 0 mnttmp /mnt/tmp tmpfs defaults 0 0
LABEL=aux /mnt/aux auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 LABEL=aux /mnt/aux ext2 noatime,nodiratime,noauto,errors=remount-ro 0 0
LABEL=var /mnt/var auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 LABEL=var /mnt/var ext4 noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0
LABEL=cfg /mnt/cfg auto noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0 LABEL=cfg /mnt/cfg ext4 noatime,nodiratime,noauto,commit=30,errors=remount-ro 0 0
hostfs /mnt/host 9p cache=none,msize=16384,noauto 0 0 hostfs /mnt/host 9p cache=none,msize=16384,noauto 0 0
/usr/libexec/infix/mnt# /cfg helper none 0 0 /usr/libexec/infix/mnt# /cfg helper none 0 0
+46 -12
View File
@@ -44,12 +44,15 @@ check_factory()
factory_reset() factory_reset()
{ {
find /sys/class/leds/ -type l -exec sh -c 'echo 100 > $0/brightness' {} \; find /sys/class/leds/ -type l -exec sh -c 'echo 100 > $0/brightness' {} \;
logger $opt -p user.crit -t "$nm" "Resetting to factory defaults." print_start "Resetting to factory defaults."
logger $opt -p user.notice -t "$nm" "Resetting to factory defaults."
rm -rf /mnt/cfg/* /mnt/var/* rm -rf /mnt/cfg/* /mnt/var/*
logger $opt -p user.crit -t "$nm" "Factory reset complete."
sync sync
logger $opt -p user.notice -t "$nm" "Factory reset complete."
print_end 0 "Factory reset complete."
print_restore
} }
is_mmc() is_mmc()
@@ -105,15 +108,18 @@ find_partition_by_label()
esac esac
disk="/dev/$devname" disk="/dev/$devname"
#
# 1. Try GPT/MBR partition label using sgdisk
#
result=$(sgdisk -p "$disk" 2>/dev/null | awk -v label="$label" -v devname="$devname" ' result=$(sgdisk -p "$disk" 2>/dev/null | awk -v label="$label" -v devname="$devname" '
/^ *[0-9]/ { /^ *[0-9]/ {
if ($7 == label) { if ($7 == label) {
if (devname ~ /^(mmcblk|nvme|loop)/) { if (devname ~ /^(mmcblk|nvme|loop)/)
print devname "p" $1 print devname "p" $1;
} else { else
print devname $1 print devname $1;
} exit 0;
exit 0
} }
} }
') ')
@@ -122,7 +128,30 @@ find_partition_by_label()
echo "$result" echo "$result"
return 0 return 0
fi fi
#
# 2. Fallback: Check if the whole disk is an ext4/ext2/ext3 filesystem
#
# Check for ext4/ext2/ext3 magic number (0xEF53) at offset 1080 (1024+56).
magic_number=$(dd if="$disk" bs=1 skip=1080 count=2 2>/dev/null | od -t x2 -A n | tr -d ' \n')
# Check for both Little-Endian ('53ef') and Big-Endian ('ef53') interpretations of 0xEF53.
# This supports bi-endian architectures like MIPS that may run in BE mode,
# as well as the LE mode which is standard for RISC-V and ext filesystems.
if [ "$magic_number" = "ef53" ] || [ "$magic_number" = "53ef" ]; then
# Read the volume label from offset 1144 (1024+120)
fslabel=$(dd if="$disk" bs=1 skip=1144 count=16 2>/dev/null | tr -d '\000')
logger $opt -p user.notice -t "$nm" "Found label $fslabel on disk $disk ..."
if [ "$fslabel" = "$label" ]; then
echo "$devname"
return 0
fi
fi
done done
return 1 return 1
} }
@@ -273,6 +302,9 @@ mount_rw()
# If something is already setup, leave it be. # If something is already setup, leave it be.
mountpoint -q "/$1" && return 0 mountpoint -q "/$1" && return 0
# If partition doesn't exist (var is optional), signal caller.
find_partition_by_label "$1" >/dev/null || return 1
# Check if /var has been resized to fill the sdcard/eMMC # Check if /var has been resized to fill the sdcard/eMMC
if [ "$1" = "var" ] && is_mmc; then if [ "$1" = "var" ] && is_mmc; then
if [ -f /mnt/aux/resized ] || [ -f /mnt/aux/resized.failed ]; then if [ -f /mnt/aux/resized ] || [ -f /mnt/aux/resized.failed ]; then
@@ -289,8 +321,10 @@ mount_rw()
# TODO: Also look for UBI partitions # TODO: Also look for UBI partitions
# # Disable periodic fsck, yet keeping safety checks on ext4
tune2fs -c 0 -i 0 LABEL="$1" 2>/dev/null if grep "LABEL=$label" /etc/fstab |grep -q ext4; then
tune2fs -c 0 -i 0 LABEL="$1" 2>/dev/null
fi
mount LABEL="$1" 2>/dev/null && return 0 mount LABEL="$1" 2>/dev/null && return 0
return 1 return 1
+6
View File
@@ -8,6 +8,12 @@ All notable changes to the project are documented in this file.
### Changes ### Changes
- The DHCP client configuration has moved from `/infix-dhcp-client:dhcp-client`
to `/interfaces/interface[name]/ipv4/infix-dhcp-client:dhcp`, issue #1109.
The configuration is automatically migrated on upgrade. The DHCP client is
now enabled using a presence container instead of a separate `enabled` leaf
- The `enabled` nore for IPv4 autoconf (ZeroConf) has been dropped, `autoconf`
is now a presence container. Configuration automatically migrated on upgrade
- Improvements to `sdcard.img` generation, useful for developers mostly: - Improvements to `sdcard.img` generation, useful for developers mostly:
- The NanoPi R2S bootloader is now automatically built and uploaded to - The NanoPi R2S bootloader is now automatically built and uploaded to
the [`latest-boot` release][lastest-boot] tag the [`latest-boot` release][lastest-boot] tag
+37 -11
View File
@@ -996,7 +996,7 @@ default.
admin@example:/> configure admin@example:/> configure
admin@example:/config/> edit interface eth0 ipv4 admin@example:/config/> edit interface eth0 ipv4
admin@example:/config/interface/eth0/ipv4/> set address 10.0.1.1 prefix-length 24 admin@example:/config/interface/eth0/ipv4/> set address 10.0.1.1 prefix-length 24
admin@example:/config/interface/eth0/ipv4/> set autoconf enabled true admin@example:/config/interface/eth0/ipv4/> set autoconf
admin@example:/config/interface/eth0/ipv4/> diff admin@example:/config/interface/eth0/ipv4/> diff
+interfaces { +interfaces {
+ interface eth0 { + interface eth0 {
@@ -1004,9 +1004,7 @@ default.
+ address 10.0.1.1 { + address 10.0.1.1 {
+ prefix-length 24; + prefix-length 24;
+ } + }
+ autoconf { + autoconf;
+ enabled true;
+ }
+ } + }
+ } + }
+} +}
@@ -1022,15 +1020,18 @@ default.
ipv6 ::1/128 (static) ipv6 ::1/128 (static)
admin@example:/> admin@example:/>
As shown, the link-local IPv4 address is configured with `set autconf As shown, the link-local IPv4 address is configured with `set autoconf`.
enabled true`. The resulting address (169.254.1.3/16) is of type The presence of the `autoconf` container enables IPv4 link-local address
*random* ([ietf-ip.yang][2]). assignment. The resulting address (169.254.1.3/16) is of type *random*
([ietf-ip.yang][2]).
The IPv4LL client also supports a `request-address` setting which can be The IPv4LL client also supports a `request-address` setting which can be
used to "seed" the client's starting address. If the address is free it used to "seed" the client's starting address. If the address is free it
will be used, otherwise it falls back to the default algorithm. will be used, otherwise it falls back to the default algorithm.
admin@example:/config/interface/eth0/ipv4/> set autoconf request-address 169.254.1.2 admin@example:/config/interface/eth0/ipv4/> edit autoconf
admin@example:/config/interface/eth0/ipv4/autoconf/> set request-address 169.254.1.2
admin@example:/config/interface/eth0/ipv4/autoconf/> leave
#### Use of DHCP for IPv4 address assignment #### Use of DHCP for IPv4 address assignment
@@ -1038,9 +1039,9 @@ will be used, otherwise it falls back to the default algorithm.
![Using DHCP for IPv4 address assignment](img/ip-address-example-ipv4-dhcp.svg) ![Using DHCP for IPv4 address assignment](img/ip-address-example-ipv4-dhcp.svg)
admin@example:/> configure admin@example:/> configure
admin@example:/config/> edit dhcp-client admin@example:/config/> edit interface eth0 ipv4
admin@example:/config/dhcp-client/> set client-if eth0 admin@example:/config/interface/eth0/ipv4/> set dhcp
admin@example:/config/dhcp-client/> leave admin@example:/config/interface/eth0/ipv4/> leave
admin@example:/> show interfaces admin@example:/> show interfaces
INTERFACE PROTOCOL STATE DATA INTERFACE PROTOCOL STATE DATA
eth0 ethernet UP 02:00:00:00:00:00 eth0 ethernet UP 02:00:00:00:00:00
@@ -1053,6 +1054,31 @@ will be used, otherwise it falls back to the default algorithm.
The resulting address (10.1.2.100/24) is of type *dhcp*. The resulting address (10.1.2.100/24) is of type *dhcp*.
To configure DHCP client options, such as sending a specific hostname to the
server, you can specify options with values:
```
admin@example:/> configure
admin@example:/config/> edit interface eth0 ipv4 dhcp
admin@example:/config/interface/eth0/ipv4/dhcp/> set option hostname value myhost
admin@example:/config/interface/eth0/ipv4/dhcp/> show
option hostname {
value myhost;
}
admin@example:/config/interface/eth0/ipv4/dhcp/> leave
admin@example:/>
```
> [!TIP]
> The special value `auto` can be used with the hostname option to
> automatically use the configured system hostname.
Other useful DHCP options include:
- `client-id` - Send a specific client identifier to the server
- `route-preference` - Set the administrative distance for DHCP-learned routes (default: 5)
For advanced usage with vendor-specific options, see the YANG model.
#### Disabling IPv6 link-local address(es) #### Disabling IPv6 link-local address(es)
+16 -28
View File
@@ -173,11 +173,13 @@ Enabling DHCPv4 client on interface *e0*, with current default options.
``` ```
~$ cat /tmp/file.json ~$ cat /tmp/file.json
{ {
"infix-dhcp-client:dhcp-client": { "ietf-interfaces:interfaces": {
"enabled": true, "interface": [
"client-if": [
{ {
"if-name": "e0" "name": "e0",
"ietf-ip:ipv4": {
"infix-dhcp-client:dhcp": {}
}
} }
] ]
} }
@@ -187,13 +189,18 @@ Enabling DHCPv4 client on interface *e0*, with current default options.
~$ ~$
``` ```
Disabling DHCPv4 client. Disabling DHCPv4 client on interface *e0* (remove the dhcp container).
``` ```
~$ cat /tmp/file.json ~$ cat /tmp/file.json
{ {
"infix-dhcp-client:dhcp-client": { "ietf-interfaces:interfaces": {
"enabled": false "interface": [
{
"name": "e0",
"ietf-ip:ipv4": {}
}
]
} }
} }
~$ scp file.json admin@example.local:/tmp/file.json ~$ scp file.json admin@example.local:/tmp/file.json
@@ -201,27 +208,8 @@ Disabling DHCPv4 client.
~$ ~$
``` ```
Configuration for client interface *e0* remains, but does not apply as To fully remove the DHCPv4 client configuration, remove the `infix-dhcp-client:dhcp`
DHCPv4 is disabled. container from the interface's ipv4 configuration.
```
admin@example:~$ sysrepocfg -X -fjson -d running -x "/infix-dhcp-client:dhcp-client"
{
"infix-dhcp-client:dhcp-client": {
"enabled": false,
"client-if": [
{
"if-name": "e0"
}
]
}
}
admin@example:~$
```
To fully remove the DHCPv4 client configuration or a specific
*client-if* with sysrepocfg, one would need to read out the full
configuration, remove relevant parts and read back.
## Enable/Disable IPv6 ## Enable/Disable IPv6
+1 -1
View File
@@ -4,7 +4,7 @@
# #
################################################################################ ################################################################################
CONFD_VERSION = 1.5 CONFD_VERSION = 1.6
CONFD_SITE_METHOD = local CONFD_SITE_METHOD = local
CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
CONFD_LICENSE = BSD-3-Clause CONFD_LICENSE = BSD-3-Clause
+17 -15
View File
@@ -169,9 +169,23 @@ if [ -n "$bridge" ]; then
"name": "$bridge", "name": "$bridge",
"type": "infix-if-type:bridge", "type": "infix-if-type:bridge",
"ietf-ip:ipv4": { "ietf-ip:ipv4": {
"infix-ip:autoconf": { EOF
"enabled": $ipv4 if [ "$ipv4" = "true" ]; then
} cat <<EOF
"infix-ip:autoconf": {}
EOF
if [ "$dhcp" = "true" ]; then
cat <<EOF
,
"infix-dhcp-client:dhcp": {}
EOF
fi
elif [ "$dhcp" = "true" ]; then
cat <<EOF
"infix-dhcp-client:dhcp": {}
EOF
fi
cat <<EOF
}, },
"ietf-ip:ipv6": { "ietf-ip:ipv6": {
"enabled": true, "enabled": true,
@@ -195,18 +209,6 @@ $(for iface in $ifaces; do gen_iface_json "$iface"; done)
$(for iface in $ports; do gen_iface_json "$iface" "$bridge"; done) $(for iface in $ports; do gen_iface_json "$iface" "$bridge"; done)
] ]
EOF EOF
if [ "$dhcp" = "true" ] && [ -n "$bridge" ]; then
cat <<EOF
},
"infix-dhcp-client:dhcp-client": {
"enabled": true,
"client-if": [
{
"if-name": "$bridge"
}
]
EOF
fi
cat <<EOF cat <<EOF
} }
} }
+2 -1
View File
@@ -1,6 +1,6 @@
AC_PREREQ(2.61) AC_PREREQ(2.61)
# confd version is same as system YANG model version, step on breaking changes # confd version is same as system YANG model version, step on breaking changes
AC_INIT([confd], [1.5], [https://github.com/kernelkit/infix/issues]) AC_INIT([confd], [1.6], [https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects) AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes) AM_SILENT_RULES(yes)
@@ -19,6 +19,7 @@ AC_CONFIG_FILES([
share/migrate/1.3/Makefile share/migrate/1.3/Makefile
share/migrate/1.4/Makefile share/migrate/1.4/Makefile
share/migrate/1.5/Makefile share/migrate/1.5/Makefile
share/migrate/1.6/Makefile
yang/Makefile yang/Makefile
yang/confd/Makefile yang/confd/Makefile
yang/test-mode/Makefile yang/test-mode/Makefile
+38
View File
@@ -0,0 +1,38 @@
#!/bin/sh
# Migrate DHCP client configuration from standalone model to ietf-ip augment
# From: /infix-dhcp-client:dhcp-client/client-if[if-name]
# To: /ietf-interfaces:interfaces/interface[name]/ipv4/infix-dhcp-client:dhcp
file=$1
temp=${file}.tmp
jq '
if .["infix-dhcp-client:dhcp-client"] then
.["infix-dhcp-client:dhcp-client"]."client-if" as $clients |
($clients // [] | map({
key: ."if-name",
value: {
"infix-dhcp-client:dhcp": (
. | del(."if-name") | del(.enabled)
)
}
}) | from_entries) as $dhcp_configs |
if .["ietf-interfaces:interfaces"] then
.["ietf-interfaces:interfaces"].interface |= map(
if $dhcp_configs[.name] then
.["ietf-ip:ipv4"] += $dhcp_configs[.name]
else
.
end
)
else
.
end |
del(.["infix-dhcp-client:dhcp-client"])
else
.
end
' "$file" > "$temp" && mv "$temp" "$file"
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
# Migrate IPv4 autoconf from enabled leaf to presence container
# Remove the "enabled" leaf, keeping the container only if it was enabled
file=$1
temp=${file}.tmp
jq '
if .["ietf-interfaces:interfaces"] then
.["ietf-interfaces:interfaces"].interface |= map(
if .["ietf-ip:ipv4"]?."infix-ip:autoconf" then
if .["ietf-ip:ipv4"]."infix-ip:autoconf".enabled == false then
# Remove autoconf container if it was disabled
.["ietf-ip:ipv4"] |= del(."infix-ip:autoconf")
else
# Keep autoconf but remove the enabled leaf
.["ietf-ip:ipv4"]."infix-ip:autoconf" |= del(.enabled)
end
else
.
end
)
else
.
end
' "$file" > "$temp" && mv "$temp" "$file"
+3
View File
@@ -0,0 +1,3 @@
migratedir = $(pkgdatadir)/migrate/1.6
dist_migrate_DATA = 10-dhcp-client-to-ipv4.sh \
20-autoconf-to-presence.sh
+1 -1
View File
@@ -1,2 +1,2 @@
SUBDIRS = 1.0 1.1 1.2 1.3 1.4 1.5 SUBDIRS = 1.0 1.1 1.2 1.3 1.4 1.5 1.6
migratedir = $(pkgdatadir)/migrate migratedir = $(pkgdatadir)/migrate
+17 -8
View File
@@ -8,8 +8,8 @@
struct confd confd; struct confd confd;
int core_startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *module, static int core_startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv) const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{ {
sr_event_t last_event = -1; sr_event_t last_event = -1;
static unsigned int last_request = -1; static unsigned int last_request = -1;
@@ -221,8 +221,10 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
goto free_diff; goto free_diff;
/* infix-containers */ /* infix-containers */
#ifdef CONTAINERS
if ((rc = infix_containers_change(session, config, diff, event, confd))) if ((rc = infix_containers_change(session, config, diff, event, confd)))
goto free_diff; goto free_diff;
#endif
/* ietf-hardware */ /* ietf-hardware */
if ((rc = ietf_hardware_change(session, config, diff, event, confd))) if ((rc = ietf_hardware_change(session, config, diff, event, confd)))
@@ -258,16 +260,21 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
AUDIT("The new configuration has been applied."); AUDIT("The new configuration has been applied.");
} }
free_diff: free_diff:
lyd_free_tree(diff); lyd_free_tree(diff);
return rc; return rc;
} }
static inline int subscribe_module(char *model, struct confd *confd, int flags) { static inline int subscribe_module(char *model, struct confd *confd, int flags)
return sr_module_change_subscribe(confd->session, model, "//.", change_cb, confd, {
CB_PRIO_PRIMARY, SR_SUBSCR_CHANGE_ALL_MODULES | SR_SUBSCR_DEFAULT | flags, &confd->sub) && ERROR("core: subscribing to models");
return sr_module_change_subscribe(confd->session, model, "//.", change_cb, confd,
CB_PRIO_PRIMARY, SR_SUBSCR_CHANGE_ALL_MODULES |
SR_SUBSCR_DEFAULT | flags, &confd->sub) &&
sr_module_change_subscribe(confd->startup, model, "//.", core_startup_save, NULL, sr_module_change_subscribe(confd->startup, model, "//.", core_startup_save, NULL,
CB_PRIO_PASSIVE, SR_SUBSCR_PASSIVE | SR_SUBSCR_CHANGE_ALL_MODULES, &confd->sub); CB_PRIO_PASSIVE, SR_SUBSCR_CHANGE_ALL_MODULES |
SR_SUBSCR_PASSIVE, &confd->sub);
} }
int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv) int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
@@ -385,9 +392,11 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
rc = ietf_system_rpc_init(&confd); rc = ietf_system_rpc_init(&confd);
if (rc) if (rc)
goto err; goto err;
#ifdef CONTAINERS
rc = infix_containers_rpc_init(&confd); rc = infix_containers_rpc_init(&confd);
if (rc) if (rc)
goto err; goto err;
#endif
rc = infix_dhcp_server_rpc_init(&confd); rc = infix_dhcp_server_rpc_init(&confd);
if (rc) if (rc)
goto err; goto err;
-1
View File
@@ -135,7 +135,6 @@ struct confd {
struct dagger netdag; struct dagger netdag;
}; };
int core_startup_save (sr_session_ctx_t *, uint32_t, const char *, const char *, sr_event_t, unsigned, void *);
static inline int register_change(sr_session_ctx_t *session, const char *module, const char *xpath, static inline int register_change(sr_session_ctx_t *session, const char *module, const char *xpath,
int flags, sr_module_change_cb cb, void *arg, sr_subscription_ctx_t **sub) int flags, sr_module_change_cb cb, void *arg, sr_subscription_ctx_t **sub)
+3 -4
View File
@@ -94,7 +94,7 @@ int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
* for various reasons: was bridge port, ipv4 was disabled... * for various reasons: was bridge port, ipv4 was disabled...
*/ */
zcip = lydx_get_child(ipconf, "autoconf"); zcip = lydx_get_child(ipconf, "autoconf");
if (zcip && lydx_is_enabled(zcip, "enabled")) { if (zcip) {
struct lyd_node *node; struct lyd_node *node;
const char *addr; const char *addr;
int diff = 0; int diff = 0;
@@ -105,9 +105,8 @@ int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
if (node) { if (node) {
const struct lyd_node *tmp; const struct lyd_node *tmp;
tmp = lydx_get_child(node, "enabled"); /* presence container created or deleted */
if (tmp) diff++;
diff++;
tmp = lydx_get_child(node, "request-address"); tmp = lydx_get_child(node, "request-address");
if (tmp) if (tmp)
diff++; diff++;
+119 -100
View File
@@ -51,89 +51,122 @@ static char *filerd(const char *fn, size_t len)
return buf; return buf;
} }
int ietf_keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd) static int gen_hostkey(const char *name, struct lyd_node *change)
{
const char *private_key, *public_key;
int rc = SR_ERR_OK;
private_key = lydx_get_cattr(change, "cleartext-private-key");
public_key = lydx_get_cattr(change, "public-key");
if (mkdir(SSH_HOSTKEYS_NEXT, 0600) && (errno != EEXIST)) {
ERRNO("Failed creating %s", SSH_HOSTKEYS_NEXT);
rc = SR_ERR_INTERNAL;
}
if (systemf("/usr/libexec/infix/mksshkey %s %s %s %s", name, SSH_HOSTKEYS_NEXT, public_key, private_key))
rc = SR_ERR_INTERNAL;
return rc;
}
static int keystore_update(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff)
{
const char *xpath = "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key";
sr_val_t *list = NULL;
size_t count = 0;
int rc;
rc = sr_get_items(session, xpath, 0, 0, &list, &count);
if (rc != SR_ERR_OK) {
ERROR("Cannot find any asymmetric keys in configuration");
return 0;
}
for (size_t i = 0; i < count; i++) {
char *name = srx_get_str(session, "%s/name", list[i].xpath);
char *public_key_format, *private_key_format;
char *pub_key = NULL, *priv_key = NULL;
sr_val_t *entry = &list[i];
if (srx_isset(session, "%s/cleartext-private-key", entry->xpath) ||
srx_isset(session, "%s/public-key", entry->xpath))
continue;
public_key_format = srx_get_str(session, "%s/public-key-format", entry->xpath);
if (!public_key_format)
continue;
private_key_format = srx_get_str(session, "%s/private-key-format", entry->xpath);
if (!private_key_format) {
free(public_key_format);
continue;
}
if (strcmp(private_key_format, "infix-crypto-types:rsa-private-key-format") ||
strcmp(public_key_format, "infix-crypto-types:ssh-public-key-format"))
continue;
NOTE("SSH key (%s) does not exist, generating...", name);
if (systemf("/usr/libexec/infix/mkkeys %s %s", SSH_PRIVATE_KEY, SSH_PUBLIC_KEY)) {
ERROR("Failed generating SSH keys for %s", name);
goto next;
}
priv_key = filerd(SSH_PRIVATE_KEY, filesz(SSH_PRIVATE_KEY));
if (!priv_key)
goto next;
pub_key = filerd(SSH_PUBLIC_KEY, filesz(SSH_PUBLIC_KEY));
if (!pub_key)
goto next;
rc = srx_set_str(session, priv_key, 0, "%s/cleartext-private-key", entry->xpath);
if (rc) {
ERROR("Failed setting private key for %s... rc: %d", name, rc);
goto next;
}
rc = srx_set_str(session, pub_key, 0, "%s/public-key", entry->xpath);
if (rc != SR_ERR_OK) {
ERROR("Failed setting public key for %s... rc: %d", name, rc);
goto next;
}
next:
if (erase(SSH_PRIVATE_KEY))
ERRNO("Failed removing SSH server private key");
if (erase(SSH_PUBLIC_KEY))
ERRNO("Failed removing SSH server public key");
if (priv_key)
free(priv_key);
if (pub_key)
free(pub_key);
free(name);
free(public_key_format);
free(private_key_format);
}
if (list)
sr_free_values(list, count);
return 0;
}
int ietf_keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff,
sr_event_t event, struct confd *confd)
{ {
struct lyd_node *changes, *change; struct lyd_node *changes, *change;
sr_val_t *list = NULL;
int rc = SR_ERR_OK; int rc = SR_ERR_OK;
size_t count = 0;
if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_)) if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_))
return SR_ERR_OK; return SR_ERR_OK;
switch (event) { switch (event) {
case SR_EV_UPDATE: case SR_EV_UPDATE:
rc = sr_get_items(session, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key", 0, 0, &list, &count); rc = keystore_update(session, config, diff);
if (rc != SR_ERR_OK) {
ERROR("Cannot find any asymmetric keys in configuration");
return 0;
}
for (size_t i = 0; i < count; ++i) {
sr_val_t *entry = &list[i];
if (!srx_isset(session, "%s/cleartext-private-key", entry->xpath) && !srx_isset(session, "%s/public-key", entry->xpath)) {
char *private_key_format, *public_key_format;
public_key_format = srx_get_str(session, "%s/public-key-format", entry->xpath);
if (!public_key_format)
continue;
private_key_format = srx_get_str(session, "%s/private-key-format", entry->xpath);
if (!private_key_format) {
free(public_key_format);
continue;
}
if (!strcmp(private_key_format, "infix-crypto-types:rsa-private-key-format") &&
!strcmp(public_key_format, "infix-crypto-types:ssh-public-key-format")) {
char *pub_key = NULL, *priv_key = NULL, *name;
name = srx_get_str(session, "%s/name", entry->xpath);
NOTE("SSH key (%s) does not exist, generating...", name);
if (systemf("/usr/libexec/infix/mkkeys %s %s", SSH_PRIVATE_KEY, SSH_PUBLIC_KEY)) {
ERROR("Failed to generate SSH keys for %s", name);
goto next;
}
priv_key = filerd(SSH_PRIVATE_KEY, filesz(SSH_PRIVATE_KEY));
if (!priv_key)
goto next;
pub_key = filerd(SSH_PUBLIC_KEY, filesz(SSH_PUBLIC_KEY));
if (!pub_key)
goto next;
rc = srx_set_str(session, priv_key, 0, "%s/cleartext-private-key", entry->xpath);
if (rc) {
ERROR("Failed setting private key for %s... rc: %d", name, rc);
goto next;
}
rc = srx_set_str(session, pub_key, 0, "%s/public-key", entry->xpath);
if (rc != SR_ERR_OK) {
ERROR("Failed setting public key for %s... rc: %d", name, rc);
goto next;
}
next:
if (erase(SSH_PRIVATE_KEY))
ERRNO("Failed removing SSH server private key");
if (erase(SSH_PUBLIC_KEY))
ERRNO("Failed removing SSH server public key");
if (priv_key)
free(priv_key);
if (pub_key)
free(pub_key);
free(name);
}
free(public_key_format);
free(private_key_format);
}
}
if (list)
sr_free_values(list, count);
break; break;
case SR_EV_CHANGE: case SR_EV_CHANGE:
case SR_EV_ENABLED: case SR_EV_ENABLED:
@@ -142,49 +175,35 @@ int ietf_keystore_change(sr_session_ctx_t *session, struct lyd_node *config, str
rmrf(SSH_HOSTKEYS_NEXT); rmrf(SSH_HOSTKEYS_NEXT);
return SR_ERR_OK; return SR_ERR_OK;
case SR_EV_DONE: case SR_EV_DONE:
if(fexist(SSH_HOSTKEYS_NEXT)) { if (fexist(SSH_HOSTKEYS_NEXT)) {
if(rmrf(SSH_HOSTKEYS)) { if (rmrf(SSH_HOSTKEYS))
ERRNO("Failed to remove old SSH hostkeys: %d", errno); ERRNO("Failed to remove old SSH hostkeys: %d", errno);
}
if (rename(SSH_HOSTKEYS_NEXT, SSH_HOSTKEYS)) if (rename(SSH_HOSTKEYS_NEXT, SSH_HOSTKEYS))
ERRNO("Failed switching to new %s", SSH_HOSTKEYS); ERRNO("Failed switching to new %s", SSH_HOSTKEYS);
} }
return SR_ERR_OK; return SR_ERR_OK;
default: default:
return SR_ERR_OK; return SR_ERR_OK;
} }
changes = lydx_get_descendant(config, "keystore", "asymmetric-keys", "asymmetric-key", NULL); changes = lydx_get_descendant(config, "keystore", "asymmetric-keys", "asymmetric-key", NULL);
LYX_LIST_FOR_EACH(changes, change, "asymmetric-key") { LYX_LIST_FOR_EACH(changes, change, "asymmetric-key") {
const char *name, *private_key_type, *public_key_type; const char *name = lydx_get_cattr(change, "name");
const char *private_key, *public_key; const char *type;
name = lydx_get_cattr(change, "name"); type = lydx_get_cattr(change, "private-key-format");
private_key_type = lydx_get_cattr(change, "private-key-format"); if (strcmp(type, "infix-crypto-types:rsa-private-key-format")) {
public_key_type = lydx_get_cattr(change, "public-key-format"); INFO("Private key %s is not of SSH type (%s)", name, type);
if (strcmp(private_key_type, "infix-crypto-types:rsa-private-key-format")) {
INFO("Private key %s is not of SSH type", name);
continue; continue;
} }
if (strcmp(public_key_type, "infix-crypto-types:ssh-public-key-format")) { type = lydx_get_cattr(change, "public-key-format");
INFO("Public key %s is not of SSH type", name); if (strcmp(type, "infix-crypto-types:ssh-public-key-format")) {
INFO("Public key %s is not of SSH type (%s)", name, type);
continue; continue;
} }
private_key = lydx_get_cattr(change, "cleartext-private-key");
public_key = lydx_get_cattr(change, "public-key");
if (mkdir(SSH_HOSTKEYS_NEXT, 0600) && (errno != EEXIST)) { gen_hostkey(name, change);
ERRNO("Failed creating %s", SSH_HOSTKEYS_NEXT);
rc = SR_ERR_INTERNAL;
}
if(systemf("/usr/libexec/infix/mksshkey %s %s %s %s", name, SSH_HOSTKEYS_NEXT, public_key, private_key))
rc = SR_ERR_INTERNAL;
} }
return rc; return rc;
+1
View File
@@ -356,6 +356,7 @@ int infix_containers_change(sr_session_ctx_t *session, struct lyd_node *config,
if (diff && !lydx_get_xpathf(diff, CFG_XPATH)) if (diff && !lydx_get_xpathf(diff, CFG_XPATH))
return SR_ERR_OK; return SR_ERR_OK;
switch (event) { switch (event) {
case SR_EV_DONE: case SR_EV_DONE:
break; break;
+37 -33
View File
@@ -14,7 +14,7 @@
#include "core.h" #include "core.h"
#define ARPING_MSEC 1000 #define ARPING_MSEC 1000
#define MODULE "infix-dhcp-client" #define MODULE "infix-dhcp-client"
#define XPATH "/infix-dhcp-client:dhcp-client" #define XPATH "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/infix-dhcp-client:dhcp"
#define CACHE_TEMPLATE "/var/lib/misc/%s.cache" #define CACHE_TEMPLATE "/var/lib/misc/%s.cache"
static char *ip_cache(const char *ifname, char *str, size_t len) static char *ip_cache(const char *ifname, char *str, size_t len)
@@ -278,57 +278,61 @@ static void del(const char *ifname)
systemf("initctl -bfq delete dhcp-client-%s", ifname); systemf("initctl -bfq delete dhcp-client-%s", ifname);
} }
int infix_dhcp_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd) int infix_dhcp_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff,
sr_event_t event, struct confd *confd)
{ {
struct lyd_node *global, *cifs, *difs, *cif, *dif; struct lyd_node *ifaces, *difaces, *iface, *diface, *ipv4, *dhcp, *ddhcp;
sr_error_t err = 0; sr_error_t err = 0;
int ena = 0;
switch (event) { if (event != SR_EV_DONE)
case SR_EV_DONE:
break;
case SR_EV_CHANGE:
case SR_EV_ABORT:
default:
return SR_ERR_OK; return SR_ERR_OK;
}
global = lydx_get_descendant(config, "dhcp-client", NULL); ifaces = lydx_get_descendant(config, "interfaces", "interface", NULL);
ena = lydx_is_enabled(global, "enabled"); difaces = lydx_get_descendant(diff, "interfaces", "interface", NULL);
cifs = lydx_get_descendant(config, "dhcp-client", "client-if", NULL); /* find the modified interfaces */
difs = lydx_get_descendant(diff, "dhcp-client", "client-if", NULL); LYX_LIST_FOR_EACH(difaces, diface, "interface") {
const char *ifname = lydx_get_cattr(diface, "name");
struct lyd_node *dipv4;
char *out;
/* find the modified one, delete or recreate only that */ lyd_print_mem(&out, diface, LYD_JSON,
LYX_LIST_FOR_EACH(difs, dif, "client-if") { LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL);
const char *ifname = lydx_get_cattr(dif, "if-name");
if (lydx_get_op(dif) == LYDX_OP_DELETE) { dipv4 = lydx_get_descendant(lyd_child(diface), "ipv4", NULL);
if (!dipv4)
continue;
ddhcp = lydx_get_descendant(lyd_child(dipv4), "dhcp", NULL);
if (!ddhcp)
continue;
/* Check if dhcp container was deleted */
if (lydx_get_op(ddhcp) == LYDX_OP_DELETE) {
del(ifname); del(ifname);
continue; continue;
} }
LYX_LIST_FOR_EACH(cifs, cif, "client-if") { /* Find corresponding interface in config to check if dhcp is present */
if (strcmp(ifname, lydx_get_cattr(cif, "if-name"))) LYX_LIST_FOR_EACH(ifaces, iface, "interface") {
if (strcmp(ifname, lydx_get_cattr(iface, "name")))
continue; continue;
if (!ena || !lydx_is_enabled(cif, "enabled")) ipv4 = lydx_get_descendant(lyd_child(iface), "ipv4", NULL);
if (!ipv4) {
del(ifname);
break;
}
dhcp = lydx_get_descendant(lyd_child(ipv4), "dhcp", NULL);
if (!dhcp)
del(ifname); del(ifname);
else else
add(ifname, cif); add(ifname, dhcp);
break; break;
} }
} }
if (!ena) {
LYX_LIST_FOR_EACH(cifs, cif, "client-if") {
const char *ifname = lydx_get_cattr(cif, "if-name");
INFO("DHCP client globally disabled, stopping client on %s ...", ifname);
del(ifname);
}
}
return err; return err;
} }
@@ -368,7 +372,7 @@ static int cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
return SR_ERR_OK; return SR_ERR_OK;
} }
err = sr_dup_changes_iter(session, XPATH "/client-if//*", &iter); err = sr_dup_changes_iter(session, XPATH "//*", &iter);
if (err) if (err)
return err; return err;
+3 -3
View File
@@ -300,9 +300,6 @@ int infix_dhcp_server_change(sr_session_ctx_t *session, struct lyd_node *config,
int enabled = 0, added = 0, deleted = 0; int enabled = 0, added = 0, deleted = 0;
sr_error_t err = 0; sr_error_t err = 0;
if (!lydx_get_xpathf(diff, CFG_XPATH))
return SR_ERR_OK;
switch (event) { switch (event) {
case SR_EV_DONE: case SR_EV_DONE:
break; break;
@@ -312,6 +309,9 @@ int infix_dhcp_server_change(sr_session_ctx_t *session, struct lyd_node *config,
return SR_ERR_OK; return SR_ERR_OK;
} }
if (!lydx_get_xpathf(diff, CFG_XPATH))
return SR_ERR_OK;
global = lydx_get_descendant(config, "dhcp-server", NULL); global = lydx_get_descendant(config, "dhcp-server", NULL);
enabled = lydx_is_enabled(global, "enabled"); enabled = lydx_is_enabled(global, "enabled");
+2 -2
View File
@@ -20,13 +20,13 @@ MODULES=(
"ietf-hardware@2018-03-13.yang -e hardware-state -e hardware-sensor" "ietf-hardware@2018-03-13.yang -e hardware-state -e hardware-sensor"
"infix-hardware@2025-10-30.yang" "infix-hardware@2025-10-30.yang"
"ieee802-dot1q-types@2022-10-29.yang" "ieee802-dot1q-types@2022-10-29.yang"
"infix-ip@2024-09-16.yang" "infix-ip@2025-11-02.yang"
"infix-if-type@2025-02-12.yang" "infix-if-type@2025-02-12.yang"
"infix-routing@2024-11-27.yang" "infix-routing@2024-11-27.yang"
"ieee802-dot1ab-lldp@2022-03-15.yang" "ieee802-dot1ab-lldp@2022-03-15.yang"
"infix-lldp@2025-05-05.yang" "infix-lldp@2025-05-05.yang"
"infix-dhcp-common@2025-01-29.yang" "infix-dhcp-common@2025-01-29.yang"
"infix-dhcp-client@2025-01-29.yang" "infix-dhcp-client@2025-11-02.yang"
"infix-dhcp-server@2025-10-28.yang" "infix-dhcp-server@2025-10-28.yang"
"infix-firewall@2025-04-26.yang" "infix-firewall@2025-04-26.yang"
"infix-firewall-services@2025-04-26.yang" "infix-firewall-services@2025-04-26.yang"
+15 -24
View File
@@ -6,6 +6,9 @@ module infix-dhcp-client {
import ietf-interfaces { import ietf-interfaces {
prefix if; prefix if;
} }
import ietf-ip {
prefix ip;
}
import infix-dhcp-common { import infix-dhcp-common {
prefix dhcp; prefix dhcp;
} }
@@ -13,6 +16,14 @@ module infix-dhcp-client {
contact "kernelkit@googlegroups.com"; contact "kernelkit@googlegroups.com";
description "This module implements a DHCPv4 client"; description "This module implements a DHCPv4 client";
revision 2025-11-02 {
description "Migrate DHCP client to ietf-ip augment.
- Relocate from /dhcp-client to /interfaces/interface/ipv4/dhcp
- Presence container instead of enabled leaf, matching autoconf
- Drop client-if list wrapper, not needed anymore
- Drop global enabled flag";
reference "internal, issue #1109";
}
revision 2025-01-29 { revision 2025-01-29 {
description "Consolidate DHCP options between client and server models. description "Consolidate DHCP options between client and server models.
- Rename option attribute 'name' -> 'id' - Rename option attribute 'name' -> 'id'
@@ -51,30 +62,10 @@ module infix-dhcp-client {
* Data Nodes * Data Nodes
*/ */
container dhcp-client { augment "/if:interfaces/if:interface/ip:ipv4" {
description "DHCPv4 client configuration"; container dhcp {
presence "Enable DHCP client for this interface.";
leaf enabled { description "DHCPv4 client configuration";
type boolean;
default "true";
description "Globally enables the DHCP client function.";
}
list client-if {
description "List of interfaces requesting DHCPv4 configuration.";
key "if-name";
leaf if-name {
type if:interface-ref;
mandatory true;
description "Name of the interface.";
}
leaf enabled {
type boolean;
default "true";
description "Enable DHCP client for this interface.";
}
leaf client-id { leaf client-id {
type string; type string;
+5 -5
View File
@@ -18,6 +18,10 @@ module infix-ip {
description "This module augments ietf-ip with Infix extensions and deviations."; description "This module augments ietf-ip with Infix extensions and deviations.";
revision 2025-11-02 {
description "Change autoconf to presence container, removing enabled leaf.";
reference "Internal, issue #1109.";
}
revision 2024-09-16 { revision 2024-09-16 {
description "Add support for IPv4LL request-address."; description "Add support for IPv4LL request-address.";
reference "Internal."; reference "Internal.";
@@ -36,14 +40,10 @@ module infix-ip {
*/ */
augment "/if:interfaces/if:interface/ip:ipv4" { augment "/if:interfaces/if:interface/ip:ipv4" {
container autoconf { container autoconf {
presence "Enable IPv4 link-local address autoconfiguration for this interface.";
description "Parameters to control the autoconfiguration of IPv4 address."; description "Parameters to control the autoconfiguration of IPv4 address.";
reference "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses"; reference "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses";
leaf enabled {
description "Use a ZeroConf/IPv4LL agent to retrieve an 169.254/16 address.";
type boolean;
}
leaf request-address { leaf request-address {
description "Try to acquire the specified IP address, if available. description "Try to acquire the specified IP address, if available.
@@ -57,9 +57,7 @@ with infamy.Test() as test:
"name": tport, "name": tport,
"enabled": True, "enabled": True,
"ipv4": { "ipv4": {
"autoconf": { "infix-ip:autoconf": {}
"enabled": True
}
} }
} }
] ]
@@ -79,8 +77,7 @@ with infamy.Test() as test:
"name": tport, "name": tport,
"enabled": True, "enabled": True,
"ipv4": { "ipv4": {
"autoconf": { "infix-ip:autoconf": {
"enabled": True,
"request-address": "169.254.42.42" "request-address": "169.254.42.42"
} }
} }
+15 -23
View File
@@ -17,7 +17,7 @@ import infamy.dhcp
from infamy.util import until, parallel from infamy.util import until, parallel
from infamy.netns import IsolatedMacVlans from infamy.netns import IsolatedMacVlans
def configure_interface(name, ip=None, prefix_length=None, forwarding=True): def configure_interface(name, ip=None, prefix_length=None, forwarding=True, dhcp=False):
interface_config = { interface_config = {
"name": name, "name": name,
"enabled": True, "enabled": True,
@@ -28,6 +28,19 @@ def configure_interface(name, ip=None, prefix_length=None, forwarding=True):
} }
if ip and prefix_length: if ip and prefix_length:
interface_config["ipv4"]["address"].append({"ip": ip, "prefix-length": prefix_length}) interface_config["ipv4"]["address"].append({"ip": ip, "prefix-length": prefix_length})
if dhcp:
interface_config["ipv4"]["infix-dhcp-client:dhcp"] = {
"option": [
{"id": "broadcast"},
{"id": "dns-server"},
{"id": "domain"},
{"id": "hostname"},
{"id": "ntp-server"},
{"id": "router"},
{"id": "netmask"}
],
"route-preference": 5
}
return interface_config return interface_config
def config_target1(target, data1, data2, link): def config_target1(target, data1, data2, link):
@@ -35,7 +48,7 @@ def config_target1(target, data1, data2, link):
"ietf-interfaces": { "ietf-interfaces": {
"interfaces": { "interfaces": {
"interface": [ "interface": [
configure_interface(data1), configure_interface(data1, dhcp=True),
configure_interface(data2, "192.168.30.1", 24), configure_interface(data2, "192.168.30.1", 24),
configure_interface(link, "192.168.50.1", 24) configure_interface(link, "192.168.50.1", 24)
] ]
@@ -61,27 +74,6 @@ def config_target1(target, data1, data2, link):
] ]
} }
} }
},
"infix-dhcp-client": {
"dhcp-client": {
"enabled": True,
"client-if": [
{
"if-name": data1,
"enabled": True,
"option": [
{"id": "broadcast"},
{"id": "dns-server"},
{"id": "domain"},
{"id": "hostname"},
{"id": "ntp-server"},
{"id": "router"},
{"id": "netmask"}
],
"route-preference": 5
}
]
}
} }
}) })
+7 -4
View File
@@ -24,13 +24,16 @@ with infamy.Test() as test:
with infamy.dhcp.Server(netns, ip=ADDRESS): with infamy.dhcp.Server(netns, ip=ADDRESS):
_, port = env.ltop.xlate("client", "data") _, port = env.ltop.xlate("client", "data")
config = { config = {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": f"{port}" "name": f"{port}",
"ipv4": {
"infix-dhcp-client:dhcp": {}
}
}] }]
} }
} }
client.put_config_dict("infix-dhcp-client", config) client.put_config_dict("ietf-interfaces", config)
with test.step("Verify client lease for 10.0.0.42"): with test.step("Verify client lease for 10.0.0.42"):
until(lambda: iface.address_exist(client, port, ADDRESS)) until(lambda: iface.address_exist(client, port, ADDRESS))
+11 -7
View File
@@ -26,16 +26,20 @@ with infamy.Test() as test:
with infamy.dhcp.Server(netns, router=ROUTER): with infamy.dhcp.Server(netns, router=ROUTER):
_, port = env.ltop.xlate("client", "data") _, port = env.ltop.xlate("client", "data")
config = { config = {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": f"{port}", "name": f"{port}",
"option": [ "ipv4": {
{"id": "router"} "infix-dhcp-client:dhcp": {
] "option": [
{"id": "router"}
]
}
}
}] }]
} }
} }
client.put_config_dict("infix-dhcp-client", config) client.put_config_dict("ietf-interfaces", config)
with test.step("Verify DHCP client has default route via 192.168.0.254"): with test.step("Verify DHCP client has default route via 192.168.0.254"):
until(lambda: route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER)) until(lambda: route.ipv4_route_exist(client, "0.0.0.0/0", ROUTER))
+12 -8
View File
@@ -59,14 +59,18 @@ with infamy.Test() as test:
with test.step("Enabling DHCP client, allow option 3 and 121"): with test.step("Enabling DHCP client, allow option 3 and 121"):
_, port = env.ltop.xlate("client", "data") _, port = env.ltop.xlate("client", "data")
client.put_config_dict("infix-dhcp-client", { client.put_config_dict("ietf-interfaces", {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": f"{port}", "name": f"{port}",
"option": [ "ipv4": {
{"id": "router"}, "infix-dhcp-client:dhcp": {
{"id": "classless-static-route"} "option": [
] {"id": "router"},
{"id": "classless-static-route"}
]
}
}
}] }]
} }
}) })
+12 -8
View File
@@ -68,14 +68,18 @@ with infamy.Test() as test:
}}) }})
client.put_config_dicts({ client.put_config_dicts({
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client["link"], "name": client["link"],
"option": [ "ipv4": {
{"id": "hostname"}, "infix-dhcp-client:dhcp": {
{"id": "domain"} "option": [
] {"id": "hostname"},
{"id": "domain"}
]
}
}
}] }]
} }
}, },
+27 -19
View File
@@ -116,15 +116,19 @@ with infamy.Test() as test:
"ietf-system": { "ietf-system": {
"system": {"hostname": HOSTNM1} "system": {"hostname": HOSTNM1}
}, },
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client1["link"], "name": client1["link"],
"option": [ "ipv4": {
{"id": "router"}, "infix-dhcp-client:dhcp": {
{"id": "client-id", "hex": HOSTCID1}, "option": [
{"id": 121} {"id": "router"},
] {"id": "client-id", "hex": HOSTCID1},
{"id": 121}
]
}
}
}] }]
} }
}, },
@@ -134,16 +138,20 @@ with infamy.Test() as test:
"ietf-system": { "ietf-system": {
"system": {"hostname": HOSTNM2} "system": {"hostname": HOSTNM2}
}, },
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client2["link"], "name": client2["link"],
"client-id": HOSTCID2, "ipv4": {
"option": [ "infix-dhcp-client:dhcp": {
{"id": "router"}, "client-id": HOSTCID2,
{"id": "hostname"}, "option": [
{"id": 121} {"id": "router"},
] {"id": "hostname"},
{"id": 121}
]
}
}
}] }]
} }
}, },
+45 -33
View File
@@ -200,17 +200,21 @@ with infamy.Test() as test:
# the server is behaving correctly. # the server is behaving correctly.
client1.put_config_dicts({ client1.put_config_dicts({
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client1["server"], "name": client1["server"],
"option": [ "ipv4": {
{"id": "hostname", "value": "auto"}, "infix-dhcp-client:dhcp": {
{"id": "router"}, "option": [
{"id": "dns-server"}, {"id": "hostname", "value": "auto"},
{"id": "ntp-server"}, {"id": "router"},
{"id": 121} {"id": "dns-server"},
] {"id": "ntp-server"},
{"id": 121}
]
}
}
}] }]
} }
}, },
@@ -222,17 +226,21 @@ with infamy.Test() as test:
}}) }})
client2.put_config_dicts({ client2.put_config_dicts({
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client2["server"], "name": client2["server"],
"option": [ "ipv4": {
{"id": "hostname", "value": "auto"}, "infix-dhcp-client:dhcp": {
{"id": "router"}, "option": [
{"id": "dns-server"}, {"id": "hostname", "value": "auto"},
{"id": "ntp-server"}, {"id": "router"},
{"id": 121} {"id": "dns-server"},
] {"id": "ntp-server"},
{"id": 121}
]
}
}
}] }]
} }
}, },
@@ -244,17 +252,21 @@ with infamy.Test() as test:
}}) }})
client3.put_config_dicts({ client3.put_config_dicts({
"infix-dhcp-client": { "ietf-interfaces": {
"dhcp-client": { "interfaces": {
"client-if": [{ "interface": [{
"if-name": client3["server"], "name": client3["server"],
"option": [ "ipv4": {
{"id": "hostname", "value": "auto"}, "infix-dhcp-client:dhcp": {
{"id": "router"}, "option": [
{"id": "dns-server"}, {"id": "hostname", "value": "auto"},
{"id": "ntp-server"}, {"id": "router"},
{"id": 121} {"id": "dns-server"},
] {"id": "ntp-server"},
{"id": 121}
]
}
}
}] }]
} }
}, },
@@ -212,7 +212,6 @@ table ip nat {
"enabled": True, "enabled": True,
"forwarding": True, "forwarding": True,
"infix-ip:autoconf": { "infix-ip:autoconf": {
"enabled": True,
"request-address": "169.254.1.1" "request-address": "169.254.1.1"
} }
}, },