mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-01 05:13:01 +02:00
Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f47bf0e0b1 | ||
|
|
e7108f52cf | ||
|
|
26c7fe40c0 | ||
|
|
1e6286f1d3 | ||
|
|
fe56591d8d | ||
|
|
18c812c1b4 | ||
|
|
bb199590ce | ||
|
|
047d818cbd | ||
|
|
2b6588eaf4 | ||
|
|
08742df166 | ||
|
|
2a176d5888 | ||
|
|
93dfd00754 | ||
|
|
94808331da | ||
|
|
74a09d2c1b | ||
|
|
0c1aede731 | ||
|
|
5ceda4569e | ||
|
|
683f696e19 | ||
|
|
ad7c6573c8 | ||
|
|
72860aa1b5 | ||
|
|
69acd21f19 | ||
|
|
eeabb44bd5 | ||
|
|
76bfd6c57f | ||
|
|
3d7fb595fb | ||
|
|
98a39d989c | ||
|
|
8e699736b8 | ||
|
|
80ab53ecbc | ||
|
|
7ce668904a | ||
|
|
93fceb9a5f | ||
|
|
4f876bc58e | ||
|
|
db0c6417b2 | ||
|
|
aeef240289 | ||
|
|
129f92afdd | ||
|
|
717c1c02f1 |
@@ -34,6 +34,10 @@ Infix comes preconfigured with:
|
||||
|
||||
The easiest way to get started is using an SD card:
|
||||
|
||||
> [!NOTE]
|
||||
> SD card boot works but we have observed stability issues. For production
|
||||
> use or long-term reliability, we recommend installing to eMMC (see below).
|
||||
|
||||
1. **Download the SD card image:** [infix-bpi-r3-sdcard.img][2]
|
||||
2. **Flash the image to an SD card:** see [this guide][0]
|
||||
3. **Set boot switches:**
|
||||
@@ -53,8 +57,8 @@ The BPI-R3 has a 4-position DIP switch that controls boot media:
|
||||
|
||||
| Position | Mode | Description |
|
||||
|----------|-------------|---------------------------------------|
|
||||
| 0000 | SD card | Boot from microSD card (recommended) |
|
||||
| 0110 | eMMC | Boot from internal eMMC storage |
|
||||
| 0000 | SD card | Boot from microSD card |
|
||||
| 0110 | eMMC | Boot from internal eMMC (recommended) |
|
||||
| 1010 | SPI NAND | Boot from SPI NAND (advanced users) |
|
||||
|
||||
> [!NOTE]
|
||||
@@ -67,10 +71,16 @@ For production deployments or better performance, you can install Infix
|
||||
to the internal eMMC storage. This is more complex but provides faster
|
||||
boot times and eliminates the external SD card.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> While Infix boots on both SD card and eMMC, we have observed stability
|
||||
> issues with SD cards on this platform. **eMMC is recommended** for
|
||||
> reliable operation.
|
||||
|
||||
### Why Use eMMC?
|
||||
|
||||
**Advantages:**
|
||||
|
||||
- More reliable than SD card (stability issues observed with SD cards)
|
||||
- Faster boot and better performance
|
||||
- No external SD card to manage
|
||||
- More robust for industrial/embedded deployments
|
||||
|
||||
+2
-2
@@ -218,8 +218,8 @@
|
||||
"symmetric-key": [
|
||||
{
|
||||
"name": "wifi",
|
||||
"infix-keystore:symmetric-key": "infixinfix",
|
||||
"infix-keystore:key-format": "infix-crypto-types:wifi-preshared-key-format"
|
||||
"cleartext-symmetric-key": "aW5maXhpbmZpeA==",
|
||||
"key-format": "infix-crypto-types:passphrase-key-format"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -107,8 +107,10 @@ To configure WiFi as a client, first store your WiFi password in the keystore:
|
||||
```
|
||||
admin@infix:/> configure
|
||||
admin@infix:/config/> edit keystore symmetric-key mywifi
|
||||
admin@infix:/config/keystore/…/mywifi/> set key-format wifi-preshared-key-format
|
||||
admin@infix:/config/keystore/…/mywifi/> set symmetric-key YourWiFiPassword
|
||||
admin@infix:/config/keystore/…/mywifi/> set key-format passphrase-key-format
|
||||
admin@infix:/config/keystore/…/mywifi/> change cleartext-symmetric-key
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@infix:/config/keystore/…/mywifi/> leave
|
||||
```
|
||||
|
||||
|
||||
@@ -358,13 +358,12 @@ extract_squashfs()
|
||||
run_qemu()
|
||||
{
|
||||
# Auto-extract rootfs.squashfs from rootfs.itb if needed for initrd mode
|
||||
if [ "$CONFIG_QEMU_ROOTFS_INITRD" = "y" ]; then
|
||||
if [ "$CONFIG_QEMU_ROOTFS" = "rootfs.squashfs" ] && [ ! -f "rootfs.squashfs" ]; then
|
||||
if [ -f "rootfs.itb" ]; then
|
||||
extract_squashfs "rootfs.itb" "rootfs.squashfs"
|
||||
else
|
||||
die "Missing rootfs.squashfs and cannot find rootfs.itb to extract it from"
|
||||
fi
|
||||
if [ "$CONFIG_QEMU_ROOTFS_INITRD" = "y" ] && [ ! -f "$CONFIG_QEMU_ROOTFS" ]; then
|
||||
itb="${CONFIG_QEMU_ROOTFS%.squashfs}.itb"
|
||||
if [ -f "$itb" ]; then
|
||||
extract_squashfs "$itb" "$CONFIG_QEMU_ROOTFS"
|
||||
else
|
||||
die "Missing $CONFIG_QEMU_ROOTFS and cannot find $itb to extract it from"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,23 +1,49 @@
|
||||
#!/bin/sh
|
||||
# Prompt for a secret with confirmation, then encode and output it.
|
||||
#
|
||||
# Default mode: hash with mkpasswd (for system passwords)
|
||||
# askpass [OUTPUT]
|
||||
#
|
||||
# Base64 mode (-b): base64-encode (for keystore passphrases)
|
||||
# askpass -b [OUTPUT]
|
||||
#
|
||||
# If OUTPUT is given, result is written to the file.
|
||||
# If omitted, result is written to stdout.
|
||||
# shellcheck disable=SC3045
|
||||
|
||||
LABEL="New password"
|
||||
MODE=hash
|
||||
if [ "$1" = "-b" ]; then
|
||||
LABEL="Passphrase"
|
||||
MODE=base64
|
||||
shift
|
||||
fi
|
||||
OUTPUT=$1
|
||||
|
||||
read -r -s -p "New password: " password
|
||||
read -r -s -p "$LABEL: " secret
|
||||
>&2 echo
|
||||
read -r -s -p "Retype password: " password_again
|
||||
read -r -s -p "Retype $LABEL: " secret_again
|
||||
>&2 echo
|
||||
|
||||
if [ "$password" != "$password_again" ]; then
|
||||
echo "Passwords do not match, try again."
|
||||
if [ "$secret" != "$secret_again" ]; then
|
||||
echo "${LABEL}s do not match, try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$OUTPUT" ]; then
|
||||
echo "$password"
|
||||
exit 0
|
||||
if [ -z "$secret" ]; then
|
||||
echo "Empty $LABEL, try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "base64" ]; then
|
||||
encoded=$(printf '%s' "$secret" | base64 -w 0)
|
||||
else
|
||||
encoded=$(printf '%s\n' "$secret" | mkpasswd -s)
|
||||
fi
|
||||
|
||||
umask 0177
|
||||
echo "$password" | mkpasswd -s > "$OUTPUT"
|
||||
exit 0
|
||||
if [ -z "$OUTPUT" ]; then
|
||||
echo "$encoded"
|
||||
else
|
||||
printf '%s' "$encoded" > "$OUTPUT"
|
||||
fi
|
||||
|
||||
@@ -63,6 +63,21 @@ calc_sha()
|
||||
sha256sum "$1" 2>/dev/null | awk '{print $1}'
|
||||
}
|
||||
|
||||
# Calculate a combined SHA256 over the container script and its
|
||||
# optional env file. Environment variables are stored separately
|
||||
# from the script, so both must be included in the checksum to
|
||||
# detect configuration changes such as added/changed env vars.
|
||||
calc_config_sha()
|
||||
{
|
||||
_envfile="/run/containers/args/${name}.env"
|
||||
|
||||
if [ -f "$_envfile" ]; then
|
||||
cat "$1" "$_envfile" | sha256sum | awk '{print $1}'
|
||||
else
|
||||
calc_sha "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check image transport, return 0 if remote, 1 if local
|
||||
is_remote()
|
||||
{
|
||||
@@ -124,7 +139,7 @@ is_uptodate()
|
||||
# If SHA matches, check container instance
|
||||
if [ "$stored_sha" = "$current_sha" ]; then
|
||||
if podman container exists "$name"; then
|
||||
config_sha=$(calc_sha "$script")
|
||||
config_sha=$(calc_config_sha "$script")
|
||||
container_sha=$(podman inspect "$name" --format '{{index .Config.Labels "config-sha256"}}' 2>/dev/null)
|
||||
container_img_sha=$(podman inspect "$name" --format '{{index .Config.Labels "meta-image-sha256"}}' 2>/dev/null)
|
||||
|
||||
@@ -143,7 +158,7 @@ is_uptodate()
|
||||
else
|
||||
# Remote image optimization: check config-sha256 only
|
||||
if podman container exists "$name"; then
|
||||
config_sha=$(calc_sha "$script")
|
||||
config_sha=$(calc_config_sha "$script")
|
||||
container_sha=$(podman inspect "$name" --format '{{index .Config.Labels "config-sha256"}}' 2>/dev/null)
|
||||
|
||||
if [ "$container_sha" = "$config_sha" ]; then
|
||||
@@ -459,7 +474,7 @@ create()
|
||||
fi
|
||||
|
||||
# Add config checksum label
|
||||
args="$args --label config-sha256=$(calc_sha "$script")"
|
||||
args="$args --label config-sha256=$(calc_config_sha "$script")"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2048
|
||||
|
||||
@@ -27,7 +27,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
@@ -27,7 +27,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
@@ -28,7 +28,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/arm/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
@@ -28,7 +28,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/arm/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
@@ -37,5 +37,6 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
@@ -37,5 +37,6 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
@@ -37,5 +37,6 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
@@ -43,6 +43,7 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y
|
||||
BR2_PACKAGE_BOOTLOADER_SPLASHSCREEN=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
@@ -38,6 +38,7 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FDT_ADD_PUBKEY=y
|
||||
BR2_PACKAGE_BOOTLOADER_SPLASHSCREEN=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
|
||||
@@ -26,7 +26,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
@@ -26,7 +26,7 @@ BR2_ROOTFS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/common/rootfs ${BR2_EXTERNA
|
||||
BR2_ROOTFS_POST_BUILD_SCRIPT="board/qemu/x86_64/post-build.sh ${BR2_EXTERNAL_INFIX_PATH}/board/common/post-build.sh"
|
||||
BR2_LINUX_KERNEL=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.7"
|
||||
BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.8"
|
||||
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
|
||||
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/linux_defconfig"
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
|
||||
+11
-2
@@ -3,7 +3,7 @@ Change Log
|
||||
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
[v26.01.0][UNRELEASED]
|
||||
[v26.01.0][] - 2026-02-03
|
||||
-------------------------
|
||||
|
||||
> [!IMPORTANT]
|
||||
@@ -23,7 +23,7 @@ All notable changes to the project are documented in this file.
|
||||
|
||||
Noteworthy changes and additions in this release are marked below in bold text.
|
||||
|
||||
- Upgrade **Linux kernel to 6.18.7** (LTS)
|
||||
- **Upgrade Linux kernel from 6.12.65 to 6.18.8 (LTS)**
|
||||
- Upgrade Buildroot to 2025.02.10 (LTS)
|
||||
- Upgrade libyang to 4.2.2
|
||||
- Upgrade sysrepo to 4.2.10
|
||||
@@ -53,6 +53,9 @@ Noteworthy changes and additions in this release are marked below in bold text.
|
||||
removed during upgrade (for the rest of the configuration to apply) and you
|
||||
need to reconfigure them again. See the [WiFi][] documentation for details
|
||||
- Add support for **WireGuard VPN tunnels**.
|
||||
- Updated CLI change command to support `cleartext-symmetric-key` (type binary).
|
||||
Used by both WireGuard and WiFi, with application-specific `key-format` for
|
||||
keys and passphrases
|
||||
- New default NACM privilege levels (user levels) in `factory-config`:
|
||||
`operator` (network & container manager) and `guest` (read-only). For
|
||||
details, see the updated system configuration documentation, as well as a
|
||||
@@ -66,12 +69,18 @@ Noteworthy changes and additions in this release are marked below in bold text.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix #515: add per-interface IPv6 forwarding control using the Linux 6.17+
|
||||
`force_forwarding` sysctl. This provides true per-interface IPv6 forwarding
|
||||
similar to IPv4, correctly mapping to the ietf-ip.yang model semantics
|
||||
- Fix #1082: Wi-Fi interfaces always scanned, introduce a `scan-mode` to the
|
||||
Wi-Fi concept in Infix
|
||||
- Fix #1313: Container is not restarted if environment variable is changed
|
||||
- Fix #1314: Raspberry Pi 4B with 1 or 8 GiB RAM does not boot. This was due
|
||||
newer EEPROM firmware in newer boards require a newer rpi-firmware package
|
||||
- Fix #1345: firewall not updating when interfaces become bridge/lag ports
|
||||
- Fix #1346: firewall complains in syslog, missing `/etc/firewalld/firewalld.conf`
|
||||
- Fix Raspberry Pi 2B build, among other things, the `aarch32_defconfig` did
|
||||
not include a dtb. Please note, the platform has now been renamed to `arm`
|
||||
- Fix default password hash in `do password encrypt` command. New hash is the
|
||||
same as the more commonly used `change password` command, *yescrypt*
|
||||
- Prevent MOTD from showing on non-shell user login attempts
|
||||
|
||||
@@ -31,6 +31,7 @@ CLI has several keybindings, most significant first:
|
||||
| Ctrl-q | Ctrl-v | Insert next character literally |
|
||||
| Ctrl-r | | History, reversed interactive search (i-search) |
|
||||
| Ctrl-t | | Transpose/Swap characters before and at cursor |
|
||||
| Meta-# | Alt-Shift-3 | Prepend # to current line and submit to history |
|
||||
|
||||
## What is Meta?
|
||||
|
||||
|
||||
@@ -415,13 +415,12 @@ admin@example:/>
|
||||
|
||||
## IPv6 forwarding
|
||||
|
||||
Due to how the Linux kernel manages IPv6 forwarding, we can not fully
|
||||
control it per interface via this setting like how IPv4 works. Instead,
|
||||
IPv6 forwarding is globally enabled when at least one interface enable
|
||||
forwarding, otherwise it is disabled.
|
||||
Forwarding must be enabled on an interface for it to route IPv6
|
||||
traffic (static or dynamic). The setting is per-interface and works
|
||||
the same way as IPv4 forwarding.
|
||||
|
||||
The following table shows the system IPv6 features that the `forwarding`
|
||||
setting control when it is *Enabled* or *Disabled:
|
||||
The following table shows the IPv6 features that the `forwarding`
|
||||
setting controls when it is *Enabled* or *Disabled*:
|
||||
|
||||
| **IPv6 Feature** | **Enabled** | **Disabled** |
|
||||
|:-----------------------------------------|:------------|:-------------|
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
# Keystore
|
||||
|
||||
The Infix keystore is a centralized storage system for cryptographic keys
|
||||
used throughout the system. It is based on the IETF standards [RFC 9641][1]
|
||||
(Keystore) and [RFC 9640][2] (Cryptographic Types), with Infix extensions
|
||||
for WiFi and WireGuard key formats.
|
||||
|
||||
## Overview
|
||||
|
||||
The keystore supports two types of cryptographic keys:
|
||||
|
||||
1. **Asymmetric Keys** — public/private key pairs used for:
|
||||
- SSH host authentication (RSA keys)
|
||||
- WireGuard VPN tunnels (X25519 keys)
|
||||
|
||||
2. **Symmetric Keys** — shared secrets used for:
|
||||
- WiFi authentication (WPA2/WPA3 pre-shared keys)
|
||||
- WireGuard VPN pre-shared keys
|
||||
|
||||
All keys are stored under the `ietf-keystore` configuration path and can be
|
||||
managed via CLI, NETCONF, or RESTCONF.
|
||||
|
||||
### Supported Formats
|
||||
|
||||
| **Asymmetric Key Format** | **Use Case** | **Key Type** |
|
||||
|----------------------------------------------------------|---------------|--------------|
|
||||
| `rsa-private-key-format` / `ssh-public-key-format` | SSH host keys | RSA |
|
||||
| `x25519-private-key-format` / `x25519-public-key-format` | WireGuard VPN | Curve25519 |
|
||||
|
||||
| **Symmetric Key Format** | **Use Case** |
|
||||
|-----------------------------|-----------------------------------|
|
||||
| `passphrase-key-format` | Human-readable passphrases (WiFi) |
|
||||
| `octet-string-key-format` | Raw symmetric keys (WireGuard) |
|
||||
|
||||
## Asymmetric Keys
|
||||
|
||||
Asymmetric keys consist of a public/private key pair. The public key can be
|
||||
shared freely, while the private key must be kept secure.
|
||||
|
||||
### SSH Host Keys
|
||||
|
||||
SSH host keys identify the system during SSH and NETCONF connections. The
|
||||
default host key is automatically generated on first boot and stored in the
|
||||
keystore with the name `genkey`.
|
||||
|
||||
See [SSH Management](management.md) for details on generating and importing
|
||||
custom SSH host keys.
|
||||
|
||||
### WireGuard Keys
|
||||
|
||||
WireGuard uses X25519 elliptic curve cryptography for key exchange. Each
|
||||
WireGuard interface requires a public/private key pair stored as an asymmetric
|
||||
key in the keystore. Key pairs can be generated directly from the CLI:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>wireguard genkey</b>
|
||||
Private: aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
Public: bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
</code></pre>
|
||||
|
||||
See [WireGuard VPN](vpn-wireguard.md) for key generation and configuration
|
||||
examples.
|
||||
|
||||
## Symmetric Keys
|
||||
|
||||
Symmetric keys are shared secrets where the same key must be configured on
|
||||
all systems that need to communicate.
|
||||
|
||||
### WiFi Pre-Shared Keys
|
||||
|
||||
WiFi networks secured with WPA2 or WPA3 use pre-shared keys stored as
|
||||
symmetric keys in the keystore with `passphrase-key-format`. The
|
||||
passphrase must be 8-63 printable ASCII characters.
|
||||
|
||||
Since symmetric keys are stored as binary (base64-encoded), the CLI
|
||||
provides the `change` command to enter passphrases interactively:
|
||||
|
||||
<pre class="cli"><code>admin@example:/config/keystore/…/my-wifi-key/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
</code></pre>
|
||||
|
||||
See [WiFi](wifi.md) for complete configuration examples.
|
||||
|
||||
### WireGuard Pre-Shared Keys
|
||||
|
||||
WireGuard supports optional pre-shared keys (PSK) that add a layer of
|
||||
symmetric encryption alongside Curve25519. PSKs use the standard IETF
|
||||
`octet-string-key-format` (32 random bytes). This provides defense-in-depth
|
||||
against future quantum computers that might break elliptic curve cryptography.
|
||||
Note, however, that WireGuard’s authentication and initial key agreement
|
||||
remain Curve25519-based, so PSKs only protect the session encryption,
|
||||
not the handshake itself.
|
||||
|
||||
PSKs can be generated directly from the CLI:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>wireguard genpsk</b>
|
||||
cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
|
||||
</code></pre>
|
||||
|
||||
See [WireGuard VPN](vpn-wireguard.md) for PSK generation and usage examples.
|
||||
|
||||
## Viewing Keys
|
||||
|
||||
The `show keystore` command in admin-exec mode gives an overview of all
|
||||
keys in the keystore. Passphrases (WiFi passwords) are decoded and shown
|
||||
in cleartext, while binary keys (WireGuard PSKs) are shown as base64:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>show keystore</b>
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
<span class="title">Symmetric Keys</span>
|
||||
<span class="header">NAME FORMAT VALUE </span>
|
||||
my-wifi-key passphrase MySecretPassword
|
||||
wg-psk octet-string zYr83O4Ykj9i1gN+/aaosJxQx...
|
||||
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
<span class="title">Asymmetric Keys</span>
|
||||
<span class="header">NAME TYPE PUBLIC KEY </span>
|
||||
genkey rsa MIIBCgKCAQEAnj0YinjhYDgYbEGuh7...
|
||||
wg-tunnel x25519 bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1...
|
||||
</code></pre>
|
||||
|
||||
To see the full (untruncated) details of a specific key, use the
|
||||
`symmetric` or `asymmetric` qualifier with the key name:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>show keystore symmetric my-wifi-key</b>
|
||||
name : my-wifi-key
|
||||
format : passphrase
|
||||
value : MySecretPassword
|
||||
|
||||
admin@example:/> <b>show keystore asymmetric genkey</b>
|
||||
name : genkey
|
||||
algorithm : rsa
|
||||
public key format : ssh-public-key
|
||||
public key : MIIBCgKCAQEAnj0YinjhY...full key...IDAQAB
|
||||
</code></pre>
|
||||
|
||||
> [!NOTE]
|
||||
> The `show keystore` command is protected by NACM. Only users in the
|
||||
> `admin` group can view keystore data. Operator-level users will see a
|
||||
> message indicating that no keystore data is available.
|
||||
|
||||
The full configuration-mode view (including private keys) is still
|
||||
available via `configure` and then `show keystore`:
|
||||
|
||||
<pre class="cli"><code>admin@example:/config/> <b>show keystore</b>
|
||||
</code></pre>
|
||||
|
||||
> [!WARNING]
|
||||
> The configuration-mode `show keystore` displays private keys in
|
||||
> cleartext. Be careful when viewing keys on shared screens or in
|
||||
> logged sessions. The admin-exec `show keystore` command never
|
||||
> displays private keys.
|
||||
|
||||
## Deleting Keys
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>delete keystore asymmetric-key mykey</b>
|
||||
admin@example:/config/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
> [!CAUTION]
|
||||
> Deleting a key that is referenced by a service (SSH, WireGuard, WiFi) will
|
||||
> cause that service to fail. Verify the key is not in use before deletion.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
The keystore is protected by NACM (Network Access Control Model) rules.
|
||||
Only users in the `admin` group can view or modify cryptographic keys.
|
||||
See [NACM](nacm.md) for details on access control.
|
||||
|
||||
Private keys are stored in cleartext in the configuration database.
|
||||
Configuration files and backups containing the keystore should be treated
|
||||
as sensitive and protected accordingly.
|
||||
|
||||
### Key Validation
|
||||
|
||||
Symmetric key values are stored as binary (base64-encoded). The system
|
||||
validates them based on their declared format:
|
||||
|
||||
- `passphrase-key-format`: Used by WiFi, must decode to 8-63 ASCII characters
|
||||
- `octet-string-key-format`: Used by Wireguard, must decode to exactly 32 bytes (256 bits)
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 9641 - A YANG Data Model for a Keystore][1]
|
||||
- [RFC 9640 - YANG Data Types and Groupings for Cryptography][2]
|
||||
- [WiFi Documentation](wifi.md)
|
||||
- [WireGuard VPN Documentation](vpn-wireguard.md)
|
||||
- [SSH Management](management.md)
|
||||
- [NACM Access Control](nacm.md)
|
||||
|
||||
[1]: https://datatracker.ietf.org/doc/html/rfc9641
|
||||
[2]: https://datatracker.ietf.org/doc/html/rfc9640
|
||||
@@ -47,6 +47,11 @@ RSA for now, thus the private key must be
|
||||
`ietf-crypto-types:rsa-private-key-format` and the public key
|
||||
`ietf-crypto-types:ssh-public-key-format`
|
||||
|
||||
> [!TIP]
|
||||
> For comprehensive information about the keystore, including key management,
|
||||
> security considerations, and examples for different key types, see the
|
||||
> [Keystore documentation](keystore.md).
|
||||
|
||||
### Use your own SSH hostkeys
|
||||
|
||||
Hostkeys can be generated with OpenSSL:
|
||||
|
||||
+17
-28
@@ -26,31 +26,25 @@ Key features of WireGuard:
|
||||
WireGuard uses public-key cryptography similar to SSH. Each WireGuard interface
|
||||
requires a private key, and each peer is identified by its public key.
|
||||
|
||||
**Generate a WireGuard key pair using the `wg` command:**
|
||||
|
||||
```bash
|
||||
admin@example:~$ wg genkey | tee privatekey | wg pubkey > publickey
|
||||
admin@example:~$ cat privatekey
|
||||
aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
admin@example:~$ cat publickey
|
||||
bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
```
|
||||
|
||||
This generates a private key, saves it to `privatekey`, derives the public key,
|
||||
and saves it to `publickey`.
|
||||
|
||||
**Import the private key into the keystore:**
|
||||
**Import the key pair into the keystore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore asymmetric-key wg-site-a</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>do wireguard genkey</b>
|
||||
Private: aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=
|
||||
Public: bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set public-key-format x25519-public-key-format</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set private-key-format x25519-private-key-format</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set public-key bN1CwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP6KsrCwZ1lTP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set private-key aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>set cleartext-private-key aMqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP5JrqBvZqkSP=</b>
|
||||
admin@example:/config/keystore/asymmetric-key/wg-site-a/> <b>leave</b>
|
||||
admin@example:/>
|
||||
</code></pre>
|
||||
|
||||
> [!TIP]
|
||||
> The `do` prefix allows running admin-exec commands from configure context.
|
||||
> Use the base for `wireguard genkey` when in admin-exec context.
|
||||
|
||||
**Import peer public keys into the truststore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
@@ -63,8 +57,7 @@ admin@example:/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Keep private keys secure! Never share your private key. Only exchange
|
||||
> public keys with peers. Delete the `privatekey` file after importing it
|
||||
> into the keystore.
|
||||
> public keys with peers.
|
||||
|
||||
## Point-to-Point Configuration
|
||||
|
||||
@@ -394,20 +387,17 @@ recording traffic today would still need the PSK even if they break Curve25519
|
||||
later. However, peer authentication still relies on Curve25519, so PSKs don't
|
||||
provide complete post-quantum security.
|
||||
|
||||
**Generate a preshared key using `wg genpsk`:**
|
||||
**Generate a preshared key:**
|
||||
|
||||
```bash
|
||||
admin@example:~$ wg genpsk > preshared.key
|
||||
admin@example:~$ cat preshared.key
|
||||
<pre class="cli"><code>admin@example:/config/> <b>do wireguard genpsk</b>
|
||||
cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=
|
||||
```
|
||||
</code></pre>
|
||||
|
||||
**Import the preshared key into the keystore:**
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key wg-psk</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key-format wireguard-symmetric-key-format</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=</b>
|
||||
<pre class="cli"><code>admin@example:/config/> <b>edit keystore symmetric-key wg-psk</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set key-format octet-string-key-format</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>set cleartext-symmetric-key cO2DxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2mUQ7LtsrDxZ2m=</b>
|
||||
admin@example:/config/keystore/symmetric-key/wg-psk/> <b>end</b>
|
||||
admin@example:/config/interface/wg0/> <b>edit wireguard peers wg-peers peer remote</b>
|
||||
admin@example:/config/interface/…/wg-peers/peer/remote/> <b>set preshared-key wg-psk</b>
|
||||
@@ -420,5 +410,4 @@ on both sides.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Preshared keys must be kept secret and exchanged through a secure channel,
|
||||
> just like passwords. Delete the `preshared.key` file after importing it
|
||||
> into both peer keystores.
|
||||
> just like passwords.
|
||||
|
||||
+45
-14
@@ -241,6 +241,23 @@ In the CLI, signal strength is reported as: excellent, good, fair or bad.
|
||||
For precise signal strength values in dBm, use NETCONF or RESTCONF to access
|
||||
the `signal-strength` leaf in the operational datastore.
|
||||
|
||||
## Passphrase Requirements
|
||||
|
||||
To ensure your connection is secure and compatible with all network
|
||||
hardware, your passphrase must meet the following criteria:
|
||||
|
||||
- Length: Between 8 and 63 characters
|
||||
- Characters: Use only standard English keyboard characters
|
||||
- Allowed: Letters (A-Z, a-z), numbers (0-9), and common symbols (e.g., ! @ # $ % ^ & * ( ) _ + - = [ ] { } | ; : ' " , . < > / ? ~)
|
||||
- Spaces: Spaces are allowed, but not at the very beginning or very end of the passphrase
|
||||
- Prohibited: Emojis, accented characters (like á or ñ), and special "control" characters
|
||||
|
||||
> [!TIP] Why the limit?
|
||||
> Standard WiFi security (WPA2/WPA3) requires a minimum of 8 characters to
|
||||
> prevent "brute-force" hacking. The character limit ensures your password
|
||||
> works on older routers and various operating systems.
|
||||
> Tips for password strength, see [XKCD #936](https://xkcd.com/936/).
|
||||
|
||||
## Station Mode (Client)
|
||||
|
||||
Station mode connects to an existing Wi-Fi network. Before configuring station
|
||||
@@ -253,11 +270,16 @@ Create a keystore entry for your WiFi password (8-63 characters):
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key my-wifi-key</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set symmetric-key MyPassword123</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/my-wifi-key/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
The `change` command prompts for the passphrase interactively and
|
||||
handles the base64 encoding required by the keystore automatically.
|
||||
|
||||
### Step 2: Connect to Network
|
||||
|
||||
Configure station mode with the SSID and password to connect:
|
||||
@@ -276,8 +298,9 @@ name : wifi0
|
||||
type : wifi
|
||||
operational status : up
|
||||
physical address : f0:09:0d:36:5f:86
|
||||
SSID : MyHomeNetwork
|
||||
Signal : excellent
|
||||
mode : station
|
||||
ssid : MyHomeNetwork
|
||||
signal : -52 dBm (good)
|
||||
</code></pre>
|
||||
|
||||
**Station configuration parameters:**
|
||||
@@ -310,8 +333,10 @@ create a keystore entry for your WiFi password and configure the AP interface:
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key my-wifi-secret</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set symmetric-key MySecurePassword123</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/my-wifi-secret/> <b>end</b>
|
||||
</code></pre>
|
||||
|
||||
@@ -371,14 +396,20 @@ admin@example:/config/hardware/component/radio0/wifi-radio/> <b>leave</b>
|
||||
|
||||
<pre class="cli"><code>admin@example:/> <b>configure</b>
|
||||
admin@example:/config/> <b>edit keystore symmetric-key main-secret</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set symmetric-key MyMainPassword</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/main-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/> <b>edit keystore symmetric-key guest-secret</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set symmetric-key GuestPassword123</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/guest-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/> <b>edit keystore symmetric-key iot-secret</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set key-format wifi-preshared-key-format</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set symmetric-key IoTDevices2025</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>set key-format passphrase-key-format</b>
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>change cleartext-symmetric-key</b>
|
||||
Passphrase: ************
|
||||
Retype passphrase: ************
|
||||
admin@example:/config/keystore/…/iot-secret/> <b>leave</b>
|
||||
</code></pre>
|
||||
|
||||
@@ -466,8 +497,8 @@ If issues arise, try the following troubleshooting steps:
|
||||
|
||||
1. **Verify signal strength**: Check that the target network shows
|
||||
"good" or "excellent" signal in scan results
|
||||
2. **Check credentials**: Verify the preshared key in the keystore
|
||||
matches the network password
|
||||
2. **Check credentials**: Use `show keystore symmetric <name>` to verify
|
||||
the passphrase matches the network password
|
||||
3. **Review logs**: Check system logs with `show log` for Wi-Fi related
|
||||
errors
|
||||
4. **Regulatory compliance**: Ensure the country-code on the radio
|
||||
|
||||
@@ -53,6 +53,7 @@ nav:
|
||||
- Access Control (NACM): nacm.md
|
||||
- Hardware Info & Status: hardware.md
|
||||
- Management: management.md
|
||||
- Keystore: keystore.md
|
||||
- Syslog Support: syslog.md
|
||||
- Support Data: support.md
|
||||
- Upgrade: upgrade.md
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Locally calculated
|
||||
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
|
||||
sha256 6e098390be2a78a56cf94eecb9d28b11c1791dd2c364e59602bbd664b508fd57 klish-plugin-sysrepo-a4b1fae697b51614dc75989e8f4fc8d277689d16-git4.tar.gz
|
||||
sha256 7bfdaef838ee8bd3995140c40144abe79ff1c6391c7b48445e6728cd4e7e56a8 klish-plugin-sysrepo-2f14503c7ea6eb24c8adaf0cf2cf7a511114b09e-git4.tar.gz
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = a4b1fae697b51614dc75989e8f4fc8d277689d16
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = 2f14503c7ea6eb24c8adaf0cf2cf7a511114b09e
|
||||
KLISH_PLUGIN_SYSREPO_SITE = https://github.com/kernelkit/klish-plugin-sysrepo.git
|
||||
#KLISH_PLUGIN_SYSREPO_VERSION = cdd3eb51a7f7ee0ed5bd925fa636061d3b1b85fb
|
||||
#KLISH_PLUGIN_SYSREPO_SITE = https://src.libcode.org/pkun/klish-plugin-sysrepo.git
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Locally calculated
|
||||
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
|
||||
sha256 39a73fdaa7e41001e804e2bbdebdc885da79d540f0246777e8fd1c0dd9fc9475 klish-1c31f50ab775d467fa18f2e0a798006949536a2a-git4.tar.gz
|
||||
sha256 cd9bc969350b8b30d9a7a31b0f19fb3218c602f04fe7e6a1d80682a75ed26d18 klish-3ae496c43d90354ffa94d364d7775c089f0e119a-git4.tar.gz
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
KLISH_VERSION = 1c31f50ab775d467fa18f2e0a798006949536a2a
|
||||
KLISH_VERSION = 3ae496c43d90354ffa94d364d7775c089f0e119a
|
||||
KLISH_SITE = https://github.com/kernelkit/klish.git
|
||||
#KLISH_VERSION = tags/3.0.0
|
||||
#KLISH_SITE = https://src.libcode.org/pkun/klish.git
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 0da46e7eaa8896066c9ef549ee6ff95a713285f7 Mon Sep 17 00:00:00 2001
|
||||
From 255ad0d9e977b007aa5099e52b5512e4f7a65e07 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 19 Sep 2023 18:38:10 +0200
|
||||
Subject: [PATCH 01/32] net: phy: marvell10g: Support firmware loading on
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 6ca7c41f05512829625df7f89c9cedfa8fce4954 Mon Sep 17 00:00:00 2001
|
||||
From 928a14f7f60993bf67dce407fe213a19f7d653d6 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 21 Nov 2023 20:15:24 +0100
|
||||
Subject: [PATCH 02/32] net: phy: marvell10g: Fix power-up when strapped to
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 4ddca512b7f01fc8a3296b04252552e78d3aa084 Mon Sep 17 00:00:00 2001
|
||||
From c3c6b12c6e6139dc887bf291ec3c805b7cd5388b Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 15 Nov 2023 20:58:42 +0100
|
||||
Subject: [PATCH 03/32] net: phy: marvell10g: Add LED support for 88X3310
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From c064ebbc07a2fe41ea6d34f1c19804a26a37214b Mon Sep 17 00:00:00 2001
|
||||
From d45fdac6168d2bc77bb95aa4b72488e3fa949abe Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 12 Dec 2023 09:51:05 +0100
|
||||
Subject: [PATCH 04/32] net: phy: marvell10g: Support LEDs tied to a single
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From ac10b342937848aefe4c09ee054cb13a973564e5 Mon Sep 17 00:00:00 2001
|
||||
From dfdde0010c609b8ea89be49d7bfc22750b12d92d Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 27 Mar 2024 10:10:19 +0100
|
||||
Subject: [PATCH 05/32] net: phy: Do not resume PHY when attaching
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 2ea2108430d61be4b2c607782c4d70d65606504c Mon Sep 17 00:00:00 2001
|
||||
From a77410775141901ea1f400421395d0a9b558e205 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Mon, 4 Mar 2024 16:47:28 +0100
|
||||
Subject: [PATCH 06/32] net: bridge: avoid classifying unknown multicast as
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 0b500d7ce8fd3e9a64c1e61b64dc7e28f1441280 Mon Sep 17 00:00:00 2001
|
||||
From ab2d786290e4760babe80f87aceccc05b43445b7 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 5 Mar 2024 06:44:41 +0100
|
||||
Subject: [PATCH 07/32] net: bridge: Ignore router ports when forwarding L2
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 11275834a80df2c7c9ebc3c185f7307c34177fe1 Mon Sep 17 00:00:00 2001
|
||||
From 72a1cbf579a58d45b89338dd9d2144e9d5ec0ed4 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Thu, 4 Apr 2024 16:36:30 +0200
|
||||
Subject: [PATCH 08/32] net: bridge: drop delay for applying strict multicast
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 5c8691c3eeda59299e0dac6ef6df7ae2cbb247c0 Mon Sep 17 00:00:00 2001
|
||||
From e9ec03fb91dcc5c55754562656d107a17194b8be Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Thu, 16 May 2024 14:51:54 +0200
|
||||
Subject: [PATCH 09/32] net: bridge: Differentiate MDB additions from
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 75a358d785acab5bc1fae8eaff98eb62bf61605a Mon Sep 17 00:00:00 2001
|
||||
From fa6387518314725d640846514292153016efb53d Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Fri, 24 Nov 2023 23:29:55 +0100
|
||||
Subject: [PATCH 10/32] nvmem: layouts: onie-tlv: Let device probe even when
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 57d37c39d87b59ea3e808dcdd7ea762103dd3e3f Mon Sep 17 00:00:00 2001
|
||||
From 4e6aaf7f4aaf1963a98bcc3a8e04169f901eb8fe Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Sun, 11 Aug 2024 11:27:35 +0200
|
||||
Subject: [PATCH 11/32] net: usb: r8152: add r8153b support for link/activity
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 9e770f7f46bd38c38b820c72bd4de0ff94b25f06 Mon Sep 17 00:00:00 2001
|
||||
From 648987531072effe796ef3fac6b2f9259f082695 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Sun, 10 Aug 2025 18:52:54 +0200
|
||||
Subject: [PATCH 12/32] arm64: dts: mediatek: mt7986a: rename BPi R3 ports to
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 4935a1baf872efaa99a7fdbe4ba6d55634bec644 Mon Sep 17 00:00:00 2001
|
||||
From 3e90bfcf47de8d09d8fe975315186beaf18b3c8f Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Wed, 20 Aug 2025 21:38:24 +0200
|
||||
Subject: [PATCH 13/32] drm/panel-simple: Add a timing for the Raspberry Pi 7"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 4f1c58f67e64ef82b9bd3ec9b9c194d8ab8ce5c3 Mon Sep 17 00:00:00 2001
|
||||
From 989adffc496130cf79e63e7486b6ce0dbdde38c3 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Thu, 21 Aug 2025 11:20:23 +0200
|
||||
Subject: [PATCH 14/32] input:touchscreen:edt-ft5x06: Add polled mode
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 0efc077ad790a598b18b4fab2b9dd614417e204b Mon Sep 17 00:00:00 2001
|
||||
From 56b1b5c9cf340e3cc954180f18d2c455408eb8d4 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 12 Mar 2024 10:27:24 +0100
|
||||
Subject: [PATCH 15/32] [FIX] net: dsa: mv88e6xxx: Fix timeout on waiting for
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From d77bb7ae1ec02a191832ecff5a7e023980e2a02d Mon Sep 17 00:00:00 2001
|
||||
From fd256efa7865ff09830bd18f326075242b773c98 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 27 Mar 2024 15:52:43 +0100
|
||||
Subject: [PATCH 16/32] net: dsa: mv88e6xxx: Improve indirect register access
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From b876e4718bc9f28cf898bddb8fe5f127b2b582b6 Mon Sep 17 00:00:00 2001
|
||||
From b6918d761fcd859f67fdc14429e269dda657e50d Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Mon, 22 Apr 2024 23:18:01 +0200
|
||||
Subject: [PATCH 17/32] net: dsa: mv88e6xxx: Honor ports being managed via
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 2d6aab8ab0ba0465a420cfd34f29dc80765d07e6 Mon Sep 17 00:00:00 2001
|
||||
From 98176be49294c8d1a2298601e8e3678834ff9a0d Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 24 Apr 2024 22:41:04 +0200
|
||||
Subject: [PATCH 18/32] net: dsa: mv88e6xxx: Limit rsvd2cpu policy to user
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 29f5994c627c326f062a7e4b1c6ec8b00a30d53a Mon Sep 17 00:00:00 2001
|
||||
From c758a83ae13bd4f4068d73d073033b3422ee616a Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 28 May 2024 10:38:42 +0200
|
||||
Subject: [PATCH 19/32] net: dsa: tag_dsa: Use tag priority as initial
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From bc037d0641c9966c676f61080b400774b30bf26f Mon Sep 17 00:00:00 2001
|
||||
From 4a6728ce6cd21d250ffa448fcea329c43a2fd8a0 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 16 Jan 2024 16:00:55 +0100
|
||||
Subject: [PATCH 20/32] net: dsa: Support MDB memberships whose L2 addresses
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From e51540686e673fff781944b04397435c61e5fc81 Mon Sep 17 00:00:00 2001
|
||||
From 3fc20bcfab7493df99038b9f0dbd9c08af2b80fd Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Thu, 21 Mar 2024 19:12:15 +0100
|
||||
Subject: [PATCH 21/32] net: dsa: Support EtherType based priority overrides
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 550559c5275610b58322692c121194b15cf1f9e6 Mon Sep 17 00:00:00 2001
|
||||
From 116f78aa422f2396f3569fbd7b0f9376b4426166 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Fri, 22 Mar 2024 16:15:43 +0100
|
||||
Subject: [PATCH 22/32] net: dsa: mv88e6xxx: Support EtherType based priority
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 4411eea4232e445db1d9b35f62cc1168874617e4 Mon Sep 17 00:00:00 2001
|
||||
From a3c810edd1110c3db1c4ea61be18469da3327b1e Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 28 May 2024 11:04:22 +0200
|
||||
Subject: [PATCH 23/32] net: dsa: mv88e6xxx: Add mqprio qdisc support
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 1acad33cba1d7a199ce6386d58b589307ff3ee8c Mon Sep 17 00:00:00 2001
|
||||
From f73be899c92dbbb0749e126b52bafe719ad9bcfa Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 29 May 2024 13:20:41 +0200
|
||||
Subject: [PATCH 24/32] net: dsa: mv88e6xxx: Use VLAN prio over IP when both
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 463e8eb7bc4673c77f56628252c495ee840cf35d Mon Sep 17 00:00:00 2001
|
||||
From 183267ad1ee42afbd6960550f6b75b60c93b3de6 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Tue, 26 Nov 2024 19:45:59 +0100
|
||||
Subject: [PATCH 25/32] [FIX] net: dsa: mv88e6xxx: Trap locally terminated
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 75795ed5e3d522c4185005a812679c2bd67010cb Mon Sep 17 00:00:00 2001
|
||||
From fc64b3536d3e9b9db0d38ee812bf5f2d826e8a20 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Thu, 16 Jan 2025 12:35:12 +0100
|
||||
Subject: [PATCH 26/32] net: dsa: mv88e6xxx: collapse disabled state into
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 6226d56b75e1087881a150f511ab78457edf9a52 Mon Sep 17 00:00:00 2001
|
||||
From b5f738be2b2d060722e40110c3f34ee495789c70 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Waldekranz <tobias@waldekranz.com>
|
||||
Date: Wed, 12 Feb 2025 22:03:14 +0100
|
||||
Subject: [PATCH 27/32] net: dsa: mv88e6xxx: Only activate LAG offloading when
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From a3fd03baa707011d9013dc1110064ae9f7674e20 Mon Sep 17 00:00:00 2001
|
||||
From f5635e0189dc4c6cf34adec22198231f3318ef5d Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Wed, 14 Jan 2026 18:22:41 +0100
|
||||
Subject: [PATCH 28/32] net: dsa: mv88e6xxx: Add LED support for 6393X
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 9bd08559ecb3dad89f67ab40c375d375d57ea019 Mon Sep 17 00:00:00 2001
|
||||
From 1f73a56bf784cc6d48d19069a5ee5103bafa5355 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Thu, 15 Jan 2026 22:47:37 +0100
|
||||
Subject: [PATCH 29/32] wifi: brcmfmac: support deletion and recreation of
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 719c24648159177aacc03a8f5e4b6da63e6e0d28 Mon Sep 17 00:00:00 2001
|
||||
From 8e17a213b71db25b4748cf73c7413b6b82c6fbf9 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Mon, 19 Jan 2026 13:06:53 +0100
|
||||
Subject: [PATCH 30/32] wifi: brcmfmac: check connection state before querying
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From a32da01444346e8e1cd70bfc0dccc97d0b12bf56 Mon Sep 17 00:00:00 2001
|
||||
From b95a87f2e34b4706b7beaa250020fe366af64520 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Tue, 20 Jan 2026 20:12:10 +0100
|
||||
Subject: [PATCH 31/32] wifi: brcmfmac: suppress log spam for
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
From 743891bbd05b33ee0ab139ab392472df23acbba7 Mon Sep 17 00:00:00 2001
|
||||
From 2e9805533f12711a22fbca9551f1da5360ff4537 Mon Sep 17 00:00:00 2001
|
||||
From: Mattias Walström <lazzer@gmail.com>
|
||||
Date: Tue, 20 Jan 2026 20:18:45 +0100
|
||||
Subject: [PATCH 32/32] wifi: brcmfmac: reduce log noise during AP to station
|
||||
@@ -1,2 +1,2 @@
|
||||
# Calculated with utils/kernel-refresh.sh
|
||||
sha256 b726a4d15cf9ae06219b56d87820776e34d89fbc137e55fb54a9b9c3015b8f1e linux-6.18.7.tar.xz
|
||||
sha256 37f0c5d5c242c1d604e87d48f08795e861a5a85f725b4ca11d0a538f12ff8cff linux-6.18.8.tar.xz
|
||||
|
||||
@@ -190,7 +190,7 @@ def interface(args: List[str]) -> None:
|
||||
elif len(args) == 1:
|
||||
iface = args[0]
|
||||
if is_valid_interface_name(iface):
|
||||
cli_pretty(data, f"show-interfaces", "-n", iface)
|
||||
cli_pretty(data, "show-interfaces", "-n", iface)
|
||||
else:
|
||||
print(f"Invalid interface name: {iface}")
|
||||
else:
|
||||
@@ -659,6 +659,25 @@ def nacm(args: List[str]) -> None:
|
||||
print(f"Unknown NACM subcommand: {subcommand}")
|
||||
|
||||
|
||||
def keystore(args: List[str]) -> None:
|
||||
data = get_json("/ietf-keystore:keystore", "running", quiet=True)
|
||||
if not data:
|
||||
user = os.environ.get('USER', 'unknown')
|
||||
print(f'No keystore data available (check NACM permissions for "{user}").')
|
||||
return
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
if len(args) == 0 or not args[0]:
|
||||
cli_pretty(data, "show-keystore")
|
||||
elif len(args) >= 2 and args[0] in ("symmetric", "asymmetric"):
|
||||
cli_pretty(data, "show-keystore", "-t", args[0], "-n", args[1])
|
||||
else:
|
||||
print("Usage: show keystore [symmetric <name> | asymmetric <name>]")
|
||||
|
||||
|
||||
def execute_command(command: str, args: List[str]):
|
||||
command_mapping = {
|
||||
'bfd': bfd,
|
||||
@@ -667,6 +686,7 @@ def execute_command(command: str, args: List[str]):
|
||||
'dhcp': dhcp,
|
||||
'hardware': hardware,
|
||||
'interface': interface,
|
||||
'keystore': keystore,
|
||||
'lldp': lldp,
|
||||
'nacm': nacm,
|
||||
'ntp': ntp,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Rename cleartext-key to symmetric-key
|
||||
# Migrate symmetric keys from v1.6 (v25.11) to IETF standard format.
|
||||
#
|
||||
# v1.6 had WiFi keys stored as:
|
||||
# - infix-keystore:cleartext-key (type string, plaintext)
|
||||
# - infix-keystore:key-format wifi-preshared-key-format
|
||||
#
|
||||
# v1.7 uses the IETF standard leaf and updated format names:
|
||||
# - cleartext-symmetric-key (type binary, base64-encoded)
|
||||
# - key-format passphrase-key-format
|
||||
|
||||
file=$1
|
||||
temp=${file}.tmp
|
||||
@@ -7,12 +15,17 @@ temp=${file}.tmp
|
||||
jq '
|
||||
if .["ietf-keystore:keystore"]?."symmetric-keys"?."symmetric-key" then
|
||||
.["ietf-keystore:keystore"]."symmetric-keys"."symmetric-key" |= map(
|
||||
if ."infix-keystore:key-format" then
|
||||
del(."infix-keystore:key-format") |
|
||||
. + { "key-format": "infix-crypto-types:passphrase-key-format" }
|
||||
else
|
||||
.
|
||||
end |
|
||||
|
||||
if ."infix-keystore:cleartext-key" then
|
||||
# Rename cleartext-key to symmetric-key
|
||||
."infix-keystore:cleartext-key" as $key_value |
|
||||
del(."infix-keystore:cleartext-key") | . + {
|
||||
"infix-keystore:symmetric-key": $key_value
|
||||
}
|
||||
."infix-keystore:cleartext-key" as $val |
|
||||
del(."infix-keystore:cleartext-key") |
|
||||
. + { "cleartext-symmetric-key": ($val | @base64) }
|
||||
else
|
||||
.
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core.h"
|
||||
#include "interfaces.h"
|
||||
#include "dagger.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define XPATH_BASE_ "/ietf-hardware:hardware"
|
||||
#define HOSTAPD_CONF "/etc/hostapd-%s.conf"
|
||||
@@ -235,8 +236,9 @@ static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name,
|
||||
/* Helper: Write SSID and security configuration (shared between primary and BSS) */
|
||||
static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd_node *config, bool is_bss)
|
||||
{
|
||||
const char *ssid, *hidden, *security_mode, *secret_name, *secret;
|
||||
const char *ssid, *hidden, *security_mode, *secret_name;
|
||||
struct lyd_node *wifi, *ap, *security, *secret_node;
|
||||
unsigned char *secret = NULL;
|
||||
const char *ifname;
|
||||
char bssid[18];
|
||||
|
||||
@@ -282,15 +284,19 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd
|
||||
security_mode = "open";
|
||||
|
||||
/* Get secret from keystore if needed */
|
||||
secret = NULL;
|
||||
if (strcmp(security_mode, "open") != 0) {
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
if (secret_name) {
|
||||
const char *b64;
|
||||
|
||||
secret_node = lydx_get_xpathf(config,
|
||||
"/keystore/symmetric-keys/symmetric-key[name='%s']/symmetric-key",
|
||||
"/keystore/symmetric-keys/symmetric-key[name='%s']/cleartext-symmetric-key",
|
||||
secret_name);
|
||||
if (secret_node)
|
||||
secret = lyd_get_value(secret_node);
|
||||
if (secret_node) {
|
||||
b64 = lyd_get_value(secret_node);
|
||||
if (b64)
|
||||
secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,6 +335,8 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd
|
||||
/* ieee80211w=1: MFP capable but optional, for WPA2 client compatibility */
|
||||
fprintf(hostapd, "ieee80211w=1\n");
|
||||
}
|
||||
|
||||
free(secret);
|
||||
}
|
||||
|
||||
/* Helper: Write radio-specific configuration */
|
||||
|
||||
+76
-8
@@ -7,14 +7,76 @@
|
||||
* configuration (hostapd) is handled by hardware.c.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
|
||||
|
||||
|
||||
int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *wifi, *station, *security, *secret_node;
|
||||
const char *ifname, *secret_name, *security_mode, *b64;
|
||||
unsigned char *decoded;
|
||||
size_t len;
|
||||
|
||||
ifname = lydx_get_cattr(cif, "name");
|
||||
wifi = lydx_get_child(cif, "wifi");
|
||||
if (!wifi)
|
||||
return SR_ERR_OK;
|
||||
|
||||
station = lydx_get_child(wifi, "station");
|
||||
if (!station)
|
||||
return SR_ERR_OK;
|
||||
|
||||
security = lydx_get_child(station, "security");
|
||||
security_mode = lydx_get_cattr(security, "mode");
|
||||
secret_name = lydx_get_cattr(security, "secret");
|
||||
|
||||
if (!secret_name || !strcmp(security_mode, "disabled"))
|
||||
return SR_ERR_OK;
|
||||
|
||||
secret_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
secret_name);
|
||||
b64 = lydx_get_cattr(secret_node, "cleartext-symmetric-key");
|
||||
if (!b64 || !*b64)
|
||||
return SR_ERR_OK;
|
||||
|
||||
decoded = base64_decode((const unsigned char *)b64, strlen(b64), &len);
|
||||
if (!decoded)
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (len < 8 || len > 63) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WiFi passphrase must be 8-63 characters, got %zu",
|
||||
ifname, len);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!isprint((unsigned char)decoded[i])) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WiFi passphrase contains non-printable "
|
||||
"character at position %zu",
|
||||
ifname, i + 1);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
free(decoded);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
wifi_mode_t wifi_get_mode(struct lyd_node *iface)
|
||||
{
|
||||
struct lyd_node *ap, *wifi;
|
||||
@@ -59,8 +121,9 @@ int wifi_mode_changed(struct lyd_node *wifi)
|
||||
*/
|
||||
int wifi_gen_station(struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio;
|
||||
const char *ifname, *ssid, *secret_name, *security_mode, *radio;
|
||||
struct lyd_node *security, *secret_node, *radio_node, *station, *wifi;
|
||||
unsigned char *secret = NULL;
|
||||
FILE *wpa_supplicant = NULL;
|
||||
char *security_str = NULL;
|
||||
const char *country;
|
||||
@@ -91,12 +154,14 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
country = lydx_get_cattr(radio_node, "country-code");
|
||||
|
||||
if (secret_name && strcmp(security_mode, "disabled") != 0) {
|
||||
const char *b64;
|
||||
|
||||
secret_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
secret_name);
|
||||
secret = lydx_get_cattr(secret_node, "symmetric-key");
|
||||
} else {
|
||||
secret = NULL;
|
||||
b64 = lydx_get_cattr(secret_node, "cleartext-symmetric-key");
|
||||
if (b64)
|
||||
secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL);
|
||||
}
|
||||
|
||||
oldmask = umask(0077);
|
||||
@@ -121,11 +186,13 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
/* If SSID is present, create network block. Otherwise, scan-only mode */
|
||||
if (ssid) {
|
||||
/* Station mode with network configured */
|
||||
if (!strcmp(security_mode, "disabled")) {
|
||||
if (!strcmp(security_mode, "disabled"))
|
||||
asprintf(&security_str, "key_mgmt=NONE");
|
||||
} else if (secret) {
|
||||
asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret);
|
||||
}
|
||||
else if (secret)
|
||||
asprintf(&security_str,
|
||||
"key_mgmt=SAE WPA-PSK\n"
|
||||
" psk=\"%s\"", secret);
|
||||
|
||||
fprintf(wpa_supplicant,
|
||||
"network={\n"
|
||||
" bgscan=\"simple: 30:-45:300\"\n"
|
||||
@@ -152,6 +219,7 @@ int wifi_gen_station(struct lyd_node *cif)
|
||||
}
|
||||
|
||||
out:
|
||||
free(secret);
|
||||
if (wpa_supplicant)
|
||||
fclose(wpa_supplicant);
|
||||
umask(oldmask);
|
||||
|
||||
@@ -2,9 +2,81 @@
|
||||
#include <srx/lyx.h>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define WIREGUARD_CONFIG "/run/wireguard-%s.conf"
|
||||
|
||||
static int wireguard_validate_psk(sr_session_ctx_t *session, struct lyd_node *cif,
|
||||
const char *ifname, const char *psk_ref)
|
||||
{
|
||||
struct lyd_node *psk_node;
|
||||
const char *psk_data;
|
||||
unsigned char *decoded;
|
||||
size_t len;
|
||||
|
||||
psk_node = lydx_get_xpathf(cif,
|
||||
"../../keystore/symmetric-keys/symmetric-key[name='%s']",
|
||||
psk_ref);
|
||||
if (!psk_node)
|
||||
return SR_ERR_OK;
|
||||
|
||||
psk_data = lydx_get_cattr(psk_node, "cleartext-symmetric-key");
|
||||
if (!psk_data || !*psk_data)
|
||||
return SR_ERR_OK;
|
||||
|
||||
decoded = base64_decode((const unsigned char *)psk_data, strlen(psk_data), &len);
|
||||
if (!decoded)
|
||||
return SR_ERR_OK;
|
||||
|
||||
if (len != 32) {
|
||||
if (session)
|
||||
sr_session_set_error_message(session,
|
||||
"%s: WireGuard preshared key '%s' must be "
|
||||
"exactly 32 bytes, got %zu",
|
||||
ifname, psk_ref, len);
|
||||
free(decoded);
|
||||
return SR_ERR_VALIDATION_FAILED;
|
||||
}
|
||||
|
||||
free(decoded);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int wireguard_validate_peers(sr_session_ctx_t *session, struct lyd_node *cif)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
struct lyd_node *wg, *bag_peer, *peer;
|
||||
|
||||
wg = lydx_get_child(cif, "wireguard");
|
||||
if (!wg)
|
||||
return SR_ERR_OK;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(wg), bag_peer, "peers") {
|
||||
const char *psk_ref;
|
||||
int rc;
|
||||
|
||||
/* Validate bag-level PSK */
|
||||
psk_ref = lydx_get_cattr(bag_peer, "preshared-key");
|
||||
if (psk_ref) {
|
||||
rc = wireguard_validate_psk(session, cif, ifname, psk_ref);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validate per-peer PSK overrides */
|
||||
LYX_LIST_FOR_EACH(lyd_child(bag_peer), peer, "peer") {
|
||||
psk_ref = lydx_get_cattr(peer, "preshared-key");
|
||||
if (psk_ref) {
|
||||
rc = wireguard_validate_psk(session, cif, ifname, psk_ref);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
/* Helper to get a peer setting with override logic:
|
||||
* 1. Check peer-specific override
|
||||
* 2. Fall back to key-bag level default
|
||||
|
||||
+44
-40
@@ -335,9 +335,13 @@ static int netdag_gen_sysctl(struct dagger *net,
|
||||
err = err ? : netdag_gen_sysctl_setting(net, ifname, &sysctl, 1, "0", node,
|
||||
"net.ipv4.conf.%s.forwarding", ifname);
|
||||
|
||||
/*
|
||||
* Use force_forwarding for IPv6 (available since Linux 6.17) which provides
|
||||
* true per-interface control, unlike the legacy forwarding sysctl.
|
||||
*/
|
||||
node = lydx_get_descendant(lyd_child(dif), "ipv6", "forwarding", NULL);
|
||||
err = err ? : netdag_gen_sysctl_setting(net, ifname, &sysctl, 1, "0", node,
|
||||
"net.ipv6.conf.%s.forwarding", ifname);
|
||||
"net.ipv6.conf.%s.force_forwarding", ifname);
|
||||
|
||||
if (!strcmp(ifname, "lo")) /* skip for now */
|
||||
goto skip_mtu;
|
||||
@@ -353,39 +357,6 @@ skip_mtu:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The global IPv6 forwarding lever is off by default, enabled when any
|
||||
* interface has IPv6 forwarding enabled.
|
||||
*/
|
||||
static int netdag_ipv6_forwarding(struct lyd_node *cifs, struct dagger *net)
|
||||
{
|
||||
struct lyd_node *cif;
|
||||
FILE *sysctl = NULL;
|
||||
int ena = 0;
|
||||
|
||||
LYX_LIST_FOR_EACH(cifs, cif, "interface")
|
||||
ena |= lydx_is_enabled(lydx_get_child(cif, "ipv6"), "forwarding");
|
||||
|
||||
if (ena)
|
||||
sysctl = dagger_fopen_next(net, "init", "@post", NETDAG_INIT_POST, "ipv6.sysctl");
|
||||
else
|
||||
sysctl = dagger_fopen_current(net, "exit", "@pre", NETDAG_EXIT_PRE, "ipv6.sysctl");
|
||||
if (!sysctl) {
|
||||
/*
|
||||
* Cannot create exit code in gen: -1. Safe to ignore
|
||||
* since ipv6 forwarding is disabled by default.
|
||||
*/
|
||||
if (dagger_is_bootstrap(net) && !ena)
|
||||
return 0;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(sysctl, "net.ipv6.conf.all.forwarding = %d\n", ena);
|
||||
fclose(sysctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
@@ -420,9 +391,11 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
|
||||
case IFT_VXLAN:
|
||||
return vxlan_gen(NULL, cif, ip);
|
||||
case IFT_WIFI:
|
||||
return wifi_add_iface(cif, net);
|
||||
return wifi_validate_secret(session, cif)
|
||||
? : wifi_add_iface(cif, net);
|
||||
case IFT_WIREGUARD:
|
||||
return wireguard_gen(NULL, cif, ip, net);
|
||||
return wireguard_validate_peers(session, cif)
|
||||
? : wireguard_gen(NULL, cif, ip, net);
|
||||
case IFT_ETH:
|
||||
return netdag_gen_ethtool(net, cif, dif);
|
||||
case IFT_LO:
|
||||
@@ -453,7 +426,8 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
|
||||
return netdag_gen_ethtool(net, cif, dif);
|
||||
case IFT_WIFI:
|
||||
if (wifi_get_mode(cif) == wifi_station)
|
||||
return wifi_gen_station(cif);
|
||||
return wifi_validate_secret(session, cif)
|
||||
? : wifi_gen_station(cif);
|
||||
return 0;
|
||||
case IFT_DUMMY:
|
||||
case IFT_GRE:
|
||||
@@ -782,9 +756,6 @@ static sr_error_t ifchange_post(sr_session_ctx_t *session, struct dagger *net,
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/* Figure out value of global IPv6 forwarding flag. Issue #785 */
|
||||
err |= netdag_ipv6_forwarding(cifs, net);
|
||||
|
||||
/* For each configured bridge, the corresponding multicast
|
||||
* querier settings depend on both the bridge config and on
|
||||
* the presence of matching VLAN uppers. Since these can be
|
||||
@@ -851,6 +822,39 @@ err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int interfaces_validate_keys(sr_session_ctx_t *session, struct lyd_node *config)
|
||||
{
|
||||
struct lyd_node *ifaces, *iface;
|
||||
int rc;
|
||||
|
||||
ifaces = lydx_get_descendant(config, "interfaces", "interface", NULL);
|
||||
LYX_LIST_FOR_EACH(ifaces, iface, "interface") {
|
||||
const char *ifname = lydx_get_cattr(iface, "name");
|
||||
|
||||
switch (iftype_from_iface(iface)) {
|
||||
case IFT_WIFI:
|
||||
rc = wifi_validate_secret(session, iface);
|
||||
break;
|
||||
case IFT_WIREGUARD:
|
||||
rc = wireguard_validate_peers(session, iface);
|
||||
break;
|
||||
default:
|
||||
rc = SR_ERR_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
continue;
|
||||
|
||||
if (session)
|
||||
return rc;
|
||||
|
||||
ERROR("%s: key fails validation, check keystore", ifname);
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int interfaces_get_all_l3(const struct lyd_node *tree, char ***ifaces)
|
||||
{
|
||||
struct lyd_node *interfaces, *cif;
|
||||
|
||||
@@ -105,6 +105,7 @@ const char *get_chassis_addr(void);
|
||||
int interface_get_phys_addr(struct lyd_node *cif, char *mac);
|
||||
int link_gen_address(struct lyd_node *cif, FILE *ip);
|
||||
int interfaces_get_all_l3(const struct lyd_node *tree, char ***ifaces);
|
||||
int interfaces_validate_keys(sr_session_ctx_t *session, struct lyd_node *config);
|
||||
|
||||
/* ip.c */
|
||||
int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
@@ -130,6 +131,7 @@ typedef enum wifi_mode_t {
|
||||
wifi_unknown
|
||||
} wifi_mode_t;
|
||||
|
||||
int wifi_validate_secret(sr_session_ctx_t *session, struct lyd_node *cif);
|
||||
int wifi_add_iface(struct lyd_node *cif, struct dagger *net);
|
||||
int wifi_del_iface(struct lyd_node *dif, struct dagger *net);
|
||||
int wifi_mode_changed(struct lyd_node *wifi);
|
||||
@@ -162,6 +164,7 @@ int ifchange_cand_infer_dhcp(sr_session_ctx_t *session, const char *path);
|
||||
int vxlan_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
|
||||
|
||||
/* infix-if-wireguard */
|
||||
int wireguard_validate_peers(sr_session_ctx_t *session, struct lyd_node *cif);
|
||||
int wireguard_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, struct dagger *net);
|
||||
|
||||
#endif /* CONFD_INTERFACES_H_ */
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
#include "base64.h"
|
||||
#include "core.h"
|
||||
#include "interfaces.h"
|
||||
|
||||
#define XPATH_KEYSTORE_ "/ietf-keystore:keystore/asymmetric-keys"
|
||||
#define XPATH_KEYSTORE_ASYM "/ietf-keystore:keystore/asymmetric-keys"
|
||||
#define XPATH_KEYSTORE_SYM "/ietf-keystore:keystore/symmetric-keys"
|
||||
#define SSH_PRIVATE_KEY "/tmp/ssh.key"
|
||||
#define SSH_PUBLIC_KEY "/tmp/ssh.pub"
|
||||
|
||||
@@ -166,7 +168,8 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
struct lyd_node *changes, *change;
|
||||
int rc = SR_ERR_OK;
|
||||
|
||||
if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_))
|
||||
if (diff && !lydx_find_xpathf(diff, XPATH_KEYSTORE_ASYM)
|
||||
&& !lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
return SR_ERR_OK;
|
||||
|
||||
switch (event) {
|
||||
@@ -174,6 +177,9 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
rc = keystore_update(session, config, diff);
|
||||
break;
|
||||
case SR_EV_CHANGE:
|
||||
if (diff && lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
rc = interfaces_validate_keys(session, config);
|
||||
break;
|
||||
case SR_EV_ENABLED:
|
||||
break;
|
||||
case SR_EV_ABORT:
|
||||
@@ -186,6 +192,8 @@ int keystore_change(sr_session_ctx_t *session, struct lyd_node *config, struct l
|
||||
if (rename(SSH_HOSTKEYS_NEXT, SSH_HOSTKEYS))
|
||||
ERRNO("Failed switching to new %s", SSH_HOSTKEYS);
|
||||
}
|
||||
if (diff && lydx_find_xpathf(diff, XPATH_KEYSTORE_SYM))
|
||||
interfaces_validate_keys(NULL, config);
|
||||
return SR_ERR_OK;
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
|
||||
@@ -15,32 +15,46 @@ module infix-crypto-types {
|
||||
revision 2025-02-04 {
|
||||
description "Initial";
|
||||
}
|
||||
|
||||
identity private-key-format {
|
||||
base ct:private-key-format;
|
||||
description
|
||||
"Base key-format identity for private keys.";
|
||||
"Base for Infix private key format extensions.";
|
||||
}
|
||||
|
||||
identity public-key-format {
|
||||
base ct:public-key-format;
|
||||
description
|
||||
"Base key-format identity for public keys.";
|
||||
"Base for Infix public key format extensions.";
|
||||
}
|
||||
|
||||
identity rsa-private-key-format {
|
||||
base private-key-format;
|
||||
base ct:rsa-private-key-format;
|
||||
description
|
||||
"RSA private key in PKCS#1 format (RFC 8017).
|
||||
Used for SSH host keys.";
|
||||
}
|
||||
|
||||
identity ssh-public-key-format {
|
||||
base public-key-format;
|
||||
base ct:ssh-public-key-format;
|
||||
description
|
||||
"SSH public key format (RFC 4253, Section 6.6).
|
||||
Used for SSH host keys.";
|
||||
}
|
||||
|
||||
identity symmetric-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
description
|
||||
"Base for symmetric key format";
|
||||
"Base for Infix symmetric key format extensions.";
|
||||
}
|
||||
identity wifi-preshared-key-format {
|
||||
|
||||
identity passphrase-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
base symmetric-key-format;
|
||||
description
|
||||
"WiFi secret key";
|
||||
"Human-readable passphrase, e.g., WiFi WPA2/WPA3 passwords.";
|
||||
}
|
||||
|
||||
identity x25519-public-key-format {
|
||||
@@ -48,7 +62,7 @@ module infix-crypto-types {
|
||||
base ct:public-key-format;
|
||||
description
|
||||
"X25519 (Curve25519) public key format for Diffie-Hellman key exchange.
|
||||
This is the format used by WireGuard.";
|
||||
This format is used by network protocols such as WireGuard VPN.";
|
||||
}
|
||||
|
||||
identity x25519-private-key-format {
|
||||
@@ -56,15 +70,6 @@ module infix-crypto-types {
|
||||
base ct:private-key-format;
|
||||
description
|
||||
"X25519 (Curve25519) private key format for Diffie-Hellman key exchange.
|
||||
This is the format used by WireGuard.";
|
||||
}
|
||||
|
||||
identity wireguard-symmetric-key-format {
|
||||
base ct:symmetric-key-format;
|
||||
base symmetric-key-format;
|
||||
description
|
||||
"WireGuard pre-shared key format.
|
||||
32-byte base64-encoded key used as an optional additional layer
|
||||
of symmetric encryption for post-quantum resistance.";
|
||||
This format is used by network protocols such as WireGuard VPN.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ submodule infix-if-wireguard {
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-crypto-types {
|
||||
prefix ct;
|
||||
}
|
||||
import infix-crypto-types {
|
||||
prefix ixct;
|
||||
}
|
||||
@@ -75,8 +78,8 @@ submodule infix-if-wireguard {
|
||||
|
||||
This provides post-quantum resistance as an attacker would need
|
||||
to break both the Curve25519 key exchange and this symmetric key.";
|
||||
must "derived-from-or-self(deref(.)/../infix-ks:key-format, 'ixct:wireguard-symmetric-key-format')" {
|
||||
error-message "Preshared key must be in wireguard-symmetric-key-format";
|
||||
must "derived-from-or-self(deref(.)/../ks:key-format, 'ct:octet-string-key-format')" {
|
||||
error-message "Preshared key must be in octet-string-key-format";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,12 @@ module infix-keystore {
|
||||
import ietf-keystore {
|
||||
prefix ks;
|
||||
}
|
||||
import ietf-crypto-types {
|
||||
prefix ct;
|
||||
}
|
||||
import infix-crypto-types {
|
||||
prefix infix-ct;
|
||||
}
|
||||
|
||||
revision 2025-12-17 {
|
||||
description "Add WireGuard support";
|
||||
description "Add WireGuard support, see infix-crypto-types.yang";
|
||||
}
|
||||
revision 2025-12-10 {
|
||||
description "Adapt to changes in final version of ietf-keystore";
|
||||
@@ -24,59 +21,4 @@ module infix-keystore {
|
||||
revision 2025-02-04 {
|
||||
description "Initial";
|
||||
}
|
||||
deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:public-key-format" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-ct:public-key-format;
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:private-key-format" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base infix-ct:private-key-format;
|
||||
}
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-format" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" {
|
||||
leaf key-format {
|
||||
type identityref {
|
||||
base infix-ct:symmetric-key-format;
|
||||
}
|
||||
description
|
||||
"Identifies the symmetric key's format
|
||||
|
||||
Valid symmetric key formats are:
|
||||
wifi-preshared-key-format - WiFi preshared key
|
||||
wireguard-symmetric-key-format - WireGuard preshared key";
|
||||
}
|
||||
}
|
||||
deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type/ks:cleartext-symmetric-key" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type" {
|
||||
case cleartext-symmetric-key {
|
||||
leaf symmetric-key {
|
||||
type string;
|
||||
must "../infix-ks:key-format != 'infix-ct:wifi-preshared-key-format' or " +
|
||||
"(string-length(.) >= 8 and string-length(.) <= 63)" {
|
||||
error-message "WiFi pre-shared key must be 8-63 characters long";
|
||||
}
|
||||
must "../infix-ks:key-format != 'infix-ct:wireguard-symmetric-key-format' or " +
|
||||
"string-length(.) = 44" {
|
||||
error-message "WireGuard pre-shared key must be 44 characters (32-byte base64-encoded)";
|
||||
}
|
||||
description
|
||||
"Cleartext symmetric key value.
|
||||
|
||||
Format depends on key-format:
|
||||
- WiFi pre-shared key: 8-63 printable ASCII characters
|
||||
- WireGuard pre-shared key: 32-byte base64-encoded key (44 chars with padding)";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +413,20 @@ int infix_groups(kcontext_t *ctx)
|
||||
"| jq -r '.\"ietf-netconf-acm:nacm\".groups.group[].name'");
|
||||
}
|
||||
|
||||
int infix_sym_keys(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
return shellf("copy running -x /ietf-keystore:keystore/symmetric-keys "
|
||||
"| jq -r '.\"ietf-keystore:keystore\".\"symmetric-keys\".\"symmetric-key\"[].name'");
|
||||
}
|
||||
|
||||
int infix_asym_keys(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
return shellf("copy running -x /ietf-keystore:keystore/asymmetric-keys "
|
||||
"| jq -r '.\"ietf-keystore:keystore\".\"asymmetric-keys\".\"asymmetric-key\"[].name'");
|
||||
}
|
||||
|
||||
int kplugin_infix_fini(kcontext_t *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
@@ -432,6 +446,8 @@ int kplugin_infix_init(kcontext_t *ctx)
|
||||
kplugin_add_syms(plugin, ksym_new("ifaces", infix_ifaces));
|
||||
kplugin_add_syms(plugin, ksym_new("users", infix_users));
|
||||
kplugin_add_syms(plugin, ksym_new("groups", infix_groups));
|
||||
kplugin_add_syms(plugin, ksym_new("sym_keys", infix_sym_keys));
|
||||
kplugin_add_syms(plugin, ksym_new("asym_keys", infix_asym_keys));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_zones", infix_firewall_zones));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_policies", infix_firewall_policies));
|
||||
kplugin_add_syms(plugin, ksym_new("firewall_services", infix_firewall_services));
|
||||
|
||||
@@ -155,6 +155,20 @@
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="SYM_KEYS">
|
||||
<COMPL>
|
||||
<ACTION sym="sym_keys@infix"/>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<PTYPE name="ASYM_KEYS">
|
||||
<COMPL>
|
||||
<ACTION sym="asym_keys@infix"/>
|
||||
</COMPL>
|
||||
<ACTION sym="STRING"/>
|
||||
</PTYPE>
|
||||
|
||||
<VIEW name="main">
|
||||
<HOTKEY key="^D" cmd="exit"/>
|
||||
|
||||
@@ -259,6 +273,21 @@
|
||||
</COMMAND>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="wireguard" help="WireGuard VPN key tools" mode="switch">
|
||||
<COMMAND name="genkey" help="Generate a WireGuard private/public key pair">
|
||||
<ACTION sym="script">
|
||||
priv=$(wg genkey)
|
||||
pub=$(printf '%s' "$priv" | wg pubkey)
|
||||
echo "Private: $priv"
|
||||
echo "Public: $pub"
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="genpsk" help="Generate a WireGuard pre-shared key">
|
||||
<ACTION sym="script">wg genpsk</ACTION>
|
||||
</COMMAND>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="set" help="Set operations, e.g., current date/time" mode="switch">
|
||||
<COMMAND name="datetime" help="Set current date and time, ISO-8601 format">
|
||||
<PARAM name="current-datetime" ptype="/STRING" help="yyyy-mm-ddThh:mm:ss(Z|+/-hh:mm)"/>
|
||||
@@ -338,6 +367,20 @@
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="keystore" help="Show keystore keys">
|
||||
<SWITCH name="subcommands" min="0">
|
||||
<COMMAND name="symmetric" help="Show details for a symmetric key">
|
||||
<PARAM name="name" ptype="/SYM_KEYS" help="Key name"/>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore symmetric $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
<COMMAND name="asymmetric" help="Show details for an asymmetric key">
|
||||
<PARAM name="name" ptype="/ASYM_KEYS" help="Key name"/>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore asymmetric $KLISH_PARAM_name</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script" in="tty" out="tty" interrupt="true">show keystore</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="nacm" help="Show users and NACM status and groups">
|
||||
<SWITCH name="subcommands" min="0">
|
||||
<COMMAND name="group" help="Show details for a specific NACM group">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
@@ -1030,6 +1031,7 @@ class DhcpServer:
|
||||
class Iface:
|
||||
# Class variable to hold routing-enabled interfaces for the current display session
|
||||
_routing_ifaces = set()
|
||||
_keystore = {}
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
@@ -3381,6 +3383,131 @@ def show_nacm_user(json):
|
||||
print("For detailed rules, use: show nacm group <name>")
|
||||
|
||||
|
||||
def _keystore_format_name(key_format):
|
||||
"""Simplify key-format to a short display name."""
|
||||
fmt = key_format.split(':')[-1] if ':' in key_format else key_format
|
||||
return fmt.replace('-key-format', '')
|
||||
|
||||
|
||||
def _keystore_decode_symmetric(key):
|
||||
"""Decode a symmetric key value for display."""
|
||||
key_format = key.get('key-format', '')
|
||||
fmt = _keystore_format_name(key_format)
|
||||
b64val = key.get('cleartext-symmetric-key', '')
|
||||
|
||||
if fmt == 'passphrase' and b64val:
|
||||
try:
|
||||
return base64.b64decode(b64val).decode('utf-8')
|
||||
except Exception:
|
||||
return b64val
|
||||
return b64val if b64val else '-'
|
||||
|
||||
|
||||
def _keystore_find_key(keystore, kind, name):
|
||||
"""Find a key by type and name in the keystore."""
|
||||
if kind == 'symmetric':
|
||||
keys = keystore.get('symmetric-keys', {}).get('symmetric-key', [])
|
||||
else:
|
||||
keys = keystore.get('asymmetric-keys', {}).get('asymmetric-key', [])
|
||||
for key in keys:
|
||||
if key.get('name') == name:
|
||||
return key
|
||||
return None
|
||||
|
||||
|
||||
def _keystore_asym_type(key):
|
||||
"""Derive asymmetric key algorithm from key format fields."""
|
||||
for field in ('private-key-format', 'public-key-format'):
|
||||
fmt = key.get(field, '')
|
||||
name = fmt.split(':')[-1] if ':' in fmt else fmt
|
||||
name = name.replace('-private-key-format', '').replace('-public-key-format', '')
|
||||
if name:
|
||||
return name
|
||||
return ''
|
||||
|
||||
|
||||
def show_keystore_detail(keystore, kind, name):
|
||||
"""Display detailed information about a specific key."""
|
||||
key = _keystore_find_key(keystore, kind, name)
|
||||
if not key:
|
||||
print(f'{kind.capitalize()} key "{name}" not found.')
|
||||
return
|
||||
|
||||
print(f"{'name':<{20}}: {name}")
|
||||
if kind == 'symmetric':
|
||||
fmt = _keystore_format_name(key.get('key-format', ''))
|
||||
value = _keystore_decode_symmetric(key)
|
||||
print(f"{'format':<{20}}: {fmt}")
|
||||
print(f"{'value':<{20}}: {value}")
|
||||
else:
|
||||
ktype = _keystore_asym_type(key)
|
||||
if ktype:
|
||||
print(f"{'algorithm':<{20}}: {ktype}")
|
||||
pub_fmt = _keystore_format_name(key.get('public-key-format', ''))
|
||||
if pub_fmt:
|
||||
print(f"{'public key format':<{20}}: {pub_fmt}")
|
||||
pub_key = key.get('public-key', '')
|
||||
if pub_key:
|
||||
print(f"{'public key':<{20}}: {pub_key}")
|
||||
|
||||
|
||||
def show_keystore(json, kind=None, name=None):
|
||||
"""Display keystore keys overview or detail for a specific key."""
|
||||
keystore = json.get("ietf-keystore:keystore", {})
|
||||
if not keystore:
|
||||
print("Keystore is empty.")
|
||||
return
|
||||
|
||||
if kind and name:
|
||||
show_keystore_detail(keystore, kind, name)
|
||||
return
|
||||
|
||||
TABLE_WIDTH = 72
|
||||
|
||||
# Symmetric keys
|
||||
sym_keys_data = keystore.get('symmetric-keys', {}).get('symmetric-key', [])
|
||||
if sym_keys_data:
|
||||
Decore.title("Symmetric Keys", TABLE_WIDTH)
|
||||
table = SimpleTable([
|
||||
Column('NAME', flexible=True),
|
||||
Column('FORMAT'),
|
||||
Column('VALUE', flexible=True)
|
||||
], min_width=TABLE_WIDTH)
|
||||
|
||||
for key in sym_keys_data:
|
||||
name = key.get('name', '')
|
||||
fmt = _keystore_format_name(key.get('key-format', ''))
|
||||
value = _keystore_decode_symmetric(key)
|
||||
table.row(name, fmt, value)
|
||||
|
||||
table.print()
|
||||
|
||||
# Asymmetric keys
|
||||
asym_keys_data = keystore.get('asymmetric-keys', {}).get('asymmetric-key', [])
|
||||
if asym_keys_data:
|
||||
Decore.title("Asymmetric Keys", TABLE_WIDTH)
|
||||
table = SimpleTable([
|
||||
Column('NAME', flexible=True),
|
||||
Column('TYPE'),
|
||||
Column('PUBLIC KEY', flexible=True)
|
||||
], min_width=TABLE_WIDTH)
|
||||
|
||||
for key in asym_keys_data:
|
||||
name = key.get('name', '')
|
||||
ktype = _keystore_asym_type(key)
|
||||
|
||||
pub_key = key.get('public-key', '')
|
||||
if len(pub_key) > 40:
|
||||
pub_key = pub_key[:37] + '...'
|
||||
|
||||
table.row(name, ktype, pub_key)
|
||||
|
||||
table.print()
|
||||
|
||||
if not sym_keys_data and not asym_keys_data:
|
||||
print("Keystore is empty.")
|
||||
|
||||
|
||||
def show_system(json):
|
||||
"""System information overivew"""
|
||||
if not json.get("ietf-system:system-state"):
|
||||
@@ -5369,6 +5496,10 @@ def main():
|
||||
subparsers.add_parser('show-nacm-group', help='Show NACM group details')
|
||||
subparsers.add_parser('show-nacm-user', help='Show NACM user details')
|
||||
|
||||
ks_parser = subparsers.add_parser('show-keystore', help='Show keystore keys')
|
||||
ks_parser.add_argument('-t', '--type', help='Key type (symmetric or asymmetric)')
|
||||
ks_parser.add_argument('-n', '--name', help='Key name')
|
||||
|
||||
subparsers.add_parser('show-ntp', help='Show NTP status') \
|
||||
.add_argument('-a', '--address', help='Show details for specific address')
|
||||
subparsers.add_parser('show-ntp-tracking', help='Show NTP tracking status')
|
||||
@@ -5438,6 +5569,8 @@ def main():
|
||||
show_nacm_group(json_data)
|
||||
elif args.command == "show-nacm-user":
|
||||
show_nacm_user(json_data)
|
||||
elif args.command == "show-keystore":
|
||||
show_keystore(json_data, getattr(args, 'type', None), args.name)
|
||||
elif args.command == "show-ntp":
|
||||
show_ntp(json_data, args.address)
|
||||
elif args.command == "show-ntp-tracking":
|
||||
|
||||
@@ -125,7 +125,7 @@ def add_protocol(routes, proto):
|
||||
|
||||
|
||||
def get_routing_interfaces():
|
||||
"""Get list of interfaces with IPv4 forwarding enabled"""
|
||||
"""Get list of interfaces with IPv4 or IPv6 forwarding enabled"""
|
||||
import json
|
||||
|
||||
# Get all interfaces
|
||||
@@ -139,11 +139,12 @@ def get_routing_interfaces():
|
||||
continue
|
||||
|
||||
# Check if IPv4 forwarding is enabled
|
||||
# Note: We only check IPv4 forwarding. IPv6 forwarding behaves differently
|
||||
# and will be handled separately when Linux 6.17+ force_forwarding is available.
|
||||
ipv4_fwd = HOST.run(tuple(['sysctl', '-n', f'net.ipv4.conf.{ifname}.forwarding']), default="0").strip()
|
||||
|
||||
if ipv4_fwd == "1":
|
||||
# Check if IPv6 force_forwarding is enabled (available since Linux 6.17)
|
||||
ipv6_fwd = HOST.run(tuple(['sysctl', '-n', f'net.ipv6.conf.{ifname}.force_forwarding']), default="0").strip()
|
||||
|
||||
if ipv4_fwd == "1" or ipv6_fwd == "1":
|
||||
routing_ifaces.append(ifname)
|
||||
|
||||
return routing_ifaces
|
||||
|
||||
@@ -5,11 +5,13 @@ ifdef::topdoc[:imagesdir: {topdoc}../../test/case/containers/environment]
|
||||
==== Description
|
||||
|
||||
Verify that environment variables can be set in container configuration
|
||||
and are available inside the running container.
|
||||
and are available inside the running container. Also verify that
|
||||
changing an environment variable triggers a container restart.
|
||||
|
||||
1 Set up a container config with multiple environment variables
|
||||
2. Serve variables back to host using a CGI script in container
|
||||
3. Verify served content against environment variables
|
||||
4. Change an environment variable and verify the container restarts
|
||||
|
||||
==== Topology
|
||||
|
||||
@@ -23,5 +25,7 @@ image::topology.svg[Container environment variables topology, align=center, scal
|
||||
. Verify container has started
|
||||
. Verify basic connectivity to data interface
|
||||
. Verify environment variables in CGI response
|
||||
. Change environment variable and verify container restarts
|
||||
. Verify container has restarted with updated env
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
Container environment variables
|
||||
|
||||
Verify that environment variables can be set in container configuration
|
||||
and are available inside the running container.
|
||||
and are available inside the running container. Also verify that
|
||||
changing an environment variable triggers a container restart.
|
||||
|
||||
1 Set up a container config with multiple environment variables
|
||||
2. Serve variables back to host using a CGI script in container
|
||||
3. Verify served content against environment variables
|
||||
4. Change an environment variable and verify the container restarts
|
||||
"""
|
||||
import infamy
|
||||
from infamy.util import until, to_binary, curl
|
||||
@@ -105,4 +107,24 @@ with infamy.Test() as test:
|
||||
|
||||
until(lambda: all(string in ns.call(lambda: curl(URL)) for string in expected_strings))
|
||||
|
||||
with test.step("Change environment variable and verify container restarts"):
|
||||
UPDATED_ENV_VARS = [
|
||||
{"key": "TEST_VAR", "value": "updated-value"},
|
||||
{"key": "APP_PORT", "value": "8080"},
|
||||
{"key": "DEBUG_MODE", "value": "true"},
|
||||
{"key": "PATH_WITH_SPACES", "value": "/path with spaces/test"}
|
||||
]
|
||||
|
||||
target.put_config_dict("infix-containers", {
|
||||
"containers": {
|
||||
"container": [{
|
||||
"name": f"{NAME}",
|
||||
"env": UPDATED_ENV_VARS,
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
with test.step("Verify container has restarted with updated env"):
|
||||
until(lambda: "TEST_VAR=updated-value" in ns.call(lambda: curl(URL)), attempts=60)
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -7,7 +7,7 @@ Tests verifying interface configuration and management:
|
||||
- IPv4 and IPv6 address assignment and management
|
||||
- IPv4 address auto-configuration mechanisms
|
||||
- Interface aliases and physical address configuration
|
||||
- Basic routing table configuration and operation
|
||||
- IPv4 and IPv6 forwarding between interfaces
|
||||
- Linux bridge creation, STP, and VLAN handling
|
||||
- Link aggregation (LAG) setup and failover behavior
|
||||
- IGMP multicast group management and forwarding
|
||||
|
||||
@@ -4,8 +4,11 @@ ifdef::topdoc[:imagesdir: {topdoc}../../test/case/interfaces/routing_basic]
|
||||
|
||||
==== Description
|
||||
|
||||
Verify routing between interfaces is possible. That enable/disable routing
|
||||
in configuration has the expected result.
|
||||
Verify that the ietf-ip forwarding setting controls whether IPv4
|
||||
and IPv6 traffic is routed between interfaces. When forwarding is
|
||||
enabled, hosts on separate subnets can reach each other through
|
||||
the device. When forwarding is disabled, that connectivity is
|
||||
expected to be lost.
|
||||
|
||||
==== Topology
|
||||
|
||||
@@ -14,11 +17,11 @@ image::topology.svg[Routing basic topology, align=center, scaledwidth=75%]
|
||||
==== Sequence
|
||||
|
||||
. Set up topology and attach to target DUTs
|
||||
. Setup host
|
||||
. Set up host addresses and default routes
|
||||
. Enable forwarding on target:data1 and target:data2
|
||||
. Verify ping from host:data1 to 10.0.0.10
|
||||
. Verify ping from host:data2 to 192.168.0.10
|
||||
. Verify cross-subnet IPv4 connectivity
|
||||
. Verify cross-subnet IPv6 connectivity
|
||||
. Disable forwarding on target:data1 and target:data2
|
||||
. Verify ping does not work host:data1 to 10.0.0.10 and host:data2 to 192.168.0.10
|
||||
. Verify cross-subnet connectivity is lost
|
||||
|
||||
|
||||
|
||||
@@ -1,58 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
# ,------------------------------------------------,
|
||||
# | [TARGET] |
|
||||
# | |
|
||||
# | |
|
||||
# | target:mgmt target:data0 targetgt:data1 |
|
||||
# | [192.168.0.1] [10.0.0.1] |
|
||||
# '------------------------------------------------'
|
||||
# | | |
|
||||
# | | |
|
||||
# ,----------------------------------------------,
|
||||
# | host:mgmt host:data0 host:data1 |
|
||||
# | [192.168.0.10] [10.0.0.10] |
|
||||
# | (ns0) (ns1) |
|
||||
# | |
|
||||
# | [ HOST ] |
|
||||
# '----------------------------------------------'
|
||||
|
||||
"""
|
||||
Routing basic
|
||||
|
||||
Verify routing between interfaces is possible. That enable/disable routing
|
||||
in configuration has the expected result.
|
||||
Verify that the ietf-ip forwarding setting controls whether IPv4
|
||||
and IPv6 traffic is routed between interfaces. When forwarding is
|
||||
enabled, hosts on separate subnets can reach each other through
|
||||
the device. When forwarding is disabled, that connectivity is
|
||||
expected to be lost.
|
||||
"""
|
||||
import infamy
|
||||
|
||||
SUBNETS = [
|
||||
{"ipv4": {"gw": "192.168.0.1", "host": "192.168.0.10", "prefix": 24},
|
||||
"ipv6": {"gw": "2001:db8:0::1", "host": "2001:db8:0::10", "prefix": 64}},
|
||||
{"ipv4": {"gw": "10.0.0.1", "host": "10.0.0.10", "prefix": 24},
|
||||
"ipv6": {"gw": "2001:db8:1::1", "host": "2001:db8:1::10", "prefix": 64}},
|
||||
]
|
||||
|
||||
def iface_cfg(port, subnet, enable_fwd):
|
||||
"""Build interface config with both IPv4 and IPv6."""
|
||||
cfg = {"name": port, "enabled": True}
|
||||
for family in ("ipv4", "ipv6"):
|
||||
af = subnet[family]
|
||||
cfg[family] = {
|
||||
"forwarding": enable_fwd,
|
||||
"address": [{"ip": af["gw"], "prefix-length": af["prefix"]}],
|
||||
}
|
||||
return cfg
|
||||
|
||||
def config_target(target, tport0, tport1, enable_fwd):
|
||||
"""Configure forwarding and addresses for both address families."""
|
||||
target.put_config_dict("ietf-interfaces", {
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
{
|
||||
"name": tport0,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": enable_fwd,
|
||||
"address": [{
|
||||
"ip": "192.168.0.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": tport1,
|
||||
"enabled": True,
|
||||
"ipv4": {
|
||||
"forwarding": enable_fwd,
|
||||
"address": [{
|
||||
"ip": "10.0.0.1",
|
||||
"prefix-length": 24
|
||||
}]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
"interfaces": {
|
||||
"interface": [
|
||||
iface_cfg(tport0, SUBNETS[0], enable_fwd),
|
||||
iface_cfg(tport1, SUBNETS[1], enable_fwd),
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
def setup_host(ns, subnet):
|
||||
"""Add IPv4 and IPv6 addresses and default routes."""
|
||||
v4 = subnet["ipv4"]
|
||||
ns.addip(v4["host"])
|
||||
ns.addroute("default", v4["gw"])
|
||||
|
||||
v6 = subnet["ipv6"]
|
||||
ns.addip(v6["host"], prefix_length=v6["prefix"], proto="ipv6")
|
||||
ns.addroute("default", v6["gw"], proto="ipv6")
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUTs"):
|
||||
@@ -65,29 +60,31 @@ with infamy.Test() as test:
|
||||
_, hport1 = env.ltop.xlate("host", "data2")
|
||||
|
||||
with infamy.IsolatedMacVlan(hport0) as ns0, \
|
||||
infamy.IsolatedMacVlan(hport1) as ns1 :
|
||||
infamy.IsolatedMacVlan(hport1) as ns1:
|
||||
|
||||
with test.step("Setup host"):
|
||||
ns0.addip("192.168.0.10")
|
||||
ns0.addroute("default", "192.168.0.1")
|
||||
|
||||
ns1.addip("10.0.0.10")
|
||||
ns1.addroute("default", "10.0.0.1")
|
||||
with test.step("Set up host addresses and default routes"):
|
||||
setup_host(ns0, SUBNETS[0])
|
||||
setup_host(ns1, SUBNETS[1])
|
||||
|
||||
with test.step("Enable forwarding on target:data1 and target:data2"):
|
||||
config_target(target, tport0, tport1, True)
|
||||
|
||||
with test.step("Verify ping from host:data1 to 10.0.0.10"):
|
||||
ns0.must_reach("10.0.0.10")
|
||||
with test.step("Verify cross-subnet IPv4 connectivity"):
|
||||
ns0.must_reach(SUBNETS[1]["ipv4"]["host"])
|
||||
ns1.must_reach(SUBNETS[0]["ipv4"]["host"])
|
||||
|
||||
with test.step("Verify ping from host:data2 to 192.168.0.10"):
|
||||
ns1.must_reach("192.168.0.10")
|
||||
with test.step("Verify cross-subnet IPv6 connectivity"):
|
||||
ns0.must_reach(SUBNETS[1]["ipv6"]["host"])
|
||||
ns1.must_reach(SUBNETS[0]["ipv6"]["host"])
|
||||
|
||||
with test.step("Disable forwarding on target:data1 and target:data2"):
|
||||
config_target(target, tport0, tport1, False)
|
||||
|
||||
with test.step("Verify ping does not work host:data1 to 10.0.0.10 and host:data2 to 192.168.0.10"):
|
||||
infamy.parallel(lambda: ns0.must_not_reach("10.0.0.10"),
|
||||
lambda: ns1.must_not_reach("192.168.0.10"))
|
||||
with test.step("Verify cross-subnet connectivity is lost"):
|
||||
infamy.parallel(
|
||||
lambda: ns0.must_not_reach(SUBNETS[1]["ipv4"]["host"]),
|
||||
lambda: ns1.must_not_reach(SUBNETS[0]["ipv4"]["host"]),
|
||||
lambda: ns0.must_not_reach(SUBNETS[1]["ipv6"]["host"]),
|
||||
lambda: ns1.must_not_reach(SUBNETS[0]["ipv6"]["host"]))
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -19,6 +19,6 @@ graph "routing_basic" {
|
||||
];
|
||||
|
||||
host:mgmt -- target:mgmt [requires="mgmt", color="lightgray"]
|
||||
host:data1 -- target:data1 [color=black, fontcolor=black, fontsize=12, taillabel=".10", label="192.168.0.0/24", headlabel=".1"]
|
||||
host:data2 -- target:data2 [color=black, fontcolor=black, fontsize=12, taillabel=".10", label="10.0.0.0/24", headlabel=".1"]
|
||||
}
|
||||
host:data1 -- target:data1 [color=black, fontcolor=black, fontsize=12, taillabel=".10", label="192.168.0.0/24\n2001:db8:0::/64", headlabel=".1"]
|
||||
host:data2 -- target:data2 [color=black, fontcolor=black, fontsize=12, taillabel=".10", label="10.0.0.0/24\n2001:db8:1::/64", headlabel=".1"]
|
||||
}
|
||||
|
||||
@@ -3,55 +3,57 @@
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: routing_basic Pages: 1 -->
|
||||
<svg width="440pt" height="79pt"
|
||||
viewBox="0.00 0.00 440.03 79.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 75)">
|
||||
<svg width="440pt" height="92pt"
|
||||
viewBox="0.00 0.00 440.03 92.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 88)">
|
||||
<title>routing_basic</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-75 436.03,-75 436.03,4 -4,4"/>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-88 436.03,-88 436.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-1.5 0,-70.5 108,-70.5 108,-1.5 0,-1.5"/>
|
||||
<text text-anchor="middle" x="25" y="-32.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-1.5 50,-70.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-55.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-47.5 108,-47.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-32.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="50,-24.5 108,-24.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-9.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polygon fill="none" stroke="black" points="0,-14.5 0,-83.5 108,-83.5 108,-14.5 0,-14.5"/>
|
||||
<text text-anchor="middle" x="25" y="-45.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-14.5 50,-83.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-68.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-60.5 108,-60.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-45.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="50,-37.5 108,-37.5 "/>
|
||||
<text text-anchor="middle" x="79" y="-22.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="308.03,-1.5 308.03,-70.5 432.03,-70.5 432.03,-1.5 308.03,-1.5"/>
|
||||
<text text-anchor="middle" x="337.03" y="-55.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-47.5 366.03,-47.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-32.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-24.5 366.03,-24.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-9.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="366.03,-1.5 366.03,-70.5 "/>
|
||||
<text text-anchor="middle" x="399.03" y="-32.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
<polygon fill="none" stroke="black" points="308.03,-14.5 308.03,-83.5 432.03,-83.5 432.03,-14.5 308.03,-14.5"/>
|
||||
<text text-anchor="middle" x="337.03" y="-68.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-60.5 366.03,-60.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-45.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data1</text>
|
||||
<polyline fill="none" stroke="black" points="308.03,-37.5 366.03,-37.5 "/>
|
||||
<text text-anchor="middle" x="337.03" y="-22.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data2</text>
|
||||
<polyline fill="none" stroke="black" points="366.03,-14.5 366.03,-83.5 "/>
|
||||
<text text-anchor="middle" x="399.03" y="-45.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M108,-59C108,-59 308.03,-59 308.03,-59"/>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M108,-72C108,-72 308.03,-72 308.03,-72"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data1--target:data1</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-36C108,-36 308.03,-36 308.03,-36"/>
|
||||
<text text-anchor="middle" x="162.52" y="-39.4" font-family="DejaVu Serif, Book" font-size="12.00">192.168.0.0/24</text>
|
||||
<text text-anchor="middle" x="302.03" y="-39.4" font-family="DejaVu Serif, Book" font-size="12.00">.1</text>
|
||||
<text text-anchor="middle" x="117.5" y="-26.4" font-family="DejaVu Serif, Book" font-size="12.00">.10</text>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-49C108,-49 308.03,-49 308.03,-49"/>
|
||||
<text text-anchor="middle" x="161.02" y="-65.4" font-family="DejaVu Serif, Book" font-size="12.00">192.168.0.0/24</text>
|
||||
<text text-anchor="middle" x="161.02" y="-52.4" font-family="DejaVu Serif, Book" font-size="12.00">2001:db8:0::/64</text>
|
||||
<text text-anchor="middle" x="302.03" y="-52.4" font-family="DejaVu Serif, Book" font-size="12.00">.1</text>
|
||||
<text text-anchor="middle" x="117.5" y="-39.4" font-family="DejaVu Serif, Book" font-size="12.00">.10</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>host:data2--target:data2</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-13C108,-13 308.03,-13 308.03,-13"/>
|
||||
<text text-anchor="middle" x="174.02" y="-16.4" font-family="DejaVu Serif, Book" font-size="12.00">10.0.0.0/24</text>
|
||||
<text text-anchor="middle" x="302.03" y="-16.4" font-family="DejaVu Serif, Book" font-size="12.00">.1</text>
|
||||
<text text-anchor="middle" x="117.5" y="-3.4" font-family="DejaVu Serif, Book" font-size="12.00">.10</text>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M108,-26C108,-26 308.03,-26 308.03,-26"/>
|
||||
<text text-anchor="middle" x="161.02" y="-16.4" font-family="DejaVu Serif, Book" font-size="12.00">10.0.0.0/24</text>
|
||||
<text text-anchor="middle" x="161.02" y="-3.4" font-family="DejaVu Serif, Book" font-size="12.00">2001:db8:1::/64</text>
|
||||
<text text-anchor="middle" x="302.03" y="-29.4" font-family="DejaVu Serif, Book" font-size="12.00">.1</text>
|
||||
<text text-anchor="middle" x="117.5" y="-29.4" font-family="DejaVu Serif, Book" font-size="12.00">.10</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.7 KiB |
@@ -75,12 +75,12 @@ def configure_server(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-client1",
|
||||
"infix-keystore:symmetric-key": psk_client1,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client1,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}, {
|
||||
"name": "psk-client2",
|
||||
"infix-keystore:symmetric-key": psk_client2,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client2,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -226,8 +226,8 @@ def configure_client1(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-server",
|
||||
"infix-keystore:symmetric-key": psk_client1,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client1,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -360,8 +360,8 @@ def configure_client2(dut):
|
||||
"symmetric-keys": {
|
||||
"symmetric-key": [{
|
||||
"name": "psk-server",
|
||||
"infix-keystore:symmetric-key": psk_client2,
|
||||
"infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format"
|
||||
"cleartext-symmetric-key": psk_client2,
|
||||
"key-format": "ietf-crypto-types:octet-string-key-format"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,20 @@ ifdef::topdoc[:imagesdir: {topdoc}../../test/case/system/nacm-basic]
|
||||
==== Description
|
||||
|
||||
Test that NACM groups (admin, operator, guest) correctly enforce
|
||||
access control with path-based rules and default policies.
|
||||
access control with permissive defaults and targeted denials.
|
||||
|
||||
Creates three user privilege levels from scratch:
|
||||
The NACM design is "permit by default, deny sensitive items":
|
||||
|
||||
- admin: Full access (permit-all rule)
|
||||
- operator: Can manage interfaces/routing, cannot modify system config
|
||||
- guest: Read-only access (write-default: deny, exec deny rule)
|
||||
- admin: Full unrestricted access (permit-all rule)
|
||||
- operator: Can configure everything EXCEPT passwords, keystore, truststore
|
||||
- guest: Read-only access (explicit deny of create/update/delete/exec)
|
||||
|
||||
Verifies that:
|
||||
|
||||
- All users can read configuration (read-default: permit)
|
||||
- Operators can modify interfaces (path-specific permit rule)
|
||||
- Operators cannot modify system configuration (write-default: deny)
|
||||
- Guests cannot modify any configuration (write-default: deny)
|
||||
- Admin can modify all configuration (permit-all rule bypasses defaults)
|
||||
- Operators can read and modify most configuration (hostname, interfaces)
|
||||
- Operators CANNOT read or write password hashes (protected path)
|
||||
- Guests can read but cannot modify any configuration
|
||||
- Admin can access everything including passwords
|
||||
|
||||
==== Topology
|
||||
|
||||
@@ -31,9 +30,11 @@ image::topology.svg[Basic NACM permissions topology, align=center, scaledwidth=7
|
||||
. Configure NACM groups, rules, and test users
|
||||
. Verify operator can read configuration
|
||||
. Verify operator can modify interface configuration
|
||||
. Verify operator cannot modify system configuration
|
||||
. Verify operator can modify hostname
|
||||
. Verify operator cannot read password hashes
|
||||
. Verify operator cannot write password hashes
|
||||
. Verify guest can read configuration
|
||||
. Verify guest cannot modify configuration
|
||||
. Verify admin can modify configuration
|
||||
. Verify admin can access passwords
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ may be supplied in places where a hostname is otherwise expected.
|
||||
|
||||
Commands:
|
||||
|
||||
scan [-a] [<iface>]
|
||||
Discover Infix devices on the LAN using mDNS-SD (avahi-browse).
|
||||
Use -a to show all mDNS devices, not just Infix. Falls back to
|
||||
IPv6 link-local multicast ping if avahi-browse is not installed
|
||||
and <iface> is specified.
|
||||
|
||||
peer <iface>
|
||||
Return the address of the first IPv6 neighbor to respond on
|
||||
<iface>'s local LAN.
|
||||
@@ -75,6 +81,9 @@ case "$cmd" in
|
||||
help)
|
||||
usage && exit 0
|
||||
;;
|
||||
scan)
|
||||
llscan "$@"
|
||||
;;
|
||||
peer)
|
||||
llpeer "$@"
|
||||
;;
|
||||
|
||||
+152
@@ -144,3 +144,155 @@ llscp()
|
||||
|
||||
$sshpasscmd scp $LLSCP_OPTS "$src" "$dst"
|
||||
}
|
||||
|
||||
# Usage: llscan [-a] [<iface>]
|
||||
#
|
||||
# Discover Infix devices (or all mDNS devices with -a) on the LAN.
|
||||
# Prefers mDNS-SD via avahi-browse; falls back to IPv6 link-local
|
||||
# multicast ping if avahi-browse is not available and an interface
|
||||
# is specified.
|
||||
llscan()
|
||||
{
|
||||
local all=0
|
||||
local iface=
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-a)
|
||||
all=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
iface="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$iface" ] && [ ! -d "/sys/class/net/$iface" ]; then
|
||||
echo "Error: Interface \"$iface\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if command -v avahi-browse >/dev/null 2>&1; then
|
||||
llscan_mdns "$all"
|
||||
elif [ "$iface" ]; then
|
||||
echo "Note: avahi-browse not found, falling back to link-local scan." >&2
|
||||
llscan_ll "$iface"
|
||||
else
|
||||
cat >&2 <<-EOF
|
||||
Error: avahi-browse not found.
|
||||
|
||||
Install avahi-utils to scan for Infix devices via mDNS:
|
||||
sudo apt install avahi-utils
|
||||
|
||||
Or specify an interface to use IPv6 link-local fallback:
|
||||
$(basename $0) scan <iface>
|
||||
EOF
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
llscan_mdns()
|
||||
{
|
||||
local all="$1"
|
||||
local flags="-tarp"
|
||||
|
||||
if avahi-browse --help 2>&1 | grep -q -- '-k'; then
|
||||
flags="-tarpk"
|
||||
fi
|
||||
|
||||
avahi-browse $flags | awk -F';' -v show_all="$all" '
|
||||
$1 == "=" {
|
||||
host = $7
|
||||
proto = $3
|
||||
addr = $8
|
||||
txt = $10
|
||||
|
||||
on = ""; ov = ""; product = ""; serial = ""; devid = ""
|
||||
n = split(txt, parts, "\" \"")
|
||||
for (i = 1; i <= n; i++) {
|
||||
gsub(/"/, "", parts[i])
|
||||
if (parts[i] ~ /^on=/) { split(parts[i], kv, "="); on = kv[2] }
|
||||
else if (parts[i] ~ /^ov=/) { split(parts[i], kv, "="); ov = kv[2] }
|
||||
else if (parts[i] ~ /^product=/) { split(parts[i], kv, "="); product = kv[2] }
|
||||
else if (parts[i] ~ /^serial=/) { split(parts[i], kv, "="); serial = kv[2] }
|
||||
else if (parts[i] ~ /^deviceid=/) { split(parts[i], kv, "="); devid = kv[2] }
|
||||
}
|
||||
|
||||
if (!show_all && on != "Infix") next
|
||||
|
||||
# Use deviceid (MAC) as unique key; fall back to hostname
|
||||
key = devid ? devid : host
|
||||
|
||||
if (!product) product = on ? on : "-"
|
||||
if (!ov) ov = "-"
|
||||
if (!serial || serial == "null") serial = "-"
|
||||
|
||||
# Prefer IPv4 for display address
|
||||
if (proto == "IPv4") {
|
||||
ipv4[key] = addr
|
||||
} else if (proto == "IPv6" && !ipv6[key]) {
|
||||
ipv6[key] = addr
|
||||
}
|
||||
|
||||
if (!seen[key]++) {
|
||||
keys[++ndevs] = key
|
||||
hosts[key] = host
|
||||
products[key] = product
|
||||
versions[key] = ov
|
||||
serials[key] = serial
|
||||
} else if (length(host) > length(hosts[key])) {
|
||||
# Prefer the unique hostname (e.g., infix-c0-ff-ee.local)
|
||||
# over the generic one (e.g., infix.local)
|
||||
hosts[key] = host
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
if (ndevs == 0) {
|
||||
if (show_all)
|
||||
print "No mDNS devices found." | "cat >&2"
|
||||
else
|
||||
print "No Infix devices found. Use -a to show all mDNS devices." | "cat >&2"
|
||||
exit 1
|
||||
}
|
||||
|
||||
fmt = "%-26s %-18s %-24s %-22s %s\n"
|
||||
hdr = sprintf(fmt, "HOSTNAME", "ADDRESS", "PRODUCT", "VERSION", "SERIAL")
|
||||
sub(/\n$/, "", hdr)
|
||||
printf "\033[7m%s\033[0m\n", hdr
|
||||
|
||||
for (i = 1; i <= ndevs; i++) {
|
||||
k = keys[i]
|
||||
a = (ipv4[k] ? ipv4[k] : (ipv6[k] ? ipv6[k] : "-"))
|
||||
p = products[k]; if (length(p) > 23) p = substr(p, 1, 22) "~"
|
||||
v = versions[k]; if (length(v) > 21) v = substr(v, 1, 20) "~"
|
||||
printf fmt, hosts[k], a, p, v, serials[k]
|
||||
}
|
||||
|
||||
printf "\n%d device(s) found.\n", ndevs
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
llscan_ll()
|
||||
{
|
||||
local iface="$1"
|
||||
|
||||
printf "Scanning %s for IPv6 link-local neighbors ...\n\n" "$iface"
|
||||
printf "\033[7m%-6s %s\033[0m\n" "#" "ADDRESS"
|
||||
|
||||
llping "$iface" -c3 -w3 2>/dev/null | awk '
|
||||
/bytes from/ {
|
||||
sub(/:$/, "", $4)
|
||||
addr = $4
|
||||
if (!seen[addr]++) {
|
||||
printf "%-6d %s\n", ++n, addr
|
||||
}
|
||||
}
|
||||
END {
|
||||
printf "\n%d neighbor(s) found on '"$iface"'.\n", n+0
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user