diff --git a/dev/search/search_index.json b/dev/search/search_index.json
index 0a501b98..d1f8f84c 100644
--- a/dev/search/search_index.json
+++ b/dev/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Introduction","text":""},{"location":"#introduction","title":"Introduction","text":"Welcome to Infix, your immutable, friendly, and secure operating system! On these pages you can find both user and developer documentation.
Most topics on configuring the system include CLI examples, but every setting, as well as status read-back from the operational datastore, is also possible to perform using NETCONF or RESTCONF. In fact, the Infix regression test system solely relies on NETCONF and RESTCONF.
Tip
The CLI documentation is also available from inside the CLI itself using the help command in admin-exec mode.
This document provides an introduction of key concepts, details how the system boots, including failure modes, and provides links to other documents for further study.
"},{"location":"#command-line-interface","title":"Command Line Interface","text":"The command line interface (CLI, see-ell-i) is the traditional way of interacting with single network equipment like switches and routers. Today users have come to expect more advanced graphical GUIs, like a web interface, to manage a device or NETCONF-based tools that allow for managing entire fleets of installed equipment.
Nevertheless, when it comes to initial deployment and debugging, it is very useful to know how to navigate and use the CLI.
Info
For more information, see the CLI Introduction and the CLI Configuration Tutorial.
"},{"location":"#key-concepts","title":"Key Concepts","text":"The two modes in the CLI are the admin-exec and the configure context.
However, when logging in to the system, from the console port or SSH, you land in a standard UNIX shell, Bash. This is for advanced users and remote scripting purposes (production equipment):
Run the command 'cli' for interactive OAM\n\n admin@example:~$\n To enter the CLI, follow the instructions, for interactive Operations, Administration, and Management (OAM), type:
admin@example:~$ cli\n admin@example:/>\n The prompt, constructed from your username and the device's hostname, changes slightly. You are now in the admin-exec context of the CLI. Here you can inspect system status and do operations to debug networking issues, e.g. ping. You can also enter configure context by typing: configure followed by commands to set, edit, apply changes using leave, or abort and return to admin-exec.
Tip
If you haven't already, the CLI Introduction would be useful to skim through at this point.
"},{"location":"#datastores","title":"Datastores","text":"The system has several datastores (or files):
factory-config consists of a set of default configurations, some static and others generated per-device, e.g., a unique hostname and number of ports/interfaces. This file is generated at boot. failure-config is also generated at boot, from the same YANG models as factory-config, and holds the system Fail Secure Mode startup-config is created from factory-config at boot if it does not exist. It is loaded as the system configuration on each boot. running-config is what is actively running on the system. If no changes have been made since the system booted, it is the same as startup-config. candidate-config is created from running-config when entering the configure context. Any changes made here can be discarded (abort, rollback) or committed (commit, leave) to running-config. Tip
Please see the Branding & Releases document for more in-depth information on how factory-config and failure-config can be adapted to different customer requirements. Including how you can override the generated versions of these files with plain per-product ones -- this may even protect against some of the failure modes below.
"},{"location":"boot/","title":"Boot Procedure","text":""},{"location":"boot/#boot-procedure","title":"Boot Procedure","text":"Systems running Infix will typically boot in multiple phases, forming a boot chain. Each link in the chain has three main responsibilities:
Ensuring the integrity of the next link before passing control to it. This avoids silent failures stemming from data corruption. Ensuring the authenticity of the next link before passing control to it, commonly referred to as Secure Boot. This protects against malicious attempts to modify a system's firmware. Preparing the system state according to the requirements of the next link. E.g. the Linux kernel requires the system's RAM to be operational. A typical chain consists of four stages:
.---------.\n | ROM >---. Determine the location of and load the SPL\n '---------' |\n.-----------------'\n| .---------.\n'---> SPL >---. Perform DDR training and load the TPL\n '---------' |\n.-----------------'\n| .---------.\n'---> TPL >---. Load Linux kernel, device tree, and root filesystem\n '---------' |\n.-----------------'\n| .---------.\n'---> Infix | Get down to business\n '---------'\n After a reset, hardware will pass control to a program (ROM) which is almost always programmed into the SoC by the vendor. This program will determine the location of the Secondary Program Loader (SPL), typically by reading a set of Sample at Reset (SaR) pins.
The SPL is sometimes provided by the SoC vendor in binary form, and is sometimes built as a part of the Tertiary Program Loader (TPL) build. Its main responsibility is usually to set up the system's memory controller and perform DDR training, if required, before loading the TPL.
Commonly referred to as the system's bootloader, the TPL is responsible for preparing the execution environment required by the Linux kernel.
This document's focus is to describe the final two phases of the boot chain, as the initial phases are very hardware dependent, better described by existing documentation provided by the SoC vendor.
"},{"location":"boot/#bootloader","title":"Bootloader","text":""},{"location":"boot/#configuration","title":"Configuration","text":"To mitigate the risk of a malicious user being able to circumvent the bootloader's validation procedure, user configuration is kept to a minimum. Two settings are available:
Boot order: Since Infix maintains two copies of its software image, and as some bootloaders support netbooting, the order in which boot sources are considered can be configured. To select the active source, use RAUC:
root@example:~# rauc status mark-active <slot>\n...\n Where <slot> is one of:
<slot> Source rootfs.0 Primary partition rootfs.1 Secondary partition net.0 Netboot (where supported) Debug: By default, the kernel will only output errors to the console during boot. Optionally, this can be altered such that all enabled messages are logged.
On systems using U-Boot, this can be enabled by running fw_setenv DEBUG 1. To restore the default behavior, run fw_setenv DEBUG.
On systems running GRUB, this can be enabled by running:
root@example:~# grub-editenv /mnt/aux/grub/grubenv set DEBUG=1\n To restore the default behavior, run:
root@example:~# grub-editenv /mnt/aux/grub/grubenv unset DEBUG\n "},{"location":"boot/#u-boot","title":"U-Boot","text":"Used on aarch64 based systems. It is able to verify both the integrity and authenticity of an Infix image. As such, it can be used as a part of a Secure Boot chain, given that the preceding links are able to do the same.
Supports booting Infix from a block device using the Disk Image layout. Currently, Virtio and MMC disks are supported.
An FIT Framed Squash Image can be used to boot Infix over the network. DHCP is used to configure the network and TFTP to transfer the image to the system's RAM.
Access to U-Boot's shell is disabled to prevent side-loading of malicious software. To configure the active boot partition, refer to the Bootloader Configuration section.
"},{"location":"boot/#grub","title":"GRUB","text":"Used on x86_64 based systems. Neither the integrity nor the authenticity of the Infix image is verified. It is only intended to provide a way of booting a Disk Image, such that a standard System Upgrade can be performed on virtualized instances.
Access to the GRUB shell is not limited in any way, and the boot partition can be selected interactively at boot using the arrow keys. It is also possible to permanently configure the default partition from Infix using the Bootloader Configuration.
"},{"location":"boot/#system-boot","title":"System Boot","text":"After the system firmware (BIOS or and boot loader start Linux the following happens. The various failure modes, e.g., missing password in VPD, are detailed later in this section.
Before mounting /cfg and /var partitions, hosting read-writable data like startup-config and container images, the system first checks if a factory reset has been requested by the user, if so it wipes the contents of these partitions Linux boots with a device tree which is used for detecting generic make and model of the device, e.g., number of interfaces. It may also reference an EEPROM with Vital Product Data. That is where the base MAC address and per-device password hash is stored. (Generic builds use the same MAC address and password) On every boot the system's factory-config and failure-config are generated from the YANG2 models of the current firmware version. This ensures that a factory reset device can always boot, and that there is a working fail safe, or rather fail secure, mode On first power-on, and after a factory reset, the system does not have a startup-config, in which case factory-config is copied to startup-config -- if a per-product specific version exists it is preferred over the generated one Provided the integrity of the startup-config is OK, a system service loads and activates the configuration "},{"location":"boot/#failure-modes","title":"Failure Modes","text":"So, what happens if any of the steps above fail?
"},{"location":"boot/#vpd-fail","title":"VPD Fail","text":"The per-device password cannot be read, or is corrupt, so the system factory-config and failure-config are not generated:
First boot, or after factory reset: startup-config cannot be created or loaded, and failure-config cannot be loaded. The system ends up in an unrecoverable state, i.e., RMA3 Mode The system has booted (at least) once with correct VPD and password and already has a startup-config. Provided the startup-config is OK (see below), it is loaded and system boots successfully In both cases, external factory reset modes/button will not help, and in the second case will cause the device to fail on the next boot.
Note
The second case does not yet have any warning or event that can be detected from the outside. This is planned for a later release.
"},{"location":"boot/#broken-startup-config","title":"Broken startup-config","text":"If loading startup-config fails for some reason, e.g., invalid JSON syntax, failed validation against the system's YANG model, or a bug in the system's confd service, the Fail Secure Mode is triggered and failure-config is loaded (unless VPD Failure, see above).
Tip
Please see the Branding & Releases document for how to provide per-product failure-config, or factory-config to suit your product's preferences.
Fail Secure Mode is a fail-safe mode provided for debugging the system. The default4 creates a setup of isolated interfaces with communication only to the management CPU, SSH and console login using the device's factory reset password, IP connectivity only using IPv6 link-local, and device discovery protocols: LLDP, mDNS-SD. The login and shell prompt are set to failure-c0-ff-ee, the last three octets of the device's base MAC address.
"},{"location":"boot/#system-upgrade","title":"System Upgrade","text":"Much of the minutiae of software upgrades is delegated to RAUC, which offers lots of benefits out-of-the-box:
Upgrade Bundles are always signed, such that their authenticity can be verified by the running operating system, before the new one is installed. The bureaucracy of interfacing with different bootloaders, manage the boot order, is a simple matter of providing a compatible configuration. Updates can be sourced from the local filesystem (including external media like USB sticks or SD-cards) and from remote servers using FTP or HTTP(S). To initiate a system upgrade from the shell1 , run:
root@example:~# rauc install <file|url>\n...\n Where the file or URL points to a RAUC Upgrade Bundle.
This will upgrade the partition not currently running. After a successful upgrade is completed, you can reboot your system, which will then boot from the newly installed image. Since the partition from which you were originally running is now inactive, running the same upgrade command again will bring both partitions into sync.
"},{"location":"boot/#image-formats","title":"Image Formats","text":""},{"location":"boot/#squashfs-image","title":"SquashFS Image","text":"Canonical Name: rootfs.squashfs
The central read-only filesystem image containing Infix's Linux kernel, device trees, and root filesystem. All other images bundle this image, or is dependent on it, in one way or another.
On its own, it can be used as an initrd to efficiently boot a virtual instance of Infix.
"},{"location":"boot/#fit-framed-squash-image","title":"FIT Framed Squash Image","text":"Canonical Name: rootfs.itb
As the name suggests, this is essentially the Squash FS Image with a Flattened Image Tree (FIT) header. Being a native format to U-Boot, using this framing allows us to verify the integrity and authenticity of the SquashFS image using standard U-Boot primitives.
In contrast to most FIT images, the kernel and device trees are not stored as separate binaries in the image tree. Instead, Infix follows the standard Linux layout where the kernel and related files are stored in the /boot directory of the filesystem.
On disk, this image is then stored broken up into its two components; the FIT header (rootfs.itbh) and the SquashFS image. The header is stored on the Auxiliary Data partition of the Disk Image, while the SquashFS image is stored in one of the Root Filesystem partitions.
When the system boots, U-Boot will concatenate the two parts to validate the SquashFS's contents. This path was chosen because:
Having a separate raw SquashFS means Linux can directly mount it as the root filesystem.
It decouples Infix from U-Boot. If a better way of validating our image is introduced, we can switch to it without major changes to Infix's boot process, as we can still use a regular SquashFS as the root filesystem.
It lets us use standard interfaces to boot Linux, like SYSLINUX. It also plays well with traditional bootloaders, like GRUB.
In its full form, it can be used to netboot Infix, as it contains all the information needed by U-Boot in a single file.
"},{"location":"boot/#rauc-upgrade-bundle","title":"RAUC Upgrade Bundle","text":"Canonical Name: infix-${ARCH}.pkg
Itself a SquashFS image, this bundle (sometimes referred to package) contains the Infix SquashFS Image along with the header of the FIT Framed Squash Image, and some supporting files to let RAUC know how install it on the target system.
When performing a System Upgrade, this is the format to use.
"},{"location":"boot/#disk-image","title":"Disk Image","text":"Canonical Name: disk.img
Infix runs from a block device (e.g. eMMC or virtio disk) with the following layout. The disk is expected to use the GPT partitioning scheme. Partitions marked with an asterisk are optional.
.-----------.\n| GPT Table |\n:-----------:\n| boot* |\n:-----------:\n| aux |\n:-----------:\n| |\n| primary |\n| |\n:-----------:\n| |\n| secondary |\n| |\n:-----------:\n| cfg |\n:-----------:\n| |\n| var* |\n| |\n'-----------'\n "},{"location":"boot/#boot-bootloader","title":"boot - Bootloader","text":"Parameter Value Required No Size 4 MiB Format Raw binary, as dictated by the hardware Optional partition containing the system's bootloader. May also reside in a separate storage device, e.g. a serial FLASH.
On x86_64, this partition holds the EFI system partition, containing the GRUB bootloader.
"},{"location":"boot/#aux-auxiliary-data","title":"aux - Auxiliary Data","text":"Parameter Value Required Yes Size 4 MiB Format EXT4 filesystem Holds information that is shared between Infix and its bootloader, such as image signatures required to validate the chain of trust, bootloader configuration etc.
Typical layout when using U-Boot bootloader:
/\n\u251c primary.itbh\n\u251c secondary.itbh\n\u2514 uboot.env\n During boot, an ITB header along with the corresponding root filesystem image are concatenated in memory, by U-Boot, to form a valid FIT image that is used to verify its integrity and origin before any files are extracted from it.
Note that the bootloader's primary environment is bundled in the binary - uboot.env is only used to import a few settings that is required to configure the boot order.
"},{"location":"boot/#primarysecondary-root-filesystems","title":"primary/secondary - Root Filesystems","text":"Parameter Value Required Yes Size >= 256 MiB Format Squash filesystem Holds the SquashFS Image. Two copies exist so that an incomplete upgrade does not brick the system, and to allow fast rollbacks when upgrading to a new version.
"},{"location":"boot/#cfg-configuration-data","title":"cfg - Configuration Data","text":"Parameter Value Required Yes Size >= 16 MiB Format EXT4 filesystem Non-volatile storage of the system configuration and user data. Concretely, user data is everything stored under /root and /home.
"},{"location":"boot/#var-variable-data","title":"var - Variable Data","text":"Parameter Value Required No Size >= 16 MiB Format EXT4 filesystem Persistent storage for everything under /var. This is maintained as a separate filesystem from the data in cfg, because while the system can funtion reasonably well without a persistent /var, loosing /cfg or /etc is much more difficult.
If var is not available, Infix will still persist /var/lib using cfg as the backing storage.
See Upgrade & Boot Order for more information.\u00a0\u21a9
YANG is a modeling language from IETF, replacing that used for SNMP (MIB), used to describe the subsystems and properties of the system.\u00a0\u21a9
Return Merchandise Authorization (RMA), i.e., broken beyond repair by end-user and eligible for return to manufacturer.\u00a0\u21a9
Customer specific builds can define their own failure-config. It may be the same as factory-config, with the hostname set to failure, or a dedicated configuration that isolates interfaces, or even disables ports, to ensure that the device does not cause any security problems on the network. E.g., start forwarding traffic between previously isolated VLANs.\u00a0\u21a9
"},{"location":"branding/","title":"Branding & Releases","text":""},{"location":"branding/#branding-releases","title":"Branding & Releases","text":"This document is for projects using Infix as a br2-external, i.e., OEMs that want to create their own \"Spin\" of Infix.
"},{"location":"branding/#branding","title":"Branding","text":"Branding is done in menuconfig, there are several settings affecting it, most are in the Infix external subsection called \"Branding\", but there is also BR2_TARGET_GENERIC_HOSTNAME, which deserves a special mention.
The hostname is used for the system default /etc/hostname, which is the base name for the \"unique:ified\" hostname + the last three octets of the base MAC1 address, e.g., infix-c0-ff-ee. This in turn is the hostname that is set at first boot and also advertised by device discovery protocols like mDNS-SD and LLDP.
See the help texts for the Infix Branding settings to understand which ones are mandatory and which are optional, menuconfig does not check this for you and you may end up with odd results.
Verify the result after a build by inspecting:
output/images/*: names, missing prefix, etc. output/target/etc/os-release: this file is sourced by other build scripts, e.g., mkgns3a.sh. For reference, see os-release(5) Important
To get a proper GIT revision (hash) from your OS spin, remember to set in menuconfig INFIX_OEM_PATH. When unset, the Infix post-build.sh script defaults to the Infix base path. The revision is stored in the file /etc/os-release as BUILD_ID, also in the file /etc/version. See below for more info.
"},{"location":"branding/#factory-failure-config","title":"Factory & Failure Config","text":"To support booting the same image (CPU architecture) on multiple boards, Infix by default generates the device's initial configuration every time at boot. This also ensures the device can always be restored to a known state after a factory reset, since the factory-config is guaranteed to be compatible with the YANG models for the given software version. (For more information on how the system boots, please see the section Key Concepts in the Introduction document.)
However, for custom builds of Infix it is possible to override this with a single static /etc/factory-config.cfg (and failure-config) in your rootfs overlay -- with a VPD you can even support several!
"},{"location":"branding/#variables-format-specifiers","title":"Variables & Format Specifiers","text":"Parts of the configuration you likely always want to generated, like the SSH hostkey used by SSH server and NETCONF, a unique hostname, or the admin user's unique (per-device with a VPD) password hash. This section lists the available keywords, see the next section for examples of how to use them:
Default password hash: $factory$ (from VPD, .dtb, or built-in) XPath: /ietf-system:system/authentication/user/password Default SSH and NETCONF hostkey: genkey (regenerated at factory reset) XPath: /ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey'] Hostname format specifiers: XPath: /ietf-system:system/hostname %i: OS ID, from /etc/os-release, from Menuconfig branding %h: Default hostname, from /etc/os-release, from branding %m: NIC specific part of base MAC, e.g., to c0-ff-ee %%: Literal % "},{"location":"branding/#static-files","title":"Static Files","text":"Caution
Maintaining a static factory-config and failure-config may seem like an obvious choice, but as YANG models evolve (even the IETF models get upgraded), you may need to upgrade your static files.
First, for one-off builds (one image per product), the simplest way is to override the location where the system looks for the files, /etc already at build time. This can be done using a Buildroot rootfs overlay providing, e.g., /etc/factory-config.cfg. Example: NanoPi R2S in ${INFIX}/board/aarch64/r2s/rootfs/etc/factory-config.cfg.
Second, to support multiple products in a single image, we can employ another method to install a /etc/factory-config.cfg override -- at runtime. This relies on the very early system probe that detects the specific product from VPD data.
The probe consists of several sequential steps that currently run from ${INFIX}/board/common/rootfs/usr/libexec/infix/init.d/. One of them check if /usr/share/product/<PRODUCT> exists, and if so attempts to copy the entire contents to /. Here, <PRODUCT> is determined from the VPD, which is available in /run/system.json as \"product-name\", after 00-probe has run. The lower case version of the string is used.
I.e., create a rootfs overlay that provides any combination of:
/usr/share/product/<PRODUCT>/etc/factory-config.cfg /usr/share/product/<PRODUCT>/etc/failure-config.cfg "},{"location":"branding/#dynamically-generated","title":"Dynamically Generated","text":"The generated factory-config and failure-config files consist of both static JSON files and part generated files at runtime for each device. The resulting files are written to the RAM disk in /run:
/run/confd/factory-config.gen /run/confd/failure-config.gen Provided no custom overrides (see above) have been installed already, these files are then copied to:
/etc/factory-config.cfg /etc/failure-config.cfg ... where the bootstrap process expects them to be in the next step.
Examples of generated contents are the SSH hostkey and hostname. The latter is constructed from the file /etc/hostname, appended with the last three octets of the system's base MAC address. To override the base hostname, set BR2_TARGET_GENERIC_HOSTNAME in your defconfig.
The static files are installed by Infix confd in /usr/share/confd/ at build time. It contains two subdirectories:
/usr/share/confd/\n |- factory.d/\n | |- 10-foo.json\n | |- 10-bar.json\n | `- 10-qux.json\n `- failure.d/\n |- 10-xyzzy.json\n `- 10-garply.json\n To override, or extend, these files in you br2-external, set up a rootfs overlay and add it last in BR2_ROOTFS_OVERLAY. Your overlay can look something like this:
./board/common/rootfs/\n |- etc/\n | |- confdrc # See below\n | `- confdrc.local\n `- usr/\n `- share/\n `- confd/\n |- 10-foo.json # Override Infix foo\n |- 30-bar.json # Extend, probably 10-bar.json\n `- 30-fred.json # Extend, your own defaults\n Using the same filename in your overlay, here 10-foo.json, completely replaces the contents of the same file provided by Infix. If you just want to extend, or replace parts of an Infix default, use 30-....json. Here the file 30-bar.json is just a helpful hit to maintainers of your br2-external that it probably extends Infix' 10-bar.json.
The reason for the jump in numbers is that 20 is reserved for files generated by Infix' gen-function scripts. Your br2-external can provide a few custom ones that the bootstrap knows about, e.g., gen-ifs-custom that overrides 20-interfaces.json. See the bootstrap script for more help, and up-to-date information.
Tip
You may not need to provide your own /etc/confdrc for your spin. The one installed by confd is usually enough. However, if you want to adjust the behavior of bootstrap you may want to override it. There is also confdrc.local, which usually is enough to change arguments to scripts like gen-interfaces, e.g., to create a bridge by default, you may want to look into GEN_IFACE_OPTS.
"},{"location":"branding/#example-snippets","title":"Example Snippets","text":""},{"location":"branding/#ietf-system","title":"IETF System","text":" \"ietf-system:system\": {\n \"hostname\": \"example-%m\",\n \"ntp\": {\n \"enabled\": true,\n \"server\": [\n {\n \"name\": \"ntp.org\",\n \"udp\": {\n \"address\": \"pool.ntp.org\"\n }\n }\n ]\n },\n \"authentication\": {\n \"user\": [\n {\n \"name\": \"admin\",\n \"password\": \"$factory$\",\n \"infix-system:shell\": \"bash\"\n }\n ]\n },\n \"infix-system:motd-banner\": \"Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=\"\n }, # <---- REMEMBER COMMA SEPARATORS IN SNIPPETS!\n # <---- ... and no comments.\n The motd-banner is a binary type, which is basically a Base64 encoded text file without line breaks (-w0):
$ echo \"Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=\" \\\n | base64 -d\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.github.io\n'-'---'-'\n "},{"location":"branding/#ietf-keystore","title":"IETF Keystore","text":"Notice how both the public and private keys are left empty here, this cause them to be always automatically regenerated after each factory reset. Keeping the factory-config snippet like this means we can use the same file on multiple devices, without risking them sharing the same host keys. Sometimes you may want the same host keys, but that is the easy use-case and not documented here.
\"ietf-keystore:keystore\": {\n \"asymmetric-keys\": {\n \"asymmetric-key\": [\n {\n \"name\": \"genkey\",\n \"public-key-format\": \"ietf-crypto-types:ssh-public-key-format\",\n \"public-key\": \"\",\n \"private-key-format\": \"ietf-crypto-types:rsa-private-key-format\",\n \"cleartext-private-key\": \"\",\n \"certificates\": {}\n }\n ]\n }\n },\n "},{"location":"branding/#ietf-netconf-server","title":"IETF NETCONF Server","text":" \"ietf-netconf-server:netconf-server\": {\n \"listen\": {\n \"endpoints\": {\n \"endpoint\": [\n {\n \"name\": \"default-ssh\",\n \"ssh\": {\n \"tcp-server-parameters\": {\n \"local-address\": \"::\"\n },\n \"ssh-server-parameters\": {\n \"server-identity\": {\n \"host-key\": [\n {\n \"name\": \"default-key\",\n \"public-key\": {\n \"central-keystore-reference\": \"genkey\"\n }\n }\n ]\n }\n }\n }\n }\n ]\n }\n }\n },\n "},{"location":"branding/#infix-services","title":"Infix Services","text":" \"infix-services:ssh\": {\n \"enabled\": true,\n \"hostkey\": [\n \"genkey\"\n ],\n \"listen\": [\n {\n \"name\": \"ipv4\",\n \"address\": \"0.0.0.0\",\n \"port\": 22\n },\n {\n \"name\": \"ipv6\",\n \"address\": \"::1\",\n \"port\": 22\n }\n ]\n }\n "},{"location":"branding/#integration","title":"Integration","text":"When integrating your software stack with Infix there may be protocols that want to change system settings like hostname and dynamically set IP address and default gateway, e.g. PROFINET. This section detail a few recommendations for maintaining co-existence in this scenario of the multiple producers problem.
First, there's a clear difference between \"singleton\" like hostsname and an interface IP address. Consider the case of a static IP and a DHCP assigned IP, these can co-exist because of the proto NUM field available in iproute2. This is used in Infix so that static addresses can be flushed independently of DHCP addresses. The same can be done by other \"address providers\", e.g., PROFINET.
Changing properties like hostname should be done by injecting a change into Infix, by for example calling sysrepocfg -Ediff.xml. Here is an example of how to get the current hostname and apply an XML diff:
root@infix-00-00-00:~# sysrepocfg -X -x \"/system/hostname\" > hostnm.xml\nroot@infix-00-00-00:~# cat hostnm.xml\n<system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>infix-00-00-00</hostname>\n</system>\nroot@infix-00-00-00:~# edit hostnm.xml\nroot@infix-00-00-00:~# sysrepocfg -Ehostnm.xml\nroot@example:~#\n Second, perform all changes on running-config, the running datastore. That way you have a clear state to return to if your application needs to do a factory reset. E.g., in PROFINET a type 1/2 factory reset will reset only the PROFINET specific settings. That way you can actually have your system startup-config disable all physical ports and the PROFINET application enables only ports that are not deactivated. (On factory reset it will not know of any ports to deactivate so it will activate all.)
You can consider the system composed of two entities:
NETCONF starts up the system using startup-config, then Hands over control to your application at runtime Infix is prepared for this by already having two \"runlevels\" for these two states. The startup-config is applied in runlevel S (bootstrap) and the system then enters runlevel 2 for normal operation.
This allow you to keep a set of functionality that is provided by the underlying system, and another managed by your application. You can of course in your br2-external provide a sysrepo plugin that block operations on certain datastores when your application is enabled. E.g., to prevent changes to startup after initial deployment. In that case a proper factory reset would be needed to get back to a \"pre-deployment\" state where you can reconfigure your baseline.
"},{"location":"branding/#releases","title":"Releases","text":"A release build requires the global variable INFIX_RELEASE to be set. It can be derived from GIT, if the source tree is kept in GIT VCS. First, let us talk about versioning in general.
"},{"location":"branding/#versioning","title":"Versioning","text":"Two popular scheme for versioning a product derived from Infix:
Track Infix major.minor, e.g. Foobar v23.08.z, where z is your patch level. I.e., Foobar v23.08.0 could be based on Infix v23.08.0, or v23.08.12, it is up to you. Maybe you based it on v23.08.12 and then back ported changes from v23.10.0, but it was the first release you made to your customer(s). Start from v1.0.0 and step the major number every time you sync with a new Infix release, or every time Infix bumps to the next Buildroot LTS. The important thing is to be consistent, not only for your own sake, but also for your end customers. The major.minor.patch style is the most common and often recommended style, which usually maps well to other systems, e.g. PROFINET GSDML files require this (VX.Y.Z). But you can of course use only two numbers, major.minor, as well.
Warning
What could be confusing, however, is if you use the name Infix with your own versioning scheme.
"},{"location":"branding/#specifying-versioning-information","title":"Specifying Versioning Information","text":"Two optional environment variables control the version information recorded in images. Both of these must be a lower-case string (no spaces or other characters outside of 0\u20139, a\u2013z, '.', '_' and '-') identifying the operating system version, excluding any OS name information or release code name, and suitable for processing by scripts or usage in generated filenames.
"},{"location":"branding/#infix_build_id","title":"INFIX_BUILD_ID","text":"Used for BUILD_ID in /etc/os-release.
Default: $(git describe --always --dirty --tags), from the top directory. By default, the top directory refers to the root of the Infix source tree, but this can be changed by setting the branding variable INFIX_OEM_PATH, e.g. in a defconfig file or via make menuconfig, to the path of an enclosing br2-external.
"},{"location":"branding/#infix_release","title":"INFIX_RELEASE","text":"Used for VERSION and VERSION_ID in /etc/os-release and generated file names like disk images, etc.
Default: ${INFIX_BUILD_ID}
The base MAC address is defined in the device's Vital Product Data (VPD) EEPROM, or similar, which is used by the kernel to create the system interfaces. This MAC address is usually also printed on a label on the device.\u00a0\u21a9
"},{"location":"container/","title":"Docker Containers","text":""},{"location":"container/#docker-container-support","title":"Docker Container Support","text":"Infix comes with native support for Docker containers using podman. The YANG model describes the current level of support, complete enough to run both system and application containers.
Key design features of Infix, like using Linux switchdev, allow users to assign switch ports directly to containers, not just bridged VETH pairs. This is a rare and in many cases unique feature of Infix.
All network specific settings are done using the IETF interfaces YANG model, with augments for containers to ensure smooth integration with container networking in podman.
Important
Even though the podman command can be used directly from a shell prompt, we strongly recommend using the CLI commands instead. They employ the services of a wrapper container script which handles the integration of Docker containers in the system.
"},{"location":"container/#caution","title":"Caution","text":"A word of warning. Containers can run on your system in privileged mode, as root, giving them full access to devices on your system. Even though containers are fenced from the host with Linux namespaces, resource limited using cgroups, and normally run with capped privileges, a privileged container is relatively easy to break out of. A trivial example is given in the Advanced section of this document.
We recommend avoiding privileged containers, if possible (they do have valid use-cases) and instead use capabilities.
Remember:
If the system is compromised, containers can be used to easily install malicious software in your system and over the network Your system is as secure as anything you run in the container If you run containers, there is no security guarantee of any kind Running 3rd party container images on your system could open a security hole/attack vector/surface An expert with knowledge how to build exploits will be able to jailbreak/elevate to root even if best practices are followed This being said, a system suspected of being compromised can always be restored to a safe state with a factory reset. Provided, of course, that it has secure boot enabled.
"},{"location":"container/#getting-started","title":"Getting Started","text":"In the CLI, containers can be run in one of two ways:
container run IMAGE [COMMAND], or enter configure context, then edit container NAME The first is useful mostly for testing, or running single commands in an image. It is a wrapper for podman run -it --rm ....
The second creates a read-only container that by default automatically start at every boot. It basically wraps podman create ....
When non-volatile storage is needed two complementary options exist:
Volumes: data stored in a volume is persisted until explicitly removed from the configuration, i.e., across host reboots and container upgrades Content Mounts: where the content of a file mounted into the container is kept along with the container configuration in the device's startup-config Podman ensures (using tmpfs) all containers have writable directories for certain critical file system paths: /dev, /dev/shm, /run, /tmp, and /var/tmp. Meaning, what you most often need is writable volumes for /var/lib and /etc, or only file mounts for a few files in /etc. The actual requirements depend on your container image and application to run.
Important
When running containers from public registries, double-check that they support the CPU architecture of your host system. Remember, unlike virtualization, containers reuse the host's CPU and kernel.
"},{"location":"container/#example-hello-world","title":"Example: Hello World","text":"Classic Hello World:
admin@example:/> container run docker://hello-world\nStarting docker://hello-world :: use Ctrl-p Ctrl-q to detach\nTrying to pull docker.io/library/hello-world:latest...\nGetting image source signatures\nCopying blob c1ec31eb5944 done\nCopying config d2c94e258d done\nWriting manifest to image destination\nStoring signatures\n\nHello from Docker!\nThis message shows that your installation appears to be working correctly.\n "},{"location":"container/#example-web-server","title":"Example: Web Server","text":"A web server with nginx, using standard docker bridge. Podman will automatically create a VETH pair for us, connecting the container to the docker0 bridge:
admin@example:/> configure\nadmin@example:/config/> edit interface docker0\nadmin@example:/config/interface/docker0/> set container-network\nadmin@example:/config/interface/docker0/> end\nadmin@example:/config/> edit container web\nadmin@example:/config/container/web/> set image docker://nginx:alpine\nadmin@example:/config/container/web/> set network publish 8080:80\nadmin@example:/config/container/web/> set network interface docker0\nadmin@example:/config/container/web/> set volume cache target /var/cache\nadmin@example:/config/container/web/> leave\nadmin@example:/> show container\n Exit to the shell and verify the service with curl, or try to attach to your device's IP address using your browser:
admin@example:~$ curl http://localhost:8080\n or connect to port 8080 of your running Infix system with a browser. See the following sections for how to add more interfaces and manage your container at runtime.
"},{"location":"container/#container-images","title":"Container Images","text":"The underlying podman project supports importing and fetching images in a variety of ways, the most common ones are also supported by Infix. In this section we present how to use them and in the next section we show how to upgrade to a newer base image.
The CLI help shows:
admin@example:/config/container/system/> help image\nNAME\n image <string>\n\nDESCRIPTION\n Docker image for the container: [transport]name[:tag|@digest]\n\n quay.io/username/myimage -- Pull myimage:latest\n docker://busybox -- Pull busybox:latest from Docker Hub\n docker://ghcr.io/usr/img -- Pull img:latest from GitHub packages\n dir:/media/usb/myimage:1.1 -- Use myimage v1.1 from USB media\n docker-archive:/tmp/archive -- Use archive:latest from tarball\n oci-archive:/lib/oci/archive -- Use archive:latest from OCI archive\n May be in .tar or .tar.gz format\n\n Additionally, the following URIs are also supported for setups\n that do not use a HUB or similar. Recommend using 'checksum'!\n\n ftp://addr/path/to/archive -- Downloaded using wget\n http://addr/path/to/archive -- Downloaded using curl\n https://addr/path/to/archive -- Downloaded using curl\n\n Note: if a remote repository cannot be reached, the creation of the\n container will be put on a queue that retries pull every time\n there is a route change in the host's system.\n Tip
The built-in help system in the CLI is generated from the YANG model, so the same information is also available for remote NETCONF users.
The two most common variants are docker:// and oci-archive:/.
The former requires a working Docker registry and the latter operates on a plain OCI archive. Infix does not come with a built-in registry, so the docker:// option is best used with external services, which in turn require networking to be up. In a deployment phase the easiest may be to set up a single interface on your host system with DHCP client.
The default method is docker://, so when setting the image for your container, you can omit the docker:// prefix. You can also use the admin-exec command container pull docker://..., and when configuring a container podman will check first if it has the image before trying to download anything. (See also the upgrade section, below.)
The oci-archive:/ is interesting since many users may not have, or do not want to, publish their images in a registry. Use the Docker OCI exporter or any other tool that supports generating OCI Image format. Infix supports loading both .tar or .tar.gz formats.
Here we show a simple example of fetching an OCI image to the system, but many others exist, tools like wget, curl, and scp come to mind.
Shell OCI Example:
admin@example:~$ cd /var/tmp/\nadmin@example:/var/tmp$ sudo wget https://github.com/kernelkit/curiOS/releases/download/edge/curios-oci-amd64.tar.gz\nConnecting to github.com (140.82.121.3:443)\nwget: note: TLS certificate validation not implemented\nConnecting to objects.githubusercontent.com (185.199.109.133:443)\nsaving to 'curios-oci-amd64.tar.gz'\ncurios-oci-amd64.tar 100% |*********************************| 7091k 0:00:00 ETA\n'curios-oci-amd64.tar.gz' saved\nadmin@example:/var/tmp$ ll\ntotal 7104\ndrwxr-xr-x 3 root root 4096 Mar 27 14:22 ./\ndrwxr-xr-x 14 root root 4096 Mar 27 11:57 ../\n-rw-r--r-- 1 root root 7261785 Mar 27 14:22 curios-oci-amd64.tar.gz\ndrwx------ 6 frr frr 4096 Mar 27 11:57 frr/\n Importing the image into Podman can be done either from the CLI admin-exec context ...
admin@example:/var/tmp$ cli\nadmin@example:/> container load /var/tmp/curios-oci-amd64.tar.gz name curios:edge\n Tip
The name curios:edge is the tag you give the imported (raw) archive which you can then reference in your container image configuration: set image curios:edge.
... or by giving the container configuration the full path to the OCI archive, which helps greatly with container upgrades (see below):
admin@example:/config/container/system/> set image oci-archive:/var/tmp/curios-oci-amd64.tar.gz\n Checksum Example:
admin@example:/> configure\nadmin@example:/config/> edit container sys\nadmin@example:/config/container/sys/> set hostname sys\nadmin@example:/config/container/sys/> set image ftp://192.168.122.1/curios-oci-amd64-v24.05.0.tar.gz\nadmin@example:/config/container/sys/> set checksum\n md5 sha256 sha512\nadmin@example:/config/container/sys/> set checksum sha256 4f01077036527498ed910f1a3e80645ae3eff629d10043cf80ebc6850c99c629\nadmin@example:/config/container/sys/> leave\nadmin@example:/> copy running-config startup-config\nadmin@example:/> show container\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nb02e945c43c9 localhost/curios-oci-amd64-v24.05.0:latest 5 seconds ago Up 5 seconds sys\n\nadmin@example:/> show log\n...\nNov 20 07:24:56 example container[5040]: Fetching ftp://192.168.122.1/curios-oci-amd64-v24.05.0.tar.gz\nNov 20 07:24:56 example container[5040]: curios-oci-amd64-v24.05.0.tar.gz downloaded successfully.\nNov 20 07:24:56 example container[5040]: curios-oci-amd64-v24.05.0.tar.gz checksum verified OK.\nNov 20 07:24:57 example container[5040]: Cleaning up extracted curios-oci-amd64-v24.05.0\nNov 20 07:24:57 example container[5040]: podman create --name sys --conmon-pidfile=/run/container:sys.pid --read-only --replace --quiet --cgroup-parent=containers --restart=always --systemd=false --tz=local --hostname sys --log-driver k8s-file --log-opt path=/run/containers/sys.fifo --network=none curios-oci-amd64-v24.05.0\nNov 20 07:24:57 example container[3556]: b02e945c43c9bce2c4be88e31d6f63cfdb1a3c8bdd02179376eb059a49ae05e4\n "},{"location":"container/#understanding-image-tags","title":"Understanding Image Tags","text":"Docker images use tags to identify different versions of the same image. Understanding the difference between mutable and immutable tags is important for managing container upgrades effectively.
"},{"location":"container/#mutable-tags","title":"Mutable Tags","text":"Tags like :latest, :edge, or :stable are mutable \u2014 they point to different images over time as new versions are published to the registry.
Advantages:
Convenient: upgrade without changing configuration Simple: use the CLI command container upgrade NAME to get the latest version, there is even a convenient RPC for controlling the remotely Good for: development, testing, and systems that auto-update Trade-offs:
Less reproducible: different systems may run different versions Less predictable: upgrades happen when you pull, not when you plan Harder to rollback: previous version may no longer be available Example mutable tags:
docker://nginx:latest # Always points to newest release\ndocker://myapp:edge # Development/bleeding edge version\noci-archive:/var/tmp/app.tar # Local archive that may be replaced\n "},{"location":"container/#immutable-tags","title":"Immutable Tags","text":"Version-specific tags like :v1.0.1, :24.11.0, or digest references like @sha256:abc123... are immutable \u2014 they always reference the exact same image content.
Advantages:
Reproducible: all systems run identical versions Predictable: upgrades only happen when you change configuration Auditable: clear history of what ran when Good for: production, compliance, and controlled deployments Trade-offs:
More explicit: must update configuration to upgrade Requires planning: need to know which version to use Example immutable tags:
docker://nginx:1.25.3 # Specific version number\ndocker://myapp:v2.1.0 # Semantic version tag\ndocker://nginx@sha256:abc123 # Cryptographic digest (most immutable)\n Tip
Best practice for production: Use specific version tags (:v1.0.1) rather than mutable tags (:latest). This ensures all your systems run identical software and upgrades happen only when you decide.
"},{"location":"container/#upgrading-container-images","title":"Upgrading Container Images","text":"The applications in your container are an active part of the system as a whole, so make it a routine to keep your container images up-to-date!
"},{"location":"container/#how-container-lifecycle-works","title":"How Container Lifecycle Works","text":"Infix intelligently manages container lifecycles to provide a smooth experience while minimizing unnecessary work:
At first setup: When you configure a container for the first time, Infix fetches the image (if needed) and creates the container instance.
At boot time: Infix checks if the container needs to be recreated by comparing checksums for:
The image archive that the container was built from The container configuration script When configuration changes: If you modify any container settings (network, volumes, environment, etc.), the container is automatically recreated with the new configuration.
When explicitly upgraded: Using the container upgrade command forces a fresh pull of the image and recreates the container.
This means that in most cases, containers persist across reboots and are only recreated when actually necessary. Your container's state stored in volumes is preserved across recreations. Since Infix containers use a read-only root filesystem, any changes written outside of volumes or the writable paths provided by Podman (/dev, /dev/shm, /run, /tmp, /var/tmp) will be lost when the container is recreated.
"},{"location":"container/#method-1-upgrading-immutable-tags","title":"Method 1: Upgrading Immutable Tags","text":"When using version-specific tags, you upgrade by explicitly changing the image reference in your configuration:
admin@example:/> configure\nadmin@example:/config/> edit container web\nadmin@example:/config/container/web/> set image docker://nginx:1.25.3\nadmin@example:/config/container/web/> leave\n What happens:
Podman pulls the new image in the background (if not already present) Your container is automatically stopped The container is recreated with the new image The container is started with your existing volumes intact Example: Upgrading from one version to another:
admin@example:/> configure\nadmin@example:/config/> edit container system\nadmin@example:/config/container/system/> show image\nimage ghcr.io/kernelkit/curios:v24.11.0;\nadmin@example:/config/container/system/> set image ghcr.io/kernelkit/curios:v24.12.0\nadmin@example:/config/container/system/> leave\nadmin@example:/> show log\n...\nDec 13 14:32:15 example container[1523]: Pulling ghcr.io/kernelkit/curios:v24.12.0...\nDec 13 14:32:18 example container[1523]: Stopping old container instance...\nDec 13 14:32:19 example container[1523]: Creating new container with updated image...\nDec 13 14:32:20 example container[1523]: Container system started successfully\n "},{"location":"container/#method-2-upgrading-mutable-tags","title":"Method 2: Upgrading Mutable Tags","text":"For images using mutable tags like :latest or :edge, use the container upgrade command:
admin@example:/> container upgrade NAME\n This command:
Stops the running container Pulls the latest version of the image from the registry Recreates the container with the new image Starts the container automatically Example using registry:
admin@example:/> container upgrade system\nsystem\nTrying to pull ghcr.io/kernelkit/curios:edge...\nGetting image source signatures\nCopying blob 07bfba95fe93 done\nCopying config 0cb6059c0f done\nWriting manifest to image destination\nStoring signatures\n0cb6059c0f4111650ddbc7dbc4880c64ab8180d4bdbb7269c08034defc348f17\nsystem: not running.\n59618cc3c84bef341c1f5251a62be1592e459cc990f0b8864bc0f5be70e60719\n Example using local OCI archive:
An OCI archive image can be upgraded in a similar manner. First, get the new archive onto the system (see Container Images section above), then, provided the oci-archive:/path/to/archive format is used in your configuration, call the upgrade command:
admin@example:/> container upgrade system\nUpgrading container system with local archive: oci-archive:/var/tmp/curios-oci-amd64.tar.gz ...\n7ab4a07ee0c6039837419b7afda4da1527a70f0c60c0f0ac21cafee05ba24b52\n OCI archives can also be fetched from ftp/http/https URLs. In that case, the upgrade works the same way as a registry image \u2014 Infix downloads the new archive and recreates the container.
"},{"location":"container/#embedded-container-images","title":"Embedded Container Images","text":"Tip
Containers running from OCI images embedded in the operating system, e.g., /lib/oci/mycontainer.tar.gz, are automatically kept in sync with the Infix system image version.
How it works: When you build a custom Infix image with embedded OCI archives, those containers will be upgraded whenever you upgrade the Infix operating system itself. At boot, Infix checks if the embedded image has changed and automatically recreates the container if needed.
Example: default builds of Infix include a couple of OCI images for reference, one is /lib/oci/curios-nftables-v24.11.0.tar.gz, but there is also a symlink called curios-nftables-latest.tar.gz in the same directory, which is what the Infix regression tests use in the image configuration of the container. When the system is upgraded and the embedded image changes, the test containers are automatically recreated with the new version.
This approach ensures your embedded containers always match your system version without any manual intervention.
"},{"location":"container/#capabilities","title":"Capabilities","text":"An unprivileged container works for almost all use-cases, but there are occasions where they are too restricted and users start looking for the privileged flag. Capabilities offers a middle ground.
For example, a system container from which ping does not work:
admin@example:/config/container/system/> edit capabilities\nadmin@example:/config/container/system/capabilities/> set add net_raw\nadmin@example:/config/container/system/capabilities/> end\nadmin@infix-00-00-00:/config/container/system/> show\n...\ncapabilities {\n add net_raw;\n}\n...\n Infix supports a subset of all capabilities that are relevant for containers. Please note, that this is an advanced topic that require time and analysis of your container application to figure out which capabilities you need.
"},{"location":"container/#networking-and-containers","title":"Networking and Containers","text":"By default, unlike other systems, persistent2 containers have no networking enabled. All network access has to be set up explicitly. Currently two types of of container networks are supported:
host: an managed host interface, e.g., one end of a VETH pair, or even a physical interface bridge: an IP masquerading bridge In the former the interface is delegated to (moved into) the container, while in the latter a VETH pair is automatically created by Podman and one end delegated to the container, while the other end is assigned to the bridge (see the next section).
Tip
For more information on VETH pairs, see the Networking Guide.
"},{"location":"container/#container-bridge","title":"Container Bridge","text":"A container bridge is what most container setups use and users want.
The difference from a regular bridge is that the container runtime fully manages them -- connecting containers with automatically created VETH pairs (look at the bridge port names) and setting up firewall rules between the host and other containers, as well as managing port forwarding. This transparent background management is what makes container use seem to be so simple.
All interface configuration is done in configure context.
admin@example:/> configure\nadmin@example:/config> edit interface docker0\nadmin@example:/config/interface/docker0/> set container-network\nadmin@example:/config/interface/docker0/> leave\n There is more to this story. When using the CLI, and sticking to common interface nomenclature, Infix helps you with some of the boring stuff. E.g., creating a new interface with a name like brN or dockerN automatically infers the interface types, which you would otherwise have to set manually:
admin@example:/config/interface/docker0/> set type bridge\nadmin@example:/config/interface/docker0/> set container-network type bridge\n Important
When configuring the system via an API such as NETCONF or RESTCONF, no settings are inferred. Instead it is up to the caller to fully define the desired setup. This makes the CLI very useful for first setup and then extracting the resulting XML from the shell using the cfg -X command.
We have to declare the interface as a container network, ensuring the interface cannot be used by the system for any other purpose. E.g., a container host interface is supposed to be used by a container, by declaring it as such we can guarantee that it would never accidentally be added as a bridge or lag port. Hence, to move an interface currently set as a bridge-port it must be removed from the bridge before being given to a container.
The default subnet for a container bridge is 172.17.0.0/16, the bridge takes the .1 address and hand out the rest of the range to containers in a round-robin like fashion. A container with this network get an automatically created VETH pair connection to the bridge and a lot of other networking parameters (DNS, default route) are set up.
Some of the defaults of a container bridge can be changed, e.g., instead of set container-network type bridge, above, do:
admin@example:/config/interface/docker0/> edit container-network\nadmin@example:/config/interface/docker0/container-network/> set type bridge\nadmin@example:/config/interface/docker0/container-network/> edit subnet 192.168.0.0/16\nadmin@example:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> set gateway 192.168.255.254\nadmin@example:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> end\nadmin@example:/config/interface/docker0/container-network/> edit route 10.0.10.0/24\nadmin@example:/config/interface/docker0/container-network/route/10.0.10.0/24/> set gateway 192.168.10.254\nadmin@example:/config/interface/docker0/container-network/route/10.0.10.0/24/> end\nadmin@example:/config/interface/docker0/container-network/> end\nadmin@example:/config/interface/docker0/> leave\n Other network settings, like DNS and domain, use built-in defaults, but can be overridden from each container. Other common settings per container is the IP address and name of the network interface inside the container. The default, after each stop/start cycle, or reboot of the host, is to name the interfaces eth0, eth1, in the order they are given in the network list, and to give the container the next address in a bridge. Below an example of a system container calls set network interface docker0, here we show how to set options for that network:
admin@example:/config/container/ntpd/> edit network interface docker0\nadmin@example:/config/container/ntpd/network/interface/docker0/>\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option\n<string> Options for masquerading container bridges.\nadmin@example:/config/container/ntpd/network/interface/docker0/> help option\nNAME\n option <string>\n\nDESCRIPTION\n Options for masquerading container bridges.\n\n Example: ip=1.2.3.4 -- request a specific IP (IPv4 or IPv6)\n mac=00:01:02:c0:ff:ee -- set fixed MAC address in container\n interface_name=foo0 -- set interface name inside container\n\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option ip=172.17.0.2\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option interface_name=wan\nadmin@example:/config/container/ntpd/network/interface/docker0/> leave\n "},{"location":"container/#container-host-interface","title":"Container Host Interface","text":"Another common use-case is to move network interfaces into the network namespace of a container1 . This of course works with plain Ethernet interfaces as well, but here we will use one end of a VETH pair as an example.
The network option setting is available also for this case, but only the interface_name=foo0 option works. Which is still very useful. To set:
IP address, use IPv4/IPv6 settings in the interface settings MAC address, to use the custom-phys-address in the interface settings For an example of both, see the next section.
"},{"location":"container/#routed-setup","title":"Routed Setup","text":"In this routed setup we reserve 192.168.0.0/24 for the network between the host and the ntpd container.
Configuration is a straight-forward VETH pair setup where we name the container-end of pair ntpd. This is just a convenience for us when reading the configuration later. The real action happens on the last line where we declare the ntpd end as a container network interface:
admin@example:/config/> edit interface veth0\nadmin@example:/config/interface/veth0/> set veth peer ntpd\nadmin@example:/config/interface/veth0/> set ipv4 address 192.168.0.1 prefix-length 24\nadmin@example:/config/interface/veth0/> end\nadmin@example:/config/> edit interface ntpd\nadmin@example:/config/interface/ntpd/> set ipv4 address 192.168.0.2 prefix-length 24\nadmin@example:/config/interface/ntpd/> set custom-phys-address static 00:c0:ff:ee:00:01\nadmin@example:/config/interface/ntpd/> set container-network\n Tip
Notice how you can also set a custom MAC address at the same time.
Adding the interface to the container is the same as before, but since everything for host interfaces is set up in the interfaces context, we can take a bit of a shortcut.
admin@example:/config/container/ntpd/> set network interface ntpd\nadmin@example:/config/container/ntpd/> leave\n Tip
Use the set network interface ntpd option interface_name=foo0 to set the name of the interface inside the container to foo0.
The point of the routed case is that port forwarding from the container in this case is limited to a single interface, not all interfaces as is the default in the masquerading container bridge setup.
"},{"location":"container/#bridged-setup","title":"Bridged Setup","text":"A perhaps more common case is to bridge the other end of the VETH pair with other physical ports. In this section we show how to add a new pair to give our container two interfaces:
We start by adding the second VETH pair:
admin@example:/config/> edit interface veth1a\nadmin@example:/config/interface/veth1a/> set veth peer veth1\nadmin@example:/config/interface/veth1a/> set ipv4 address 192.168.1.2 prefix-length 24\n Note
The LAN bridge (br1) in this example has IP address 192.168.1.1.
When a container has multiple host interfaces it can often be useful to have a default route installed. This can be added from the host with a 0.0.0.0/0 route on one of the interfaces:
admin@example:/config/interface/veth1a/> set container-network route 0.0.0.0/0 gateway 192.168.1.1\nadmin@example:/config/interface/veth1a/> show\ntype veth;\ncontainer-network {\n type host;\n route 0.0.0.0/0 {\n gateway 192.168.1.1;\n }\n}\nveth {\n peer veth1;\n}\nadmin@example:/config/interface/veth1a/> end\nadmin@example:/config/> set interface veth1 bridge-port bridge br1\n Please note, container network routes require the base interface also have a static IP address set. Setting only the route, but no address, means the route is skipped.
"},{"location":"container/#host-networking","title":"Host Networking","text":"The third use-case is host networking, this is where a container share the network namespace of the host. An example here could be a nftables or ntpd container -- single applications which add core functionality to the host operating system.
The host networking setup cannot be combined with any other network.
For an example, see below.
"},{"location":"container/#mounts-and-volumes","title":"Mounts and Volumes","text":"It is possible to mount files, directories, and even files matching a glob, into a container. This gives precise control over the container's file system:
admin@example:/config/container/system/> edit mount leds\nadmin@example:/config/container/system/mount/leds> set source /sys/class/leds\nadmin@example:/config/container/system/mount/leds> set target /sys/class/leds\nadmin@example:/config/container/system/mount/leds> end\nadmin@example:/config/container/system/>\n Any type of file can be bind mounted into the container, just watch out for permissions though. In the example above, /sys/class/leds is not writable from a container unless it runs in privileged mode. For plain configuration files you get more freedom, and your container can rely on, e.g., inotify events to trigger reloading its services when you change the file on the host.
So it depends on the container, and indeed your overall setup, what to use. An intriguing option is Content Mounts, which when changed also trigger a container restart.
Other times volumes are a better fit. A volume is an automatically created read-writable entity that follows the life of your container.
admin@example:/config/container/ntpd/> set volume varlib target /var/lib\n Volumes are persistent across both reboots and upgrades of the base image. They are created by Podman when the container first starts up, unlike a regular bind mount it synchronizes with the contents of the underlying container image's path at first use. I.e., \"bind-mount, if empty: then rsync\".
Note
Infix support named volumes (only), and it is not possible to share a volume between containers. All the tricks possible with volumes may be added in a later release.
"},{"location":"container/#volume-management","title":"Volume Management","text":"Volumes are persistent storage that survive container restarts and image upgrades, making them ideal for application data. However, this also means they are not automatically removed when a container is deleted from the configuration.
This design choice prevents accidental data loss, especially in scenarios where:
A container is temporarily removed and re-added with the same name A container is replaced with a different configuration but same name System upgrades or configuration changes affect container definitions To clean up unused volumes and reclaim disk space, use the admin-exec command:
admin@example:/> container prune\nDeleted Images\n...\nDeleted Volumes\nntpd-varlib\nsystem-data\n\nTotal reclaimed space: 45.2MB\n The container prune command safely removes:
Unused container images Volumes not attached to any container (running or stopped) Other unused container resources Tip
You can monitor container resource usage with the command:
admin@example:/> show container usage\n This displays disk space used by images, containers, and volumes, helping you decide when to run the prune command.
To see which volumes exist and which containers use them:
admin@example:/> show container volumes\n "},{"location":"container/#content-mounts","title":"Content Mounts","text":"Content mounts are a special type of file mount where the file contents is stored with the container configuration. This can be very useful when deploying similar systems at multiple sites. When the host loads its startup-config (or even factory-config) a temporary file is created using the decoded base64 data from the content node.
admin@example:/config/container/ntpd/> edit mount ntpd.conf\nadmin@example:/config/container/ntpd/mount/ntpd.conf> text-editor content\n... interactive editor starts up ...\nadmin@example:/config/container/ntpd/mount/ntpd.conf> set target /etc/ntpd.conf\nadmin@example:/config/container/ntpd/mount/ntpd.conf> end\nadmin@example:/config/container/ntpd/>\n The editor is a small Emacs clone called Mg, see the built-in help text, or press Ctrl-x Ctrl-c to exit and save. When the editor exits the contents are base64 encoded and stored in the candidate datastore.
Note
Since these files are always recreated when the host is restarted, changes made by the container are not preserved, or saved back to the host's startup-config.
Infix has three different text editors available. For more information, see CLI Text Editor.
"},{"location":"container/#example-containers","title":"Example Containers","text":""},{"location":"container/#system-container","title":"System Container","text":"Let's try out what we've learned by setting up a system container, a container providing multiple services, using the docker0 interface we created previously:
admin@example:/> configure\nadmin@example:/config> edit container system\nadmin@example:/config/container/system/> set image ghcr.io/kernelkit/curios:edge\nadmin@example:/config/container/system/> set network interface docker0\nadmin@example:/config/container/system/> set publish 222:22\nadmin@example:/config/container/system/> leave\n Note
Ensure you have a network connection to the registry. If the image cannot be pulled, creation of the container will be put in a queue and be retried every time there is a change in the routing table, e.g., default route is added, and every 60 seconds.
Provided the image is downloaded successfully, a new system container now runs behind the docker0 interface, forwarding container port 22 to port 222 on all of the host's interfaces. (See help publish in the container configuration context for the full syntax.)
Available containers can be accessed from admin-exec:
admin@example:/> show container\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n439af2917b44 ghcr.io/kernelkit/curios:edge 41 hours ago Up 16 hours 0.0.0.0:222->222/tcp system\n This is a system container, so you can \"attach\" to it by starting a shell (or logging in with SSH):
admin@example:/> container shell system\nroot@439af2917b44:/#\n Notice how the hostname inside the container changes. By default the container ID (hash) is used, but this can be easily changed:
root@439af2917b44:/# exit\nadmin@example:/> configure\nadmin@example:/config/> edit container system\nadmin@example:/config/container/system/> set hostname sys101\nadmin@example:/config/container/system/> leave\nadmin@example:/> container shell system\nroot@sys101:/#\n In fact, the container hostname setting supports the same format specifiers as the host's hostname setting:
%i: OS ID, from /etc/os-release, from Menuconfig branding %h: Default hostname, from /etc/os-release, from branding %m: NIC specific part of base MAC, e.g., to c0-ff-ee %%: Literal % The most useful combination is probably \"container-name-%m\", which in this example give the container hostname container-name-c0-ff-ee.
"},{"location":"container/#application-container-nftables","title":"Application Container: nftables","text":"Infix currently does not have a native firewall configuration, and even when it does it will never expose the full capabilities of nftables. For advanced setups, the following is an interesting alternative.
admin@example:/> configure\nadmin@example:/config> edit container nftables\nadmin@example:/config/container/nftables/> set image ghcr.io/kernelkit/curios-nftables:edge\nadmin@example:/config/container/nftables/> set network host\nadmin@example:/config/container/nftables/> set privileged\nadmin@example:/config/container/nftables/> edit mount nftables.conf\nadmin@example:/config/container/nftables/mount/nftables.conf/> set target /etc/nftables.conf\nadmin@example:/config/container/nftables/mount/nftables.conf/> text-editor content\n... interactive editor starts up where you can paste your rules ...\nadmin@example:/config/container/nftables/mount/nftables.conf/> leave\n Notice how we set network host, so the container can see and act on all the host's interfaces, and that we also have to run the container in privileged mode.
"},{"location":"container/#application-container-ntpd","title":"Application Container: ntpd","text":"The default NTP server/client in Infix is Chrony, a fully working and capable workhorse for most use-cases. However, it does not support a feature like multicasting, for that you need ISC ntpd.
As we did with nftables, previously, we can use host networking and set up a read-only config file that is bind-mounted into the container's file system and store in the host's startup-config. However, ntpd also saves clock drift information in /var/lib/ntpd, so we will also use volumes in this example.
admin@example:/> configure\nadmin@example:/config> edit container ntpd\nadmin@example:/config/container/ntpd/> set image ghcr.io/kernelkit/curios-ntpd:edge\nadmin@example:/config/container/ntpd/> set network interface ntpd # From veth0 above\nadmin@example:/config/container/ntpd/> edit mount ntp.conf\nadmin@example:/config/container/ntpd/mount/ntp.conf/> set target /etc/ntp.conf\nadmin@example:/config/container/ntpd/mount/ntp.conf/> text-editor content\n... interactive editor starts up where you can paste your rules ...\nadmin@example:/config/container/ntpd/mount/ntp.conf/> end\nadmin@example:/config/container/ntpd/> edit volume varlib\nadmin@example:/config/container/ntpd/volume/varlib/> set target /var/lib\nadmin@example:/config/container/ntpd/volume/varlib/> leave\nadmin@example:/> copy running-config startup-config\n The ntp.conf file is stored in the host's startup-config and any state data in the container's /var/lib is retained between reboots and across image upgrades.
"},{"location":"container/#advanced","title":"Advanced","text":"This section covers advanced, and sometimes dangerous, topics. Please read any warnings and always consider the security aspects.
"},{"location":"container/#running-host-commands-from-container","title":"Running Host Commands From Container","text":"SSH login with keys is very handy, both remote scripting friendly and secure, but it does require a few extra configuration steps. The way to set it up is covered in part in SSH Authorized Key.
Another insecure approach is to access the host system directly, bypassing the namespaces that make up the boundary between host and container.
Caution
Please note, this completely demolishes the isolation barrier between container and host operating system. It is only suitable in situations where the container serves more as a unit of distribution rather than as a separate component of the system. Strongly recommended to use this only in trusted setups! Consider also limiting the time frame in which this is active!
First, enable Privileged mode, this unlocks the door and allows the container to manage resources on the host system. An example is the nftables container mentioned previously.
admin@example:/config/container/system/> set privileged\n Second, mount the host's /proc/1 directory to somewhere inside your container. Here we pick /1:
admin@example:/config/container/system/> edit mount host\nadmin@example:/config/container/system/mount/host/> set source /proc/1\nadmin@example:/config/container/system/mount/host/> set target /1\nadmin@example:/config/container/system/mount/host/> leave\n Third, from inside the container, use the host's PID 1 namespaces with the nsenter3 command to slide through the container's walls. Here we show two example calls to hostname, first the container's own name and then asking what the hostname is on the host:
root@sys101:/# hostname\nsys101\nroot@sys101:/# nsenter -m/1/ns/mnt -u/1/ns/uts -i/1/ns/ipc -n/1/ns/net hostname\nexample\n One use-case for this method is when extending Infix with a management container that connects to other systems. For some tips on how to control an Infix system this way, see Scripting Infix.
"},{"location":"container/#container-requirements","title":"Container Requirements","text":"In addition to general best practices for container images, there are a few more things to consider when targeting embedded systems:
Ensure the image targets the CPU architecture of the target system, learn more about Multi-platform Builds Follow best practices for naming and tagging, e.g., :latest vs :1.0 Follow OCI recommendations and layout, learn more about OCI and Docker Exporters If the Docker documentation is not enough, there are plenty of guides online with examples on how to create your own container image. For the more advanced, please see the next section.
"},{"location":"container/#advanced-users","title":"Advanced Users","text":"Most people prefer their system containers small, often based on Alpine Linux, or similar, with only a few small applications, including their own, and an SSH server perhaps. For some developers, even this is too big, so they roll their own from source. This section is for you.
Depending on your needs, here is a checklist:
you need something that can forward signals, e.g., tini Bash only, or BusyBox init, a classic most embedded developers know, but read on ... a system container only need the bare necessities of a system bringup E.g., BusyBox's init, but not everything Some of the networking is set up by Podman and CNI for you, but you may want to run a DHCP client? Do not rename interfaces inside the container, use the dedicated interface_name option in the configuration instead Remember, Podman provides a tmpfs for all critical system paths: /dev, /dev/shm, /run, /tmp, and /var/tmp, so you don't need to clean or set up any of these mount points Examples using tini and BusyBox init are available from the KernelKit curiOS project. It is a small Buildroot based container image builder that generates OCI compatible image tarballs without any tools from Docker or Podman -- ready-made images exist for testing on both AMD64 and ARM64 targets, as well as docker pull images and and OCI tarballs with SHA256 checksums for integrity checking.
Finally, if you build your own version of Infix, and embed OCI tarballs in the system image, then see the tip at the end of Upgrading a Container Image (above).
Something which the container bridge network type does behind the scenes with one end of an automatically created VETH pair.\u00a0\u21a9
this does not apply to the admin-exec command container run. This command is intended to be used for testing and evaluating container images. Such containers are given a private network behind an IP masquerading bridge.\u00a0\u21a9
The nsenter program is available from either the util-linux package in Debian/Ubuntu/Mint, or in BusyBox. Note, however, it may not be enabled by default in BusyBox.\u00a0\u21a9
"},{"location":"developers-guide/","title":"Developer's Guide","text":""},{"location":"developers-guide/#developers-guide","title":"Developer's Guide","text":"Please note, by default the root account is disabled in Infix NETCONF builds. Meaning, the only way to access the system is with the admin account, which is created based on credentials found in the VPD area -- for Qemu devices this is emulated using qemu_fw_cfg.
For developers this can be quite frustrating to be blocked from logging in to debug the system. So we recommend enabling the root account in the Buildroot make menuconfig system.
make menuconfig\n -> System configuration\n -> [*]Enable root login with password\n Important
Please see the Contributing section, below, for details on how to fork and clone when contributing to Infix.
"},{"location":"developers-guide/#cloning","title":"Cloning","text":"When pre-built releases are not enough, for instance when you want to add or modify some Open Source components, you can clone the Infix tree to your PC:
$ mkdir ~/Projects; cd ~/Projects\n$ git clone https://github.com/kernelkit/infix.git\n..\n$ cd infix/\n$ git submodule update --init\n..\n "},{"location":"developers-guide/#customer-builds","title":"Customer Builds","text":"Customer builds add product specific device trees, more OSS packages, e.g., Frr and podman, and sometimes integrates proprietary software. What's important to remember, however, is that they are all made by setting up Infix as a GIT submodule, similar to how Infix set up a GIT submodule for Buildroot.
So, in addition to using the customer's specific defconfig(s), one must also make sure to update all submodules, otherwise you will likely end up with a broken build.
$ ...\n$ git submodule update --init --recursive\n ~~~~~~~~~~~\n Other caveats should be documented in the customer specific trees.
"},{"location":"developers-guide/#building","title":"Building","text":"Tip
For more details, see the Getting Started and System Requirements sections of the excellent Buildroot manual.
Buildroot is almost stand-alone, it needs a few locally installed tools to bootstrap itself. The most common ones are usually part of the base install of the OS, but specific ones for building need the following. The instructions here are for Debian/Ubuntu based systems (YMMV):
$ sudo apt install bc binutils build-essential bzip2 cpio \\\n diffutils file findutils git gzip \\\n libncurses-dev libssl-dev perl patch \\\n python3 rsync sed tar unzip wget \\\n autopoint bison flex autoconf automake \\\n mtools\n To build an Infix image; select the target and then make:
make x86_64_defconfig\nmake\n Online help is available:
make help\n To see available defconfigs for supported targets, use:
make list-defconfigs\n "},{"location":"developers-guide/#test","title":"Test","text":"Working with the regression test framework, Infamy, a few more tools and services are required on your system:
$ sudo apt install jq graphviz qemu-system-x86 qemu-system-arm \\\n ethtool gdb-multiarch tcpdump tshark\n..\n To be able to build the test specification you also need:
$ sudo apt-get install python3-graphviz ruby-asciidoctor-pdf\n..\n "},{"location":"developers-guide/#documentation","title":"Documentation","text":"The documentation is written in Markdown, with GitHub extensions, and published using MkDocs, material theme. This means some features require MkDocs hinting which may not render fully when previewing on GitHub -- this is OK.
MkDocs is packaged and available to install via apt, but not all of the plugins and extensions we rely on are available, so instead we do recommend using pipx to install the necessary tooling:
$ sudo apt install pipx\n$ pipx install mkdocs\n$ pipx inject mkdocs mkdocs-material pymdown-extensions mkdocs-callouts mike mkdocs-to-pdf\n The last two packages, mike and mkdocs-to-pdf, are used for online versioning and PDF generation by GitHub Actions, but since they are in the mkdocs.yml file, everyone who wants to preview the documentation have to install all the tooling.
Preview with:
$ cd ~/src/infix/\n$ mkdocs serve\n "},{"location":"developers-guide/#development","title":"Development","text":"Developing with Infix is the same as developing with Buildroot. When working with a package, be it locally kept sources, or when using local.mk, you only want to rebuild the parts you have modified:
make foo-rebuild\n or
make foo-reconfigure\n or, as a last resort when nothing seems to bite:
make foo-dirclean foo-rebuild\n As shown here, you can combine multiple build targets and steps in one go, like this:
make foo-rebuild bar-rebuild all run\n This rebuilds (and installs) foo and bar, the all target calls on Buildroot to finalize the target filesystem and generate the images. The final run argument is explained below.
"},{"location":"developers-guide/#yang-model","title":"YANG Model","text":"When making changes to the confd and statd services, you will often need to update the YANG models. If you are adding a new YANG module, it's best to follow the structure of an existing one. However, before making any changes, always discuss them with the Infix core team. This helps avoid issues later in development and makes pull request reviews smoother.
"},{"location":"developers-guide/#confd","title":"confd","text":"The Infix src/confd/ is the engine of the system. Currently it is a plugin for systemd-plugind and contains XPath subscriptions to all the supported YANG models.
There are essentially two ways of adding support for a new YANG model:
The sysrepo way, or The Infix way, using libsrx (the lydx_*() functions) The former is well documented in sysrepo, and the latter is best taught by example, e.g., src/confd/src/infix-dhcp.c. Essentially libsrx is a way of traversing the libyang tree instead of fetching changes by XPath.
When working with confd you likely want to enable full debug mode, this is how you do it:
Open the file package/confd/confd.conf Uncomment the first line set DEBUG=1 Change the following line to add -v3 at the end[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon\n to:
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -v3 -- Configuration daemon\n Now you can rebuild confd, just as described above, and restart Infix:
make confd-rebuild all run\n "},{"location":"developers-guide/#statd","title":"statd","text":"The Infix status daemon, src/statd, is responsible for populating the sysrepo operational datastore. Like confd, it uses XPath subscriptions, but unlike confd, it relies entirely on yanger, a Python script that gathers data from local linux services and feeds it into sysrepo.
To apply changes, rebuild the image:
make python-statd-rebuild statd-rebuild all\n Rebuilding the image and testing on target for every change during development process can be tedious. Instead, yanger allows remote execution, running the script directly on the host system (test container):
infamy0:test # ../src/statd/python/yanger/yanger -x \"../utils/ixll -A ssh d3a\" ieee802-dot1ab-lldp\n ixll is a utility script that lets you run network commands using an interface name instead of a hostname. It makes operations like ssh, scp, and network discovery easier.
Normally, yanger runs commands locally to retrieve data (e.g., lldpcli when handling ieee802-dot1ab-lldp). However, when executed with -x \"../utils/ixll -A ssh d3a\" it redirects these commands to a remote system connected to the local d3a interface via SSH. This setup is used for running yanger in an interactive test environment. The yanger script runs on the host system, but key commands are executed on the target system.
For debugging or testing, you can capture system command output and replay it later without needing a live system.
To capture:
infamy0:test # ../src/statd/python/yanger/yanger -c /tmp/capture ieee802-dot1ab-lldp\n To replay:
infamy0:test # ../src/statd/python/yanger/yanger -r /tmp/capture ieee802-dot1ab-lldp\n This is especially useful when working in isolated environments or debugging issues without direct access to the DUT.
"},{"location":"developers-guide/#upgrading-packages","title":"Upgrading Packages","text":""},{"location":"developers-guide/#buildroot","title":"Buildroot","text":"The Kernelkit team maintains an internal fork of Buildroot, with branches following the naming scheme YYYY.MM.patch-kkit e.g. 2025.02.1-kkit, which means a new branch should be created whenever Buildroot is updated. These branches should contain only changes to existing packages (but no new patches), modifications to Buildroot itself or upstream backports.
The team tracks the latest Buildroot LTS (Long-Term Support) release and updates. The impact of minor LTS release upgrades is expected to have a very low impact and should be done as soon there is a patch release of a Buildroot LTS available.
Depending on your setup, follow the appropriate steps below.
"},{"location":"developers-guide/#repo-locally-cloned-already","title":"Repo locally cloned already","text":" Navigate to the Buildroot directory
cd buildroot/\n Pull the latest changes from KernelKit
git pull\n Fetch the latest tags from upstream
git fetch upstream --tags\n "},{"location":"developers-guide/#no-local-repo-yet","title":"No local repo yet","text":" Clone the Kernelkit Buildroot repository
git clone git@github.com:kernelkit/buildroot.git\n Add the upstream remote
git remote add upstream https://gitlab.com/buildroot.org/buildroot.git\n Checkout old KernelKit branch
git checkout 2025.02.1-kkit\n Note
Below, it is not allowed to rebase the branch when bumped in Infix.
"},{"location":"developers-guide/#continue-here","title":"Continue Here","text":" Create a new branch based on the previous KernelKit Buildroot release (e.g. 2025.02.1-kkit) and name it according to the naming scheme (e.g. 2025.02.2-kkit)
git checkout -b 2025.02.2-kkit\n Rebase the new branch onto the corresponding upstream release
git rebase 2025.02.2\n Push the new branch and tags
git push origin 2025.02.2-kkit --tags\n In Infix, checkout new branch of Buildroot
cd buildroot\ngit fetch\ngit checkout 2025.02.2-kkit\n Commit and push the changes. Remember to update the ChangeLog!
Create a pull request.
Note
Remember to set the pull request label to ci:main to ensure full CI coverage.
"},{"location":"developers-guide/#linux-kernel","title":"Linux kernel","text":"The KernelKit team maintains an internal fork of Linux kernel, with branches following the naming scheme kkit-linux-[version].y, e.g. kkit-6.12.y, which means a new branch should be created whenever the major kernel version is updated. This branch should contain all kernel patches used by Infix.
The team tracks the latest Linux kernel LTS (Long-Term Support) release and updates. The upgrade of LTS minor releases is expected to have low impact and should be done as soon as a patch release of the LTS Linux kernel is available.
"},{"location":"developers-guide/#repo-locally-cloned-already_1","title":"Repo locally cloned already","text":" ./utils/kernel-upgrade.sh /path/to/linux/tree Update Changelog push and create a pull request Note
Remember to set the pull request label to ci:main to ensure full CI coverage.
"},{"location":"developers-guide/#testing","title":"Testing","text":"Manual testing can be done using Qemu by calling make run, see also Infix in Virtual Environments, or on a physical device by upgrading to the latest build or \"netbooting\" and running the image from RAM. The latter is how most board porting work is done -- much quicker change-load-test cycles.
The Infix automated test suite is built around Qemu and Qeneth, see:
Regression Testing with Infamy Docker Image With any new feature added to Infix, it is essential to include relevant test case(s). See the Test Development section for guidance on adding test cases.
"},{"location":"developers-guide/#reviewing","title":"Reviewing","text":"While reviewing a pull request, you might find yourself wanting to play around with a VM running that exact version. For such occasions, gh-dl-artifact.sh is your friend in need! It employs the GitHub CLI (gh) to locate a prebuilt image from our CI workflow, download it, and prepare a local output directory from which you can launch both make run instances, and run regression tests with make test and friends.
For example, if you are curious about how PR 666 behaves in some particular situation, you can use gh to switch to that branch, from which gh-dl-artifact.sh can then download and prepare the corresponding image for execution with our normal tooling:
gh pr checkout 666\n./utils/gh-dl-artifact.sh\ncd x-artifact-a1b2c3d4-x86_64\nmake run\n Note
CI artifacts are built from a merge commit of the source and target branches. Therefore, the version in the Infix banner will not match the SHA of the commit you have checked out.
"},{"location":"developers-guide/#contributing","title":"Contributing","text":"Infix is built from many components, when contributing you need to set up your own fork, create a local branch for your change, push to your fork, and then use GitHub to create a Pull Reqeuest.
For this to work as painlessly as possible for everyone involved:
Fork Infix to your own user or organization1 Fork all the Infix submodules, e.g., kernelkit/buildroot to your own user or organization as well Clone your fork of Infix to your laptop/workstation Deactivate the Actions you don't want in your fork Please read the Contributing Guidelines as well! $ cd ~/Projects\n$ git clone https://github.com/YOUR_USER_NAME/infix.git\n...\n$ cd infix/\n$ git submodule update --init\n...\n Note
When updating/synchronizing with upstream Infix changes you may have to synchronize your forks as well. GitHub have a Sync fork button in the GUI for your fork for this purpose. A cronjob on your server of choice can do this for you with the GitHub CLI tool.
Organizations should make sure to lock the main (or master) branch of their clones to ensure members do not accidentally merge changes there. Keeping these branches in sync with upstream Infix is highly recommended as a baseline and reference. For integration of local changes another company-specific branch can be used instead.\u00a0\u21a9
"},{"location":"dhcp/","title":"DHCP Server","text":""},{"location":"dhcp/#dhcp-server","title":"DHCP Server","text":"The DHCPv4 server provides automatic IP address assignment and network configuration for clients. It supports address pools, static host assignments, and customizable DHCP options. It also serves as a DNS proxy for local subnets and can even forward queries to upstream DNS servers1 .
Note
When using the CLI, the system automatically enables essential options like DNS servers and default gateway based on the system's network configuration. These options can be disabled, changed or overridden, at any level: global, subnet, or per-host.
"},{"location":"dhcp/#basic-configuration","title":"Basic Configuration","text":"The following example configures a DHCP server for subnet 192.168.2.0/24 with an address pool:
admin@example:/> configure\nadmin@example:/config/> edit dhcp-server subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> set pool start-address 192.168.2.100 end-address 192.168.2.200\nadmin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> leave\n When setting up the server from the CLI, the system automatically adds a few default DHCP options that will be sent to clients: both DNS server and default gateway will use the system address on the matching interface.
admin@example:/> show running-config\n \"infix-dhcp-server:dhcp-server\": {\n \"subnet\": [\n {\n \"subnet\": \"192.168.2.0/24\",\n \"option\": [\n {\n \"id\": \"dns-server\",\n \"address\": \"auto\"\n },\n {\n \"id\": \"router\",\n \"address\": \"auto\"\n }\n ],\n \"pool\": {\n \"start-address\": \"192.168.2.100\",\n \"end-address\": \"192.168.2.200\"\n }\n }\n ]\n }\n Important
Remember to set up an interface in this subnet, avoid using addresses in the DHCP pool, or reserved for static hosts. In Class C networks the router usually has address .1. Depending on the use-case, you may also want to set up routing.
"},{"location":"dhcp/#static-host-assignment","title":"Static Host Assignment","text":"To reserve specific IP addresses for clients based on their MAC address, hostname, or client ID:
admin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> edit host 192.168.2.10\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> set match mac-address 00:11:22:33:44:55\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> set hostname printer\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> leave\n Match hosts using a client identifier instead of MAC address:
admin@example:/config/dhcp-server/\u2026/192.168.1.0/24/> edit host 192.168.1.50\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> edit match\nadmin@example:/config/dhcp-server/\u2026/match/> set client-id hex c0:ff:ee\nadmin@example:/config/dhcp-server/\u2026/match/> leave\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> set lease-time infinite\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> leave\n The hex prefix here ensures matching of client ID is done using the hexadecimal octets c0:ff:ee, three bytes. Without the prefix the ASCII string \"c0:ff:ee\", eight bytes, is used.
Note
The DHCP server is fully RFC conformant, in the case of option 61 this means that using the hex prefix will require the client to set the htype field of the option to 00. See RFC 2132 for details.
"},{"location":"dhcp/#custom-dhcp-options","title":"Custom DHCP Options","text":"Configure additional DHCP options globally, per subnet, or per host:
admin@example:/config/dhcp-server/> edit subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> edit option dns-server\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/dns-server/> set address 8.8.8.8\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/dns-server/> leave\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> edit option ntp-server\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/ntp-server/> set address 192.168.2.1\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/ntp-server/> leave\n When configuring, e.g., dns-server, or router options with the value auto, the system uses the IP address from the interface matching the subnet. For example:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 192.168.1.1/24 (static)\neth1 ethernet UP 02:00:00:00:00:01\n ipv4 192.168.2.1/24 (static)\n\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> edit option dns-server\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/option/dns-server/> set address auto\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/option/dns-server/> leave\n In this case, clients in subnet 192.168.1.0/24 will receive 192.168.1.1 as their DNS server address.
"},{"location":"dhcp/#multiple-subnets","title":"Multiple Subnets","text":"Configure DHCP for multiple networks:
admin@example:/> configure\nadmin@example:/config/> edit dhcp-server\nadmin@example:/config/dhcp-server/> edit subnet 192.168.1.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> set pool start-address 192.168.1.100 end-address 192.168.1.200\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> leave\nadmin@example:/config/dhcp-server/> edit subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> set pool start-address 192.168.2.100 end-address 192.168.2.200\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> leave\n "},{"location":"dhcp/#monitoring","title":"Monitoring","text":"View active leases and server statistics:
admin@example:/> show dhcp-server\nIP ADDRESS MAC HOSTNAME CLIENT ID EXPIRES\n192.168.2.22 00:a0:85:00:02:05 00:c0:ff:ee 3591s\n192.168.1.11 00:a0:85:00:04:06 foo 01:00:a0:85:00:04:06 3591s\n\nadmin@example:/> show dhcp-server statistics\nDHCP offers sent : 6\nDHCP ACK messages sent : 5\nDHCP NAK messages sent : 0\nDHCP decline messages received : 0\nDHCP discover messages received : 6\nDHCP request messages received : 5\nDHCP release messages received : 6\nDHCP inform messages received : 6\n This requires the system DNS resolver to be configured.\u00a0\u21a9
"},{"location":"discovery/","title":"Device Discovery","text":""},{"location":"discovery/#device-discovery","title":"Device Discovery","text":"Infix advertises itself via the mDNS-SD and LLDP discovery protocols. mDNS-SD has good client support in Windows, macOS and on Linux systems. More on these protocols later.
An even simpler method is available when directly attached to an Infix device:
.----. Ethernet .--------.\n| PC +---------------------+ Device |\n'----' if1 e1 '--------'\n With IPv6 you can ping the all-hosts address (ff02::1), the device's link-local IPv6 address is then seen in the response. In the following example, the PC here uses tap0 as if1, Infix responds with address fe80::ff:fec0:ffed.
linux-pc:# ping -6 -L -c 3 ff02::1%tap0\nPING ff02::1%tap0(ff02::1%tap0) 56 data bytes\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=1 ttl=64 time=0.558 ms\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=2 ttl=64 time=0.419 ms\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=3 ttl=64 time=0.389 ms\n\n--- ff02::1%tap0 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2043ms\nrtt min/avg/max/mdev = 0.389/0.455/0.558/0.073 ms\nlinux-pc:# \n Tip
The -L option ignores local responses from the PC.
This address can then be used to connect to the device, e.g., using SSH. Notice the syntax username@address%interface:
linux-pc:# ssh admin@fe80::ff:fec0:ffed%tap0\nadmin@fe80::ff:fec0:ffed%tap0's password: admin\nadmin@infix-c0-ff-ee:~$ \n "},{"location":"discovery/#lldp","title":"LLDP","text":"Infix supports LLDP (IEEE 802.1AB). For a device with factory default settings, the link-local IPv6 address can be read from the Management Address TLV using tcpdump or other sniffing tools1 :
linux-pc:# tcpdump -i tap0 -Qin -v ether proto 0x88cc\ntcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n15:51:52.061071 LLDP, length 193\n Chassis ID TLV (1), length 7\n Subtype MAC address (4): 02:00:00:c0:ff:ee (oui Unknown)\n Port ID TLV (2), length 7\n Subtype MAC address (3): 02:00:00:c0:ff:ee (oui Unknown)\n Time to Live TLV (3), length 2: TTL 120s\n System Name TLV (5), length 14: infix-c0-ff-ee\n System Description TLV (6), length 91\n Infix by KernelKit Linux 5.19.17 #1 SMP PREEMPT_DYNAMIC Wed Jun 7 08:47:23 CEST 2023 x86_64\n System Capabilities TLV (7), length 4\n System Capabilities [Bridge, WLAN AP, Router, Station Only] (0x009c)\n Enabled Capabilities [Station Only] (0x0080)\n Management Address TLV (8), length 24\n Management Address length 17, AFI IPv6 (2): fe80::ff:fec0:ffed\n Interface Index Interface Numbering (2): 2\n Port Description TLV (4), length 4: eth0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n Link aggregation Subtype (3)\n aggregation status [supported], aggregation port ID 0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n MAC/PHY configuration/status Subtype (1)\n autonegotiation [none] (0x00)\n PMD autoneg capability [unknown] (0x8000)\n MAU type Unknown (0x0000)\n End TLV (0), length 0\n^C\n1 packet captured\nlinux-pc:# \n If the device has an IPv4 address assigned, it is shown in an additional Management Address TLV.
Note
The Management Addresses shown by LLDP are not necessarily associated with the port transmitting the LLDP message.
In the example below, the IPv4 address (10.0.1.1) happens to be assigned to eth0, while the IPv6 address (2001:db8::1) is not.
linux-pc:# sudo tcpdump -i tap0 -Qin -v ether proto 0x88cc\ntcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n15:46:07.908665 LLDP, length 207\n Chassis ID TLV (1), length 7\n Subtype MAC address (4): 02:00:00:c0:ff:ee (oui Unknown)\n Port ID TLV (2), length 7\n Subtype MAC address (3): 02:00:00:c0:ff:ee (oui Unknown)\n Time to Live TLV (3), length 2: TTL 120s\n System Name TLV (5), length 14: infix-c0-ff-ee\n System Description TLV (6), length 91\n Infix by KernelKit Linux 5.19.17 #1 SMP PREEMPT_DYNAMIC Wed Jun 7 08:47:23 CEST 2023 x86_64\n System Capabilities TLV (7), length 4\n System Capabilities [Bridge, WLAN AP, Router, Station Only] (0x009c)\n Enabled Capabilities [Station Only] (0x0080)\n Management Address TLV (8), length 12\n Management Address length 5, AFI IPv4 (1): 10.0.1.1\n Interface Index Interface Numbering (2): 2\n Management Address TLV (8), length 24\n Management Address length 17, AFI IPv6 (2): 2001:db8::1\n Interface Index Interface Numbering (2): 3\n Port Description TLV (4), length 4: eth0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n Link aggregation Subtype (3)\n aggregation status [supported], aggregation port ID 0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n MAC/PHY configuration/status Subtype (1)\n autonegotiation [none] (0x00)\n PMD autoneg capability [unknown] (0x8000)\n MAU type Unknown (0x0000)\n End TLV (0), length 0\n^C\n1 packet captured\n2 packets received by filter\n0 packets dropped by kernel\nlinux-pc:#\n The following capabilities are available via NETCONF/RESTCONF or the Infix CLI.
"},{"location":"discovery/#lldp-enabledisable","title":"LLDP Enable/Disable","text":"The LLDP service can be disabled using the following commands.
admin@infix-c0-ff-ee:/> configure\nadmin@infix-c0-ff-ee:/config/> no lldp \nadmin@infix-c0-ff-ee:/config/> leave\nadmin@infix-c0-ff-ee:/> \n To reenable it from the CLI config mode:
admin@test-00-01-00:/config/> set lldp enabled \nadmin@test-00-01-00:/config/> leave\n "},{"location":"discovery/#lldp-message-transmission-interval","title":"LLDP Message Transmission Interval","text":"By default, LLDP uses a message-tx-interval of 30 seconds, as defined by the IEEE standard. Infix allows this value to be customized. To change it using the CLI:
admin@test-00-01-00:/config/> set lldp message-tx-interval 1\nadmin@test-00-01-00:/config/> leave\n "},{"location":"discovery/#lldp-administrative-status-per-interface","title":"LLDP Administrative Status per Interface","text":"Infix supports configuring the LLDP administrative status on a per-port basis. The default mode is tx-and-rx, but the following options are also supported:
rx-only \u2013 Receive LLDP packets only tx-only \u2013 Transmit LLDP packets only disabled \u2013 Disable LLDP on the interface Example configuration:
admin@test-00-01-00:/config/> set lldp port e8 dest-mac-address 01:80:C2:00:00:0E admin-status disabled\nadmin@test-00-01-00:/config/> set lldp port e5 dest-mac-address 01:80:C2:00:00:0E admin-status rx-only \nadmin@test-00-01-00:/config/> set lldp port e6 dest-mac-address 01:80:C2:00:00:0E admin-status tx-only\nadmin@test-00-01-00:/config/> leave\n Note
The destination MAC address must be the standard LLDP multicast address: 01:80:C2:00:00:0E.
"},{"location":"discovery/#displaying-lldp-neighbor-information","title":"Displaying LLDP Neighbor Information","text":"In CLI mode, Infix also provides a convenient show lldp command to list LLDP neighbors detected on each interface:
admin@test-00-01-00:/> show lldp \nINTERFACE REM-IDX TIME CHASSIS-ID PORT-ID \ne5 1 902 00:a0:85:00:04:01 00:a0:85:00:04:07 \ne6 3 897 00:a0:85:00:03:01 00:a0:85:00:03:07 \ne8 2 901 00:a0:85:00:02:01 00:a0:85:00:02:05\n "},{"location":"discovery/#mdns-sd","title":"mDNS-SD","text":"DNS-SD/mDNS-SD can be used to discover Infix devices and services. By default, Infix use the .local domain for advertising services. Some networks use .lan instead, so this configurable:
admin@infix-c0-ff-ee:/> configure\nadmin@infix-c0-ff-ee:/config/> edit mdns\nadmin@infix-c0-ff-ee:/config/mdns/> set domain lan\n Other available settings include limiting the interfaces mDNS responder acts on:
admin@infix-c0-ff-ee:/config/> set interfaces allow e1\n or
admin@infix-c0-ff-ee:/config/> set interfaces deny wan\n The allow and deny settings are complementary, deny always wins.
In Linux, tools such as avahi-browse or mdns-scan2 can be used to search for devices advertising their services via mDNS.
linux-pc:# avahi-browse -ar\n+ tap0 IPv6 infix-c0-ff-ee SFTP File Transfer local\n+ tap0 IPv4 infix-c0-ff-ee SFTP File Transfer local\n+ tap0 IPv6 infix-c0-ff-ee SSH Remote Terminal local\n+ tap0 IPv4 infix-c0-ff-ee SSH Remote Terminal local\n= tap0 IPv4 infix-c0-ff-ee SFTP File Transfer local\n hostname = [infix-c0-ff-ee.local]\n address = [10.0.1.1]\n port = [22]\n txt = []\n= tap0 IPv4 infix-c0-ff-ee SSH Remote Terminal local\n hostname = [infix-c0-ff-ee.local]\n address = [10.0.1.1]\n port = [22]\n txt = []\n= tap0 IPv6 infix-c0-ff-ee SFTP File Transfer local\n hostname = [infix-c0-ff-ee.local]\n address = [fe80::ff:fec0:ffed]\n port = [22]\n txt = []\n= tap0 IPv6 infix-c0-ff-ee SSH Remote Terminal local\n hostname = [infix-c0-ff-ee.local]\n address = [fe80::ff:fec0:ffed]\n port = [22]\n txt = []\n^C\nlinux-pc:#\n Tip
The -t option is also very useful, it stops browsing automatically when a \"more or less complete list\" has been printed. However, some devices on the LAN may be in deep sleep so run the command again if you cannot find the device you are looking for.
Additionally, avahi-resolve-host-name can be used to verify domain name mappings for IP addresses. By default, it translates from IPv4 addresses. This function allows users to confirm that addresses are mapped correctly.
linux-pc:# avahi-resolve-host-name infix-c0-ff-ee.local\ninfix-c0-ff-ee.local 10.0.1.1\nlinux-pc:#\n Thanks to mDNS we can use the advertised name instead of the IP address for operations like ping and ssh as shown below:
linux-pc:# ping infix-c0-ff-ee.local -c 3\nPING infix-c0-ff-ee.local (10.0.1.1) 56(84) bytes of data.\n64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.852 ms\n64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=1.12 ms\n64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=1.35 ms\n\n--- infix-c0-ff-ee.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 0.852/1.105/1.348/0.202 ms\n\nlinux-pc:# ssh admin@infix-c0-ff-ee.local\n(admin@infix-c0-ff-ee.local) Password: \n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-\n\nRun the command 'cli' for interactive OAM\n\nlinux-pc:#\n To disable mDNS/mDNS-SD, type the commands:
admin@infix-c0-ff-ee:/> configure \nadmin@infix-c0-ff-ee:/config/> no mdns\nadmin@infix-c0-ff-ee:/config/> leave\n "},{"location":"discovery/#human-friendly-hostname-alias","title":"Human-Friendly Hostname Alias","text":"Each Infix deviuce advertise itself as infix.local, in addition to its full hostname (e.g., infix-c0-ff-ee.local or foo.local). This alias works seamlessly on a network with a single Infix device, and makes it easy to connect when the exact hostname is not known in advance. The examples below show how the alias can be used for actions such as pinging or establishing an SSH connection:
linux-pc:# ping infix.local -c 3\nPING infix.local (10.0.1.1) 56(84) bytes of data.\n64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.751 ms\n64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=2.28 ms\n64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=1.42 ms\n\n--- infix.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 0.751/1.482/2.281/0.626 ms\n\nlinux-pc:# ssh admin@infix.local\n(admin@infix.local) Password: \n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-c0-ff-ee:~$\n When multiple Infix devices are present on the LAN the alias will not uniquely identify a device; infix.local will refer to any of the Infix devices, likely the one that first appeared.
Note
When multiple Infix devices are present on the LAN, use the full name, e.g., infix-c0-ff-ee.local or foo.local rather than the alias infix.local to deterministically connect to the device.
"},{"location":"discovery/#browse-network-using-networklocal","title":"Browse Network Using network.local","text":"Another mDNS alias that all Infix devices advertise is network.local. This is a web service which basically runs avahi-browse and displays a table of other Infix devices and their services.
With multiple Infix devices on the LAN, one will take the role of your portal to access all others, if it goes down another takes its place.
To disable the netbrowse service, and the network.local alias, the following commands can be used:
admin@infix-c0-ff-ee:/> configure \nadmin@infix-c0-ff-ee:/config/> edit web\nadmin@infix-c0-ff-ee:/config/web/> no netbrowse\nadmin@infix-c0-ff-ee:/config/web/> leave\n E.g., lldpd which includes the lldpcli too, handy to sniff and display LLDP packets.\u00a0\u21a9
mdns-scan: a tool for scanning for mDNS/DNS-SD services on the local network.\u00a0\u21a9
"},{"location":"eth-counters/","title":"RMON Counters","text":""},{"location":"eth-counters/#rmon-counters","title":"RMON Counters","text":"This document show the mapping between YANG and Linux / Ethtool counters.
YANG Linux / Ethtool in-total-octets FramesReceivedOK, FrameCheckSequenceErrors, FramesLostDueToIntMACRcvError, AlignmentErrors, etherStatsOversizePkts, etherStatsJabbers in-frames FramesReceivedOK in-multicast-frames MulticastFramesReceivedOK in-broadcast-frames BroadcastFramesReceivedOK in-error-fcs-frames FrameCheckSequenceErrors in-error-undersize-frames undersize_pkts in-error-oversize-frames etherStatsJabbers, etherStatsOversizePkts in-error-mac-internal-frames FramesLostDueToIntMACRcvError out-frames FramesTransmittedOK out-multicast-frames MulticastFramesXmittedOK out-broadcast-frames BroadcastFramesXmittedOK infix-eth:out-good-octets OctetsTransmittedOK infix-eth:in-good-octets OctetsReceivedOK"},{"location":"firewall/","title":"Firewall Configuration","text":""},{"location":"firewall/#firewall-documentation","title":"Firewall Documentation","text":""},{"location":"firewall/#introduction","title":"Introduction","text":"A zone-based firewall aims to simplify network security. Instead of complex per-interface rules, you work with zones and policies. Briefly, zones define a level of trust for all interfaces or networks assigned to it, and policies regulate the traffic flow between zones.
Figure 1. Zones group interfaces, policies control traffic flows.
Three distinct traffic flows exist: traffic destined for the host itself, traffic between interfaces within the same zone (intra-zone), and traffic between different zones (inter-zones).
The zone approach is not just more intuitive and maintainable, it allows you to think more in terms of trust relationships:
\"internal networks can access the Internet\" \"Internet cannot access my internal network, except this port forward\" When you add new interfaces to existing zones, they automatically inherit the established security policies. The amount of actual rules that matter to you is kept to a minimum.
Impatient and ready to get going?
Fast forward to the Examples: End Device, Home/Office Router, Enterprise Gateway
"},{"location":"firewall/#visual-overview","title":"Visual Overview","text":"Use the zone matrix to quickly audit your firewall configuration and identify potential security gaps. It provides an overview and shows the relationship between zones and the policies that connect them. Each cell in the matrix represents a potential traffic flow, with rows indicating the ingress zone and columns the egress zone.
Figure 2. Example output from show firewall command.
The matrix uses visual indicators to show the status of each zone and policy:
\u2713 Green checkmark \u2014 traffic is explicitly allowed by policy \u2717 Red cross \u2014 traffic is blocked (default behavior) \u26a0 Yellow warning \u2014 watch out! Some traffic allowed, such as port forwarding rules
This visualization helps you quickly understand your firewall's behavior and identify any unintended gaps or overly permissive rules in your configuration.
Use the ? key in the CLI
This admin-exec (top-level) CLI command has sub-commands that you can use to drill down on the operational data. Tap the ? key once to see an overview after show firewall, or just use the classic UNIX Tab key to complete everything until you've found your command.
"},{"location":"firewall/#zones","title":"Zones","text":"Zones are logical groupings of network interfaces or IP networks that share the same trust level. Each zone has a default action that determines what happens to traffic destined for the host itself (INPUT chain). A LAN zone may have this set to accept, while a DMZ zone may be set to reject by default and only allow a subset of available services (e.g., DHCP, DNS, SSH) that devices in the DMZ can use to reach the host.
Important
Interfaces and networks are mutually exclusive in zones \u2014 attempting to configure both will result in a validation error. When setting up port forwarding from one zone to another, the destination network must be declared in a zone.
"},{"location":"firewall/#default-zone","title":"Default Zone","text":"You must specify a default zone. This serves as a safety net: any interface not explicitly assigned to a zone automatically belongs to the default zone, ensuring network interfaces remain protected by the firewall. This automatic assignment is particularly useful when configuring new interfaces (e.g., VLANs, bridges, or hotplugged devices).
Choose your default zone carefully \u2014 it should be the most restrictive zone appropriate for unmanaged interfaces. For routers, this is typically the wan zone, but you can of course also set up a dedicated block zone. In the CLI, when first enabling the firewall, a public zone is created. See more about this in the example below.
Remember IP forwarding on interfaces!
Firewall policies only control whether traffic is allowed on input, to be forwarded, or blocked (default). For the actual routing between interfaces to work, you must also enable IP forwarding on the relevant interfaces.
"},{"location":"firewall/#intra-zone-traffic","title":"Intra-Zone Traffic","text":"Traffic between different interfaces, or networks, in the same zone is not forwarded by default. In most cases, if devices on separate interfaces need to communicate, they should be in different zones with a policy between them. Alternatively, if you want true LAN-like behavior, bridge the interfaces at layer-2 instead of routing between them at layer-3.
Intra-zone forwarding \u2014 routing traffic within a single zone \u2014 is rarely needed. But if you do require it, create a policy where both ingress and egress are set to the same zone, e.g., lan \u2192 lan.
"},{"location":"firewall/#port-forwarding","title":"Port Forwarding","text":"Port forwarding, also known as destination NAT (DNAT), redirects inbound traffic to another address and/or port. This allows external access to internal services. See Enterprise Gateway for an example.
Each zone can have port forwarding rules that apply to traffic arriving at that zone's interfaces or matching its networks. The forwarded traffic must then be allowed by appropriate policies to reach the destination zone.
The Zone Matrix shows a \u26a0 conditional warning flag, coloring the cell yellow, when exceptions like port forwarding are active.
"},{"location":"firewall/#policies","title":"Policies","text":"In short, policy rules control traffic between zones. By default all inter-zone (and intra-zone) traffic is rejected. Meaning you must explicitly allow the traffic flows you intend.
Figure 3. Example of common traffic flows (policies) between zones.
IP masquerading (SNAT) is a policy setting that applies to traffic egressing a target zone. (Essential for Internet access from private networks.)
A policy, like zones, have a default action. If it is not set to accept you must specify which services on the host any zone interface and network are allowed access to.
Note
Policy rules apply in-order, the first matching rule with action drop will terminate the traffic flow. Use action continue to allow processing to go to the next rule, until the last (implicit) default-drop rule at the end.
The CLI currently does not support reordering rules. As a workaround, save your running-config to startup-config, then exit to the shell and edit the file with edit /cfg/startup-config.cfg.
See the examples below for how to set up a policy. The built-in help system can also be useful:
admin@example:/config/firewall/policy/lan-to-dmz/> help masquerade\nNAME\n masquerade \nDESCRIPTION\n Enable masquerading (SNAT) for traffic matching this policy.\n Matching traffic will have their source IP address changed on egress,\n using the IP address of the interface the traffic egresses.\";\nadmin@example:/config/firewall/policy/lan-to-dmz/>\n
"},{"location":"firewall/#symbolic-names","title":"Symbolic Names","text":"The symbolic names HOST and ANY are available for use in both ingress and egress zones. In fact, the CLI uses inference when first enabling the firewall to inject a default policy to allow automatic IPv6 address assignment.
HOST \u2192 ANY: Control device to any outbound connection (default: allowed) ANY \u2192 HOST: Control what can reach device services (uncommon, usually per-zone) Zone \u2192 HOST: Allow specific zone to access device services "},{"location":"firewall/#custom-filters","title":"Custom Filters","text":"For more advanced firewall scenarios custom filters can be used. Currently only various ICMP type traffic control is supported. Enough to support the built-in allow-host-ipv6 policy and allow certain ICMP types on input or forward.
You can inspect this built-in (locked) policy from admin-exec level with the command: show firewall policy allow-host-ipv6.
"},{"location":"firewall/#default-behavior","title":"Default Behavior","text":"ICMP messages (particularly echo-request and echo-reply) can be used to reveal network information for malicious purposes. Therefore, the firewall blocks ICMP requests by default. This applies unless the zone's default action is accept.
To enable echo-request (IPv4) for any interface, or per zone when action is set to drop or reject, set up a dedicated policy with ingress ANY and egress HOST that use a custom filter to accept that ICMP type. Make this policy the first rule in your list of policies, and remember to use continue for non-matching traffic.
Another built-in behavior is automatically allowing \"established,related\" return traffic flows. This uses connection tracking - the firewall remembers outbound connections you initiate and automatically allows the corresponding inbound response traffic. This means you only need to configure rules for new connections; the firewall handles return traffic automatically without additional rules.
"},{"location":"firewall/#services","title":"Services","text":"Several pre-defined services exist, that cover most use-cases, but you can also define custom services for applications not covered by the built-in ones.
The firewall includes over 100 pre-defined services, such as:
ssh \u2014 Secure Shell (port 22/tcp) http \u2014 Web traffic (port 80/tcp) https \u2014 Secure web traffic (port 443/tcp) dns \u2014 Domain Name System (port 53/tcp and 53/udp) dhcp \u2014 DHCP server (port 67/udp) dhcpv6-client \u2014 DHCPv6 client traffic netconf \u2014 Network Configuration Protocol (port 830/tcp) restconf \u2014 REST-based Network Configuration Protocol (port 443/tcp) Use the ? key in the CLI
See the YANG model for the full list, or tap the ? key when setting up an allowed host service in a zone set service
"},{"location":"firewall/#examples","title":"Examples","text":""},{"location":"firewall/#end-device-protection","title":"End Device Protection","text":"This is the default firewall setup, useful for end devices on untrusted networks. It provides maximum protection while allowing essential connectivity.
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> show\ndefault public;\nzone public {\n action reject;\n description \"Public, unknown network. Only SSH and DHCPv6 client allowed.\";\n service dhcpv6-client;\n service ssh;\n}\nadmin@example:/config/firewall/> leave\n
The reject action differs from drop in that it responds to ICMP messages, although maybe not how you may think. Pinging the device we may1 see this:
$ ping 192.168.122.161\nFrom 192.168.122.161 icmp_seq=1 Packet filtered\n
If we run tcpdump it shows us why:
$ tcpdump -lni eth0\n20:10:40.245707 IP 192.168.122.1 > 192.168.122.161: ICMP echo request, id 56838, seq 1, length 64\n20:10:40.245961 IP 192.168.122.161 > 192.168.122.1: ICMP host 192.168.122.161 unreachable - admin prohibited filter, length 92\n
The key here is that, yes the device responds, but not with ICMP reply but ICMP unreachable, and a little helpful message.
The default zone is public, so all interfaces that are not explicitly assigned to another zone will be operationally placed in this zone as a safeguard. Inspect this from admin-exec context with show firewall, as can be seen in the below screenshot, the only interface e1 has been assigned automatically to the public zone. This information is also saved to the system log.
Figure 4. Zone matrix and firewall overview from show firewall.
Important
These defaults are inferred for interactive CLI users. Enabling the firewall using NETCONF/RESTCONF will not yield the same results.
"},{"location":"firewall/#homeoffice-router","title":"Home/Office Router","text":"For typical routers that need to protect internal devices while providing internet access. The LAN zone trusts internal devices, while the WAN zone blocks external threats.
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> set default wan\nadmin@example:/config/firewall/> edit zone lan\nadmin@example:/config/firewall/\u2026/lan/> set description \"Internal LAN network - trusted\"\nadmin@example:/config/firewall/\u2026/lan/> set action accept\nadmin@example:/config/firewall/\u2026/lan/> set interface eth1\nadmin@example:/config/firewall/\u2026/lan/> set service ssh\nadmin@example:/config/firewall/\u2026/lan/> set service dhcp\nadmin@example:/config/firewall/\u2026/lan/> set service dns\nadmin@example:/config/firewall/\u2026/lan/> end\nadmin@example:/config/firewall/> edit zone wan\nadmin@example:/config/firewall/\u2026/wan/> set description \"External WAN interface - untrusted\"\nadmin@example:/config/firewall/\u2026/wan/> set action drop\nadmin@example:/config/firewall/\u2026/wan/> set interface eth0\nadmin@example:/config/firewall/\u2026/wan/> end\nadmin@example:/config/firewall/> edit policy loc-to-wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set description \"Allow LAN traffic to WAN with SNAT\"\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set ingress lan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set egress wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set action accept\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set masquerade\nadmin@example:/config/firewall/\u2026/loc-to-wan/> leave\n
"},{"location":"firewall/#enterprise-gateway","title":"Enterprise Gateway","text":"For businesses that need to host public services while protecting internal resources. We can build upon the Home/Office Router example above and add a DMZ zone with additional policies for controlled access.
admin@example:/> configure\nadmin@example:/config/> edit firewall zone dmz\nadmin@example:/config/firewall/\u2026/dmz/> set description \"Semi-trusted public services\"\nadmin@example:/config/firewall/\u2026/dmz/> set action drop\nadmin@example:/config/firewall/\u2026/dmz/> set interface eth1\nadmin@example:/config/firewall/\u2026/dmz/> set service ssh\nadmin@example:/config/firewall/\u2026/dmz/> end\nadmin@example:/config/firewall/> edit policy loc-to-wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set description \"Allow local networks (LAN+DMZ) to WAN with SNAT\"\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set ingress dmz\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set egress wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set action accept\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set masquerade\nadmin@example:/config/firewall/\u2026/loc-to-wan/> end\nadmin@example:/config/firewall/> edit policy lan-to-dmz\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set description \"Allow LAN to manage DMZ services\"\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set ingress lan\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set egress dmz\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set action accept\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> end\nadmin@example:/config/firewall/> edit zone wan port-forward 8080 tcp\nadmin@example:/config/firewall/\u2026/tcp/> set to addr 192.168.2.10\nadmin@example:/config/firewall/\u2026/tcp/> set to port 80\nadmin@example:/config/firewall/\u2026/tcp/> leave\n
This adds a DMZ zone for public services, updates the internet access policy to include DMZ traffic, allows LAN management of DMZ services, and forwards external web traffic to the DMZ server.
"},{"location":"firewall/#logging-and-monitoring","title":"Logging and Monitoring","text":"Different log levels are available to monitor and debug firewall behavior. Configure logging using the CLI:
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> set logging all\nadmin@example:/config/firewall/> leave\n
Firewall logs help you understand traffic patterns and security events. The CLI admin-exec command show firewall shows the last 10 log messages in the overview:
Figure 5. Summary of recent logs at the end of show firewall.
Use the command show log firewall.log to display the full logfile (remember, the syslog daemon rotates and zips too big log files). You can also use the follow firewall.log command to continuously monitor firewall log messages.
"},{"location":"firewall/#netfilter-integration","title":"Netfilter Integration","text":"The Infix firewall operates through Linux netfilter hooks. Understanding how the zones and policy concepts map to these hooks will hopefully help you understand the firewall's behavior and ease troubleshooting.
"},{"location":"firewall/#packet-flow","title":"Packet Flow","text":"Figure 6. Linux netfilter hooks in layer-3 traffic flow.
Netfilter Hook Function Description prerouting ZONE Classification of incoming traffic, match interfaces/networks with zones prerouting ZONE Port forwarding (DNAT) from zone configuration input ZONE Host input filtering (services) input ZONE Default action for non-matching services (action) forward POLICY Allow traffic between zones (inter-zone rules) postrouting POLICY Masquerade (SNAT) when traffic egresses a zone"},{"location":"firewall/#prerouting-hook","title":"PREROUTING Hook","text":" Zone Classification: Traffic is tagged based on ingress interface or source network Port Forwarding: DNAT from zone configuration occurs before routing decisions Connection Tracking: Early state establishment for stateful filtering "},{"location":"firewall/#input-hook","title":"INPUT Hook","text":" ANY-to-HOST Policies: Enforces policy rules for traffic destined to the host itself Zone Services: Allows configured services (SSH, HTTP, etc.) based on zone trust level Zone Action: Applies a default action (accept/reject/drop) for unmatched traffic "},{"location":"firewall/#forward-hook","title":"FORWARD Hook","text":" Policy Enforcement: Primary location for inter-zone traffic filtering Custom Filters: ICMP and other protocol-specific rules within policies Service Matching: Allows or denies services based on policy configuration "},{"location":"firewall/#postrouting-hook","title":"POSTROUTING Hook","text":" Masquerading: Source NAT for outbound traffic when policies enable masquerading "},{"location":"firewall/#emergency-lockdown","title":"Emergency Lockdown","text":"For security emergencies (active breaches, suspicious activity), the firewall supports an immediate lockdown mode that blocks ALL traffic.
Danger
This will immediately terminate all network connections, including SSH. Physical console access is required to restore normal operation. It is also possible to restore normal operation by power-cycling the device.
To activate emergency lockdown:
~$ curl -kX POST -u admin:admin -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"infix-firewall:input\": {\"operation\": \"now\"}}' \\\n https://example.local/restconf/operations/infix-firewall:firewall/lockdown-mode\n To cancel lockdown mode (requires console access):
~$ curl -kX POST -u admin:admin -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"infix-firewall:input\": {\"operation\": \"cancel\"}}' \\\n https://example.local/restconf/operations/infix-firewall:firewall/lockdown-mode\n You can check the current lockdown state:
~$ curl -kX GET -u admin:admin -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/infix-firewall:firewall/lockdown\n{\n \"infix-firewall:firewall\": {\n \"lockdown\": false\n }\n}\n The output from ping clients differ A LOT. Some do not consider ICMP unreachable to be a proper response and it will appear as if the device is not responding at all. Use tcpdump or wireshark to get to the bottom of network mysteries.\u00a0\u21a9
"},{"location":"hardware/","title":"Hardware Info & Status","text":""},{"location":"hardware/#hardware-information-and-status","title":"Hardware Information and Status","text":"The hardware infomation and status is handled by the YANG model IETF hardware, with deviations and augmentations in infix-hardware.
"},{"location":"hardware/#usb-ports","title":"USB Ports","text":"For Infix to be able to control USB port(s), a device tree modification is needed (see alder.dtsi for full example).
chosen {\n infix {\n usb-ports = <&cp0_usb3_1>;\n usb-port-names = \"USB\";\n };\n };\n Two USB ports are also exposed in QEMU for test purpose.
All USB ports in the system will be disabled during boot due to the file board/common/rootfs/etc/modprobe.d/usbcore.conf. If you do not want Infix to control USB port(s), remove the file or manually enable the USB bus, here is an example:
# Enable the bus\necho 1 > /sys/bus/usb/devices/usb1/authorized\n And then enable sub-devices (e.g. USB memory)
# Enable a device plugged into usb1\necho 1 > /sys/bus/usb/devices/usb1/1-1/authorized\n "},{"location":"hardware/#current-status","title":"Current status","text":"admin@example:/> show hardware\n USB PORTS\n NAME STATE\n USB unlocked\n An USB port can be in two states unlocked and locked. When a port is locked, all connected devices will get power, but never authorized by Linux to use.
"},{"location":"hardware/#configure-usb-port","title":"Configure USB port","text":"Note
You can only configure USB ports known to the system. See the CLI command show hardware in admin-exec context. (Use do prefix in configure context.)
admin@example:/> configure\nadmin@example:/config/> set hardware component USB state admin-state unlocked\nadmin@example:/config/> leave\nadmin@example:/>\n "},{"location":"hardware/#using-a-usb-stick","title":"Using a USB Stick","text":"With the USB port unlocked, a memory stick can be used to expand the storage of the device. Plug it in, either directly, or via a USB hub. All partitions of VFAT or exFAT type are automatically mounted to the /media/<LABLEL> directory, e.g., /media/memorystick, or if a partition does not have a label, using the kernel device name, e.g., /media/sda.
Depending on the partition type the the media is either mounted in flush or sync mode to ensure files are properly flushed to the backing store of the memory stick. This to protect against users yanking out a device before file(s) have been written to it.
The only way currently to safely \"eject\" a USB memory stick is to use umount command from a UNIX shell, which explicitly synchronizes any cached data to disk before returning the prompt:
admin@example:~$ sudo umount /media/log\n "},{"location":"license/","title":"Origin & Licensing","text":""},{"location":"license/#origin-licensing","title":"Origin & Licensing","text":"Infix is entirely built on Open Source components (packages). Most of them, as well as the build system with its helper scripts and tools, is from Buildroot, which is distributed under the terms of the GNU General Public License (GPL). See the file COPYING for details.
Some files in Buildroot may contain a different open source license statement. Those files are licensed under the license contained in the file itself.
Buildroot and Infix also bundle patch files, which are applied to the sources of the various packages. Those patches are not covered by the license of Buildroot or Infix. Instead, they are covered by the license of the software to which the patches are applied. When said software is available under multiple licenses, the patches are only provided under the publicly accessible licenses.
Infix releases include the license information covering all Open Source packages. This is extracted automatically at build time using the tool make legal-info. Any proprietary software built on top of Infix, or Buildroot, would need separate auditing to ensure it does not link with any GPL1 licensed library.
Infix image builds use GNU libc (GLIBC) which is covered by the LGPL. The LGPL does allow proprietary software, as long as said software is linking dynamically, not statically, to GLIBC.\u00a0\u21a9
"},{"location":"management/","title":"Management","text":""},{"location":"management/#system-management","title":"System Management","text":"The system utilizes YANG models for keeping configuration and operational data. These databases can be managed through different interfaces such as NETCONF, RESTCONF, and CLI via SSH or Console.
"},{"location":"management/#ssh-management","title":"SSH Management","text":"An SSH server (SSHv2) is provided for remote management. It can be enabled/disabled as shown below.
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> set enabled\nadmin@example:/config/ssh/>\n By default the SSH server accepts connections to port 22 on all its IP addresses, but this can be adjusted using the listen command. To make the server (only) listen for incoming connections to IP address 192.168.1.1 and port 12345 the following commands can be used.
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> show\nenabled true;\nhostkey genkey;\nlisten ipv4 {\n address 0.0.0.0;\n port 22;\n}\nlisten ipv6 {\n address ::;\n port 22;\n}\nadmin@example:/config/ssh/> no listen ipv6\nadmin@example:/config/ssh/> edit listen ipv4\nadmin@example:/config/ssh/listen/ipv4/> set address 192.168.1.1\nadmin@example:/config/ssh/listen/ipv4/> set port 12345\nadmin@example:/config/ssh/listen/ipv4/>\n The default SSH hostkey is generated on first boot and is used in both SSH and NETCONF (SSH transport). Custom keys can be added to the configuration in ietf-keystore. The only supported hostkey type is 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
"},{"location":"management/#use-your-own-ssh-hostkeys","title":"Use your own SSH hostkeys","text":"Hostkeys can be generated with OpenSSL:
openssl genpkey -quiet -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform PEM > mykey\nopenssl rsa -RSAPublicKey_out < mykey > mykey.pub\n Store the keys in ietf-keystore without the header and footer information created by OpenSSL.
After the key has been stored in the keystore and given the name mykey it can be added to SSH configuration:
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> set hostkey mykey\n "},{"location":"management/#console-port","title":"Console Port","text":"For units with a console port, it is possible for users to login to shell/CLI with functionality similar to what is provided via SSH.
The type and setup for your console port is product specific. For instance, it can be a USB-C port connected to the CPU serial port using a USB-to-serial converter. To connect you would need a USB-C cable connected to the console port of the device. The serial port is typically setup to run at 115200 baud, 8N1.
Infix OS \u2014 Immutable.Friendly.Secure v24.11.1 (ttyS0)\nexample login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$\n The resize command can be used to update terminal settings to the size of your terminal window.
admin@example:~$ resize\nCOLUMNS=115;LINES=59;export COLUMNS LINES;\nadmin@example:~$\n CLI can be entered from shell in the same way as for SSH.
admin@example:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet LOWER-DOWN 00:53:00:06:03:01\ne2 ethernet LOWER-DOWN 00:53:00:06:03:02\n...\nadmin@example:/>\n "},{"location":"management/#web-web-console-and-restconf","title":"Web, Web-console and RESTCONF","text":"The system provides a set of Web services:
a rudimentary Web server, currently limited to an information page a RESTCONF server with equivalent management capabilities as NETCONF a Web console service, where the shell/CLI can be accessed via HTTPS, similar to connecting via a console port or SSH There is also a Netbrowse Web service presenting information about the unit's neighbors, collected via mDNS (see Discovery for more details).
admin@example:/> configure\nadmin@example:/config/> edit web\nadmin@example:/config/web/> help\n enabled Enable or disable on all web services.\n console Web console interface.\n netbrowse mDNS Network Browser.\n restconf IETF RESTCONF Server.\nadmin@example:/config/web/>\n "},{"location":"management/#enabledisable-web-service-and-server","title":"Enable/disable Web Service and Server","text":"The Web service can be enabled as shown below.
admin@example:/> configure\nadmin@example:/config/> edit web\nadmin@example:/config/web/> set enabled\nadmin@example:/config/web/> \n Enabling the Web service implies that a Web server is enabled. Currently this Web server provides generic Infix information, as well as a link to a Web console. The Web server uses HTTPS; any HTTP request is redirected to HTTPS.
The enabled setting for the Web service acts as a global enable/disable setting for the other Web services (Web console, RESTCONF and Netbrowse).
"},{"location":"management/#enabledisable-web-console","title":"Enable/disable Web Console","text":"The Web console service provides a terminal service similar to Console or SSH. The Web console is secured via HTTPS on port 7861.
The Web console has its own enable/disable setting, but will only be activated if the Web service is enabled. The example below shows how to disable the Web console.
admin@example:/config/web/> edit console\nadmin@example:/config/web/console/> no enabled\nadmin@example:/config/web/console/>\n "},{"location":"management/#enabledisable-restconf-service","title":"Enable/disable RESTCONF Service","text":"Alternatively, the system can be managed remotely using RESTCONF. Meaning you can curl it instead of using a dedicated NETCONF client.
The RESTCONF service has its own enable/disable setting, but will only be activated if the Web service is enabled. The example below shows how to disable the RESTCONF service.
admin@example:/config/web/> edit restconf\nadmin@example:/config/web/restconf/> no enabled\nadmin@example:/config/web/restconf/>\n "},{"location":"management/#system-upgrade","title":"System Upgrade","text":"See Upgrade & Boot Order for information on upgrading.
"},{"location":"netboot/","title":"Netboot HowTo","text":""},{"location":"netboot/#netboot-howto","title":"Netboot HowTo","text":"This document describes how to set up network booting U-Boot devices on a LAN, e.g., when working with an evaluation board or other embedded system. The most secure way to do this is with a local LAN between a PC and the device.
Instead of setting up everything in U-Boot to download the Linux Image, device tree, and initramfs, we will let U-Boot download a script with instructions to run. When you have multiple systems (boards) this quickly becomes a lot easier to manage.
Note
Instructions in this HowTo assume a Debian based development system, e.g., Ubuntu or Linux Mint.
"},{"location":"netboot/#network-interface-setup","title":"Network Interface Setup","text":"For two dedicated network interfaces, here eth2 and eth3 (ymmv), we create an old-style interfaces config1 with the following content:
# /etc/network/interfaces.d/gimli\nauto eth2\niface eth2 inet static\n address 192.168.0.1\n netmask 255.255.255.0\n\nauto eth3\niface eth3 inet manual\n Tip
Use configuration file names in .d/ directories that make sense and can easily be remembered. Here we use the hostname of the PC.
"},{"location":"netboot/#dhcptftp-server-setup","title":"DHCP/TFTP Server Setup","text":"The examples given here use dnsmasq, which provides both DHCP and TFTP server support. The same can be achieved with other implementations.
Similar to interfaces.d, dnsmasq has an /etc/dnsmasq.d directory so we can use \"snippets\" instead of modifying /etc/dnsmasq.conf directly. Add a file called /etc/dnsmasq.d/gimli.
Initial content:
# Remember IP address handed out to BOOTP clients\nbootp-dynamic\n# Disable check-if-ip-address-is-already-used\nno-ping\n\n# Enable TFTP server, use /srv/ftp, same as any FTP server, useful\n# when using the same images for system upgrade as for netbooting.\nenable-tftp\ntftp-root=/srv/ftp\n Caution
First of all, make sure you DO NOT accidentally set up dnsmasq so that it starts acting as a DHCP server also on your office LAN! Follow the instructions below for more details.
If you have many interfaces used for lab equipment and only one office LAN interface, then use something like this:
# Disable DHCP server on loopback and office LAN (eth0)\nexcept-interface=lo\nexcept-interface=eth0\n To further lock this down, we only run the DHCP server on each of the interfaces used for lab equipment, with a dedicated IP range as well:
# Currently I have an imx8mp-evk on eth2, so on my system I have a\n# symlink bootfile-eth2 -> netboot.scr\ninterface=eth2\ndhcp-range=192.168.0.100,192.168.0.199,1h\ndhcp-boot=tag:eth2,bootfile-eth2\n "},{"location":"netboot/#bootfile-netbootscr","title":"Bootfile netboot.scr","text":"The bootfile U-Boot retrieves from the TFTP server is a script that looks like this, netboot.sh:
setenv ramdisk_addr_r 0x58000000\nsetenv fdt_addr_r 0x50400000\n\nsetenv autoboot off\ntftp ${fdt_addr_r} imx8mp-evk/imx8mp-evk.dtb\ntftp ${kernel_addr_r} imx8mp-evk/Image\ntftp ${ramdisk_addr_r} imx8mp-evk/rootfs.squashfs\n\nsetenv bootargs console=ttymxc1,115200 root=/dev/ram0 brd.rd_size=500000 rauc.slot=net\nbooti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}\n U-Boot cannot read script files directly, so we need to wrap it with a FIT format header, this is done by first converting it on the PC:
$ mkimage -T script -d netboot.sh netboot.scr\n The output is netboot.scr which we symlink to above in the dnsmasq setup step.
"},{"location":"netboot/#u-boot-commands","title":"U-Boot Commands","text":"U-Boot is a maze of environment variables, some with values, some wrap commands, and most are undocumented. We will use a prefix for our variables to ensure we do not overwrite anything you may want to use later.
==> setenv ixboot 'dhcp && source \\${fileaddr}'\n==> saveenv\n To prevent NetworkManager from automatically managing the interfaces.\u00a0\u21a9
"},{"location":"networking/","title":"Network Configuration","text":""},{"location":"networking/#network-configuration","title":"Network Configuration","text":"Infix aims to support all Linux Networking constructs. The YANG models used to describe the system are chosen to fit well and leverage the underlying Linux kernel's capabilities. The ietf-interfaces.yang model forms the base, extended with ietf-ip.yang and other layer-3 IETF models. The layer-2 bridge and aggregate models are defined by Infix to exploit the unique features not available in IEEE models.
Important
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"networking/#interface-lego","title":"Interface LEGO\u00ae","text":"The network building blocks available in Linux are akin to the popular LEGO\u00ae bricks.
There are two types of relationships that can link two blocks together:
Lower-to-upper: Visually represented by an extruding square connected upwards to a square socket. An interface can only have a single lower-to-upper relationship, i.e., it can be attached to a single upper interface like a bridge or a LAG. In iproute2 parlance, this corresponds to the interface's master setting Upper-to-lower: Visually represented by an extruding semicircle connected downwards to a semicircle socket. The lower interface in these relationships accepts multiple upper-to-lower relationships from different upper blocks. E.g., multiple VLANs and IP address blocks can be connected to the same lower interface
An interface may simultaneously have a lower-to-upper relation to some other interface, and be the target of one or more upper-to-lower relationships. It is valid, for example, for a physical port to be attached to a bridge, but also have a VLAN interface stacked on top of it. In this example, traffic assigned to the VLAN in question would be diverted to the VLAN interface before entering the bridge, while all other traffic would be bridged as usual.
Type Yang Model Description bridge infix-if-bridge SW implementation of an IEEE 802.1Q bridge ip ietf-ip, infix-ip IP address to the subordinate interface vlan infix-if-vlan Capture all traffic belonging to a specific 802.1Q VID lag infix-if-lag Link aggregation, static and IEEE 802.3ad (LACP) lo ietf-interfaces Software loopback interface eth ieee802-ethernet-interface Physical Ethernet device/port. infix-ethernet-interface veth infix-if-veth Virtual Ethernet pair, typically one end is in a container common ietf-interfaces, Properties common to all interface types infix-interfaces"},{"location":"networking/#data-plane","title":"Data Plane","text":"The blocks you choose, and how you connect them, defines your data plane. Here we see an example of how to bridge a virtual port with a physical LAN.
Depending on the (optional) VLAN filtering of the bridge, the container may have full or limited connectivity with outside ports, as well as the internal CPU.
In fact the virtual port connected to the bridge can be member of several VLANs, with each VLAN being an interface with an IP address inside the container.
Thanks to Linux, and technologies like switchdev, that allow you to split a switching fabric into unique (isolated) ports, the full separation and virtualization of all Ethernet layer properties are possible to share with a container. Meaning, all the building blocks used on the left hand side can also be used freely on the right hand side as well.
"},{"location":"networking/#general","title":"General","text":"General interface settings include type, enable, custom MAC address, and text description. Other settings have their own sections, below.
The type is important to set when configuring devices remotely because unlike the CLI, a NETCONF or RESTCONF session cannot guess the interface type for you. The operating system provides an override of the available interface types.
An enabled interface can be inspected using the operational datastore, nodes admin-state and oper-state show the status, . Possible values are listed in the YANG model.
The custom-phys-address can be used to set an interface's MAC address. This is an extension to the ietf-interfaces YANG model, which defines phys-address as read-only4 . The following shows the different configuration options.
The description is saved as Linux ifalias on an interface. It is a free-form string, useful for describing purpose or just adding comments for remote debugging, e.g., using the operational datastore.
Caution
There is no validation or safety checks performed by the system when using custom-phys-address. In particular the offset variant can be dangerous to use -- pay attention to the meaning of bits in the upper-most octet: local bit, multicast/group, etc.
"},{"location":"networking/#fixed-custom-mac","title":"Fixed custom MAC","text":"admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address static 00:ab:00:11:22:33\n\n=> 00:ab:00:11:22:33\n "},{"location":"networking/#chassis-mac","title":"Chassis MAC","text":"Chassis MAC, sometimes also referred to as base MAC. In these two examples it is 00:53:00:c0:ff:ee.
admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address chassis\n\n=> 00:53:00:c0:ff:ee\n "},{"location":"networking/#chassis-mac-with-offset","title":"Chassis MAC, with offset","text":"When constructing a derived address it is recommended to set the locally administered bit. Same chassis MAC as before.
admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address chassis offset 02:00:00:00:00:02\n\n=> 02:53:00:c0:ff:f0\n "},{"location":"networking/#bridging","title":"Bridging","text":"This is the most central part of the system. A bridge is a switch, and a switch is a bridge. In Linux, setting up a bridge with ports connected to physical switch fabric, means you manage the actual switch fabric!
"},{"location":"networking/#mac-bridge","title":"MAC Bridge","text":"In Infix ports are by default not switch ports, unless the customer specific factory config sets it up this way. To enable switching, with offloading if you have a switch chipset, between ports you create a bridge and then add ports to that bridge. Like this:
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> up\nadmin@example:/config/> set interface eth0 bridge-port bridge br0\nadmin@example:/config/> set interface eth1 bridge-port bridge br0\nadmin@example:/config/> leave\n Here we add two ports to bridge br0: eth0 and eth1.
Tip
The CLI has several built-in helpers governed by convention. E.g., naming bridges brN, where N is a number, the type is inferred automatically and unlocks all bridge features. Other conventions are vethNA, where N is a number and A is a letter ('a' for access port and 'b' for bridge side is common), and ethN.M for VLAN M on top of ethN, or dockerN for a IP masquerading container bridge.
Note, this inference only works with the CLI, configuring networking over NETCONF or RESTCONF requires setting the type explicitly.
It is possible to create multiple MAC bridges, however, it is currently5 not recommended to use more than one MAC bridge on products with Marvell LinkStreet switching ASICs. A VLAN filtering bridge should be used instead.
"},{"location":"networking/#vlan-filtering-bridge","title":"VLAN Filtering Bridge","text":"By default bridges in Linux do not filter based on VLAN tags. This can be enabled when creating a bridge by adding a port to a VLAN as a tagged or untagged member. Use the port default VID (PVID) setting to control VLAN association for traffic ingressing a port untagged (default PVID: 1).
admin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> up\nadmin@example:/config/> set interface eth0 bridge-port bridge br0\nadmin@example:/config/> set interface eth0 bridge-port pvid 10\nadmin@example:/config/> set interface eth1 bridge-port bridge br0\nadmin@example:/config/> set interface eth1 bridge-port pvid 20\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 10 untagged eth0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 20 untagged eth1\n This sets eth0 as an untagged member of VLAN 10 and eth1 as an untagged member of VLAN 20. Switching between these ports is thus prohibited.
To terminate a VLAN in the switch itself, either for switch management or for routing, the bridge must become a (tagged) member of the VLAN.
admin@example:/config/interface/br0/> set bridge vlans vlan 10 tagged br0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 20 tagged br0\n To route or to manage via a VLAN, a VLAN interface needs to be created on top of the bridge, see section VLAN Interfaces below for more on this topic.
Note
In some use-cases only a single management VLAN on the bridge is used. For the example above, if the bridge itself is an untagged member only in VLAN 10, IP addresses can be set directly on the bridge without the need for dedicated VLAN interfaces on top of the bridge.
"},{"location":"networking/#multicast-filtering-and-snooping","title":"Multicast Filtering and Snooping","text":"Multicast filtering in the bridge is handled by the bridge itself. It can filter both IP multicast and MAC multicast. For IP multicast it also supports \"snooping\", i.e., IGMP and MLD, to automatically reduce the broadcast effects of multicast. See the next section for a summary of the terminology used.
Important
Currently there is no way to just enable multicast filtering without also enabling snooping. This may change in the future, in which case a filtering enabled setting will be made available along with the existing snooping setting.
When creating your bridge you must decide if you need a VLAN filtering bridge or a plain bridge (see previous section). Multicast filtering is supported for either, but take note that it must be enabled and set up per VLAN when VLAN filtering is enabled -- there are no global multicast settings in this operating mode.
In the following example we have a regular 8-port bridge without VLAN filtering. We focus on the multicast specific settings:
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> set bridge multicast snooping\nadmin@example:/config/interface/br0/> set ipv4 address 192.168.2.1 prefix-length 24\nadmin@example:/config/interface/br0/> leave\nadmin@example:/> copy running-config startup-config\n Here we enable snooping and set a static IPv4 address so that the switch can take part in IGMP querier elections. (MLD querier election currently not supported.) We can inspect the current state:
admin@example:/> show ip multicast\nMulticast Overview\nQuery Interval (default): 125 sec\nRouter Timeout : 255\nFast Leave Ports :\nRouter Ports :\nFlood Ports : e0, e1, e2, e3, e4, e5, e6, e7\n\nInterface VID Querier State Interval Timeout Ver\nbr0 192.168.2.1 Up 125 None 3\n\nBridge VID Multicast Group Ports\nbr0 224.1.1.1 e3, e2\nbr0 ff02::6a br0\n This is a rather small LAN, so our bridge has already become the elected IGMP querier. We see it is ours because the timeout is None, and we recognize the IP address the system has detected, as ours. We can also see two ports that have joined the same IPv4 multicast group, 224.1.1.1, and one join from the system itself for the IPv6 group ff02::6a.
Now, let us see what happens when we add another bridge, this time with VLAN filtering enabled. We skip the boring parts about how to move ports e4-e7 to br1 and assign them to VLANs, and again, focus on the multicast bits only:
admin@example:/> configure\nadmin@example:/config/> edit interface br1\nadmin@example:/config/interface/br1/> set bridge vlans vlan 1 multicast snooping\nadmin@example:/config/interface/br1/> set bridge vlans vlan 2 multicast snooping\nadmin@example:/config/interface/br1/> leave\nadmin@example:/> copy running-config startup-config\n Let us see what we get:
admin@example:/> show ip multicast\nMulticast Overview\nQuery Interval (default): 125 sec\nRouter Timeout : 255\nFast Leave Ports : e5\nRouter Ports : e1, e2, e5, e6, e7\nFlood Ports : e1, e2, e3, e4, e5, e6, e7, e8\n\nInterface VID Querier State Interval Timeout Ver\nbr0 192.168.2.1 Up 125 None 3\nbr1 1 0.0.0.0 Up 125 None 3\nbr1 2 0.0.0.0 Up 125 None 3\n\nBridge VID Multicast Group Ports\nbr0 224.1.1.1 e2\nbr0 ff02::fb br0\nbr0 ff02::6a br0\nbr0 ff02::1:ff00:0 br0\nbr1 1 224.1.1.1 e5\nbr1 2 224.1.1.1 e7\nbr1 1 ff02::fb br1\nbr1 1 ff02::1:ff00:0 br1\n In this setup we have a lot more going on. Multiple multicast router ports have been detected, and behind the scenes someone has also added an IGMP/MLD fast-leave port.
"},{"location":"networking/#terminology-abbreviations","title":"Terminology & Abbreviations","text":" IGMP: Internet Group Membership Protocol, multicast subscription for IPv4, for details see RFC3376 MLD: Multicast Listener Discovery (Protocol), multicast subscription for IPv6, for details see RFC3810 Unknown/Unregistered multicast: multicast groups that are not in the multicast forwarding database (MDB) Known/Registered multicast: multicast groups that are in the multicast forwarding database (MDB) MDB: the multicast forwarding database, consists of filters for multicast groups, directing where multicast is allowed to egress. A filter entry consists of a group and a port list. The bridge filters with a unique database per VLAN, in the same was as the unicast FDB Join/Leave: the terminology used in earlier versions of the two protocols to subscribe and unsubscribe to a multicast group. For more information, see Membership Report Membership Report A membership report is sent by end-devices and forwarded by switches to the elected querier on the LAN. They consist of multiple \"join\" and \"leave\" operations on groups. They can also, per group, list which senders to allow or block. Switches usually only support the group subscription, and even more common also only support filtering on the MAC level3 Querier election: the process of determining who is the elected IGMP/MLD querier on a LAN. Lowest numerical IP address wins, the special address 0.0.0.0 (proxy querier) never wins Proxy querier: when no better querier exists on a LAN, one or more devices can send proxy queries with source address 0.0.0.0 (or :: for IPv6). See Query Interval, below, why this is a good thing Query interval: the time in seconds between two queries from an IGMP/MLD querier. It is not uncommon that end-devices do not send their membership reports unless they first hear a query Fast Leave: set on a bridge port to ensure multicast is pruned as quickly as possible when a \"leave\" membership report is received. In effect, this option marks the port as directly connected to an end-device. When not set (default), a query with timeout is first sent to ensure no unintentional loss of multicast is incurred Router port: can be both configured statically and detected at runtime based on connected devices, usually multicast routers. On a router port all multicast is forwarded, both known and unknown Flood port: set on a bridge port (default: enabled) to ensure all unknown multicast is forwarded Router timeout: the time in seconds until a querier is deemed to have been lost and another device (switch/router) takes over. In the tables shown above, a None timeout is declared when the current device is the active querier Tip
The reason why multicast flooding is enabled by default is to ensure safe co-existence with MAC multicast, which is common in industrial networks. It also allows end devices that do not know of IGMP/MLD to communicate over multicast as long as the group they have chosen is not used by other IGMP/MLD aware devices on the LAN.
As soon as an IGMP/MLD membership report to \"join\" a group is received the group is added to the kernel MDB and forwarding to other ports stop. The only exception to this rule is multicast router ports.
If your MAC multicast forwarding is not working properly, it may be because an IP multicast group maps to the same MAC address. Please see RFC 1112 for details. Use static multicast router ports, or static multicast MAC filters, to mitigate.
"},{"location":"networking/#forwarding-of-ieee-reserved-group-addresses","title":"Forwarding of IEEE Reserved Group Addresses","text":"Addresses in the range 01:80:C2:00:00:0X are used by various bridge signaling protocols, and are not forwarded by default. Still, it is sometimes useful to let the bridge forward such packets, this can be done by specifying protocol names or the last address nibble as decimal value 0..15:
admin@example:/config/> edit interface br0 bridge\nadmin@example:/config/interface/br0/bridge/> set ieee-group-forward # Tap the ? ley for alternatives\n [0..15] List of IEEE link-local protocols to forward, e.g., STP, LLDP\n dot1x 802.1X Port-Based Network Access Control.\n lacp 802.3 Slow Protocols, e.g., LACP.\n lldp 802.1AB Link Layer Discovery Protocol (LLDP).\n stp Spanning Tree (STP/RSPT/MSTP).\nadmin@example:/config/interface/br0/bridge/> set ieee-group-forward\n The following example configures bridge br0 to forward LLDP packets.
admin@example:/config/interface/br0/bridge/> set ieee-group-forward lldp\nadmin@example:/config/interface/br0/bridge/>\n "},{"location":"networking/#link-aggregation","title":"Link Aggregation","text":"A link aggregate, or lag, allows multiple physical interfaces to be combined into a single logical interface, providing increased bandwidth (in some cases) and redundancy (primarily). Two modes of qualifying lag member ports are available:
static: Active members selected based on link status (carrier) lacp: IEEE 802.3ad Link Aggregation Control Protocol In LACP mode, LACPDUs are exchanged by the link partners to qualify each lag member, while in static mode only carrier is used. This additional exchange in LACP ensures traffic can be forwarded in both directions.
Traffic distribution, for both modes, across the active lag member ports is determined by the hash policy1 . It uses an XOR of the source, destination MAC addresses and the EtherType field. This, IEEE 802.3ad-compliant, algorithm will place all traffic to a particular network peer on the same link. Meaning there is no increased bandwidth for communication between two specific devices.
Tip
Similar to other interface types, naming your interface lagN, where N is a number, allows the CLI to automatically infer the interface type as LAG.
"},{"location":"networking/#basic-configuration","title":"Basic Configuration","text":"Creating a link aggregate interface and adding member ports:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode static\nadmin@example:/config/interface/lag0/> end\nadmin@example:/config/> set interface eth7 lag-port lag lag0\nadmin@example:/config/> set interface eth8 lag-port lag lag0\nadmin@example:/config/> leave\n A static lag responds only to link (carrier) changes of member ports. E.g., in this example egressing traffic is continuously distributed over the two links until link down on one link is detected, triggering all traffic to be steered to the sole remaining link.
"},{"location":"networking/#lacp-configuration","title":"LACP Configuration","text":"LACP mode provides dynamic negotiation of the link aggregate. Key settings include:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode lacp\nadmin@example:/config/interface/lag0/> set lag lacp mode passive\nadmin@example:/config/interface/lag0/> set lag lacp rate fast\nadmin@example:/config/interface/lag0/> set lag lacp system-priority 100\n LACP mode supports two operational modes:
active: Initiates negotiation by sending LACPDUs (default) passive: Waits for peer to initiate negotiation Note
At least one end of the link must be in active mode for negotiation to occur.
The LACP rate setting controls protocol timing:
slow: LACPDUs sent every 30 seconds, with 90 second timeout (default) fast: LACPDUs sent every second, with 3 second timeout "},{"location":"networking/#link-flapping","title":"Link Flapping","text":"To protect against link flapping, debounce timers can be configured to delay link qualification. Usually only the up delay is needed:
admin@example:/config/interface/lag0/lag/link-monitor/> edit debounce\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set up 500\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set down 200\n "},{"location":"networking/#operational-status-overview","title":"Operational Status, Overview","text":"Like other interfaces, link aggregates are also available in the general interfaces overview in the CLI admin-exec context. Here is the above static mode aggregate:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\n.\n.\n.\nlag0 lag UP static: balance-xor, hash: layer2\n\u2502 ethernet UP 00:a0:85:00:02:00\n\u251c eth7 lag ACTIVE\n\u2514 eth8 lag ACTIVE\n Same aggregate, but in LACP mode:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\n.\n.\n.\nlag0 lag UP lacp: active, rate: fast (1s), hash: layer2\n\u2502 ethernet UP 00:a0:85:00:02:00\n\u251c eth7 lag ACTIVE active, short_timeout, aggregating, in_sync, collecting, distributing\n\u2514 eth8 lag ACTIVE active, short_timeout, aggregating, in_sync, collecting, distributing\n "},{"location":"networking/#operational-status-detail","title":"Operational Status, Detail","text":"In addition to basic status shown in the interface overview, detailed LAG status can be inspected:
admin@example:/> show interface lag0\nname : lag0\nindex : 25\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag mode : static\nlag type : balance-xor\nlag hash : layer2\nlink debounce up : 0 msec\nlink debounce down : 0 msec\nipv4 addresses :\nipv6 addresses :\nin-octets : 0\nout-octets : 2142\n Same aggregate, but in LACP mode:
admin@example:/> show interface lag0\nname : lag0\nindex : 24\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag mode : lacp\nlag hash : layer2\nlacp mode : active\nlacp rate : fast (1s)\nlacp aggregate id : 1\nlacp system priority: 65535\nlacp actor key : 9\nlacp partner key : 9\nlacp partner mac : 00:a0:85:00:03:00\nlink debounce up : 0 msec\nlink debounce down : 0 msec\nipv4 addresses :\nipv6 addresses :\nin-octets : 100892\nout-octets : 111776\n Member ports provide additional status information:
Link failure counter: number of detected link failures LACP state flags: various states of LACP negotiation: active: port is actively sending LACPDUs short_timeout: using fast rate (1s) vs. slow rate (30s) aggregating: port is allowed to aggregate in this LAG in_sync: port is synchronized with partner collecting: port is allowed to receive traffic distributing: port is allowed to send traffic defaulted: using default partner info (partner not responding) expired: partner info has expired (no LACPDUs received) Aggregator ID: unique identifier for this LAG group Actor state: LACP state flags for this port (local) Partner state: LACP state flags from the remote port Example member port status:
admin@example:/> show interface eth7\nname : eth7\nindex : 8\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag member : lag0\nlag member state : active\nlacp aggregate id : 1\nlacp actor state : active, short_timeout, aggregating, in_sync, collecting, distributing\nlacp partner state : active, short_timeout, aggregating, in_sync, collecting, distributing\nlink failure count : 0\nipv4 addresses :\nipv6 addresses :\nin-octets : 473244\nout-octets : 499037\n "},{"location":"networking/#example-switch-uplink-with-lacp","title":"Example: Switch Uplink with LACP","text":"LACP mode provides the most robust operation, automatically negotiating the link aggregate and detecting configuration mismatches.
A common use case is connecting a switch to an upstream device:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode lacp\n Enable fast LACP for quicker fail-over:
admin@example:/config/interface/lag0/> set lag lacp rate fast\n Add uplink ports
admin@example:/config/interface/lag0/> end\nadmin@example:/config/> set interface eth7 lag-port lag lag0\nadmin@example:/config/> set interface eth8 lag-port lag lag0\n Enable protection against \"link flapping\".
admin@example:/config/interface/lag0/> edit lag link-monitor\nadmin@example:/config/interface/lag0/lag/link-monitor/> edit debounce\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set up 500\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set down 200\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> top\n Add to bridge for switching
admin@example:/config/interface/lag0/lag/link-monitor/debounce/> end\nadmin@example:/config/> set interface lag0 bridge-port bridge br0\nadmin@example:/config/> leave\n "},{"location":"networking/#vlan-interfaces","title":"VLAN Interfaces","text":"Creating a VLAN can be done in many ways. This section assumes VLAN interfaces created atop another Linux interface. E.g., the VLAN interfaces created on top of the Ethernet interface or bridge in the picture below.
A VLAN interface is basically a filtering abstraction. When you run tcpdump on a VLAN interface you will only see the frames matching the VLAN ID of the interface, compared to all the VLAN IDs if you run tcpdump on the lower-layer interface.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0.20\nadmin@example:/config/interface/eth0.20/> show\ntype vlan;\nvlan {\n tag-type c-vlan;\n id 20;\n lower-layer-if eth0;\n}\nadmin@example:/config/interface/eth0.20/> leave\n The example below assumes bridge br0 is already created, see VLAN Filtering Bridge.
admin@example:/> configure\nadmin@example:/config/> edit interface vlan10\nadmin@example:/config/interface/vlan10/> set vlan id 10\nadmin@example:/config/interface/vlan10/> set vlan lower-layer-if br0\nadmin@example:/config/interface/vlan10/> leave\n As conventions, a VLAN interface for VID 20 on top of an Ethernet interface eth0 is named eth0.20, and a VLAN interface for VID 10 on top of a bridge interface br0 is named vlan10.
Note
If you name your VLAN interface foo0.N or vlanN, where N is a number, the CLI infers the interface type automatically.
"},{"location":"networking/#physical-ethernet-interfaces","title":"Physical Ethernet Interfaces","text":""},{"location":"networking/#ethernet-settings-and-status","title":"Ethernet Settings and Status","text":"Physical Ethernet interfaces provide low-level settings for speed/duplex as well as packet status and statistics.
By default, Ethernet interfaces defaults to auto-negotiating speed/duplex modes, advertising all speed and duplex modes available. In the example below, the switch would by default auto-negotiate speed 1 Gbit/s on port eth1 and 100 Mbit/s on port eth4, as those are the highest speeds supported by H1 and H2 respectively.
The speed and duplex status for the links can be listed as shown below, assuming the link operational status is 'up'.
admin@example:/> show interface eth1\nname : eth1\nindex : 2\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 1000\nphysical address : 00:53:00:06:11:01\nipv4 addresses :\nipv6 addresses : \nin-octets : 75581\nout-octets : 43130\n...\nadmin@example:/> show interface eth4\nname : eth4\nindex : 5\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 100\nphysical address : 00:53:00:06:11:04\nipv4 addresses :\nipv6 addresses : \nin-octets : 75439\nout-octets : 550704\n...\nadmin@example:/>\n "},{"location":"networking/#configuring-fixed-speed-and-duplex","title":"Configuring fixed speed and duplex","text":"Auto-negotiation of speed/duplex mode is desired in almost all use-cases, but it is possible to disable auto-negotiation and specify a fixed speed and duplex mode.
Important
When setting a fixed speed and duplex mode, ensure both sides of the link have matching configuration. If speed does not match, the link will not come up. If duplex mode does not match, the result is reported collisions and/or bad throughput.
The example below configures port eth3 to fixed speed 100 Mbit/s half-duplex mode.
admin@example:/> configure\nadmin@example:/config/> edit interface eth3 ethernet\nadmin@example:/config/interface/eth3/ethernet/> set speed 0.1\nadmin@example:/config/interface/eth3/ethernet/> set duplex half\nadmin@example:/config/interface/eth3/ethernet/> set auto-negotiation enable false\nadmin@example:/config/interface/eth3/ethernet/> show\nauto-negotiation {\n enable false;\n}\nduplex half;\nspeed 0.1;\nadmin@example:/config/interface/eth3/ethernet/> leave\nadmin@example:/> \n Speed metric is in Gbit/s. Auto-negotiation needs to be disabled in order for fixed speed/duplex to apply. Only speeds 0.1(100 Mbit/s) and 0.01 (10 Mbit/s) can be specified. 1 Gbit/s and higher speeds require auto-negotiation to be enabled.
"},{"location":"networking/#ethernet-statistics","title":"Ethernet statistics","text":"Ethernet packet statistics6 can be listed as shown below.
admin@example:/> show interface eth1\nname : eth1\nindex : 2\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 1000\nphysical address : 00:53:00:06:11:0a\nipv4 addresses :\nipv6 addresses : \nin-octets : 75581\nout-octets : 43130\n\neth-in-frames : 434\neth-in-multicast-frames : 296\neth-in-broadcast-frames : 138\neth-in-error-fcs-frames : 0\neth-in-error-oversize-frames : 0\neth-out-frames : 310\neth-out-multicast-frames : 310\neth-out-broadcast-frames : 0\neth-out-good-octets : 76821\neth-in-good-octets : 60598\nadmin@example:/> \n "},{"location":"networking/#veth-pairs","title":"VETH Pairs","text":"A Virtual Ethernet (VETH) pair is basically a virtual Ethernet cable. A cable can be \"plugged in\" to a bridge and the other end can be given to a container, or plugged into another bridge.
The latter example is useful if you have multiple bridges in the system with different properties (VLAN filtering, IEEE group forwarding, etc.), but still want some way of communicating between these domains.
admin@example:/> configure\nadmin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set veth peer veth0b\nadmin@example:/config/interface/veth0a/> end\nadmin@example:/config/> diff\ninterfaces {\n+ interface veth0a {\n+ type veth;\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ }\n}\nadmin@example:/config/>\n Tip
This is another example of the automatic inference of the interface type from the name. Any name can be used, but then you have to set the interface type to veth manually.
"},{"location":"networking/#management-plane","title":"Management Plane","text":"This section details IP Addresses And Other Per-Interface IP settings.
Infix support several network interface types, each can be assigned one or more IP addresses, both IPv4 and IPv6 are supported. (There is no concept of a \"primary\" address.)
"},{"location":"networking/#ipv4-address-assignment","title":"IPv4 Address Assignment","text":"Multiple address assignment methods are available:
Type Yang Model Description static ietf-ip Static assignment of IPv4 address, e.g., 10.0.1.1/24 link-local infix-ip Auto-assignment of IPv4 address in 169.254.x.x/16 range dhcp infix-dhcp-client Assignment of IPv4 address by DHCP server, e.g., 10.0.1.1/24 Note
The DHCP address method is only available for LAN interfaces (Ethernet, virtual Ethernet (veth), bridge, link aggregates, etc.)
Supported DHCP (request) options, configurability (Cfg) and defaults, are listed below. Configurable options can be disabled on a per client interface basis, some options, like clientid and option 81, are possible to set the value of as well.
Opt Name Cfg Description 1 netmask No Request IP address and netmask 3 router Yes Default route(s), see also option 121 and 249 6 dns-server Yes DNS server(s), static ones take precedence 12 hostname Yes DHCP cannot set hostname, only for informing server 15 domain Yes Default domain name, for name resolution 28 broadcast Yes Broadcast address, calculated if disabled 42 ntp-server Yes NTP server(s), static ones take precedence 50 address Yes Request (previously cached) address 61 client-id Yes Default MAC address (and option 12) 81 fqdn Yes Similar to option 12, request FQDN update in DNS 119 search Yes Request domain search list 121 classless-static-route Yes Classless static routes 249 ms-classless-static-route Yes Microsoft static route, same as option 121 Default: router, dns-server, domain, broadcast, ntp-server, search, address, classless-static-route, ms-classless-static-route
When configuring a DHCP client, ensure that the NTP client is enabled for the ntp-server DHCP option to be processed correctly. If the NTP client is not enabled, any NTP servers provided by the DHCP server will be ignored. For details on how to enable the NTP client, see the NTP Client Configuration section.
Important
Per RFC3442, if the DHCP server returns both a Classless Static Routes option (121) and Router option (3), the DHCP client must ignore the latter.
"},{"location":"networking/#ipv6-address-assignment","title":"IPv6 Address Assignment","text":"Multiple address assignment methods are available:
Type Yang Model Description static ietf-ip Static assignment of IPv6 address, e.g., 2001:db8:0:1::1/64 link-local ietf-ip2 (RFC4862) Auto-configured link-local IPv6 address (fe80::0 prefix + interface identifier, e.g., fe80::ccd2:82ff:fe52:728b/64) global auto-conf ietf-ip (RFC4862) Auto-configured (stateless) global IPv6 address (prefix from router + interface identifier, e.g., 2001:db8:0:1:ccd2:82ff:fe52:728b/64 dhcp infix-dhcpv6-client Assignment of IPv6 address by DHCPv6 server, e.g., 2001:db8::42/128 Both for link-local and global auto-configuration, it is possible to auto-configure using a random suffix instead of the interface identifier.
Note
The DHCPv6 address method is only available for LAN interfaces (Ethernet, virtual Ethernet (veth), bridge, link aggregates, etc.)
Supported DHCPv6 (request) options, configurability (Cfg) and defaults, are listed below. Configurable options can be disabled on a per client interface basis, some options, like client-id and client-fqdn, are possible to set the value of as well.
Opt Name Cfg Description 1 client-id Yes Client identifier (DUID), auto-generated by default 2 server-id Yes Server identifier (DUID) 23 dns-server Yes DNS recursive name servers, static ones take precedence 24 domain-search Yes Domain search list 25 ia-pd Yes Prefix delegation for downstream networks 31 sntp-server Yes Simple Network Time Protocol servers 32 information-refresh-time Yes Refresh time for stateless DHCPv6 39 client-fqdn Yes Client FQDN, request DNS update from server 56 ntp-server Yes NTP time servers, static ones take precedence Default: dns-server, domain-search, ntp-server
DHCPv6 supports both stateful (address assignment) and stateless (information-only) modes:
Stateful DHCPv6: The server assigns IPv6 addresses to clients. This is the default mode when enabling the DHCPv6 client. Stateless DHCPv6: Used with SLAAC (Stateless Address Autoconfiguration) when only configuration information (DNS, NTP, etc.) is needed. Enable with the information-only setting. When configuring a DHCPv6 client, ensure that the NTP client is enabled for the ntp-server DHCPv6 option to be processed correctly. If the NTP client is not enabled, any NTP servers provided by the DHCPv6 server will be ignored. For details on how to enable the NTP client, see the NTP Client Configuration section.
"},{"location":"networking/#examples","title":"Examples","text":"admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n To illustrate IP address configuration, the examples below uses a switch with a single Ethernet interface (eth0) and a loopback interface (lo). As shown above, these examples assume eth0 has an IPv6 link-local address and lo has static IPv4 and IPv6 addresses by default.
"},{"location":"networking/#static-and-link-local-ipv4-addresses","title":"Static and link-local IPv4 addresses","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4\nadmin@example:/config/interface/eth0/ipv4/> set address 10.0.1.1 prefix-length 24\nadmin@example:/config/interface/eth0/ipv4/> set autoconf\nadmin@example:/config/interface/eth0/ipv4/> diff\n+interfaces {\n+ interface eth0 {\n+ ipv4 {\n+ address 10.0.1.1 {\n+ prefix-length 24;\n+ }\n+ autoconf;\n+ }\n+ }\n+}\nadmin@example:/config/interface/eth0/ipv4/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 169.254.1.3/16 (random)\n ipv4 10.0.1.1/24 (static)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n As shown, the link-local IPv4 address is configured with set autoconf. The presence of the autoconf container enables IPv4 link-local address assignment. The resulting address (169.254.1.3/16) is of type random (ietf-ip.yang).
The IPv4LL client also supports a request-address setting which can be used to \"seed\" the client's starting address. If the address is free it will be used, otherwise it falls back to the default algorithm.
admin@example:/config/interface/eth0/ipv4/> edit autoconf\nadmin@example:/config/interface/eth0/ipv4/autoconf/> set request-address 169.254.1.2\nadmin@example:/config/interface/eth0/ipv4/autoconf/> leave\n "},{"location":"networking/#use-of-dhcp-for-ipv4-address-assignment","title":"Use of DHCP for IPv4 address assignment","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4\nadmin@example:/config/interface/eth0/ipv4/> set dhcp\nadmin@example:/config/interface/eth0/ipv4/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 10.1.2.100/24 (dhcp)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n The resulting address (10.1.2.100/24) is of type dhcp.
To configure DHCP client options, such as sending a specific hostname to the server, you can specify options with values:
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4 dhcp\nadmin@example:/config/interface/eth0/ipv4/dhcp/> set option hostname value myhost\nadmin@example:/config/interface/eth0/ipv4/dhcp/> show\noption hostname {\n value myhost;\n}\nadmin@example:/config/interface/eth0/ipv4/dhcp/> leave\nadmin@example:/>\n Tip
The special value auto can be used with the hostname option to automatically use the configured system hostname.
Other useful DHCP options include:
client-id - Send a specific client identifier to the server route-preference - Set the administrative distance for DHCP-learned routes (default: 5) For advanced usage with vendor-specific options, see the YANG model.
"},{"location":"networking/#use-of-dhcpv6-for-ipv6-address-assignment","title":"Use of DHCPv6 for IPv6 address assignment","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set dhcp\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8::42/128 (dhcp)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n The resulting address (2001:db8::42/128) is of type dhcp.
To configure DHCPv6 client options, such as requesting prefix delegation for downstream networks, you can specify options:
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6 dhcp\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set option ia-pd\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set option dns-server\nadmin@example:/config/interface/eth0/ipv6/dhcp/> show\noption dns-server;\noption ia-pd;\nadmin@example:/config/interface/eth0/ipv6/dhcp/> leave\nadmin@example:/>\n For stateless DHCPv6 (used with SLAAC to get only configuration information):
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6 dhcp\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set information-only true\nadmin@example:/config/interface/eth0/ipv6/dhcp/> show\ninformation-only true;\noption dns-server;\noption domain-search;\nadmin@example:/config/interface/eth0/ipv6/dhcp/> leave\nadmin@example:/>\n Other useful DHCPv6 options include:
duid - Set a specific DHCPv6 Unique Identifier (auto-generated by default) client-fqdn - Request the server to update DNS records with client's FQDN route-preference - Set the administrative distance for DHCPv6-learned routes (default: 5) For advanced usage with vendor-specific options, see the YANG model.
"},{"location":"networking/#disabling-ipv6-link-local-addresses","title":"Disabling IPv6 link-local address(es)","text":"The (only) way to disable IPv6 link-local addresses is by disabling IPv6 on the interface.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set enabled false\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#static-ipv6-address","title":"Static IPv6 address","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set address 2001:db8::1 prefix-length 64\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8::1/64 (static)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#stateless-auto-configuration-of-global-ipv6-address","title":"Stateless Auto-configuration of Global IPv6 Address","text":"Stateless address auto-configuration of global addresses is enabled by default. The address is formed by concatenating the network prefix advertised by the router (here 2001:db8:0:1::0/64) and the interface identifier. The resulting address is of type link-layer, as it is formed based on the interface identifier (ietf-ip.yang).
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:0:ff:fe00:0/64 (link-layer)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Disabling auto-configuration of global IPv6 addresses can be done as shown below.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set autoconf create-global-addresses false\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#random-link-identifiers-for-ipv6-stateless-autoconfiguration","title":"Random Link Identifiers for IPv6 Stateless Autoconfiguration","text":"By default, the auto-configured link-local and global IPv6 addresses are formed from a link-identifier based on the MAC address.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:0:ff:fe00:0/64 (link-layer)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n To avoid revealing identity information in the IPv6 address, it is possible to specify use of a random identifier (ietf-ip.yang and RFC8981).
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set autoconf create-temporary-addresses true\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:b705:8374:638e:74a8/64 (random)\n ipv6 fe80::ad3d:b274:885a:9ffb/64 (random)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Both the link-local address (fe80::) and the global address (2001:) have changed type to random.
"},{"location":"networking/#ipv4-forwarding","title":"IPv4 forwarding","text":"To be able to route (static or dynamic) on the interface it is required to enable forwarding. This setting controls if packets received on this interface can be forwarded.
admin@example:/config/> edit interface eth0\nadmin@example:/config/interface/eth0/> set ipv4 forwarding\nadmin@example:/config/interface/eth0/> leave\nadmin@example:/>\n "},{"location":"networking/#ipv6-forwarding","title":"IPv6 forwarding","text":"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.
The following table shows the system IPv6 features that the forwarding setting control when it is Enabled or *Disabled:
IPv6 Feature Enabled Disabled IsRouter set in Neighbour Advertisements Yes No Transmit Router Solicitations No Yes Router Advertisements are ignored Yes Yes Accept Redirects No Yes admin@example:/config/> edit interface eth0\nadmin@example:/config/interface/eth0/> set ipv6 forwarding\nadmin@example:/config/interface/eth0/> leave\nadmin@example:/>\n "},{"location":"networking/#routing-support","title":"Routing support","text":"Currently supported YANG models:
YANG Model Description ietf-routing Base model for all other models ietf-ipv4-unicast-routing Static IPv4 unicast routing ietf-ipv6-unicast-routing Static IPv6 unicast routing ietf-ospf OSPF routing infix-routing Infix deviations and extensions The base model, ietf-routing, is where all the other models hook in. It is used to set configuration and read operational status (RIB tables) in the other models.
Note
The standard IETF routing models allows multiple instances, but Infix currently only support one instance per routing protocol! In the examples presented here, the instance name default is used.
"},{"location":"networking/#ipv4-static-routes","title":"IPv4 Static routes","text":"The standard IETF model for static routes reside under the static control plane protocol. For our examples we use the instance name default, you can use any name.
For a route with destination 192.168.200.0/24 via 192.168.1.1:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv4\nadmin@example:/config/routing/\u2026/ipv4/> set route 192.168.200.0/24 next-hop next-hop-address 192.168.1.1\nadmin@example:/config/routing/\u2026/ipv4/> leave\nadmin@example:/>\n For a \"floating\" static route with destination 10.0.0.0/16 via a backup router 192.168.1.1, using the highest possible distance:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv4\nadmin@example:/config/routing/\u2026/ipv4/> set route 10.0.0.0/16 next-hop next-hop-address 192.168.1.1 route-preference 254\nadmin@example:/config/routing/\u2026/ipv4/> leave\nadmin@example:/>\n Tip
Remember to enable IPv4 forwarding for the interfaces you want to route between.
"},{"location":"networking/#ipv6-static-routes","title":"IPv6 Static routes","text":"admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv6\nadmin@example:/config/routing/\u2026/ipv6/> set route 2001:db8:3c4d:200::/64 next-hop next-hop-address 2001:db8:3c4d:1::1\nadmin@example:/config/routing/\u2026/ipv6/> leave\nadmin@example:/>\n "},{"location":"networking/#ospfv2-routing","title":"OSPFv2 Routing","text":"The system supports OSPF dynamic routing for IPv4, i.e., OSPFv2. To enable OSPF and set one active interface in area 0:
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface e0 enabled\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n Tip
Remember to enable IPv4 forwarding for all the interfaces you want to route between.
"},{"location":"networking/#ospf-area-types","title":"OSPF area types","text":"In addition to regular OSPF areas, area types NSSA and Stub are also supported. To configure an NSSA area with summary routes:
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.1 area-type nssa-area\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.1 summary true\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n "},{"location":"networking/#bidirectional-forwarding-detection-bfd","title":"Bidirectional Forwarding Detection (BFD)","text":"It is possible to enable BFD per OSPF interface to speed up detection of link loss.
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface e0 bfd enabled true\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n "},{"location":"networking/#ospf-interface-settings","title":"OSPF interface settings","text":"We have already seen how to enable OSPF per interface (enabled true) and BFD for OSPF per interface (bfd enabled true). These and other OSPF interface settings are done in context of an OSFP area, e.g., area 0.0.0.0. Available commands can be listed using the ? mark.
admin@example:/config/routing/\u2026/> edit ospf area 0.0.0.0\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/> edit interface e0\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set ?\n bfd BFD interface configuration.\n cost Interface's cost.\n dead-interval Interval after which a neighbor is declared down\n enabled Enables/disables the OSPF protocol on the interface.\n hello-interval Interval between Hello packets (seconds). It must\n interface-type Interface type.\n passive Enables/disables a passive interface. A passive\n retransmit-interval Interval between retransmitting unacknowledged Link\n transmit-delay Estimated time needed to transmit Link State Update\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set\n For example, setting the OSPF interface type to point-to-point for an Ethernet interface can be done as follows.
admin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set interface-type point-to-point\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/>\n "},{"location":"networking/#ospf-global-settings","title":"OSPF global settings","text":"In addition to area and interface specific settings, OSPF provides global settings for route redistribution and OSPF router identifier.
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set ?\n area List of OSPF areas.\n default-route-advertise Distribute default route to network\n explicit-router-id Defined in RFC 2328. A 32-bit number\n redistribute Redistribute protocols into OSPF\nadmin@example:/config/routing/\u2026/ospf/> set\n Explicit router ID: By default the router will pick an IP address from one of its OSPF interfaces as OSPF router ID. An explicit ID is used to get a deterministic behavior, e.g., set explicit-router-id 1.1.1.1. Redistribution: set redistribute static and set redistribute connected can be used to include static or connected routes into the OSPF routing domain. These routes are redistributed as external type-2 (E2) routes. Advertising default route: An OSPF router can be made to distribute a default route into the OSPF domain by command set default-route-advertise enabled. This route is distributed as long as the router itself has an active default route in its routing table. By adding command set default-route-advertise always the router will distribute a default route even when it lacks a default route. The default route will be distributed as an external type-2 (E2) route. "},{"location":"networking/#debug-ospfv2","title":"Debug OSPFv2","text":"Using NETCONF and the YANG model ietf-routing it is possible to read the OSPF routing table, neighbors and more, that may be useful for debugging the OSPFv2 setup. The CLI has various OSPF status commands such as show ospf neighbor, show ospf interface and show ospf routes.
admin@example:/> show ospf neighbor\n\nNeighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL\n10.1.1.2 1 Full/- 3h46m59s 30.177s 10.1.1.2 e0:10.1.1.1 0 0 0\n10.1.1.3 1 Full/- 3h46m55s 34.665s 10.1.1.3 e1:10.1.1.1 0 0 0\n\nadmin@example:/>\n For more detailed troubleshooting, OSPF debug logging can be enabled to capture specific protocol events. Debug messages are written to the routing log file (/var/log/routing).
Caution
Debug logging significantly increases log output and may impact performance. Only enable debug categories needed for troubleshooting, and disable them when done.
To enable specific OSPF debug categories:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf debug\nadmin@example:/config/routing/\u2026/ospf/debug/> set bfd true\nadmin@example:/config/routing/\u2026/ospf/debug/> set nsm true\nadmin@example:/config/routing/\u2026/ospf/debug/> leave\nadmin@example:/>\n Available debug categories include:
bfd: BFD (Bidirectional Forwarding Detection) events packet: Detailed packet debugging (all OSPF packets) ism: Interface State Machine events nsm: Neighbor State Machine events default-information: Default route origination nssa: Not-So-Stubby Area events All debug options are disabled by default. Refer to the infix-routing YANG model for the complete list of available debug options.
To view current debug settings:
admin@example:/> show running-config routing control-plane-protocol\n To disable all debug logging, simply delete the debug settings or set all options back to false:
admin@example:/> configure\nadmin@example:/config/> delete routing control-plane-protocol ospfv2 name default ospf debug\nadmin@example:/config/> leave\nadmin@example:/>\n "},{"location":"networking/#view-routing-table","title":"View routing table","text":"The routing table can be inspected from the operational datastore, XPath /routing/ribs, using sysrepocfg, NETCONF/RESTCONF, or using the CLI.
"},{"location":"networking/#ipv4-routing-table","title":"IPv4 routing table","text":"This CLI example shows the IPv4 routing table with a few connected routes and some routes learned from OSPF. See the next section for an explanation of route preferences (PREF).
The > at the start of a line marks a selected route (in the IETF YANG model referred to as active), if there are more than one route with the same destination the * marks the next-hop used and installed in the kernel FIB (the YANG model refers to this as installed).
admin@example:/> show ip route\n DESTINATION PREF NEXT-HOP PROTO UPTIME\n>* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 4h2m43s\n>* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 4h2m43s\n 10.0.0.3/32 110/0 lo ospfv2 4h2m57s\n>* 10.0.0.3/32 0/0 lo direct 4h2m58s\n 10.0.13.0/30 110/2000 e5 ospfv2 4h2m57s\n>* 10.0.13.0/30 0/0 e5 direct 4h2m58s\n 10.0.23.0/30 110/1 e6 ospfv2 4h2m57s\n>* 10.0.23.0/30 0/0 e6 direct 4h2m58s\n 192.168.3.0/24 110/1 e2 ospfv2 4h2m57s\n>* 192.168.3.0/24 0/0 e2 direct 4h2m58s\nadmin@example:/>\n "},{"location":"networking/#ipv6-routing-table","title":"IPv6 routing table","text":"This CLI example show the IPv6 routing table.
admin@example:/> show ipv6 route\n DESTINATION PREF NEXT-HOP PROTO UPTIME\n>* ::/0 1/0 2001:db8:3c4d:50::1 static 0h1m20s\n>* 2001:db8:3c4d:50::/64 0/0 e6 direct 0h1m20s\n>* 2001:db8:3c4d:200::1/128 0/0 lo direct 0h1m20s\n * fe80::/64 0/0 e7 direct 0h1m20s\n * fe80::/64 0/0 e6 direct 0h1m20s\n * fe80::/64 0/0 e5 direct 0h1m20s\n * fe80::/64 0/0 e4 direct 0h1m20s\n * fe80::/64 0/0 e3 direct 0h1m20s\n * fe80::/64 0/0 e2 direct 0h1m20s\n>* fe80::/64 0/0 e1 direct 0h1m20s\nadmin@example:/>\n "},{"location":"networking/#route-preference","title":"Route Preference","text":"The operating system leverages FRRouting (Frr) as routing engine for both static and dynamic routing. Even routes injected from a DHCP client, and IPv4 link-local (IPv4) routes, are injected into Frr to let it weigh all routes before installing them into the kernel routing table (sometimes referred to as FIB).
Routes have different weights made up from a distance and a metric. The kernel routing table only talks about metric, which unfortunately is not the same -- this is one of the reasons why the term route preference is used instead. It is recommended to use the CLI, or any of the other previously mentioned YANG based front-ends, to inspect the routing table.
Default distances used (lower numeric value wins):
Distance Protocol 0 Kernel routes, i.e., connected routes 1 Static routes 5 DHCP routes 110 OSPF 254 IPv4LL (ZeroConf) device routes 255 Route will not be used or redistributed Hence, a route learned from OSPF may be overridden by a static route set locally. By default, even a route to the same destination, but with a different next-hop, learned from a DHCP server wins over an OSPF route.
The distance used for static routes and DHCP routes can be changed by setting a different routing preference value.
Note
The kernel metric is an unsigned 32-bit value, which is read by Frr as (upper) 8 bits distance and 24 bits metric. But it does not write it back to the kernel FIB this way, only selected routes are candidates to be installed in the FIB by Frr.
"},{"location":"networking/#source-protocol","title":"Source protocol","text":"The source protocol describes the origin of the route.
Protocol Description kernel Added when setting a subnet address on an interface static User created, learned from DHCP, or IPv4LL ospfv2 Routes learned from OSPFv2 The YANG model ietf-routing support multiple ribs but only two are currently supported, namely ipv4 and ipv6.
(source MAC XOR destination MAC XOR EtherType) MODULO num_links \u21a9
Link-local IPv6 addresses are implicitly enabled when enabling IPv6. IPv6 can be enabled/disabled per interface in the ietf-ip YANG model.\u00a0\u21a9
For example, IPv4 groups are mapped to MAC multicast addresses by mapping the low-order 23-bits of the IP address in the low-order 23 bits of the Ethernet address 01:00:5E:00:00:00. Meaning, more than one IP multicast group maps to the same MAC multicast group.\u00a0\u21a9
A YANG deviation was previously used to make it possible to set phys-address, but this has been replaced with the more flexible custom-phys-address.\u00a0\u21a9
MAC bridges on Marvell Linkstreet devices are currently limited to a single MAC database, this may be a problem if the same MAC address appears in different MAC bridges.\u00a0\u21a9
Ethernet counters are described in ieee802-ethernet-interface.yang and infix-ethernet-interface.yang. There is a dedicated document on Ethernet Counters that provide additional details on the statistics support.\u00a0\u21a9
"},{"location":"override-package/","title":"Developing with Buildroot","text":""},{"location":"override-package/#package-override","title":"Package Override","text":"This guide demonstrates how the local.mk file is utilized to override a Buildroot package, which can be very useful when fixing bugs, adding features, or evaluating new releases of packages. As an example we use tcpdump to illustrate this process.
Tip
For a comprehensive guide to utilizing Buildroot during development, including the <pkg>_OVERRIDE_SRCDIR mechanism, shown below, please see Using Buildroot during development in the official docs.
"},{"location":"override-package/#setup","title":"Setup","text":"Since the output/ directory is often wiped for rebuilds, make sure you keep the file local.mk in the top directory and only symlink it to your build directory:
~$ cd infix/\n~/infix(main)$ touch local.mk\n~/infix/output(main)$ ln -s ../local.mk .\n~/infix/output(main)$ cd ..\n "},{"location":"override-package/#override","title":"Override","text":"Now edit the file:
~/infix(main)$ editor local.mk\n Add an override for tcpdump. The file is a Makefile snippet so you can add a lot of things. Comment out lines with the UNIX comment # character if needed:
TCPDUMP_OVERRIDE_SRCDIR = /path/to/tcpdump/repo\n "},{"location":"override-package/#building","title":"Building","text":"The execution of make tcpdump-rebuild all triggers a process where Buildroot synchronizes the tcpdump source code from the specified override directory to output/build/tcpdump-custom, followed by the rebuilding of the entire project.
~/infix$(main)$ make tcpdump-rebuild all\n Buildroot follows a process of downloading and processing tarballs: extraction, configuration, compilation, and installation. The source for each package is extracted to a temporary build directory:
output/build/<package>-<version> # e.g., tcpdump-4.99.4\n Let's have a look at what we got:
~/infix$(main)$ ll /output/build/ | grep tcpdump\ndrwxr-xr-x 7 group user 20480 Nov 10 18:26 tcpdump-4.99.4/\ndrwxr-xr-x 7 group user 12288 Nov 10 18:28 tcpdump-custom/\n As long as your local override is in place, Buildroot will use your custom version.
Note
Remember, the build directory is ephemeral, so be careful to change any of the files therein. It can be useful though during debugging, but just make sure to learn the difference between the various Buildroot commands to build, clean, reconfigure, etc.
"},{"location":"qos/","title":"Quality of Service","text":""},{"location":"qos/#quality-of-service","title":"Quality of Service","text":"On occasion, most networks will experience congestion due to some extraordinary load being placed upon it. If the load is transient, switches and routers may be able to absorb such bursts of traffic by queuing packets in internal memories. However, if the load is sustained over long periods of time, queues will fill up and packets will start to be dropped. When such situations arise, it is the job of the network's Quality of Service (QoS) policy to define which packets to drop and which ones to prioritize, such that critical services remain operational.
"},{"location":"qos/#hardware-forwarded-traffic","title":"Hardware Forwarded Traffic","text":"The default QoS policy for flows which are offloaded to a switching ASIC is defined by the hardware defaults of the device in question.
"},{"location":"qos/#marvell-linkstreet","title":"Marvell LinkStreet","text":"This family of devices, sometimes also referred to as SOHO, are managed by the mv88e6xxx driver in the Linux kernel. While older chips in this family where limited to 4 output queues per port, this documentation is only valid for newer generations with 8 output queues per port.
The picture illustrates packets having their priority determined at ingress, here interface e1 and e3. In this example, both packets are forwarded to the same outgoing interface (e2), subject to output queueing. The sections below provides more information on these topics.
"},{"location":"qos/#default-policy","title":"Default Policy","text":""},{"location":"qos/#queueing","title":"Queueing","text":"Both layer 2 (VLAN PCP) and layer 3 (IP DSCP) priority marks are considered when selecting the output queue of an incoming frame. PCP to queue mapping is done 1:1. For IP packets, the 3 most significant bits of the DSCP is used to select the queue:
PCP DSCP \u21d2 Queue Weight 0 0-7 \u21d2 0 1 1 8-15 \u21d2 1 2 2 16-23 \u21d2 2 3 3 24-31 \u21d2 3 6 4 32-39 \u21d2 4 12 5 40-47 \u21d2 5 17 6 48-55 \u21d2 6 25 7 56-63 \u21d2 7 33 For packets containing both a VLAN tag and an IP header, PCP priority takes precedence over DSCP priority. In cases where neither are available, packets are always assigned to queue 0.
Each port's set of 8 egress queues operate on a Weighted Round Robin (WRR) schedule, using the weights listed in the table above. The sum of all weights adds up to 99, meaning that the weight of any given queue is roughly equivalent to the percentage of the available bandwidth reserved for it.
"},{"location":"qos/#marking","title":"Marking","text":"Any priority marks available on ingress are left unmodified when the frame egresses an output port. In the case when an IP packet ingresses without a VLAN tag, and is to egress with a VLAN tag, its PCP is set to the 3 most significant bits of it. If no priority information is available in the frame on ingress (i.e. untagged non-IP), then packets will egress out of tagged ports with PCP set to 0.
"},{"location":"qos/#software-forwarded-traffic","title":"Software Forwarded Traffic","text":"For packets which are processed by a CPU, i.e. typically routed traffic, and bridged traffic between interfaces that do not belong to the same hardware switching domain, an nftables container can be used to define a QoS policy.
For VLAN interfaces, Infix provides support for mapping the Priority Code Point (PCP) to internal priority on ingress, and the reverse on egress.
These ingress-qos and egress-qos settings are done per VLAN, both defaulting to '0'. The example below shows how to keep the PCP priority for packets being routed between two VLAN interfaces.
admin@example:/config/> edit interface e1.10\nadmin@example:/config/interface/e1.10/> set vlan ingress-qos priority from-pcp \nadmin@example:/config/interface/e1.10/> up\nadmin@example:/config/> edit interface e1.20\nadmin@example:/config/interface/e1.20/> set vlan egress-qos pcp from-priority \nadmin@example:/config/interface/e1.20/> leave\nadmin@example:/> \n "},{"location":"qos/#a-complex-example","title":"A complex example","text":"The picture below shows a packet flow being subject both to software forwarding and hardware offloading.
"},{"location":"scripting-netconf/","title":"With NETCONF","text":""},{"location":"scripting-netconf/#scripting-with-netconf","title":"Scripting with NETCONF","text":"NETCONF (Network Configuration Protocol) provides a standardized mechanism for managing network devices using XML-based RPC operations over SSH (port 830). This guide shows practical examples for interacting with Infix using NETCONF.
NETCONF offers robust capabilities for network automation:
Transactional operations: Validate before commit, rollback on error Fine-grained locking: Prevent concurrent configuration conflicts Structured data: XML with YANG schema validation Standardized operations: Get, edit-config, copy-config, etc. "},{"location":"scripting-netconf/#netconf-vs-restconf","title":"NETCONF vs RESTCONF","text":"Both protocols use the same YANG data models, but differ in approach:
Feature NETCONF RESTCONF Transport SSH HTTPS Encoding XML JSON/XML Operations RPC-based REST/HTTP methods Best for Automation scripts Web integration, simple queries Choose NETCONF when you need:
Transactional configuration changes Configuration validation before commit Locking to prevent concurrent changes Integration with existing NETCONF tooling Choose RESTCONF for:
Simple queries and updates Web-based applications When you prefer JSON over XML RESTful API patterns "},{"location":"scripting-netconf/#quick-start-with-netopeer2-cli","title":"Quick Start with netopeer2-cli","text":"netopeer2-cli is an interactive NETCONF client, useful for learning and testing. Install it:
~$ sudo apt install netopeer2-cli\n Connect to your Infix device:
~$ netopeer2-cli\n> connect --host example.local --login admin\nadmin@example.local password:\n> status\nCurrent NETCONF session:\n ID : 1\n Host : example.local\n Port : 830\n Transport : SSH\n Capabilities: 35\n "},{"location":"scripting-netconf/#basic-operations-in-netopeer2-cli","title":"Basic Operations in netopeer2-cli","text":"Get entire configuration:
> get-config --source running\n Get specific subtree (hostname):
> get-config --source running --filter-xpath /system/hostname\n<data xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>example</hostname>\n </system>\n</data>\n Get operational state:
> get --filter-xpath /interfaces\n Edit configuration:
> edit-config --target candidate --config=/tmp/config.xml\n> commit\n Disconnect:
> disconnect\n> quit\n "},{"location":"scripting-netconf/#discovery-common-patterns","title":"Discovery & Common Patterns","text":"Before working with specific configuration items, you often need to discover what exists on the system. This section shows common discovery patterns and practical workflows.
"},{"location":"scripting-netconf/#discovering-available-interfaces","title":"Discovering Available Interfaces","text":"List all interface names:
<rpc message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name/>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Using netopeer2-cli:
> get --filter-xpath /interfaces/interface/name\n This returns all interface names, useful for iterating through interfaces in scripts.
"},{"location":"scripting-netconf/#get-all-yang-capabilities","title":"Get All YANG Capabilities","text":"Discover which YANG modules and features are available:
> status\n Or programmatically via the <hello> message capabilities received during connection establishment.
"},{"location":"scripting-netconf/#get-entire-running-configuration","title":"Get Entire Running Configuration","text":"Useful for exploration or backup:
<rpc message-id=\"2\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get-config>\n <source>\n <running/>\n </source>\n </get-config>\n</rpc>\n Using netopeer2-cli:
> get-config --source running\n "},{"location":"scripting-netconf/#common-workflow-patterns","title":"Common Workflow Patterns","text":""},{"location":"scripting-netconf/#pattern-1-find-interface-by-ip-address","title":"Pattern 1: Find interface by IP address","text":"Get all interfaces with their IPs, then filter:
> get --filter-xpath /interfaces\n Parse the XML output to find which interface has the desired IP.
"},{"location":"scripting-netconf/#pattern-2-check-which-interfaces-are-down","title":"Pattern 2: Check which interfaces are down","text":"> get --filter-xpath /interfaces/interface/oper-status\n Look for interfaces with <oper-status>down</oper-status>.
"},{"location":"scripting-netconf/#pattern-3-get-interface-statistics-for-monitoring","title":"Pattern 3: Get interface statistics for monitoring","text":"<rpc message-id=\"3\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <statistics/>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Returns in-octets, out-octets, in-errors, out-errors for all interfaces.
"},{"location":"scripting-netconf/#scripting-with-netconf-client","title":"Scripting with netconf-client","text":"netconf-client is a lightweight Python-based NETCONF client designed for scripting and automation.
"},{"location":"scripting-netconf/#installation","title":"Installation","text":"~$ pip install netconf-client\n Or clone and install from source:
~$ git clone https://github.com/wires-se/netconf-client.git\n~$ cd netconf-client\n~$ pip install .\n "},{"location":"scripting-netconf/#basic-usage","title":"Basic Usage","text":"The client provides a simple command-line interface:
~$ netconf-client --host example.local --user admin --password admin <operation>\n Common operations:
get-config - Retrieve configuration edit-config - Modify configuration get - Retrieve operational state copy-config - Copy between datastores lock/unlock - Lock datastores commit - Commit candidate configuration "},{"location":"scripting-netconf/#python-api","title":"Python API","text":"You can also use netconf-client as a Python library:
from netconf_client.client import NetconfClient\n\n# Connect\nclient = NetconfClient(\n host='example.local',\n username='admin',\n password='admin'\n)\n\n# Get configuration\nconfig = client.get_config(source='running')\nprint(config)\n\n# Edit configuration\nxml_config = \"\"\"\n<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\n\"\"\"\nclient.edit_config(target='candidate', config=xml_config)\nclient.commit()\n\n# Close connection\nclient.close()\n "},{"location":"scripting-netconf/#configuration-examples","title":"Configuration Examples","text":""},{"location":"scripting-netconf/#read-hostname","title":"Read Hostname","text":"XML request:
<rpc message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get-config>\n <source>\n <running/>\n </source>\n <filter type=\"subtree\">\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname/>\n </system>\n </filter>\n </get-config>\n</rpc>\n Using netopeer2-cli:
> get-config --source running --filter-xpath /system/hostname\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get-config --source running --xpath /system/hostname\n Response:
<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <data>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>example</hostname>\n </system>\n </data>\n</rpc-reply>\n "},{"location":"scripting-netconf/#set-hostname","title":"Set Hostname","text":"XML request:
<rpc message-id=\"2\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <edit-config>\n <target>\n <candidate/>\n </target>\n <config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n </config>\n </edit-config>\n</rpc>\n Using netopeer2-cli:
Save the config to /tmp/hostname.xml:
<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\n Then apply:
> edit-config --target candidate --config=/tmp/hostname.xml\n> commit\n Using netconf-client:
~$ cat > hostname.xml <<EOF\n<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\nEOF\n\n~$ netconf-client --host example.local --user admin \\\n edit-config --target candidate --config hostname.xml\n\n~$ netconf-client --host example.local --user admin commit\n "},{"location":"scripting-netconf/#add-ip-address-to-interface","title":"Add IP Address to Interface","text":"Save the config to ip-config.xml:
<config>\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name>eth0</name>\n <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n <address>\n <ip>192.168.1.100</ip>\n <prefix-length>24</prefix-length>\n </address>\n </ipv4>\n </interface>\n </interfaces>\n</config>\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n edit-config --target candidate --config ip-config.xml\n~$ netconf-client --host example.local --user admin commit\n "},{"location":"scripting-netconf/#copy-running-to-startup","title":"Copy Running to Startup","text":"XML request:
<rpc message-id=\"3\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <copy-config>\n <source>\n <running/>\n </source>\n <target>\n <startup/>\n </target>\n </copy-config>\n</rpc>\n Using netopeer2-cli:
> copy-config --source running --target startup\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n copy-config --source running --target startup\n "},{"location":"scripting-netconf/#operational-data-examples","title":"Operational Data Examples","text":""},{"location":"scripting-netconf/#read-interface-state","title":"Read Interface State","text":"XML request:
<rpc message-id=\"4\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name>eth0</name>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Using netopeer2-cli:
> get --filter-xpath /interfaces/interface[name='eth0']\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/interfaces/interface[name='eth0']\"\n This returns operational state including admin/oper status, statistics, MAC address, MTU, and IP addresses.
"},{"location":"scripting-netconf/#read-all-interfaces","title":"Read All Interfaces","text":"Using netopeer2-cli:
> get --filter-xpath /interfaces\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath /interfaces\n "},{"location":"scripting-netconf/#read-routing-table","title":"Read Routing Table","text":"Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/routing/ribs/rib[name='ipv4-default']\"\n "},{"location":"scripting-netconf/#read-ospf-neighbors","title":"Read OSPF Neighbors","text":"Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/routing/control-plane-protocols/control-plane-protocol[type='ietf-ospf:ospfv2'][name='default']/ietf-ospf:ospf\"\n "},{"location":"scripting-netconf/#advanced-scripting","title":"Advanced Scripting","text":""},{"location":"scripting-netconf/#python-script-backup-configuration","title":"Python Script: Backup Configuration","text":"#!/usr/bin/env python3\nfrom netconf_client.client import NetconfClient\nfrom datetime import datetime\nimport sys\n\ndef backup_config(host, user, password, output_file):\n try:\n # Connect\n client = NetconfClient(host=host, username=user, password=password)\n\n # Get running config\n config = client.get_config(source='running')\n\n # Save to file with timestamp\n timestamp = datetime.now().strftime('%Y%m%d-%H%M%S')\n filename = f\"{output_file}-{timestamp}.xml\"\n\n with open(filename, 'w') as f:\n f.write(config)\n\n print(f\"Configuration backed up to {filename}\")\n\n client.close()\n return 0\n\n except Exception as e:\n print(f\"Error: {e}\", file=sys.stderr)\n return 1\n\nif __name__ == '__main__':\n if len(sys.argv) != 5:\n print(f\"Usage: {sys.argv[0]} <host> <user> <password> <output_prefix>\")\n sys.exit(1)\n\n sys.exit(backup_config(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))\n Usage:
~$ chmod +x backup.py\n~$ ./backup.py example.local admin admin config-backup\nConfiguration backed up to config-backup-20250102-143022.xml\n "},{"location":"scripting-netconf/#python-script-monitor-interface-statistics","title":"Python Script: Monitor Interface Statistics","text":"#!/usr/bin/env python3\nfrom netconf_client.client import NetconfClient\nimport xml.etree.ElementTree as ET\nimport time\nimport sys\n\ndef get_interface_stats(client, interface):\n \"\"\"Get interface statistics\"\"\"\n xpath = f\"/interfaces/interface[name='{interface}']/statistics\"\n data = client.get(filter_xpath=xpath)\n\n # Parse XML to extract counters\n root = ET.fromstring(data)\n ns = {'if': 'urn:ietf:params:xml:ns:yang:ietf-interfaces'}\n\n stats = {}\n for stat in root.findall('.//if:statistics/*', ns):\n stats[stat.tag.split('}')[1]] = int(stat.text)\n\n return stats\n\ndef monitor_interface(host, user, password, interface, interval=5):\n \"\"\"Monitor interface statistics\"\"\"\n client = NetconfClient(host=host, username=user, password=password)\n\n print(f\"Monitoring {interface} on {host} (Ctrl-C to stop)\")\n print(f\"{'Time':<20} {'RX Packets':<15} {'TX Packets':<15} {'RX Bytes':<15} {'TX Bytes':<15}\")\n print(\"-\" * 80)\n\n try:\n while True:\n stats = get_interface_stats(client, interface)\n timestamp = time.strftime('%Y-%m-%d %H:%M:%S')\n\n print(f\"{timestamp:<20} \"\n f\"{stats.get('in-unicast-pkts', 0):<15} \"\n f\"{stats.get('out-unicast-pkts', 0):<15} \"\n f\"{stats.get('in-octets', 0):<15} \"\n f\"{stats.get('out-octets', 0):<15}\")\n\n time.sleep(interval)\n\n except KeyboardInterrupt:\n print(\"\\nMonitoring stopped\")\n finally:\n client.close()\n\nif __name__ == '__main__':\n if len(sys.argv) < 5:\n print(f\"Usage: {sys.argv[0]} <host> <user> <password> <interface> [interval]\")\n sys.exit(1)\n\n interval = int(sys.argv[5]) if len(sys.argv) > 5 else 5\n monitor_interface(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], interval)\n Usage:
~$ chmod +x monitor.py\n~$ ./monitor.py example.local admin admin eth0 2\nMonitoring eth0 on example.local (Ctrl-C to stop)\nTime RX Packets TX Packets RX Bytes TX Bytes\n--------------------------------------------------------------------------------\n2025-01-02 14:35:10 12453 8932 1847392 892341\n2025-01-02 14:35:12 12489 8967 1851204 895673\n...\n "},{"location":"scripting-netconf/#shell-script-batch-configuration","title":"Shell Script: Batch Configuration","text":"#!/bin/bash\n# Apply configuration to multiple devices\n\nDEVICES=\"device1.local device2.local device3.local\"\nUSER=\"admin\"\nPASSWORD=\"admin\"\nCONFIG_FILE=\"$1\"\n\nif [ -z \"$CONFIG_FILE\" ]; then\n echo \"Usage: $0 <config.xml>\"\n exit 1\nfi\n\nfor device in $DEVICES; do\n echo \"Configuring $device...\"\n\n # Edit candidate\n netconf-client --host \"$device\" --user \"$USER\" --password \"$PASSWORD\" \\\n edit-config --target candidate --config \"$CONFIG_FILE\"\n\n if [ $? -eq 0 ]; then\n # Commit if edit succeeded\n netconf-client --host \"$device\" --user \"$USER\" --password \"$PASSWORD\" commit\n echo \" \u2713 $device configured successfully\"\n else\n echo \" \u2717 $device configuration failed\"\n fi\ndone\n "},{"location":"scripting-netconf/#other-netconf-tools","title":"Other NETCONF Tools","text":""},{"location":"scripting-netconf/#ncclient-python","title":"ncclient (Python)","text":"Popular Python library for NETCONF:
~$ pip install ncclient\n Example:
from ncclient import manager\n\nwith manager.connect(host='example.local', port=830,\n username='admin', password='admin',\n hostkey_verify=False) as m:\n # Get config\n c = m.get_config(source='running')\n print(c)\n "},{"location":"scripting-netconf/#ansible","title":"Ansible","text":"Ansible includes NETCONF modules for automation:
- name: Get interface config\n netconf_get:\n source: running\n filter: <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/>\n "},{"location":"scripting-netconf/#cisco-tools","title":"Cisco Tools","text":" NSO (Network Services Orchestrator): Enterprise orchestration platform YANG Suite: Web-based YANG exploration and testing tool Cisco pyATS: Network test automation "},{"location":"scripting-netconf/#troubleshooting","title":"Troubleshooting","text":""},{"location":"scripting-netconf/#enable-netconf-debugging","title":"Enable NETCONF Debugging","text":"For netconf-client, use verbose mode:
~$ netconf-client --host example.local --user admin --verbose get-config\n For netopeer2-cli, enable debug output:
> debug 1\n> get-config --source running\n "},{"location":"scripting-netconf/#common-issues","title":"Common Issues","text":"Connection refused:
Verify SSH is running on port 830: ssh -p 830 admin@example.local Check firewall rules Authentication failed:
Verify credentials Check user has NETCONF access permissions Operation not supported:
Verify NETCONF capability: netopeer2-cli \u2192 status \u2192 check capabilities Some operations require specific YANG modules "},{"location":"scripting-netconf/#references","title":"References","text":" NETCONF Protocol (RFC 6241) netconf-client on GitHub netopeer2 Documentation YANG Data Modeling Language (RFC 7950) ietf-interfaces YANG module ietf-system YANG module ncclient Documentation "},{"location":"scripting-prod/","title":"Production Testing","text":""},{"location":"scripting-prod/#scripting-for-production-tests","title":"Scripting for Production Tests","text":"This document shows how to set up and remotely script devices with a focus on production testing.
"},{"location":"scripting-prod/#vlan-snake","title":"VLAN Snake","text":"As part of production tests, verification of Ethernet ports is usually expected. A common way for devices with multiple bridged Ethernet ports is to connect a test PC to two ports and send a ping traversing all ports. This can be achieved by using VLANs, on the switch, as described in this section. The resulting configuration file can be applied to the running configuration of the produced unit, e.g, use config file restore as described previously.
In this example we assume a 10 port switch, with ports e1-e10. The following VLAN configuration and cable connections will be used:
VLAN & Ports Connect VLAN 10: e1 & e2 e2 <=> e3 VLAN 20: e3 & e4 e4 <=> e5 VLAN 30: e5 & e6 e6 <=> e7 VLAN 40: e7 & e8 e8 <=> e9 VLAN 50: e9 & e10 The test PC is connected to e1 and e10 via different interfaces (alternatively, two different PCs are used).
Tip
Configuration here is done via console. When configuring remotely over SSH, remember to keep one IP address (the one used for the SSH connection)! I.e., set a static IP address first, then perform the VLAN configuration step.
"},{"location":"scripting-prod/#configuration-at-start","title":"Configuration at Start","text":"Starting out, we assume a configuration where all ports are network interfaces (possibly with IPv6 enabled).
admin@example:/> show interfaces\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet LOWER-DOWN 00:53:00:06:11:01\ne2 ethernet LOWER-DOWN 00:53:00:06:11:02\ne3 ethernet LOWER-DOWN 00:53:00:06:11:03\ne4 ethernet LOWER-DOWN 00:53:00:06:11:04\ne5 ethernet LOWER-DOWN 00:53:00:06:11:05\ne6 ethernet LOWER-DOWN 00:53:00:06:11:06\ne7 ethernet LOWER-DOWN 00:53:00:06:11:07\ne8 ethernet LOWER-DOWN 00:53:00:06:11:08\ne9 ethernet LOWER-DOWN 00:53:00:06:11:09\ne10 ethernet UP 00:53:00:06:11:0a\n ipv6 fe80::0053:00ff:fe06:110a/64 (link-layer)\nadmin@example:/>\n "},{"location":"scripting-prod/#creating-bridge-and-adding-ports","title":"Creating Bridge and Adding Ports","text":"The following example creates a bridge and adds all Ethernet ports to it. On a device with layer-2 offloading (switch fabric), this sets all ports in the same VLAN. The next section sets up VLAN isolation.
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> end\nadmin@example:/config/> set interface e1 bridge-port bridge br0\nadmin@example:/config/> set interface e2 bridge-port bridge br0\nadmin@example:/config/> set interface e3 bridge-port bridge br0\nadmin@example:/config/> set interface e4 bridge-port bridge br0\nadmin@example:/config/> set interface e5 bridge-port bridge br0\nadmin@example:/config/> set interface e6 bridge-port bridge br0\nadmin@example:/config/> set interface e7 bridge-port bridge br0\nadmin@example:/config/> set interface e8 bridge-port bridge br0\nadmin@example:/config/> set interface e9 bridge-port bridge br0\nadmin@example:/config/> set interface e10 bridge-port bridge br0\nadmin@example:/config/>\n The interface status can be viewed using show interfaces after leaving configuration context. When configuring via SSH, first assign an IP address to br0 before leaving configuration context, e.g.
admin@example:/config/> set interface br0 ipv6 enabled\n This enables IPv6 SLAAC, auto-configured address. Or skip leave and stay in configuration context until you have completed all the device setup, including setting IP address.
admin@example:/config/> leave\nadmin@example:/>\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge LOWER-DOWN\n\u251c e2 bridge LOWER-DOWN\n\u251c e3 bridge LOWER-DOWN\n\u251c e4 bridge LOWER-DOWN\n\u251c e5 bridge LOWER-DOWN\n\u251c e6 bridge LOWER-DOWN\n\u251c e7 bridge LOWER-DOWN\n\u251c e8 bridge LOWER-DOWN\n\u251c e9 bridge LOWER-DOWN\n\u2514 e10 bridge FORWARDING\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"scripting-prod/#assign-vlans-to-ports","title":"Assign VLANs to Ports","text":"Now, configure VLANs as outlined previously: default VID for ingress (PVID), which is done per port, and egress mode (untagged), which is done at the bridge level. See the VLAN bridges section for more information.
admin@example:/>\nadmin@example:/> configure\nadmin@example:/config/> set interface e1 bridge-port pvid 10\nadmin@example:/config/> set interface e2 bridge-port pvid 10\nadmin@example:/config/> set interface e3 bridge-port pvid 20\nadmin@example:/config/> set interface e4 bridge-port pvid 20\nadmin@example:/config/> set interface e5 bridge-port pvid 30\nadmin@example:/config/> set interface e6 bridge-port pvid 30\nadmin@example:/config/> set interface e7 bridge-port pvid 40\nadmin@example:/config/> set interface e8 bridge-port pvid 40\nadmin@example:/config/> set interface e9 bridge-port pvid 50\nadmin@example:/config/> set interface e10 bridge-port pvid 50\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> edit bridge vlans\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e1\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e2\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e3\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e4\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e5\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e6\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e7\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e8\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e9\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e10\nadmin@example:/config/interface/br0/bridge/vlans/> leave\nadmin@example:/>\n Interface status would now should something like the following
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge LOWER-DOWN vlan:10u pvid:10\n\u251c e2 bridge LOWER-DOWN vlan:10u pvid:10\n\u251c e3 bridge LOWER-DOWN vlan:20u pvid:20\n\u251c e4 bridge LOWER-DOWN vlan:20u pvid:20\n\u251c e5 bridge LOWER-DOWN vlan:30u pvid:30\n\u251c e6 bridge LOWER-DOWN vlan:30u pvid:30\n\u251c e7 bridge LOWER-DOWN vlan:40u pvid:40\n\u251c e8 bridge LOWER-DOWN vlan:40u pvid:40\n\u251c e9 bridge LOWER-DOWN vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"scripting-prod/#connect-cables-and-test","title":"Connect Cables and Test","text":"We can now connect the PC to e1 and e10, while the other ports are patched according to above. We should see link up and FORWARDING on all ports in the bridge.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge FORWARDING vlan:10u pvid:10\n\u251c e2 bridge FORWARDING vlan:10u pvid:10\n\u251c e3 bridge FORWARDING vlan:20u pvid:20\n\u251c e4 bridge FORWARDING vlan:20u pvid:20\n\u251c e5 bridge FORWARDING vlan:30u pvid:30\n\u251c e6 bridge FORWARDING vlan:30u pvid:30\n\u251c e7 bridge FORWARDING vlan:40u pvid:40\n\u251c e8 bridge FORWARDING vlan:40u pvid:40\n\u251c e9 bridge FORWARDING vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Here we use IPv6 ping all hosts (ff02::1) on PC interface eth1 to check reachability to the other interface of the PC.
Tip
We recommend using network namespaces (Linux only) on the PC to ensure that traffic is actually sent out to the switch, rather than being looped back internally. Alternatively, use two separate PCs.
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.496 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.514 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.473 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.736 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.563 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.507 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n6 packets transmitted, 6 received, 0% packet loss, time 5108ms\nrtt min/avg/max/mdev = 0.473/0.548/0.736/0.088 ms\n~ $\n We can verify that traffic goes through the switch by disconnecting one of the patch cables, e.g., between e4 and e5
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.510 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.448 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.583 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.515 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.521 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.495 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=7 ttl=64 time=0.743 ms\n... Disconnecting patch cable, thus losing packets\n... and reconnecting again. Connectivity resumes.\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=16 ttl=64 time=0.961 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=17 ttl=64 time=0.513 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=18 ttl=64 time=0.794 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=19 ttl=64 time=0.755 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=20 ttl=64 time=0.779 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n20 packets transmitted, 12 received, 40% packet loss, time 19432ms\nrtt min/avg/max/mdev = 0.448/0.634/0.961/0.156 ms\n~ $\n "},{"location":"scripting-prod/#set-ip-address","title":"Set IP Address","text":"The configuration so far does not provide a means to connect to the switch management via SSH or NETCONF, as the switch has no IP address. The example below shows how to add the switch to VLAN 10 (as used for ports e1 and e2) and enables IPv6.
admin@example:/config/> edit interface vlan10\nadmin@example:/config/interface/vlan10/> set vlan lower-layer-if br0\nadmin@example:/config/interface/vlan10/> set ipv6 enabled\nadmin@example:/config/interface/vlan10/> show\ntype vlan;\nipv6 {\n enabled true;\n}\nvlan {\n tag-type c-vlan;\n id 10;\n lower-layer-if br0;\n}\nadmin@example:/config/interface/vlan10/>\nadmin@example:/config/interface/vlan10/> end\nadmin@example:/config/> edit interface br0 bridge vlans\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 tagged br0\nadmin@example:/config/interface/br0/bridge/vlans/> leave\nadmin@example:/>\n Interface vlan10 with an auto-configured IPv6 address should appear.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge vlan:10t\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge FORWARDING vlan:10u pvid:10\n\u251c e2 bridge FORWARDING vlan:10u pvid:10\n\u251c e3 bridge FORWARDING vlan:20u pvid:20\n\u251c e4 bridge FORWARDING vlan:20u pvid:20\n\u251c e5 bridge FORWARDING vlan:30u pvid:30\n\u251c e6 bridge FORWARDING vlan:30u pvid:30\n\u251c e7 bridge FORWARDING vlan:40u pvid:40\n\u251c e8 bridge FORWARDING vlan:40u pvid:40\n\u251c e9 bridge FORWARDING vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nvlan10 ethernet UP 00:53:00:06:11:01\n\u2502 ipv6 fe80::0053:00ff:fe06:1101/64 (link-layer)\n\u2514 br0 ethernet UP 00:53:00:06:11:01\nadmin@example:/>\n When pinging \"IPv6 all hosts\" from the PC, there should be two responses for every ping, one from the switch and one from the PC attached to e10.
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.508 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=1 ttl=64 time=0.968 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.866 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=2 ttl=64 time=0.867 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=3 ttl=64 time=0.467 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.469 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.452 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=4 ttl=64 time=0.453 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n4 packets transmitted, 4 received, +4 duplicates, 0% packet loss, time 3031ms\nrtt min/avg/max/mdev = 0.452/0.631/0.968/0.211 ms\n~ $\n It should now be possible to access the switch from the PC via SSH (or NETCONF).
~ $ ssh admin@fe80::0053:00ff:fe06:1101%eth1\nadmin@fe80::0053:00ff:fe06:1101%eth1's password:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$ exit\n~ $\n See previous sections on backup and restore of the created configuration.
"},{"location":"scripting-restconf/","title":"With RESTCONF","text":""},{"location":"scripting-restconf/#scripting-with-restconf","title":"Scripting with RESTCONF","text":"RESTCONF provides a programmatic interface to both configuration and operational data over HTTPS. This guide shows practical examples using curl to interact with the RESTCONF API.
All examples use the following conventions:
Host: example.local (replace with your device hostname/IP) Credentials: admin:admin (default username:password) HTTPS: Self-signed certificates require -k flag in curl "},{"location":"scripting-restconf/#helper-script","title":"Helper Script","text":"To simplify RESTCONF operations, create a curl.sh wrapper script:
#!/bin/sh\n# RESTCONF CLI wrapper for curl\n\n# Show usage and exit\nusage()\n{\n cat <<-EOF >&2\n Usage: $0 [-h HOST] [-d DATASTORE] [-u USER:PASS] METHOD PATH [CURL_ARGS...]\n\n Options:\n -h HOST Target host (default: infix.local)\n -d DS Datastore: running, operational, startup (default: running)\n -u CREDS Credentials as user:pass (default: admin:admin)\n\n Methods: GET, POST, PUT, PATCH, DELETE\n EOF\n exit \"$1\"\n}\n\n# Default values\nHOST=${HOST:-infix.local}\nDATASTORE=running\nAUTH=admin:admin\n\n# Parse options\nwhile getopts \"h:d:u:\" opt; do\n case $opt in\n h) HOST=\"$OPTARG\" ;;\n d) DATASTORE=\"$OPTARG\" ;;\n u) AUTH=\"$OPTARG\" ;;\n *) usage 1 ;;\n esac\ndone\nshift $((OPTIND - 1))\n\n# Validate required arguments\nif [ $# -lt 2 ]; then\n echo \"Error: METHOD and PATH are required\" >&2\n usage 1\nfi\n\nMETHOD=$1\nPATH=$2\nshift 2\n\n# Ensure PATH starts with /\ncase \"$PATH\" in\n /*) ;;\n *) PATH=\"/$PATH\" ;;\nesac\n\n# Build URL based on datastore\ncase \"$DATASTORE\" in\n running|startup)\n URL=\"https://${HOST}/restconf/data${PATH}\"\n ;;\n operational)\n URL=\"https://${HOST}/restconf/data${PATH}\"\n ;;\n *)\n echo \"Error: Invalid datastore '$DATASTORE'. Use: running, operational, or startup\" >&2\n exit 1\n ;;\nesac\n\n# Execute curl with all remaining arguments passed through\nexec /usr/bin/curl \\\n --insecure \\\n --user \"${AUTH}\" \\\n --request \"${METHOD}\" \\\n --header \"Content-Type: application/yang-data+json\" \\\n --header \"Accept: application/yang-data+json\" \\\n \"$@\" \\\n \"${URL}\"\n Make it executable:
~$ chmod +x curl.sh\n This wrapper handles authentication, headers, SSL certificates, and URL construction, making commands much cleaner. You can override defaults with command-line options or environment variables:
# Using command-line options\n~$ ./curl.sh -h 192.168.1.10 -d operational -u admin:secret GET /ietf-interfaces:interfaces\n\n# Using environment variables\n~$ HOST=192.168.1.10 ./curl.sh GET /ietf-system:system\n The examples below show both raw curl commands and the equivalent using curl.sh where applicable.
"},{"location":"scripting-restconf/#discovery-common-patterns","title":"Discovery & Common Patterns","text":"Before working with specific configuration items, you often need to discover what exists on the system. This section shows common discovery patterns and practical workflows.
"},{"location":"scripting-restconf/#discovering-available-interfaces","title":"Discovering Available Interfaces","text":"List all interface names:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][].name'\nlo\ne0\ne1\n This is essential for automation - interface names vary by platform (eth0, e1, enp0s3, etc.), so scripts should discover them rather than hardcode.
"},{"location":"scripting-restconf/#get-api-capabilities","title":"Get API Capabilities","text":"Discover what YANG modules are available:
~$ curl -kX GET -u admin:admin \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/ietf-yang-library:yang-library\n This returns all supported YANG modules, revisions, and features.
"},{"location":"scripting-restconf/#get-entire-running-configuration","title":"Get Entire Running Configuration","text":"Useful for exploration or backup:
~$ ./curl.sh -h example.local GET / -o backup.json\n "},{"location":"scripting-restconf/#common-workflow-patterns","title":"Common Workflow Patterns","text":""},{"location":"scripting-restconf/#pattern-1-find-interface-by-ip-address","title":"Pattern 1: Find interface by IP address","text":"Get all interfaces with IPs and search:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | select(.[\"ietf-ip:ipv4\"][\"address\"][]?.ip == \"192.168.1.100\") | .name'\n "},{"location":"scripting-restconf/#pattern-2-list-all-interfaces-that-are-down","title":"Pattern 2: List all interfaces that are down","text":"~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | select(.[\"oper-status\"] == \"down\") | .name'\n "},{"location":"scripting-restconf/#pattern-3-get-statistics-for-all-interfaces","title":"Pattern 3: Get statistics for all interfaces","text":"~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | \"\\(.name): RX \\(.statistics[\"in-octets\"]) TX \\(.statistics[\"out-octets\"])\"'\n Output:
lo: RX 29320 TX 29320\ne0: RX 1847392 TX 892341\ne1: RX 0 TX 0\n "},{"location":"scripting-restconf/#pattern-4-check-if-interface-exists-before-configuring","title":"Pattern 4: Check if interface exists before configuring","text":"~$ if ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q \"ietf-interfaces:interface\"; then\n echo \"Interface eth0 exists\"\n else\n echo \"Interface eth0 not found\"\n fi\n "},{"location":"scripting-restconf/#configuration-operations","title":"Configuration Operations","text":""},{"location":"scripting-restconf/#read-hostname","title":"Read Hostname","text":"Example of fetching JSON configuration data:
Using curl directly:
~$ curl -kX GET -u admin:admin \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/ietf-system:system/hostname\n{\n \"ietf-system:system\": {\n \"hostname\": \"foo\"\n }\n}\n Using curl.sh:
~$ ./curl.sh -h example.local GET /ietf-system:system/hostname\n{\n \"ietf-system:system\": {\n \"hostname\": \"foo\"\n }\n}\n "},{"location":"scripting-restconf/#set-hostname","title":"Set Hostname","text":"Example of updating configuration with inline JSON data:
Using curl directly:
~$ curl -kX PATCH -u admin:admin \\\n -H 'Content-Type: application/yang-data+json' \\\n -d '{\"ietf-system:system\":{\"hostname\":\"bar\"}}' \\\n https://example.local/restconf/data/ietf-system:system\n Using curl.sh:
~$ ./curl.sh -h example.local PATCH /ietf-system:system \\\n -d '{\"ietf-system:system\":{\"hostname\":\"bar\"}}'\n "},{"location":"scripting-restconf/#add-ip-address-to-interface","title":"Add IP Address to Interface","text":"Add an IP address to the loopback interface:
~$ ./curl.sh -h example.local POST \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \\\n -d '{ \"prefix-length\": 32 }'\n "},{"location":"scripting-restconf/#delete-ip-address-from-interface","title":"Delete IP Address from Interface","text":"Remove an IP address from the loopback interface:
~$ ./curl.sh -h example.local DELETE \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254\n "},{"location":"scripting-restconf/#copy-running-to-startup","title":"Copy Running to Startup","text":"No copy command available yet to copy between datastores, and the Rousette back-end also does not support \"write-through\" to the startup datastore.
To save running-config to startup-config, fetch running to a local file and then update startup with it:
Using curl directly:
~$ curl -kX GET -u admin:admin -o running-config.json \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/ds/ietf-datastores:running\n\n~$ curl -kX PUT -u admin:admin -d @running-config.json \\\n -H 'Content-Type: application/yang-data+json' \\\n https://example.local/restconf/ds/ietf-datastores:startup\n Using curl.sh:
~$ ./curl.sh -h example.local GET / -o running-config.json\n~$ ./curl.sh -h example.local -d startup PUT / -d @running-config.json\n "},{"location":"scripting-restconf/#operational-data","title":"Operational Data","text":""},{"location":"scripting-restconf/#read-interface-configuration","title":"Read Interface Configuration","text":"Get the running configuration for the loopback interface:
~$ ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=lo\n "},{"location":"scripting-restconf/#read-interface-operational-state","title":"Read Interface Operational State","text":"Get operational data (state, statistics, etc.) for an interface:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=lo\n This includes administrative and operational state, MAC address, MTU, and statistics counters.
"},{"location":"scripting-restconf/#read-interface-statistics","title":"Read Interface Statistics","text":"Extract specific statistics using jq:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][0][\"statistics\"][\"in-octets\"]'\n "},{"location":"scripting-restconf/#list-all-interfaces","title":"List All Interfaces","text":"Get operational data for all interfaces:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces\n "},{"location":"scripting-restconf/#read-routing-table","title":"Read Routing Table","text":"Get the IPv4 routing table:
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/ribs/rib=ipv4-default\n "},{"location":"scripting-restconf/#read-ospf-state","title":"Read OSPF State","text":"Get OSPF operational data (neighbors, routes, etc.):
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default\n Or get just the neighbor information:
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default/ietf-ospf:ospf/areas/area=0.0.0.0/interfaces\n "},{"location":"scripting-restconf/#system-operations-rpcs","title":"System Operations (RPCs)","text":""},{"location":"scripting-restconf/#factory-reset","title":"Factory Reset","text":"Reset the system to factory defaults:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n https://example.local/restconf/operations/ietf-factory-default:factory-reset\ncurl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0\n Note: The connection error is expected - the device resets immediately.
"},{"location":"scripting-restconf/#system-reboot","title":"System Reboot","text":"Reboot the system:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n https://example.local/restconf/operations/ietf-system:system-restart\n "},{"location":"scripting-restconf/#set-date-and-time","title":"Set Date and Time","text":"Example of an RPC that takes input/arguments:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"ietf-system:input\": {\"current-datetime\": \"2024-04-17T13:48:02-01:00\"}}' \\\n https://example.local/restconf/operations/ietf-system:set-current-datetime\n Verify the change with SSH:
~$ ssh admin@example.local 'date'\nWed Apr 17 14:48:12 UTC 2024\n "},{"location":"scripting-restconf/#advanced-examples","title":"Advanced Examples","text":""},{"location":"scripting-restconf/#makefile-for-common-operations","title":"Makefile for Common Operations","text":"Create a Makefile to simplify common operations:
HOST ?= infix.local\n\nlo-running:\n ./curl.sh -h $(HOST) GET /ietf-interfaces:interfaces/interface=lo\n\nlo-operational:\n ./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=lo\n\nlo-add-ip:\n ./curl.sh -h $(HOST) POST \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \\\n -d '{ \"prefix-length\": 32 }'\n\nlo-del-ip:\n ./curl.sh -h $(HOST) DELETE \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254\n\n%-stats:\n @./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=$* 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][0][\"statistics\"][\"in-octets\"]'\n\n%-monitor:\n while sleep 0.2; do make -s HOST=$(HOST) $*-stats; done \\\n | ttyplot -t \"$(HOST):$* in-octets\" -r\n Usage examples:
# Get loopback operational state\n~$ make lo-operational\n\n# Add IP to loopback\n~$ make lo-add-ip\n\n# Get eth0 statistics\n~$ make eth0-stats\n\n# Monitor eth0 traffic in real-time (requires ttyplot)\n~$ make eth0-monitor\n You can override the host:
~$ make HOST=192.168.1.10 lo-operational\n "},{"location":"scripting-restconf/#monitoring-interface-traffic","title":"Monitoring Interface Traffic","text":"The %-monitor target demonstrates real-time monitoring by polling interface statistics and piping to ttyplot for visualization. Install ttyplot with:
~$ sudo apt install ttyplot\n Then monitor any interface:
~$ make eth0-monitor\n This creates a live ASCII graph of incoming octets on eth0.
"},{"location":"scripting-restconf/#references","title":"References","text":" RESTCONF Protocol (RFC 8040) YANG Data Modeling Language (RFC 7950) ietf-interfaces YANG module ietf-routing YANG module ietf-system YANG module "},{"location":"scripting-sysrepocfg/","title":"Legacy Scripting","text":"Note
This method is a legacy \"simple and human-friendly\" way to manage the system. These days we strongly recommend using RESTCONF instead.
"},{"location":"scripting-sysrepocfg/#legacy-scripting","title":"Legacy Scripting","text":"Although not the primary interface for Infix, it is possible to interact with the system using raw sysrepocfg commands. This way you get to interact directly with the YANG models when logged in to Infix. Thus, a set config, read config, read status and an RPC can be conducted using sysrepocfg for any supported YANG model.
See sysrepocfg for more information. Examples below will utilize:
sysrepocfg -I FILE -fjson -d DATABASE to import/write a JSON formatted configuration file to the specified database. sysrepocfg -E FILE -fjson -d DATABASE to edit/merge JSON formatted configuration in FILE with the specified database. sysrepocfg -R FILE -fjson to execute remote procedure call (RPC) defined in FILE (JSON formatted). sysrepocfg -X -fjson -d DATABASE -x xpath to read configuration or status from specified database. For importing (-I) and editing (-E), -d running is typically used in examples below. Specify -d startup to apply changes to startup configuration. Exporting (-X) could operate on configuration (e.g., -d running) or status (-d operational).
Some commands require a file as input. In the examples below we assume it has been transferred to the device in advance, e.g. using scp:
~$ cat file.json\n{\n \"ietf-factory-default:factory-reset\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$\n "},{"location":"scripting-sysrepocfg/#factory-reset","title":"Factory Reset","text":"~$ cat file.json\n{\n \"ietf-factory-default:factory-reset\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n^C\n~$\n See Factory Reset for another (simpler) alternative.
If it is only wished to copy factory config to running config the following RPC is available
~$ cat file.json\n{\n \"infix-factory-default:factory-default\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n^C\n~$\n "},{"location":"scripting-sysrepocfg/#system-reboot","title":"System Reboot","text":"~$ cat /tmp/file.json\n{\n \"ietf-system:system-restart\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n~$\n See System Reboot for another (simpler) alternative.
"},{"location":"scripting-sysrepocfg/#set-date-and-time","title":"Set Date and Time","text":"~$ ssh admin@example.local 'date'\nSun Nov 20 10:20:23 UTC 2005\n~$ cat file.json\n{\n \"ietf-system:set-current-datetime\": {\n \"current-datetime\": \"2024-04-17T13:48:02-01:00\"\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n~$ ssh admin@example.local 'date'\nWed Apr 17 14:48:12 UTC 2024\n~$\n See Set Date and Time for another (simpler) alternative.
"},{"location":"scripting-sysrepocfg/#remote-control-of-ethernet-ports","title":"Remote Control of Ethernet Ports","text":"Reading administrative status of interface e0 of running configuration.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running -e report-all \\\n -x \\\"/ietf-interfaces:interfaces/interface[name='e0']/enabled\\\"'\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"enabled\": true\n }\n ]\n }\n}\n~$\n Note
Without -e report-all argument the line \"enabled: true would not be shown as true is default.
~$ ssh admin@example.local \"sysrepocfg -X -fjson -d running \\\n -x \\\"/ietf-interfaces:interfaces/interface[name='e0']/enabled\\\"\"\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\"\n }\n ]\n }\n}\n~$\n Setting the administrative status of interface e0 of running configuration.
$ cat file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"enabled\": false\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n "},{"location":"scripting-sysrepocfg/#enabledisable-dhcpv4-client","title":"Enable/Disable DHCPv4 client","text":"Enabling DHCPv4 client on interface e0, with current default options.
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv4\": {\n \"infix-dhcp-client:dhcp\": {}\n }\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n Disabling DHCPv4 client on interface e0 (remove the dhcp container).
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv4\": {}\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n To fully remove the DHCPv4 client configuration, remove the infix-dhcp-client:dhcp container from the interface's ipv4 configuration.
"},{"location":"scripting-sysrepocfg/#enabledisable-ipv6","title":"Enable/Disable IPv6","text":"IPv6 is typically enabled on all interfaces by default. The example below shows IPv4 and IPv6 addresses assigned on e0.
~$ ssh admin@example.local 'ip addr show dev e0'\n2: e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 02:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff\n inet 10.0.2.15/24 scope global proto dhcp e0\n valid_lft forever preferred_lft forever\n inet6 fec0::ff:fe00:0/64 scope site dynamic mngtmpaddr proto kernel_ra\n valid_lft 86380sec preferred_lft 14380sec\n inet6 fe80::ff:fe00:0/64 scope link proto kernel_ll\n valid_lft forever preferred_lft forever\n~$\n IPv6 is enabled/disabled per interface. The example below disables IPv6 on interface e0.
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv6\": {\n \"enabled\": false\n }\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$ ssh admin@example.local 'ip addr show dev e0'\n2: e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 02:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff\n inet 10.0.2.15/24 scope global proto dhcp e0\n valid_lft forever preferred_lft forever\n~$\n "},{"location":"scripting-sysrepocfg/#change-a-binary-setting","title":"Change a Binary Setting","text":"A YANG binary type setting is Base64 encoded and requires a little bit more tricks. We take the opportunity to showcase a shell script helper: /usr/bin/text-editor, which works just like the text-editor command in the CLI, but this one takes an XPath argument to the binary leaf to edit.
Stripped down, it looks something like this:
if tmp=$(sysrepocfg -G \"$xpath\"); then\n file=$(mktemp)\n\n echo \"$tmp\" | base64 -d > \"$file\"\n if edit \"$file\"; then\n tmp=$(base64 -w0 < \"$file\")\n sysrepocfg -S \"$xpath\" -u \"$tmp\"\n fi\n\n rm -f \"$file\"\nelse\n echo \"Failed to retrieve value for $xpath\"\n exit 1\nfi\n An example container configuration, with an embedded file that is mounted to /var/www/index.html can look like this:
\"infix-containers:containers\": {\n \"container\": [\n {\n \"name\": \"web\",\n \"image\": \"oci-archive:/lib/oci/curios-httpd-latest.tar.gz\",\n \"hostname\": \"web\",\n \"network\": {\n \"interface\": [\n {\n \"name\": \"veth-sys0\"\n }\n ]\n },\n \"mount\": [\n {\n \"name\": \"index.html\",\n \"content\": \"PCFET0NUWVBFIGh0bWwjibberish.shortened.down==\",\n \"target\": \"/var/www/index.html\"\n }\n ]\n }\n ]\n }\n The command to edit this file, and restart the container with the new contents, look like this:
admin@infix:~$ cfg edit \"/infix-containers:containers/container[name='web']/mount[name='index.html']/content\"\n "},{"location":"scripting-sysrepocfg/#backup-configuration","title":"Backup Configuration","text":"Displaying running or startup configuration is possible with sysrepocfg -X, as shown below.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running'\n{\n \"ieee802-dot1ab-lldp:lldp\": {\n \"infix-lldp:enabled\": true\n...\n~$\n An example for backing up startup configuration from remote PC.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d startup > /tmp/backup.json'\n~$ scp admin@example.local:/tmp/backup.json .\n~$\n Or possibly skip intermediate storage of file
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d startup' > backup.json\n~$\n A final example is to only use scp. This is simpler, but only works to backup the startup configuration (not running).
~$ scp admin@example.local:/cfg/startup-config.cfg backup.json\n~$\n "},{"location":"scripting-sysrepocfg/#restore-configuration","title":"Restore Configuration","text":"To restore a backup configuration to startup, the simplest way is to use scp and reboot as shown below
~$ scp admin@example.local:/cfg/startup-config.cfg backup.json\n~$ ssh admin@example.local 'reboot'\nConnection to switch.local closed by remote host.\n~$\n An alternative method to restore a backup configuration is to use the sysrepocfg -I FILE (import) command.
The example below imports the backup configuration to startup, and reboots the unit.
~$ scp backup.json admin@example.local:/tmp/\n~$ ssh admin@example.local 'sudo sysrepocfg -I /tmp/backup.json -fjson -d startup'\n~$ ssh admin@example.local 'reboot'\nConnection to switch.local closed by remote host.\n~$\n Note
The login credentials (hash) for the admin user are stored as part of the configuration file. When replacing a switch and applying the backed up configuration from the former switch, the password on the replacement unit will also change.
"},{"location":"scripting-sysrepocfg/#copy-running-to-startup","title":"Copy Running to Startup","text":"The following command reads out the running config via sysrepocfg -X and writes the result to the startup configuration.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running > /cfg/startup-config.cfg'\n~$\n An alternative is to write it to a temporary file, and use sysrepocfg -I to import it to startup.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running > /tmp/running.json'\n~$ ssh admin@example.local 'sysrepocfg -I /tmp/running.json -fjson -d startup'\n~$\n "},{"location":"scripting-sysrepocfg/#read-hardware-information","title":"Read Hardware Information","text":"The IETF Hardware YANG model has been augmented for ONIE formatted production data stored in EEPROMs, if available. For details, see the VPD documentation and the ietf-hardware and infix-hardware YANG models.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d operational -x /ietf-hardware:hardware'\n{\n \"ietf-hardware:hardware\": {\n \"component\": [\n {\n \"name\": \"product\",\n \"class\": \"infix-hardware:vpd\",\n \"serial-num\": \"12345\",\n \"model-name\": \"Switch2010\",\n \"mfg-date\": \"2024-01-30T16:42:37+00:00\",\n \"infix-hardware:vpd-data\": {\n \"product-name\": \"Switch2010\",\n \"part-number\": \"ABC123-001\",\n \"serial-number\": \"007\",\n \"mac-address\": \"00:53:00:01:23:45\",\n \"manufacture-date\": \"01/30/2024 16:42:37\",\n \"num-macs\": 11,\n \"manufacturer\": \"ACME Production\",\n \"vendor\": \"SanFran Networks\"\n }\n },\n {\n \"name\": \"USB\",\n \"class\": \"infix-hardware:usb\",\n \"state\": {\n \"admin-state\": \"unlocked\",\n \"oper-state\": \"enabled\"\n }\n }\n ]\n }\n}\n~$\n "},{"location":"scripting/","title":"Introduction","text":""},{"location":"scripting/#scripting-infix","title":"Scripting Infix","text":"In some situations a user cannot, or does not want to, use the NETCONF API for interacting with Infix. Examples include production tasks and simpler remote scripting jobs to one or more remote devices.
This document assumes you have the password for the admin user, and that you can connect to the device. Please see Finding my Device for help on locating it.
Furthermore, the example commands shown here that are execute from a PC to a remote device over SSH, use Linux/UNIX. With advances lately in both Windows and macOS, many of the user friendly tools previously only available in Linux are now available there too.
The shell prompt for the PC laptop side:
~$\n The shell prompt when logged in to an Infix device:
admin@example:~$\n Note
The shell script commands used here are the raw variants which the CLI usually wraps in a warm and snugly blanket. Meaning they may change over time, while the CLI wrappers do not. That being said, please let us know if you find any inconsistencies.
"},{"location":"scripting/#tips","title":"Tips","text":" Ensure the admin user does not have clish as login shell Enable SSH key authentication Deploy same SSH public key to many Infix devices Secure your private SSH key using, e.g., ssh-agent The ssh-keygen command, used to create the private/public key pair, asks for a passphrase and although this is technically optional it is highly recommended to set one. For ease of use, in particular when scripting, use ssh-agent to avoid retyping the passphrase for every command.
Useful links on SSH, keys, and using ssh-agent:
https://en.wikipedia.org/wiki/Ssh-agent https://www.cyberciti.biz/faq/how-to-use-ssh-agent-for-authentication-on-linux-unix/ https://goteleport.com/blog/how-to-use-ssh-agent-safely/ "},{"location":"scripting/#admin-user-not-authorized","title":"Admin User Not Authorized","text":"All system services and critical configuration files are owned by the locked-down root user. It is not possible to activate the root user account for remote logins. Instead, use admin user and the sudo command prefix.
Here we are logged in to an example device:
admin@example:~$ cp /cfg/startup-config.cfg /cfg/backup-config.cfg\ncp: can't create '/cfg/backup-config.cfg': Permission denied\nadmin@example:~$ sudo cp /cfg/startup-config.cfg /cfg/backup-config.cfg\n "},{"location":"scripting/#examples","title":"Examples","text":"The following example commands are run from the PC over SSH. The following is a very brief introduction.
The notation is ssh username@address, where address can be an IPv4 or IPv6 address, a DNS name, or an mDNS name, e.g. infix.local. In the case of IPv6: address%interface, where interface differs between operating systems. On Linux and macOS the interface name is used, but on Windows the interface index1 is used.
"},{"location":"scripting/#logging-in-to-a-device","title":"Logging in to a device","text":"~$ ssh admin@fe80::ff:fe00:0%eth0\nThe authenticity of host 'fe80::ff:fe00:0%eth0 (fe80::ff:fe00:0%eth0)' can't be established.\nED25519 key fingerprint is SHA256:5/9mw64jhmYyD8MD+SwrsG3RXMBbP48pDe2T8bg14RQ.\nThis key is not known by any other names\nAre you sure you want to continue connecting (yes/no/[fingerprint])? yes\nWarning: Permanently added 'fe80::ff:fe00:0%eth0' (ED25519) to the list of known hosts.\nadmin@fe80::ff:fe00:0%eth0's password: *****\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-c0-ff-ee:/>\n "},{"location":"scripting/#running-a-remote-command","title":"Running a remote command","text":"~$ ssh admin@fe80::ff:fe00:0%eth0 echo hej\nadmin@fe80::ff:fe00:0%eth0's password: *****\nhej\n "},{"location":"scripting/#made-easy-with-mdns","title":"Made Easy with mDNS","text":"Connecting to networked devices using IP addresses is the way many people are used to. The above example with IPv6 tend to scare off people, so for the rest of this document we'll use the mDNS name instead:
~$ ssh admin@infix.local%eth0\nThe authenticity of host 'infix.local%eth0' can't be established.\nAre you sure you want to continue connecting (yes/no/[fingerprint])? yes\n...\nadmin@infix:/>\n "},{"location":"scripting/#factory-reset","title":"Factory Reset","text":"The command option -y disables any \"are you sure?\" interaction and immediately triggers a factory reset and reboot of the device. It is when the device boots up it erases all writable storage.
~$ ssh admin@infix.local%eth0 factory -y\nadmin@infix.local%eth0's password: *****\n "},{"location":"scripting/#system-reboot","title":"System Reboot","text":"~$ ssh admin@infix.local%eth0 reboot\nadmin@infix.local%eth0's password: *****\n "},{"location":"scripting/#set-date-and-time","title":"Set Date and Time","text":"Devices running Infix may have their system time completely off and this can cause problems for upgrading and when accessing the web interface over HTTPS (certificate validation looks at start and end dates).
To set the device's system time, and sync that with the RTC: use the PCs current time as argument:
~$ ssh admin@infix.local%eth0 \"sudo date -s '2024-03-20 18:14+01:00' && \\\n sudo hwclock -w -u\"\nadmin@infix.local%eth0's password: *****\n Tip
The -u option ensures saving system time to the RTC in UTC time.
Verify that the change took:
~$ ssh admin@infix.local%eth0 date\nadmin@infix.local%eth0's password: *****\nWed Mar 20 17:14:47 UTC 2024\n "},{"location":"scripting/#remote-control-of-ethernet-ports","title":"Remote Control of Ethernet Ports","text":"There are two ways to do it:
Change the configuration without saving it to startup-config Change the operational state The first involves sending a NETCONF command/config in XML. The second we will cover here. We start by querying available interfaces (ports) on the remote system:
~$ ssh admin@infix.local%qtap0 ip -br a\nadmin@infix.local%qtap0's password:\nlo UP 127.0.0.1/8 ::1/128\ne0 UP fe80::ff:fe00:0/64\ne1 UP\ne2 UP\ne3 UP\ne4 UP\ne5 UP fe80::ff:fe00:5/64\ne6 UP fe80::ff:fe00:6/64\ne7 UP fe80::ff:fe00:7/64\ne8 UP fe80::ff:fe00:8/64\ne9 UP 192.168.2.200/24 fe80::ff:fe00:9/64\nbr0 UP\n Here we see a loopback interface (lo), ten Ethernet ports (e0-e9) and a bridge (br0). From this quick glance we can guess that the ports e1-e4 are bridged (you can verify this with the remote command bridge link) because they do not have a link-local IPv6 address.
I know it's port e6 that I want to take down:
~$ ssh admin@infix.local%qtap0 ip link set e6 down\nadmin@infix.local%qtap0's password:\nRTNETLINK answers: Operation not permitted\n~$ ssh admin@infix.local%qtap0 sudo ip link set e6 down\nadmin@infix.local%qtap0's password:\n Changing the operational link state of a port is a privileged command, so we have to prefix our command with sudo.
Inspecting the link state again show the port is now down:
~$ ssh admin@infix.local%qtap0 ip -br a\nadmin@infix.local%qtap0's password:\nlo UP 127.0.0.1/8 ::1/128\ne0 UP fe80::ff:fe00:0/64\ne1 UP\ne2 UP\ne3 UP\ne4 UP\ne5 UP fe80::ff:fe00:5/64\ne6 DOWN\ne7 UP fe80::ff:fe00:7/64\ne8 UP fe80::ff:fe00:8/64\ne9 UP 192.168.2.200/24 fe80::ff:fe00:9/64\nbr0 UP\n "},{"location":"scripting/#check-network-connectivity","title":"Check Network Connectivity","text":"Say you want to perform a [System Upgrade][#system-upgrade] and it just does not work, a good place to start troubleshooting is ensuring the device can actually reach the upgrade server.
We run the following ping command remotely on the device:
~$ ssh admin@infix.local%eth0 ping -c 3 server.local\nadmin@infix.local%qtap0's password: *****\nPING server.local (192.168.2.42) 56(84) bytes of data.\n64 bytes from server.local: icmp_seq=1 ttl=64 time=0.201 ms\n64 bytes from server.local: icmp_seq=2 ttl=64 time=0.432 ms\n64 bytes from server.local: icmp_seq=3 ttl=64 time=0.427 ms\n\n--- server.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2050ms\nrtt min/avg/max/mdev = 0.201/0.353/0.432/0.107 ms\n Here we get a reply, so whatever is the issue with the upgrade was not hiding behind a connectivity issue at least.
"},{"location":"scripting/#system-upgrade","title":"System Upgrade","text":"The underlying software that handles upgrades is called RAUC. To trigger an upgrade you (currently) need an FTP/TFTP or HTTP/HTTPS server where RAUC can fetch the upgrade from. In this example we use an FTP server to upgrade the currently inactive \"slot\":
~$ ssh admin@infix.local%eth0 rauc install ftp://server.local/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\ninstalling\n 0% Installing\n 0% Determining slot states\n 20% Determining slot states done.\n 20% Checking bundle\n 20% Verifying signature\n 40% Verifying signature done.\n 40% Checking bundle done.\n 40% Checking manifest contents\n 60% Checking manifest contents done.\n 60% Determining target install group\n 80% Determining target install group done.\n 80% Updating slots\n 80% Checking slot rootfs.1\n 90% Checking slot rootfs.1 done.\n 90% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nidle\nInstalling `ftp://server.local/infix-aarch64-24.06.0.pkg` succeeded\n~$\n The inactive slot is now marked active and will be used on the next boot. To upgrade the partition we booted from, we must first reboot.
For more information, see the Boot Procedure document.
Alternative:
If you know your device has sufficient storage, eMMC or RAM disk (check with the remote command df -h), you can also copy the .pkg file to the device instead of having to set up an FTP/TFTP or HTTP/HTTPS server.
Create an upload directory where admin has write permission:
~$ ssh admin@infix.local%eth0 \"sudo mkdir /var/tmp/upload; \\\n sudo chown admin /var/tmp/upload\"\nadmin@infix.local%eth0's password:\n Copy the file with secure copy, first we show the nasty IPv6 version of the command:
~$ scp infix-aarch64-24.06.0.pkg admin@\\[fe80::ff:fe00:0%eth0\\]:/var/tmp/upload/\nadmin@fe80::ff:fe00:0%eth0's password:\ninfix-aarch64-24.06.0.pkg 100% 296 601.4KB/s 00:00\n And the upgrade command itself:
~$ ssh admin@infix.local%eth0 rauc install /var/tmp/upload/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\n.\n.\n.\n Remember to remove the file from the upload directory when you are done, this can be done before or after the reboot to activate the upgrade. If you want to upgrade both \"slots\", then you can of course keep the file until you are done (provided the upload directory was created on persistent storage).
~$ ssh admin@infix.local%eth0 rm /var/tmp/upload/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\n~$\n "},{"location":"scripting/#controlling-leds","title":"Controlling LEDs","text":"As part of production testing, LED verification is often expected to be performed. Infix uses standard Linux support for LED management, where LEDs appear in the file system under /sys/class/leds and can be controlled using echo command. sudo privileges are required.
When interacting with LEDs this way, first disable the Infix iitod daemon to avoid conflicting LED control.
~$ ssh admin@example.local 'initctl stop iitod'\n Then run the test, e.g., visually control that a red LED labeled 'LAN' is working.
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\\:lan/trigger'\n~$ ssh admin@example.local 'echo 1 | sudo tee /sys/class/leds/red\\:lan/brightness'\n To turn off the same LED, run the following commands.
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\\:lan/trigger'\n~$ ssh admin@example.local 'echo 0 | sudo tee /sys/class/leds/red\\:lan/brightness'\n When done with LED testing, enable Infix iitod daemon again.
~$ ssh admin@example.local 'initctl start iitod'\n "},{"location":"scripting/#reading-power-feed-status","title":"Reading Power Feed Status","text":"As part of production tests, verification of Power Feed sensors is often expected to be performed. Infix uses standard Linux support for Power management, where power sources appear in the file system under /sys/class/power_supply. The following example reads status of two power supplies named pwr1 and pwr2.
~$ ssh admin@example 'cat /sys/class/power_supply/pwr1/online'\n1\n~$ ssh admin@example 'cat /sys/class/power_supply/pwr2/online'\n0\n~$\n Here, only pwr1 have power.
Press Win-r to bring up the Run dialog, enter cmd.exe and press enter. Then type in ipconfig /all to list all interfaces, their status, as well as interface index.\u00a0\u21a9
"},{"location":"syslog/","title":"Syslog Support","text":""},{"location":"syslog/#syslog-support","title":"Syslog Support","text":"The system comes with native logging to internal storage, /var/log/, or /log for a shortcut. Depending on the device, this may be a RAM disk, meaning logs are not retained across reboots. This document details how to log to external media or remote syslog servers.
It is also possible to set up the device to act as a syslog server (log sink), this is covered briefly at the very end of this document.
Note
The default logging setup in the system cannot be modified, only the log file rotation. Please see the dir admin-exec command for a listing of existing log files.
"},{"location":"syslog/#log-to-file","title":"Log to File","text":"Logging to a local file is useful when combined with an external media. E.g., a USB stick with a log partition (named/labeled: \"log\"). Below is an example.
For a list of available log facilities, see the table in a later section.
admin@example:/> configure\nadmin@example:/config/> edit syslog actions log-file file:/media/log/mylog\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list\n all audit auth authpriv console cron cron2 daemon ftp kern\n local0 local1 local2 local3 local4 local5 local6 local7 lpr mail\n news ntp syslog user uucp\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list all severity\n alert all critical debug emergency error info none notice warning\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list all severity critical\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list mail severity warning\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> leave\nadmin@example:/>\n Important
The log-file syntax requires the leading prefix file:. If the path is not absolute, e.g., file:mylog, the file is saved to the system default path, i.e., /log/mylog. In this case, verify that the filename is not already in use.
"},{"location":"syslog/#log-rotation","title":"Log Rotation","text":"By default log files are allowed to grow to a size of 1 MiB after which they are \"rotated\". The whole reason for this is to not fill up the disk with outdated logs. A rotated file is saved in stages and older ones are also compressed (using gzip). Use the show log command in admin-exec context to start the log file viewer:
admin@example:/config/syslog/> do show log\nlog log.0 log.1.gz log.2.gz log.3.gz log.4.gz log.5.gz\nadmin@example:/config/syslog/> do show log log.1.gz\n Tip
Use the Tab key on your keyboard list available log files. The do prefix is also very useful in configure context to access commands from admin-exec context.
By default 10 compressed older files are saved. Here the oldest is log.5.gz and the most recently rotated one is log.0.
Log file rotation can be configured both globally and per log file. Here we show the global settings, the set up is the same for per log file, which if unset inherit the global settings:
admin@example:/> configure\nadmin@example:/config/> edit syslog file-rotation\nadmin@example:/config/syslog/file-rotation/> show\nadmin@example:/config/syslog/file-rotation/>\n The defaults are not shown. We can inspect them by asking the YANG model for the help texts:
admin@example:/config/syslog/file-rotation/> help\n max-file-size number-of-files\nadmin@example:/config/syslog/file-rotation/> help max-file-size \nNAME\n max-file-size kilobytes\n\nDESCRIPTION\n Maximum log file size (kiB), before rotation.\n\nDEFAULT\n 1024\nadmin@example:/config/syslog/file-rotation/> help number-of-files \nNAME\n number-of-files [0..4294967295]\n\nDESCRIPTION\n Maximum number of log files retained.\n\nDEFAULT\n 10\n To change the defaults to something smaller, 512 kiB and 20 (remember everything after .0 is compressed, and text compresses well):
admin@example:/config/syslog/file-rotation/> set max-file-size 512\nadmin@example:/config/syslog/file-rotation/> set number-of-files 20\nadmin@example:/config/syslog/file-rotation/> show\nnumber-of-files 20;\nmax-file-size 512;\nadmin@example:/config/syslog/file-rotation/> leave\nadmin@example:/>\n "},{"location":"syslog/#log-format","title":"Log Format","text":"There are three major syslog log formats, the default is RFC3164 for log files and BSD for remote logging. Depending on time synchronization and remote log server capabilities, or policies, the RFC5424 format is often preferred since it not only has better time resolution but also supports structured logging:
BSD : myproc[8710]: Kilroy was here.\nRFC3164 : Aug 24 05:14:15 192.0.2.1 myproc[8710]: Kilroy was here.\nRFC5424 : 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - Kilroy was here.\n The BSD format is only applicable to remote logging. It remains the default for compatibility reasons, and is recommended since the device may not have proper time, making it better for the remote log server to perform time stamping at the time of arrival.
Configuring the log format is the same for log files and remotes:
admin@example:/config/> edit syslog actions log-file file:foobar\nadmin@example:/config/syslog/\u2026/file:foobar/> set log-format\n bsd rfc3164 rfc5424\nadmin@example:/config/syslog/\u2026/file:foobar/> set log-format rfc5424\nadmin@example:/config/syslog/\u2026/file:foobar/> leave\nadmin@example:/>\n "},{"location":"syslog/#log-to-remote-server","title":"Log to Remote Server","text":"Logging to a remote syslog server is the recommended way of supervising the system. This way all login attempts (console, SSH, or web) and any configuration changes can be traced, even in cases of a remote attacker tries to cover their traces by deleting logs.
The recommended setup involves using a fixed IP address, default BSD log format, and the default Internet port (514). This is the most reliable, because your device may not have DNS set up or even available, and some remote syslog servers do not support receiving time stamped log messages -- this is of course entirely dependent on how the remote server is set up, as well as local policy.
admin@example:/config/> edit syslog\n actions file-rotation server\nadmin@example:/config/> edit syslog actions destination moon\nadmin@example:/config/syslog/\u2026/moon/> set\n facility-list log-format udp\nadmin@example:/config/syslog/\u2026/moon/> set udp\n address port\nadmin@example:/config/syslog/\u2026/moon/> set udp address 192.168.0.12\nadmin@example:/config/syslog/\u2026/moon/> set facility-list container severity all\nadmin@example:/config/syslog/\u2026/moon/> leave\nadmin@example:/>\n Tip
The alternatives shown below each prompt in the example above can be found by tapping the Tab key.
"},{"location":"syslog/#acting-as-a-log-server","title":"Acting as a Log Server","text":"The syslog server can act as a log sink for other devices on a LAN. For this to work you need a static IP address, here we use 10.0.0.1/24.
admin@example:/> configure\nadmin@example:/config/> edit syslog server\nadmin@example:/config/syslog/server/> set enabled true\nadmin@example:/config/syslog/server/> set listen udp 514 address 10.0.0.1\nadmin@example:/config/syslog/server/> leave\nadmin@example:/>\n See the above Log to File section on how to set up filtering of received logs to local files. Advanced filtering based on hostname and message properties is also available, see the next section for details.
"},{"location":"syslog/#advanced-filtering","title":"Advanced Filtering","text":"The syslog subsystem supports several advanced filtering options that allow fine-grained control over which messages are logged. These can be combined with facility and severity filters to create sophisticated logging rules.
"},{"location":"syslog/#pattern-matching","title":"Pattern Matching","text":"Messages can be filtered using regular expressions (POSIX extended regex) on the message content. This is useful when you want to log only messages containing specific keywords or patterns:
admin@example:/config/> edit syslog actions log-file file:errors\nadmin@example:/config/syslog/\u2026/file:errors/> set pattern-match \"ERROR|CRITICAL|FATAL\"\nadmin@example:/config/syslog/\u2026/file:errors/> set facility-list all severity info\nadmin@example:/config/syslog/\u2026/file:errors/> leave\nadmin@example:/>\n This will log all messages containing ERROR, CRITICAL, or FATAL.
"},{"location":"syslog/#advanced-severity-comparison","title":"Advanced Severity Comparison","text":"By default, severity filtering uses \"equals-or-higher\" comparison, meaning a severity of error will match error, critical, alert, and emergency messages. You can change this behavior:
admin@example:/config/> edit syslog actions log-file file:daemon-errors\nadmin@example:/config/syslog/\u2026/file:daemon-errors/> set facility-list daemon\nadmin@example:/config/syslog/\u2026/daemon/> set severity error\nadmin@example:/config/syslog/\u2026/daemon/> set advanced-compare compare equals\nadmin@example:/config/syslog/\u2026/daemon/> leave\nadmin@example:/>\n This will log only error severity messages, not higher severities.
You can also block specific severities:
admin@example:/config/syslog/\u2026/daemon/> set advanced-compare action block\n This will exclude error messages from the log.
"},{"location":"syslog/#hostname-filtering","title":"Hostname Filtering","text":"When acting as a log server, you can filter messages by hostname. This is useful for directing logs from different devices to separate files:
admin@example:/config/> edit syslog actions log-file file:router1\nadmin@example:/config/syslog/\u2026/file:router1/> set hostname-filter router1\nadmin@example:/config/syslog/\u2026/file:router1/> set facility-list all severity info\nadmin@example:/config/syslog/\u2026/file:router1/> leave\nadmin@example:/>\n Multiple hostnames can be added to the filter list.
"},{"location":"syslog/#property-based-filtering","title":"Property-Based Filtering","text":"For more advanced filtering, you can match on specific message properties using various comparison operators:
admin@example:/config/> edit syslog actions log-file file:myapp\nadmin@example:/config/syslog/\u2026/file:myapp/> edit property-filter\nadmin@example:/config/syslog/\u2026/property-filter/> set property programname\nadmin@example:/config/syslog/\u2026/property-filter/> set operator isequal\nadmin@example:/config/syslog/\u2026/property-filter/> set value myapp\nadmin@example:/config/syslog/\u2026/property-filter/> leave\nadmin@example:/>\n Available properties: - msg: Message body - msgid: RFC5424 message identifier - programname: Program/tag name - hostname: Source hostname - source: Alias for hostname - data: RFC5424 structured data
Available operators: - contains: Substring match - isequal: Exact equality - startswith: Prefix match - regex: Basic regular expression - ereregex: Extended regular expression (POSIX ERE)
The comparison can be made case-insensitive:
admin@example:/config/syslog/\u2026/property-filter/> set case-insensitive true\n Or negated to exclude matching messages:
admin@example:/config/syslog/\u2026/property-filter/> set negate true\n "},{"location":"syslog/#facilities","title":"Facilities","text":"Code Facility Description 0 kern Kernel log messages 1 user User-level messages 2 mail Mail system 3 daemon General system daemons 4 auth Security/authorization messages 5 syslog Messages generated by syslogd 6 lpr Line printer subsystem 7 news Network news subsystem 8 uucp UNIX-to-UNIX copy 9 cron Clock/cron daemon (BSD, Linux) 10 authpriv Security/authorization messages (private) 11 ftp FTP daemon 12 ntp NTP subsystem 13 audit Log audit (security) 14 console Log alert 15 cron2 Clock/cron daemon (Solaris) 16 rauc* local0, reserved for RAUC 17 container* local1, reserved for containers 18 local2 Currently unused 19 local3 Currently unused 20 local4 Currently unused 21 local5 Currently unused 22 reserved* local6, reserved for industrial Ethernet 23 web* local7, reserved for nginx web server Facilities marked * are local augments to the model.
"},{"location":"system/","title":"Configuration","text":""},{"location":"system/#system-configuration","title":"System Configuration","text":"System settings in Infix are provided by the ietf-system YANG model, augmented with Linux specific extensions in infix-system, like Message of the Day (login message) and user login shell. More on this later on in this document.
For the sake of brevity, the hostname in the following examples has been shortened to host. The default hostname is composed from a product specific string followed by the last three octets of the system base MAC address, e.g., switch-12-34-56. An example of how to change the hostname is included below.
Note
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"system/#changing-password","title":"Changing Password","text":"User management, including passwords, SSH keys, remote authentication is available in the system authentication configuration context.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> change password\nNew password:\nRetype password:\nadmin@host:/config/system/authentication/user/admin/> leave\n The change password command starts an interactive dialogue that asks for the new password, with a confirmation, and then salts and encrypts the password with sha512crypt.
It is also possible to use the set password ... command. This allows setting an already hashed password. To manually hash a password, use the do password encrypt command. This launches the admin-exec command to hash, and optionally salt, your password. This encrypted string can then be used with set password ....
Tip
If you are having trouble thinking of a password, there is a nifty password generate command in admin-exec context which generates random passwords using the UNIX command pwgen. Use the do prefix when inside any configuration context to access admin-exec commands.
"},{"location":"system/#ssh-authorized-key","title":"SSH Authorized Key","text":"Logging in remotely with SSH is possible by adding a public key to a user. Here we add the authorized key to the admin user, multiple keys are supported.
With SSH keys in place it is possible to disable password login, just remember to verify SSH login and network connectivity before doing so.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> edit authorized-key example@host\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> show\nalgorithm ssh-rsa;\nkey-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=;\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> leave\n Note
The ssh-keygen program already base64 encodes the public key data, so there is no need to use the text-editor command, set does the job.
"},{"location":"system/#multiple-users","title":"Multiple Users","text":"The system supports multiple users and multiple user levels, or groups, that a user can be a member of. Access control is entirely handled by the NETCONF \"NACM\" YANG model, which provides granular access to configuration, data, and RPC commands over NETCONF.
By default the system ships with a single group, admin, which the default user admin is a member of. The broad permissions granted by the admin group is what gives its users full system administrator privileges. There are no restrictions on the number of users with administrator privileges, nor is the admin user reserved or protected in any way -- it is completely possible to remove the default admin user from the configuration. However, it is recommended to keep at least one user with administrator privileges in the system, otherwise the only way to regain full access is to perform a factory reset.
"},{"location":"system/#adding-a-user","title":"Adding a User","text":"Similar to how to change password, adding a new user is done using the same set of commands:
admin@host:/config/> edit system authentication user jacky\nadmin@host:/config/system/authentication/user/jacky/> change password\nNew password:\nRetype password:\nadmin@host:/config/system/authentication/user/jacky/> leave\n An authorized SSH key is added the same way as presented previously.
"},{"location":"system/#adding-a-user-to-the-admin-group","title":"Adding a User to the Admin Group","text":"The following commands add user jacky to the admin group.
admin@host:/config/> edit nacm group admin\nadmin@host:/config/nacm/group/admin/> set user-name jacky\nadmin@host:/config/nacm/group/admin/> leave\n "},{"location":"system/#security-aspects","title":"Security Aspects","text":"The NACM user levels apply primarily to NETCONF, with exception of the admin group which is granted full system administrator privileges to the underlying UNIX system with the following ACL rules:
...\n \"module-name\": \"*\",\n \"access-operations\": \"*\",\n \"action\": \"permit\",\n ...\n A user in the admin group is allowed to also use a POSIX login shell and use the sudo command to perform system administrative commands. This makes it possible to use all the underlying UNIX tooling, which to many can be very useful, in particular when debugging a system, but please remember to use with care -- the system is not built to require managing from the shell. The tools available in the CLI and automated services, started from the system's configuration, are the recommended way of using the system, in addition to NETCONF tooling.
"},{"location":"system/#changing-hostname","title":"Changing Hostname","text":"Notice how the hostname in the prompt does not change until the change is committed by issuing the leave command.
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname example\nadmin@host:/config/system/> leave\nadmin@example:/>\n The hostname is advertised over mDNS-SD in the .local domain. If another device already has claimed the example.local CNAME, in our case, mDNS will advertise a \"uniqified\" variant, usually suffixing with an index, e.g., example-1.local. Use an mDNS browser to scan for available devices on your LAN.
In some cases you may want to set the device's domain name as well. This is handled the same way:
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname foo.example.com\nadmin@host:/config/system/> leave\nadmin@foo:/>\n Both host and domain name are stored in the system files /etc/hosts and /etc/hostname. The latter is exclusively for the host name. The domain may be used by the system DHCP server when handing out leases to clients, it is up to the clients to request the domain name option.
Note
Critical services like syslog, mDNS, LLDP, and similar that advertise the hostname, are restarted when the hostname is changed.
"},{"location":"system/#changing-login-banner","title":"Changing Login Banner","text":"The motd-banner setting is an Infix augment and an example of a binary type setting that can be changed interactively with the built-in text-editor command.
Tip
See the next section for how to change the editor used to something you may be more familiar with.
admin@host:/config/> edit system\nadmin@host:/config/system/> text-editor motd-banner\nadmin@host:/config/system/> leave\nadmin@host:/>\n Log out and log back in again to inspect the changes.
"},{"location":"system/#changing-the-editor","title":"Changing the Editor","text":"The system has three different built-in editors that can be used as the text-editor command:
emacs (Micro Emacs) nano (GNU Nano) vi (Visual Editor) To change the editor to GNU Nano:
admin@host:/> configure\nadmin@host:/config/> edit system\nadmin@host:/config/system/> set text-editor nano\nadmin@host:/config/system/> leave\nadmin@host:/>\n Important
Configuration changes only take effect after issuing the leave command. I.e., you must change the editor first, and then re-enter configure context to use your editor of choice.
"},{"location":"system/#dns-resolver-configuration","title":"DNS Resolver Configuration","text":"The system supports both static and dynamic (DHCP) DNS setup. The locally configured (static) server is preferred over any acquired from a DHCP client.
admin@host:/> configure\nadmin@host:/config/> edit system dns-resolver\nadmin@host:/config/system/dns-resolver/> set server google udp-and-tcp address 8.8.8.8\nadmin@host:/config/system/dns-resolver/> show\nserver google {\n udp-and-tcp {\n address 8.8.8.8;\n }\n}\nadmin@host:/config/system/dns-resolver/> leave\n It is also possible to configure resolver options like timeout and retry attempts. See the YANG model for details, or use the built-in help system in the CLI.
Note
When acting as a DHCP server and DNS proxy for other devices, any local DNS server configured here is automatically used as upstream DNS server.
"},{"location":"system/#ntp-client-configuration","title":"NTP Client Configuration","text":"Below is an example configuration for enabling NTP with a specific server and the iburst option for faster initial synchronization.
admin@host:/> configure\nadmin@host:/config/> edit system ntp\nadmin@host:/config/system/ntp/> set enabled\nadmin@host:/config/system/ntp/> set server ntp-pool\nadmin@host:/config/system/ntp/> set server ntp-pool udp address pool.ntp.org\nadmin@host:/config/system/ntp/> set server ntp-pool iburst\nadmin@host:/config/system/ntp/> set server ntp-pool prefer\nadmin@host:/config/system/ntp/> leave\n This configuration enables the NTP client and sets the NTP server to pool.ntp.org with the iburst and prefer options. The iburst option ensures faster initial synchronization, and the prefer option designates this server as preferred.
prefer false: The NTP client will choose the best available source based on several factors, such as network delay, stratum, and other metrics (default config). prefer true: The NTP client will try to use the preferred server as the primary source unless it becomes unreachable or unusable. "},{"location":"system/#show-ntp-sources","title":"Show NTP Sources","text":"The status for NTP sources is availble in YANG and accessable with CLI/NETCONF/RESTCONF.
To view the sources being used by the NTP client, run:
admin@target:/> show ntp\nADDRESS MODE STATE STRATUM POLL-INTERVAL\n192.168.1.1 server candidate 1 6\n192.168.2.1 server candidate 1 6\n192.168.3.1 server selected 1 6\n
"},{"location":"system/#show-ntp-status","title":"Show NTP Status","text":"To check the status of NTP synchronization (only availble in CLI), use the following command:
admin@host:/> show ntp tracking\nReference ID : C0248F86 (192.36.143.134)\nStratum : 2\nRef time (UTC) : Mon Oct 21 10:06:45 2024\nSystem time : 0.000000001 seconds slow of NTP time\nLast offset : -3845.151367188 seconds\nRMS offset : 3845.151367188 seconds\nFrequency : 4.599 ppm slow\nResidual freq : +1293.526 ppm\nSkew : 12.403 ppm\nRoot delay : 1.024467230 seconds\nRoot dispersion : 0.273462683 seconds\nUpdate interval : 0.0 seconds\nLeap status : Normal\nadmin@host:/>\n This output provides detailed information about the NTP status, including reference ID, stratum, time offsets, frequency, and root delay.
Tip
The system uses chronyd Network Time Protocol (NTP) daemon. The output shown here is best explained in the Chrony documentation.
"},{"location":"system/#support-data-collection","title":"Support Data Collection","text":"When troubleshooting issues or seeking support, the support command provides a convenient way to collect comprehensive system diagnostics. This command gathers configuration files, logs, network state, and other system information into a single compressed archive.
"},{"location":"system/#collecting-support-data","title":"Collecting Support Data","text":"To collect support data and save it to a file:
admin@host:~$ support collect > support-data.tar.gz\n(admin@host) Password: ***********\nStarting support data collection from host...\nThis may take up to a minute. Please wait...\nTailing /var/log/messages for 30 seconds (please wait)...\nLog tail complete.\nCollection complete. Creating archive...\nadmin@host:~$ ls -l support-data.tar.gz\n-rw-rw-r-- 1 admin admin 508362 nov 30 13:05 support-data.tar.gz\n The command can also be run remotely via SSH from your workstation:
$ ssh admin@host support collect > support-data.tar.gz\n...\n The collection process may take up to a minute depending on system load and the amount of logging data. Progress messages are shown during the collection process.
"},{"location":"system/#encrypted-collection","title":"Encrypted Collection","text":"For secure transmission of support data, the archive can be encrypted with GPG using a password:
admin@host:~$ support collect -p mypassword > support-data.tar.gz.gpg\nStarting support data collection from host...\nThis may take up to a minute. Please wait...\n...\nCollection complete. Creating archive...\nEncrypting with GPG...\n The support collect command even supports omitting mypassword and will then prompt interactively for the password. This works over SSH too, but the local ssh client may then echo the password.
Tip
To hide the encryption password for an SSH session, the script supports reading from stdin: echo \"$MYSECRET\" | ssh user@device support collect -p > file.tar.gz.gpg
After transferring the resulting file to your workstation, decrypt it with the password:
$ gpg -d support-data.tar.gz.gpg > support-data.tar.gz\n$ tar xzf support-data.tar.gz\n or
$ gpg -d support-data.tar.gz.gpg | tar xz\n Important
Make sure to share mypassword out-of-band from the encrypted data with the recipient of the data. I.e., avoid sending both in the same plain-text email for example.
"},{"location":"system/#what-is-collected","title":"What is Collected","text":"The support archive includes:
System identification (hostname, uptime, kernel version) Running and operational configuration (sysrepo datastores) System logs (/var/log directory and live tail of messages log) Network configuration and state (interfaces, routes, neighbors, bridges) FRRouting information (OSPF, BFD status) Container information (podman containers and their configuration) System resource usage (CPU, memory, disk, processes) Hardware information (PCI, USB devices, network interfaces) "},{"location":"test-arch/","title":"Test System Architecture","text":""},{"location":"test-arch/#test-system-architecture","title":"Test System Architecture","text":""},{"location":"test-arch/#tenets","title":"Tenets","text":" Keep overhead to a minimum. Tests should be fast to both write and run. Ideally, the developer should want to add tests early in the development cycle because they instinctively feel that that is the quickest route to arrive at a correct and robust implementation.
Both physical and virtual hardware matters. Infix is primarily deployed on physical hardware, so being able to run the test suite on real devices is crucial to guarantee a high quality product. At the same time, there is much value in running the same suite on virtual hardware, as it makes it easy to catch regressions early. It is also much more practical and economical to build large virtual networks than physical ones.
Avoid CLI scripting & scraping. Reliably interacting with a DUT over a serial line in a robust way is very hard to get right. Given that we have a proper API (RESTCONF), we should leverage that when testing. Front-ends can be tested by other means.
"},{"location":"test-arch/#overview","title":"Overview","text":"The test system is made up of several independent components, which are typically used in concert to run a full test suite.
"},{"location":"test-arch/#test-cases","title":"Test Cases","text":"A test case is an executable, receiving the physical topology as a positional argument, which produces TAP compliant output on its stdout. I.e., it is executed in the following manner:
test-case [OPTS] <physical-topology>\n Test cases are typically written in Python, using the Infamy library. Ultimately though, it can be implemented in any language, as long as it matches the calling convention above.
"},{"location":"test-arch/#infamy","title":"Infamy","text":"Rather than having each test case come up with its own implementation of how to map topologies, how to push NETCONF data to a device, etc., we provide a library of functions to take care of all that, dubbed \"Infamy\". When adding a new test case, ask yourself if any parts of it might belong in Infamy as a generalized component that can be reused by other tests.
Some of the core functions provided by Infamy are:
Mapping a logical topology to a physical one Finding and attaching to a device over an Ethernet interface, using NETCONF Pushing/pulling NETCONF data to/from a device Generating TAP compliant output "},{"location":"test-arch/#9pm","title":"9PM","text":"To run multiple tests, we employ 9PM. It let's us define test suites as simple YAML files. Suites can also be hierarchically structured, with a suite being made up of other suites, etc.
It also validates the TAP output, making sure to catch early exits from a case, and produces a nice summary report.
"},{"location":"test-arch/#testenv","title":"/test/env","text":"A good way to ensure that nobody ever runs the test suite is to make it really hard to do so. /test/env's job is instead to make it very easy to create a reproducible environment in which tests can be executed.
Several technologies are leveraged to accomplish this:
Containers: The entire execution is optionally done inside a standardized docker container environment. This ensures that the software needed to run the test suite is always available, no matter which distribution the user is running on their machine.
Python Virtual Environments: To make sure that the expected versions of all Python packages are available, the execution is wrapped inside a venv. This is true for containerized executions, where the container comes with a pre-installed environment, but it can also be sourced from the host system when running outside of the container.
Virtual Test Topology: Using Qeneth, the environment can optionally be started with a virtual topology of DUTs to run the tests on.
docker is the only supported container environment when running tests in the host's network namespace. When running on a virtual Qeneth topology, podman may also be used by installing the podman-docker package from your host system's distro.
"},{"location":"test-arch/#physical-and-logical-topologies","title":"Physical and Logical Topologies","text":"Imagine that we want to create a test with three DUTs; one acting as a DHCP server, and the other two as DHCP clients - with all three having a management connection to the host PC running the test. In other words, the test requires a logical topology like the one below.
graph \"dhcp-client-server\" {\n host [\n label=\"host | { <c1> c1 | <srv> srv | <c2> c2 }\",\n kind=\"controller\",\n ];\n\n server [\n label=\"{ <mgmt> mgmt } | server | { <c1> c1 | <c2> c2 }\",\n kind=\"infix\",\n ];\n client1 [\n label=\"{ <mgmt> mgmt } | client1 | { <srv> srv }\",\n kind=\"infix\",\n ];\n client2 [\n label=\"{ <mgmt> mgmt } | client2 | { <srv> srv }\",\n kind=\"infix\",\n ];\n\n host:srv -- server:mgmt\n host:c1 -- client1:mgmt\n host:c2 -- client2:mgmt\n\n server:c1 -- client1:srv;\n server:c2 -- client2:srv;\n}\n When running in a virtualized environment, one could simply create a setup that matches the test's logical topology. But in scenarios when devices are physical systems, connected by real copper cables, this is not possible (unless you have some wicked L1 relay matrix thingy).
Instead, the test implementation does not concern itself with the exact nodes used to run the test, only that the logical topology can be mapped to some subset of the physical topology. In mathematical terms, the physical topology must contain a subgraph that is isomorphic to the logical topology.
Standing on the shoulders of giants (i.e. people with mathematics degrees), we can deploy well-known algorithms to find such subgraphs. Continuing our example, let's say we want to run our DHCP test on the physical topology below.
graph \"quad-ring\" {\n host [\n label=\"host | { <d1a> d1a | <d1b> d1b | <d1c> d1c | <d2a> d2a | <d2b> d2b | <d2c> d2c | <d3a> d3a | <d3b> d3b | <d3c> d3c | <d4a> d4a | <d4b> d4b | <d4c> d4c }\",\n kind=\"controller\",\n ];\n\n dut1 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut1 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut2 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut2 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut3 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut3 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut4 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut4 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n\n host:d1a -- dut1:e1\n host:d1b -- dut1:e2\n host:d1c -- dut1:e3\n\n host:d2a -- dut2:e1\n host:d2b -- dut2:e2\n host:d2c -- dut2:e3\n\n host:d3a -- dut3:e1\n host:d3b -- dut3:e2\n host:d3c -- dut3:e3\n\n host:d4a -- dut4:e1\n host:d4b -- dut4:e2\n host:d4c -- dut4:e3\n\n dut1:e5 -- dut2:e4\n dut2:e5 -- dut3:e4\n dut3:e5 -- dut4:e4\n dut4:e5 -- dut1:e4\n}\n Our test (in fact, all tests) receives the physical topology as an input parameter, and then maps the desired logical topology onto it, producing a mapping from logical nodes and ports to their physical counterparts.
{\n \"client1\": \"dut1\",\n \"client1:mgmt\": \"dut1:e1\",\n \"client1:srv\": \"dut1:e4\",\n \"client2\": \"dut3\",\n \"client2:mgmt\": \"dut3:e2\",\n \"client2:srv\": \"dut3:e5\",\n \"host\": \"host\",\n \"host:c1\": \"host:d1a\",\n \"host:c2\": \"host:d3b\",\n \"host:srv\": \"host:d4c\",\n \"server\": \"dut4\",\n \"server:c1\": \"dut4:e5\",\n \"server:c2\": \"dut4:e4\",\n \"server:mgmt\": \"dut4:e3\"\n}\n With this information, the test knows that, in this particular environment, the server should be managed via the port called d4c on the node called host; that the port connected to the server on client1 is e4 on dut1, etc. Thereby separating the implementation of the test from any specific physical setup.
Testcases are not required to use a logical topology; they may choose to accept whatever physical topology its given, and dynamically determine the DUTs to use for testing. As an example, an STP test could accept an arbitrary physical topology, run the STP algorithm on it offline, enable STP on all DUTs, and then verify that the resulting spanning tree matches the expected one.
"},{"location":"test-arch/#integration-to-infix","title":"Integration to Infix","text":"When the test environment is started with Qeneth, it doesn't use the base image directly. Instead, it creates a copy and inserts a test-mode flag into it. During the bootstrap phase, the system checks for the presence of the test-mode flag (file).
If the flag exists, a 'test-config.cfg' file is generated. In the following step, the system loads the 'test-config' instead of the standard startup-config (or factory-config). This configuration is simple and safe, equivalent to the one used in 'Secure Mode' (also known as 'failure-config').
Additionally, the configuration enables extra RPCs related to system restart and configuration overrides, allowing tests to be run even on systems where the factory configuration may potentially create L2 loops.
"},{"location":"testing/","title":"Regression Testing","text":""},{"location":"testing/#regression-testing-with-infamy","title":"Regression Testing with Infamy","text":"Infix comes with a test suite that is intended to provide end-to-end verification of supported features. Generally speaking, this means that one or more DUTs are configured over NETCONF or RESTCONF; the resulting network is then black-box tested by injecting and inspecting network traffic at various points.
This document is intended to be a practical guide on how to run, develop and debug tests. There is a separate document describing the Test System Architecture.
"},{"location":"testing/#modes-of-testing","title":"Modes of Testing","text":""},{"location":"testing/#virtual-devices","title":"Virtual Devices","text":"By default, tests are run on a topology made up of virtual Infix nodes using Qeneth. To run the full regression test suite: build Infix for x86_64:
$ make x86_64_defconfig\n...\n$ make\n...\n$ make test\n...\n "},{"location":"testing/#physical-devices","title":"Physical Devices","text":"To run the tests on a preexisting topology from the host's network namespace, specify the host TEST_MODE:
$ make TEST_MODE=host test\n...\n This typically used when testing on physical hardware. By default the topology will be sourced from /etc/infamy.dot, but this can be overwritten by setting the TOPOLOGY variable:
$ make TEST_MODE=host TOPOLOGY=~/my-topology.dot test\n...\n "},{"location":"testing/#make-run-devices","title":"make run Devices","text":"Some tests only require a single DUT. These can therefore be run against an Infix image started from make run. This requires that the instance is configured to use TAP networking.
When the instance is running, you can open a separate terminal and run the subset of the test suite that can be mapped to it:
$ make TEST_MODE=run test\n...\n "},{"location":"testing/#interactive-usage","title":"Interactive Usage","text":"When developing and debugging tests, the overhead of repeatedly setting up and tearing down the test environment can quickly start to weigh you down. In these situation, you can start an interactive test environment:
$ make test-sh\nInfo: Generating topology\nInfo: Generating node YAML\nInfo: Generating executables\nInfo: Launching dut1\nInfo: Launching dut2\nInfo: Launching dut3\nInfo: Launching dut4\n11:42:52 infamy0:test #\n The example above uses the default mode (qeneth), but the host and run modes also support the test-sh target.
It takes a little while to start up, but then we have a shell prompt inside the container running Infamy. It's a very limited environment, but it has enough to easily run single tests, connect to the virtual devices, and step your code.
"},{"location":"testing/#running-subsets-of-tests","title":"Running Subsets of Tests","text":"Each test case is a separate executable, which can be run without arguments:
11:42:53 infamy0:test # ./case/dhcp/dhcp_basic.py\n To run a suite of tests, e.g., only the DHCP client tests, pass the suite as an argument to 9PM:
11:42:53 infamy0:test # ./9pm/9pm.py case/dhcp/dhcp.yaml\n To run the suite of all tests:
11:42:53 infamy0:test # ./9pm/9pm.py case/all.yaml\n "},{"location":"testing/#connecting-to-infamy","title":"Connecting to Infamy","text":"The test system runs in a Docker container, so to get a shell prompt in another terminal you need to connect to that container. Infamy comes with a helper script for this:
$ ./test/shell\n11:42:53 infamy0:test #\n By default it connect to the latest started Infamy instance. If you for some reason run multiple instances of Infamy the shell script takes an optional argument \"system\", which is the hostname of the container you want to connect to:
$ ./test/shell infamy2\n11:42:53 infamy2:test #\n "},{"location":"testing/#connecting-to-a-dut","title":"Connecting to a DUT","text":"Tip
The console script uses telnet to connect to a port forwarded to localhost by the Docker container. To exit Telnet, use Ctrl-] and then 'q' followed by enter. This can be customized in ~/.telnetrc
All DUTs in a virtual Infamy topology are emulated in Qemu instances managed by qeneth. If you want to watch what's happening on one of the target systems, e.g., tail a log file or run tcpdump during the test, there is another helper script in Infamy for this:
$ ./test/console 1\nTrying 127.0.0.1...\nConnected to 127.0.0.1.\n\nInfix OS \u2014 Immutable.Friendly.Secure v23.11.0-226-g0c144da (console)\ninfix-00-00-00 login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-00-00-00:~$\n From here we can observe dut1 freely while running tests.
Like the shell script, console takes an optional \"system\" argument in case you run multiple instances of Infamy:
$ ./test/console 1 infamy2\n...\n You can also connect to the console of a DUT from within a shell:
$ ./test/shell\n11:42:54 infamy0:test # qeneth status\n11:42:54 infamy0:test # qeneth console dut1\nlogin: admin\npassword: *****\nadmin@infix-00-00-00:~$\n Tip
Disconnect from the qeneth console by bringing up the old Telnet \"menu\" using Ctrl-], compared to standard Telnet this is the BusyBox version so you press 'e' + enter instead of 'q' to quit.
"},{"location":"testing/#attaching-to-macvlans","title":"Attaching to MACVLANs","text":"To fully isolate the host's interfaces from one another, many tests will stack a MACVLAN on an interface, which is then placed in a separate network namespace.
It is often useful to attach to those namespaces, so that you can interactively inject traffic into the test setup.
First, connect to the Infamy instance in question:
$ ./test/shell\n11:43:19 infamy0:test #\n Then, attach to the MACVLAN namespace by running the nsenter helper script from the test directory, supplying the base interface name as the first argument:
11:43:19 infamy0:test # ./nsenter d1b\n11:43:20 infamy0(d1b):test #\n By default, an interactive shell is started, but you can also supply another command:
11:43:19 infamy0:test # ./nsenter d1b ip -br addr\nlo UNKNOWN 127.0.0.1/8 ::1/128\niface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64\n You can now freely debug the network activity of your test and the responses from the DUT.
"},{"location":"testing/#using-the-python-debugger","title":"Using the Python Debugger","text":"The built in breakpoint() function in Python is very useful when you want to run a test case to a certain point at which you might want to interactively inspect either the test's or the device's state.
Simply insert a call to breakpoint() at the point of interest in your test and run it as normal. Once Python executes the call, it will drop you into the Python debugger:
11:42:58 infamy0:test # ./case/dhcp/dhcp_basic.py\n# Starting (2024-02-10 11:42:59)\n# Probing dut1 on port d1a for IPv6LL mgmt address ...\n# Connecting to mgmt IP fe80::ff:fe00:0%d1a:830 ...\nok 1 - Initialize\n> /home/jocke/src/infix/test/case/dhcp/dhcp_basic.py(44)<module>()\n(Pdb)\n At this point you have full access to the test's state, but it is also an opportunity to inspect the state of the DUTs (e.g. via their console or over SSH).
It is also possible to run a test under Pdb from the get-go, if you want to setup breakpoints without modifying the source, or simply step through the code:
11:42:58 infamy0:test # python -m pdb case/dhcp/dhcp_basic.py\n "},{"location":"testing/#deterministic-topology-mappings","title":"Deterministic Topology Mappings","text":"By default, mappings from logical to physical topologies are not stable across test case executions. This can be very frustrating when debugging a failing test, since logical nodes are suffled around between phyical nodes. In such cases, supplying a PYTHONHASHSEED variable (set to any 32-bit unsigned integer) when launching the test environment will make sure that topology mappings are deterministic:
$ make PYTHONHASHSEED=0 test-sh\n If a seed is not supplied, a random value is chosen. This seed is logged by the meta/reproducible.py test case when running a test suite:
$ make test\nInfo: Generating topology\nInfo: Generating node YAML\nInfo: Generating executables\nInfo: Launching dut1\nInfo: Launching dut2\nInfo: Launching dut3\nInfo: Launching dut4\n9PM - Simplicity is the ultimate sophistication\n\nStarting test 0002-reproducible.py\n2024-05-03 10:40:30 # Starting (2024-05-03 10:40:30)\n2024-05-03 10:40:30 # Specify PYTHONHASHSEED=3773822171 to reproduce this test environment\n2024-05-03 10:40:30 ok 1 - $PYTHONHASHSEED is set\n2024-05-03 10:40:30 # Exiting (2024-05-03 10:40:30)\n2024-05-03 10:40:30 1..1\n...\n This is useful because this value can then be used to rerun a test (or the whole suite) with identical topology mappings:
$ make PYTHONHASHSEED=3773822171 TESTS=case/system/hostname.py test\n "},{"location":"testing/#deterministic-transport-protocol","title":"Deterministic Transport Protocol","text":"By default, the communication transport protocol (NETCONF/RESTCONF) is chosen randomly. If you supply a PYTHONHASHSEED as described above, you get the same protocol used for that hash. But if you want to choose the protocol, add extra arguments to Infamy:
$ make INFAMY_EXTRA_ARGS=\"--transport=restconf\" TESTS=case/system/hostname.py test\n or, when running interactively:
$ make test-sh\n09:08:17 infamy0:test # ./9pm/9pm.py -o\"--transport=restconf\" case/system/hostname.py\n "},{"location":"testing/#test-specification","title":"Test specification","text":"The test specification is automaticly generated from the test cases, this puts some constraints on the test cases:
All test cases must be written so that it can be understandable with only the steps so the result can be reproduced manually. E.g., instead of set ip address on the interface it should be more human readable, for example:
test.step(\"Set IPv4 address on the interface dut:cross\")\ntest.step(\"Verify the IP address is set on dut:cross\")\n where dut, cross and the IPv4 address could be found inside the logical topology for the test.
Each test case must begin with the test name followed by a empty line and the description of the test. For example:
\"\"\"\nSet hostname\n\nVerify that it it possible to change hostname\n\"\"\"\n The test specifaction can be genererated with:
$ make test-spec\n...\n "},{"location":"testing/#test-development","title":"Test Development","text":"For adding a new test to the automated regression test suite, it's best to start by reviewing an existing test case.
All tests are located in the infix/test/case repository and are grouped by the features they verify. For example, infix/test/case/services contains tests for various Infix services, such as LLDP and mDNS.
While test grouping is flexible, each test should be placed in a logically relevant category.
When creating a new test group, add it to infix/test/case/all.yaml, to enable it to run as a subset of the test suite:
- name: infix-services\n suite: services/services.yaml\n A new test (e.g., lldp_enable_disable) should be added to the corresponding test group .yaml file, such as infix/test/cases/services.yaml:
- name: lldp_enable_disable\n case: lldp_enable_disable/test.py\n It is necessary to include the test in infix/test/case/services/Readme.adoc to ensure proper test specification generation:
include::lldp_enable_disable/Readme.adoc[]\n Each test case should have its own directory under, infix/test/case/services, containing:
test.py - the test script topology.dot - the logical topology definition. When the test specification is generated, topology.svg and Readme.adoc should also be created.
"},{"location":"tunnels/","title":"Tunneling (L2/L3)","text":""},{"location":"tunnels/#tunnel-configuration","title":"Tunnel Configuration","text":"Infix supports multiple tunnel encapsulation protocols for connecting remote networks or devices across an IP backbone. Tunnels encapsulate packets within IP datagrams, allowing traffic to traverse intermediate networks transparently.
Important
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"tunnels/#generic-routing-encapsulation-gre","title":"Generic Routing Encapsulation (GRE)","text":"GRE tunnels provide a simple and efficient method to encapsulate various network layer protocols over IP networks. Infix supports both IPv4 and IPv6 tunnels in two modes:
GRE (Layer 3): Point-to-point IP tunnel for routing protocols and routed traffic GRETAP (Layer 2): Ethernet tunnel for bridging Layer 2 networks Tip
If you name your tunnel interface greN or gretapN, where N is a number, the CLI infers the interface type automatically.
"},{"location":"tunnels/#basic-gre-configuration","title":"Basic GRE Configuration","text":"A basic GRE tunnel for routing between two sites:
admin@example:/> configure\nadmin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre local 192.168.3.1 remote 192.168.3.2\nadmin@example:/config/interface/gre0/> set ipv4 address 10.255.0.1 prefix-length 30\nadmin@example:/config/interface/gre0/> leave\nadmin@example:/>\n This creates a Layer 3 tunnel between 192.168.3.1 and 192.168.3.2 using the outer IP addresses, with the tunnel itself using 10.255.0.0/30 for the inner IP addressing.
"},{"location":"tunnels/#gretap-configuration","title":"GRETAP Configuration","text":"GRETAP tunnels operate at Layer 2, allowing bridging across the tunnel:
admin@example:/> configure\nadmin@example:/config/> edit interface gretap0\nadmin@example:/config/interface/gretap0/> set type gretap\nadmin@example:/config/interface/gretap0/> set gre local 192.168.3.1 remote 192.168.3.2\nadmin@example:/config/interface/gretap0/> leave\nadmin@example:/>\n GRETAP interfaces can be added to a bridge, bridging local and remote Ethernet segments. See the Bridge Configuration for more on bridges.
"},{"location":"tunnels/#ospf-over-gre","title":"OSPF over GRE","text":"GRE tunnels are commonly used to carry dynamic routing protocols like OSPF across networks that don't support multicast or where you want to create a virtual topology different from the physical network.
Example topology: Two sites connected via a GRE tunnel, running OSPF to exchange routes.
Site A configuration:
admin@siteA:/> configure\nadmin@siteA:/config/> edit interface gre0\nadmin@siteA:/config/interface/gre0/> set gre local 203.0.113.1 remote 203.0.113.2\nadmin@siteA:/config/interface/gre0/> set ipv4 address 10.255.0.1 prefix-length 30\nadmin@siteA:/config/interface/gre0/> set ipv4 forwarding\nadmin@siteA:/config/interface/gre0/> end\nadmin@siteA:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@siteA:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface gre0\nadmin@siteA:/config/routing/\u2026/ospf/> leave\nadmin@siteA:/>\n Site B configuration:
admin@siteB:/> configure\nadmin@siteB:/config/> edit interface gre0\nadmin@siteB:/config/interface/gre0/> set gre local 203.0.113.2 remote 203.0.113.1\nadmin@siteB:/config/interface/gre0/> set ipv4 address 10.255.0.2 prefix-length 30\nadmin@siteB:/config/interface/gre0/> set ipv4 forwarding\nadmin@siteB:/config/interface/gre0/> end\nadmin@siteB:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@siteB:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface gre0\nadmin@siteB:/config/routing/\u2026/ospf/> leave\nadmin@siteB:/>\n Once configured, OSPF will establish a neighbor relationship through the tunnel and exchange routes between the sites. For more info on OSPF configuration, see OSPFv2 Routing.
Note
Consider adjusting MTU on the tunnel interface to account for GRE overhead (typically 24 bytes for IPv4, 44 bytes for IPv6) to avoid fragmentation issues.
"},{"location":"tunnels/#advanced-tunnel-settings","title":"Advanced Tunnel Settings","text":"All tunnel types support common parameters for controlling tunnel behavior and performance.
"},{"location":"tunnels/#time-to-live-ttl","title":"Time To Live (TTL)","text":"The TTL setting controls the Time To Live value for the outer tunnel packets. By default, tunnels use a fixed TTL of 64, which allows packets to traverse multiple hops between tunnel endpoints.
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre ttl 255\nadmin@example:/config/interface/gre0/> leave\n Valid values are 1-255, or the special value inherit which copies the TTL from the encapsulated packet.
Important
The inherit mode can cause problems with routing protocols like OSPF that use TTL=1 for their packets. For tunnels carrying routing protocols, always use a fixed TTL value (typically 64 or 255).
"},{"location":"tunnels/#type-of-service-tos","title":"Type of Service (ToS)","text":"The ToS setting controls QoS marking for tunnel traffic:
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre tos 0x10\nadmin@example:/config/interface/gre0/> leave\n Valid values are 0-255 for fixed ToS/DSCP marking, or inherit (default) to copy the ToS value from the encapsulated packet.
"},{"location":"tunnels/#path-mtu-discovery-gre-only","title":"Path MTU Discovery (GRE only)","text":"The pmtu-discovery setting can be used to control the Path MTU Discovery on GRE tunnels. When enabled (default), the tunnel respects the Don't Fragment (DF) bit and performs PMTU discovery:
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre pmtudisc false\nadmin@example:/config/interface/gre0/> leave\n Disabling PMTU discovery may be necessary in networks with broken ICMP filtering but can lead to suboptimal performance and fragmentation.
"},{"location":"tunnels/#virtual-extensible-local-area-network-vxlan","title":"Virtual eXtensible Local Area Network (VXLAN)","text":"VXLAN is a network virtualization technology that encapsulates Layer 2 Ethernet frames within Layer 4 UDP datagrams. It uses a 24-bit segment ID, termed VXLAN Network Identifier (VNI), allowing up to 16 million isolated networks.
Infix supports both IPv4 and IPv6 for VXLAN tunnel endpoints.
"},{"location":"tunnels/#basic-vxlan-configuration","title":"Basic VXLAN Configuration","text":"Tip
If you name your VXLAN interface vxlanN, where N is a number, the CLI infers the interface type automatically.
admin@example:/> configure\nadmin@example:/config/> edit interface vxlan100\nadmin@example:/config/interface/vxlan100/> set vxlan local 192.168.3.1\nadmin@example:/config/interface/vxlan100/> set vxlan remote 192.168.3.2\nadmin@example:/config/interface/vxlan100/> set vxlan vni 100\nadmin@example:/config/interface/vxlan100/> leave\nadmin@example:/>\n The VNI uniquely identifies the VXLAN segment and must match on both tunnel endpoints.
"},{"location":"tunnels/#vxlan-with-custom-udp-port","title":"VXLAN with Custom UDP Port","text":"The default VXLAN UDP destination port is 4789 (IANA assigned). In some cases you may need to use a different port:
admin@example:/> configure\nadmin@example:/config/> edit interface vxlan100\nadmin@example:/config/interface/vxlan100/> set vxlan local 192.168.3.1\nadmin@example:/config/interface/vxlan100/> set vxlan remote 192.168.3.2\nadmin@example:/config/interface/vxlan100/> set vxlan vni 100\nadmin@example:/config/interface/vxlan100/> set vxlan remote-port 8472\nadmin@example:/config/interface/vxlan100/> leave\nadmin@example:/>\n The remote-port setting allows interoperability with systems using non-standard VXLAN ports.
Note
VXLAN tunnels also support the ttl and tos settings described in the Advanced Tunnel Settings section above.
"},{"location":"upgrade/","title":"Upgrade","text":""},{"location":"upgrade/#upgrading-boot-order","title":"Upgrading & Boot Order","text":"For resilience purposes, Infix maintains two software images referred to as the primary and secondary partition image. In addition, some bootloaders support netbooting.
The boot order defines which image is tried first, and is listed with the CLI show software command. It also shows Infix version installed per partition, and which image was used when booting (STATE booted).
admin@example:/> show software\nBOOT ORDER\nprimary secondary net\n\nNAME STATE VERSION DATE\nprimary booted v25.01.0 2025-04-25T10:15:00+00:00\nsecondary inactive v25.01.0 2025-04-25T10:07:20+00:00\nadmin@example:/>\n YANG support for upgrading Infix, inspecting and modifying the boot-order, is defined in infix-system-software.
"},{"location":"upgrade/#changing-boot-order","title":"Changing Boot Order","text":"The boot order can be manually changed using the set boot-order command. This is useful for rolling back to a previous version or changing the preferred boot source.
The command accepts one to three boot targets as separate arguments, in the desired boot order. Valid boot targets are:
primary - The primary partition secondary - The secondary partition net - Network boot (if supported by bootloader) The CLI provides tab-completion for boot targets, making it easy to enter valid values.
Example: View current boot order and change it:
admin@example:/> show boot-order\nprimary secondary net\nadmin@example:/> set boot-order secondary primary net\nadmin@example:/> show boot-order\nsecondary primary net\nadmin@example:/>\n Example: Set boot order to only try primary partition:
admin@example:/> show boot-order\nsecondary primary net\nadmin@example:/> set boot-order primary\nadmin@example:/> show boot-order\nprimary\nadmin@example:/>\n Example: Using tab-completion (press TAB to see available options):
admin@example:/> set boot-order <TAB>\nnet primary secondary\nadmin@example:/> set boot-order secondary <TAB>\nnet primary secondary\nadmin@example:/> set boot-order secondary primary\nadmin@example:/> show boot-order\nsecondary primary\nadmin@example:/>\n The new boot order takes effect on the next reboot and can be verified with show boot-order or show software:
admin@example:/> show software\nBOOT ORDER\nsecondary primary\n\nNAME STATE VERSION DATE\nprimary booted v25.01.0 2025-04-25T10:15:00+00:00\nsecondary inactive v25.01.0 2025-04-25T10:07:20+00:00\nadmin@example:/>\n Note
The boot order is automatically updated when performing an upgrade. The newly installed image will be set as the first boot target.
Duplicate boot targets are not allowed. The CLI will reject attempts to specify the same target multiple times.
"},{"location":"upgrade/#upgrading","title":"Upgrading","text":"Upgrading Infix is done one partition at a time. If the system has booted from one partition, an upgrade will apply to the other (inactive) partition.
Download and unpack the release to install. Make the image pkg bundle available at some URL2 (Optional) Backup the startup configuration Assume the unit has booted the primary image. Then running the upgrade command installs a new image on the secondary partition As part of a successful upgrade, the boot-order is implictly changed to boot the newly installed image Reboot the unit The unit now runs the new image. To upgrade the remaining partition (primary), run the same upgrade command again, and (optionally) reboot to verify the upgrade Caution
During boot (step 5), the unit may migrate the startup configuration for any syntax changes. It is therefore important that you make sure to upgrade the other partition as well after reboot, of course after having verified your setup.
The CLI example below shows steps 2-5.
Backup startup configuration: It is recommended to backup the startup configuration before performing an upgrade. The backup is useful if the upgrade fails, and makes a later downgrade a smoother process.
admin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg\n\nadmin@example:/> copy /cfg/startup-config.cfg /cfg/v25.01.0-startup-config.cfg\nadmin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg v25.01.0-startup-config.cfg\n\nadmin@example:/> \n Upgrade: Here the image pkg bundle was made available via TFTP.
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.03.1.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n...\n 98% Copying image to rootfs.1\n 99% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.03.1.pkg` succeeded\nadmin@example:/>\n Reboot: The unit will boot on the other partition, with the newly installed image. The Loading startup-config step conducts migration of startup configuration if applicable.
admin@example:/> reboot\n[ OK ] Stopping Static routing daemon\n[ OK ] Stopping Zebra routing daemon\n...\n[ OK ] Loading startup-config\n[ OK ] Verifying self-signed https certificate\n[ OK ] Update DNS configuration\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.03.1 (ttyS0)\nexample login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@example:/> show software\nBOOT ORDER\nsecondary primary net\n\nNAME STATE VERSION DATE\nprimary inactive v25.01.0 2025-04-25T10:15:00+00:00\nsecondary booted v25.03.1 2025-04-25T10:24:31+00:00\nadmin@example:/>\n As shown, the boot order has been updated, so that secondary is now the preferred boot source.
To upgrade the remaining partition (primary), run the upgrade URL command again, and (optionally) reboot.
"},{"location":"upgrade/#configuration-migration","title":"Configuration Migration","text":"The example above illustrated an upgrade from Infix v25.01.0 to v25.03.1. Inbetween these versions, YANG configuration definitions changed slightly (more details given below).
During boot, Infix inspects the version meta information within the startup configuration file to determine if configuration migration is needed. In this specific case, the configuration file has version 1.4 while the booted software expects version 1.5 (the configuration version numbering differs from the Infix image version numbering). The startup configuration is migrated to 1.5 definitions and stored, while a backup previous startup configuration is stored in directory /cfg/backup/.
admin@example:/> dir /cfg/backup/\n/cfg/backup/ directory\nstartup-config-1.4.cfg\n\nadmin@example:/>\n The modifications made to the startup configuration can be viewed by comparing the files from the shell. An example is shown below.
admin@example:/> exit\nadmin@example:~$ diff /cfg/backup/startup-config-1.4.cfg /cfg/startup-config.cfg\n--- /cfg/backup/startup-config-1.4.cfg\n+++ /cfg/startup-config.cfg\n...\n- \"public-key-format\": \"ietf-crypto-types:ssh-public-key-format\",\n+ \"public-key-format\": \"infix-crypto-types:ssh-public-key-format\",\n...\n- \"private-key-format\": \"ietf-crypto-types:rsa-private-key-format\",\n+ \"private-key-format\": \"infix-crypto-types:rsa-private-key-format\",\n...\n- \"version\": \"1.4\"\n+ \"version\": \"1.5\"\n...\nadmin@example:~$\n "},{"location":"upgrade/#downgrading","title":"Downgrading","text":"Downgrading to an earlier version is possible, however, downgrading is not guaranteed to work smoothly. In particular, when the unit boots up with the downgraded version, it may fail to apply the startup config, and instead apply its failure config.
We consider two cases: downgrading with and without applying a backup startup configuration before rebooting.
In both cases we start out with a unit running Infix v25.03.1, and wish to downgrade to v25.01.0.
admin@example:/> show software\nBOOT ORDER\nprimary secondary net\n\nNAME STATE VERSION DATE\nprimary booted v25.03.1 2025-04-25T11:36:26+00:00\nsecondary inactive v25.03.1 2025-04-25T10:24:31+00:00\nadmin@example:/>\n "},{"location":"upgrade/#with-backup-startup-config","title":"With Backup startup-config","text":"This is the recommended approach to downgrade, given that you have a backup configuration available. The objective is to avoid ending up with the unit in failure config.
Find the backup configuration file Run upgrade URL to install Infix image to downgrade to Copy backup startup configuration to current startup configuration (from shell) Reboot Find the backup configuration file:
Assume you have a backup startup config for the Infix version to downgrade to (here Infix v25.01.0, config version 1.4).
The preferred approach is to use a startup configuration backed up when running Infix v25.01.0 on the unit. See section Upgrading above for more information. In the following example, there is a backup file available named v25.01.0-startup-config.cfg:
admin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg v25.01.0-startup-config.cfg\n\nadmin@example:/> \n The alternative is to use a startup config implicitly backed up by the system as part of Configuration Migration.
admin@example:/> dir /cfg/backup/\n/cfg/backup/ directory\nstartup-config-1.4.cfg\n\nadmin@example:/>\n Caution
Using a backup configuration file stored when the unit was running the old version (e.g., v25.01.0-startup-config.cfg) is preferred. Although backup files stored due to configuration migration (e.g., startup-config-1.4.cfg) usually works too if the configuration file version (1.4) matches, there are situations when the system may fail to apply it as described below.
The configuration file version (1.4) is only incremented when changes in YANG configuration syntax mandates it to handle upgrading. Say the next Infix version includes a new feature setting, it can still have version 1.4, as upgrading to it would not need migration. If a user then enables the new feature setting, the new configuration will no longer be compatible with the previous Infix version. A downgrade after enabling new features risks ending up with the unit in failure config.
Use upgrade command to downgrade:
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.01.0.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n ...\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.01.0.pkg` succeeded\nadmin@example:/>\n Apply the backup configuration file:
It is recommended to use a backup configuration file for the Infix version to downgrade to, if there is one available.
admin@example:/> copy /cfg/v25.01.0-startup-config.cfg /cfg/startup-config.cfg\nOverwrite existing file /cfg/startup-config.cfg (y/N)? y\nadmin@example:/>\n An alternative is to use a backup file stored when the system conducted a configuration migration. See the caution note above.
admin@example:/> copy /cfg/backup/startup-config-1.4.cfg /cfg/startup-config.cfg\nOverwrite existing file /cfg/startup-config.cfg (y/N)? y\nadmin@example:/>\n Reboot:
The unit will come up with the applied backup configuration.
admin@example:/> reboot\n[ OK ] Saving system clock to file\n[ OK ] Stopping Software update service\n[ OK ] Stopping Status daemon\n...\n[ OK ] Bootstrapping YANG datastore\n[ OK ] Starting Configuration daemon\n[ OK ] Loading startup-config\n[ OK ] Update DNS configuration\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\nexample login:\n Note
If the unit despite these measures ends up in failure config, see the next section for more information on how to recover.
"},{"location":"upgrade/#without-a-backup-startup-config","title":"Without a Backup startup-config","text":"This procedure assumes you have access to the unit's console port and its default login credentials1 .
Downgrade Reboot Login with unit's default credentials Conduct factory reset (Then go on configure the unit as you wish) Use upgrade command to downgrade:
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.01.0.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n ...\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.01.0.pkg` succeeded\nadmin@example:/>\n Reboot:
Conduct a reboot. During boot, the unit fails to apply the existing startup configuration (config version 1.5 while software expects version 1.4 or earlier), and instead applies its failure config. This is what is seen on the console when this situation occurs. Note that the login prompt displays failed as part of the hostname.
admin@example:/> reboot\n[ OK ] Saving system clock to file\n[ OK ] Stopping Software update service\n[ OK ] Stopping Status daemon\n...\n[ OK ] Verifying SSH host keys\n[ OK ] Bootstrapping YANG datastore\n[ OK ] Starting Configuration daemon\n[FAIL] Loading startup-config\n[ OK ] Loading failure-config\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\n\nERROR: Corrupt startup-config, system has reverted to default login credentials\nfailed-00-00-00 login:\n To remedy a situation like this, you can login with the unit's default login credentials, preferrably via a console port. The unit's default credentials are typically printed on a sticker on the unit.
failed-00-00-00 login: admin\nPassword:\n\nRun the command 'cli' for interactive OAM\n\nadmin@failed-00-00-00:~$\n When it is safe from a network operations perspective, you can conduct a factory reset and reboot. It is recommended to remove the unit from any production network before doing this, as a factory reset may enable undesired connectivity between the unit's ports.
admin@failed-00-00-00:~$ factory\nFactory reset device (y/N)? y\nfactory: scheduled factory reset on next boot.\nReboot now to perform reset, (y/N)? y\n[ OK ] Saving system time (UTC) to RTC\n[ OK ] Stopping mDNS alias advertiser\n...\n[ OK ] Starting Configuration daemon\n[ OK ] Loading startup-config\n[ OK ] Update DNS configuration\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n[ OK ] Starting Status daemon\n\n\nPlease press Enter to activate this console.\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\nexample login:\n Continued configuration is done as with any unit after factory reset.
In failure config, Infix puts all Ethernet ports as individual interfaces. With direct access, one can connect with e.g., SSH, using link local IPv6 addresses. This as an alternative to connecting via a console port.\u00a0\u21a9
Set up an FTP/TFTP/SFTP or HTTP/HTTPS server on the same LAN.\u00a0\u21a9
"},{"location":"virtual/","title":"Virtual Environments","text":""},{"location":"virtual/#virtual-environments","title":"Virtual Environments","text":"Infix primarily targets real hardware, deployment to the cloud is not a priority at the moment. However, for development and testing purposes there is an x86_64 build that runs in Qemu.
These images also work with the Graphical Network Simulator (GNS3), which is a very user-friendly tool for playing around with simple to complex network setups, verifying interoperability between vendors, etc.
"},{"location":"virtual/#qemu","title":"QEMU","text":"Tip
Installation for Debian/Ubuntu based systems can be done by \"simply\": sudo apt install virt-manager -- dependencies ensure the relevant Qemu packages are pulled in as well. This trick, installing virt-manager, helps set up Qemu networking on your system.
A virtualized Infix x86_64 instance can easily be launched from a Linux system, with Qemu installed, by issuing:
$ ./qemu.sh\n...\n from an unpacked release tarball. From a built source tree of Infix the same functionality is bundled as:
$ make run\n...\n To change settings, e.g. networking, make run-menuconfig, or from a pre-built Infix release tarball, using ./qemu.sh -c
The Infix test suite is built around Qemu and Qeneth, see:
Regression Testing with Infamy Infamy Docker Image "},{"location":"virtual/#gns3","title":"GNS3","text":"Download the latest build of the x86_64, or aarch64 if your host machine is Arm. Unpack the tarball in a dedicated directory and use \"Import Appliance\" to install the .gns3a file into GNS3.
Infix is in the \"Router\" category, it comes with 10 interfaces available by default for use as switch ports or routing.
"},{"location":"vpd/","title":"Vital Product Data (VPD)","text":""},{"location":"vpd/#vital-product-data","title":"Vital Product Data","text":"Infix can source Vital Product Data (VPD) from EEPROMs containing an ONIE TLV. The load bearing information from VPDs, used by Infix by default, is:
Base (chassis) MAC address of the system Factory default admin password hash Additionally, every VPD is listed as a separate component in the ietf-hardware model, in which Infix exports all available attributes to the user. This is useful to read out exact hardware revisions of each board making up a system, figure out if a system belongs to a particular production batch, etc.
"},{"location":"vpd/#json-encoding","title":"JSON Encoding","text":"To make EEPROM binary generation less cumbersome, Infix defines a JSON encoding of an ONIE TLV, and provides a tool, onieprom, to translate from one encoding to another. I.e. a JSON object can be translated to its binary equivalent, and vice versa.
A mapping between TLV attribute IDs and JSON keys is defined, using the Linux kernel driver as a reference:
TLV ID Description Key Type 0x21 Product Name \"product-name\" String 0x22 Part Number \"part-number\" String 0x23 Serial Number \"serial-number\" String 0x24 MAC #1 Base \"mac-address\" String (\"c0:ff:ee:00:00:00\") 0x25 Manufacture Date \"manufacture-date\" String (\"MM/DD/YYYY HH:NN:SS\") 0x26 Device Version \"device-version\" Integer (u8) 0x27 Label Revision \"label-revision\" String 0x28 Platform Name \"platform-name\" String 0x29 ONIE Version \"onie-version\" String 0x2a Num MACs \"num-macs\" Integer (u16) 0x2b Manufacturer \"manufacturer\" String 0x2c Country Code \"country-code\" String (ISO 3166-1 2-byte code) 0x2d Vendor \"vendor\" String 0x2e Diag Version \"diag-version\" String 0x2f Service Tag \"service-tag\" String 0xfd Vendor Extension \"vendor-extension\" List of extensions (see below) The JSON encoding of a TLV is a single JSON object with an arbitrary subset of the attributes from the list above.
Example:
{\n \"product-name\": \"Wacky Widget\",\n \"serial-number\": \"#1\",\n \"manufacture-date\": \"02/13/2024 11:29:52\"\n}\n
"},{"location":"vpd/#vendor-extensions","title":"Vendor Extensions","text":"As the specification explicitly mentions the option of supplying multiple vendor extensions, possibly multiple ones of the same type, the JSON chosen encoding is a list, where each element is itself a list of exactly two elements:
Element # Type 1 Integer (u32, IANA enterprise number) 2 String (UTF-8 encoded extension data) The format of the extension data is defined by the entity indicated by the IANA enterprise number. Restricting extension data to UTF-8 encoded strings is a decision imposed by Infix; the ONIE specification allows for arbitrary binary data.
Example:
{\n ...\n \"vendor-extension\": [\n [ 12345, \"my extension data\" ]\n ]\n}\n "},{"location":"vpd/#infix-specific-extensions","title":"Infix Specific Extensions","text":"Kernelkit's IANA enterprise number is 61046, under which any extensions required by Infix are stored. The only valid extension data that may be stored under this number is documented in this section. If other device specific data needs to be stored in a VPD, you must associate that with an enterprise number under our control.
Every Kernelkit extension must be a valid JSON object containing an arbitrary subset of the following attributes:
Key Value Description \"pwhash\" String (output of mkpasswd(1)) Factory default password for admin Since the extension is itself stored in a JSON document, it has to be appropriately quoted.
Example:
{\n ...\n \"vendor-extension\": [\n [ 61046, \"{\\\"pwhash\\\":\\\"$6$9rufAxdqCrxrwfQR$G0l9cTVlu/vOhxgo/uMKfRDOmZRd5XWF3vKr5da6qYoxuTJBS/Pl9K.5lrabWoWFFc.71yFMaSlZz0O8FtAtl.\\\"}\" ]\n ]\n}\n "},{"location":"vpd/#creating-and-parsing-onie-eeprom-binaries","title":"Creating and Parsing ONIE EEPROM Binaries","text":"usage: onieprom [-h] [-e] [-d] [infile] [outfile]\n\npositional arguments:\n infile\n outfile\n\noptions:\n -h, --help show this help message and exit\n -e, --encode Encode JSON input to binary output\n -d, --decode Decode binary input to JSON output\n To convert a compatible JSON document (using the first example above) to its binary equivalent, we ask onieprom to encode it for us:
~$ onieprom -e example.json >example.bin\n~$ hexdump -C example.bin\n00000000 54 6c 76 49 6e 66 6f 00 01 00 2d 25 13 30 32 2f |TlvInfo...-%.02/|\n00000010 31 33 2f 32 30 32 34 20 31 31 3a 32 39 3a 35 32 |13/2024 11:29:52|\n00000020 21 0c 57 61 63 6b 79 20 57 69 64 67 65 74 23 02 |!.Wacky Widget#.|\n00000030 23 31 fe 04 dd 69 88 97 |#1...i..|\n00000038\n We can also run the process in reverse, to inspect the contents of a binary in its equivalent JSON representation:
~$ onieprom -d example.bin >example-again.json\n~$ jq . example-again.json\n{\n \"manufacture-date\": \"02/13/2024 11:29:52\",\n \"product-name\": \"Wacky Widget\",\n \"serial-number\": \"#1\"\n}\n "},{"location":"wifi/","title":"Wireless LAN (WiFi)","text":""},{"location":"wifi/#wi-fi-wireless-lan","title":"Wi-Fi (Wireless LAN)","text":"Infix includes built-in Wi-Fi client support for connecting to wireless networks. When a compatible Wi-Fi adapter is detected, the system automatically begins scanning for available networks.
"},{"location":"wifi/#current-limitations","title":"Current Limitations","text":" Only client mode is supported (no access point functionality) USB hotplug is not supported - adapters must be present at boot Interface naming may be inconsistent with multiple USB Wi-Fi adapters "},{"location":"wifi/#supported-wi-fi-adapters","title":"Supported Wi-Fi Adapters","text":"Wi-Fi support is primarily tested with Realtek chipset-based adapters.
"},{"location":"wifi/#known-working-chipsets","title":"Known Working Chipsets","text":" RTL8821CU Other Realtek chipsets may work but are not guaranteed Some Realtek chipsets require proprietary drivers not included in the standard kernel
Firmware requirements vary by chipset\n Check kernel logs if your adapter is not detected\n "},{"location":"wifi/#configuration","title":"Configuration","text":"Add a supported Wi-Fi network device. To verify that it has been detected, look for wifi0 in show interface
admin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\nlo loopback UP\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet UP 02:00:00:00:00:01\n ipv6 fe80::ff:fe00:1/64 (link-layer)\n ipv6 fec0::ff:fe00:1/64 (link-layer)\nwifi0 ethernet DOWN f0:09:0d:36:5f:86\n wifi ssid: ------, signal: ------\n Add the new Wi-Fi interface to the configuration to start scanning. admin@example:/config/> set interface wifi0\nadmin@example:/config/> leave\n Now the system will now start scanning in the background. To see the result read the operational datastore for interface wifi0 or use the CLI
admin@infix-00-00-00:/> show interface wifi0\nname : wifi0\ntype : wifi\nindex : 3\nmtu : 1500\noperational status : down\nphysical address : f0:09:0d:36:5f:86\nipv4 addresses :\nipv6 addresses :\nSSID : ----\nSignal : ----\n\nSSID ENCRYPTION SIGNAL\nssid1 WPA2-Personal excellent\nssid2 WPA2-Personal excellent\nssid3 WPA2-Personal excellent\nssid4 WPA2-Personal good\nssid5 WPA2-Personal good\nssid6 WPA2-Personal good\n In the CLI, signal strength is reported as: excellent, good, poor or bad. For precise values, use NETCONF or RESTCONF, where the RSSI (in dBm) is available in the operational datastore.
Configure your Wi-Fi secret in the keystore, it should be between 8 and 63 characters
admin@example:/> configure\nadmin@example:/config/> edit keystore symmetric-key example\nadmin@example:/config/keystore/\u2026/example/> set key-format wifi-preshared-key-format\nadmin@example:/config/keystore/\u2026/example/> set cleartext-key mysecret\nadmin@example:/config/keystore/\u2026/example/> leave\nadmin@example:/>\n Configure the Wi-Fi settings, set secret to the name selected above for the symmetric key, in this case example.
WPA2 or WPA3 encryption will be automatically selected based on what the access point supports. No manual selection is required unless connecting to an open network. No support for certificate based authentication yet.
Unencrypted network is also supported, to connect to an unencrypted network (generally not recommended):
admin@example:/config/interface/wifi0/> set wifi encryption disabled\n
A valid country-code is also required for regulatory compliance, the valid codes are documented in the YANG model infix-wifi-country-codes
admin@example:/> configure\nadmin@example:/config/> edit interface wifi0\nadmin@example:/config/interface/wifi0/>\nadmin@example:/config/interface/wifi0/> set wifi ssid ssid1\nadmin@example:/config/interface/wifi0/> set wifi secret example\nadmin@example:/config/interface/wifi0/> set wifi country-code SE\nadmin@example:/config/interface/wifi0/> leave\n The Wi-Fi negotiation should now start immediately, provided that the SSID and pre-shared key are correct. You can verify the connection by running show interface again.
admin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\nlo loopback UP\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet UP 02:00:00:00:00:01\n ipv6 fe80::ff:fe00:1/64 (link-layer)\n ipv6 fec0::ff:fe00:1/64 (link-layer)\nwifi0 ethernet UP f0:09:0d:36:5f:86\n wifi ssid: ssid1, signal: excellent\n\nadmin@example:/>\n "},{"location":"wifi/#troubleshooting-connection-issues","title":"Troubleshooting Connection Issues","text":"Use show wifi scan wifi0 and show interface to verify signal strength and connection status. If issues arise, try the following troubleshooting steps:
Verify signal strength: Check that the target network shows \"good\" or \"excellent\" signal Check credentials: Verify the preshared key in ietf-keystore Review logs: Check system logs with show log for Wi-Fi related errors Regulatory compliance: Ensure the country-code matches your location Hardware detection: Confirm the adapter appears in show interface If issues persist, check the system log for specific error messages that can help identify the root cause.
"},{"location":"cli/configure/","title":"Configuration","text":""},{"location":"cli/configure/#cli-configure-context","title":"CLI Configure Context","text":"Enter the configure context from admin-exec by typing configure followed by Enter. Available commands, press ? at the prompt:
admin@host:/> configure\nadmin@host:/config/>\n abort Abandon candidate\n change Interactively change setting, e.g. password\n check Validate candidate\n commit Commit current candidate to running-config\n delete Delete configuration setting(s)\n diff Summarize uncommitted changes\n do Execute operational mode command\n edit Descend to the specified configuration node\n end Alias to up, leave this subsection/node\n exit Ascend to the parent configuration node, or abort (from top)\n leave Finalize candidate and apply to running-config\n no Alias for delete\n rollback Restore candidate to running-config\n set Set configuration setting\n show Show configuration\n text-editor Modify binary content in a text editor\n top Ascend to the configuration root\n up Ascend to the parent configuration node\n The edit command lets you change to a sub-configure context, e.g.:
admin@host:/config/> edit interface eth0\nadmin@host:/config/interface/eth0/>\n Use up to go up one level.
admin@host:/config/interface/eth0/> up\nadmin@host:/config/>\n Note: commands in configure context are automatically generated from the system's YANG models, hence different products likely have a different set of commands. However, both the ietf-interfaces.yang and ietf-ip.yang models, for instance, that provide the networking support are common to all systems.
"},{"location":"cli/configure/#set-ip-address-on-an-interface","title":"Set IP Address on an Interface","text":"admin@host:/config/> edit interface eth0\nadmin@host:/config/interface/eth0/>\nadmin@host:/config/interface/eth0/> set ipv4 address 192.168.2.200 prefix-length 24\n From anywhere in configure context you can see the changes you have made by typing diff:
admin@host:/config/interface/eth0/> diff\ninterfaces {\n interface eth0 {\n+ ipv4 {\n+ address 192.168.2.200 {\n+ prefix-length 24;\n+ }\n+ }\n }\n}\n "},{"location":"cli/configure/#saving-changes","title":"Saving Changes","text":"Apply the changes (from candidate to running-config):
admin@host:/config/interface/eth0/> leave\nadmin@host:/> show running-config\n...\ninterfaces {\n interface eth0 {\n type ethernetCsmacd;\n ipv4 {\n address 192.168.2.200 {\n prefix-length 24;\n }\n }\n }\n...\n Since we did not get any warnings we can save the running (RAM only) configuration to startup, so the changes are made persistent across reboots:
admin@host:/> copy running-config startup-config\n The startup-config can also be inspected with the show command to verify the changes are saved.
Note: all commands need to be spelled out, no short forms are allowed in the CLI. Use the TAB key to make your life easier.
"},{"location":"cli/configure/#changing-hostname","title":"Changing Hostname","text":"Settings like hostname are located in the ietf-system.yang model. Here is how it can be set.
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname example\nadmin@host:/config/system/> leave\nadmin@example:/> \n Notice how the hostname in the prompt does not change until the change is committed.
Note: critical services like syslog, mDNS, LLDP, and similar that advertise the hostname, are restarted when the hostname is changed.
"},{"location":"cli/configure/#changing-password","title":"Changing Password","text":"User management, including passwords, is also a part of ietf-system.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> change password\nNew password: \nRetype password: \nadmin@host:/config/system/authentication/user/admin/> leave\n The change password command starts an interactive dialogue that asks for the new password, with a confirmation, and then salts and encrypts the password with sha512crypt.
It is also possible to use the set password ... command. This allows setting an already hashed password. To manually hash a password, use the do password encrypt command. This launches the admin-exec command to hash, and optionally salt, your password. This encrypted string can then be used with set password ....
Tip: if you are having trouble thinking of a password, there is also do password generate, which generates random but readable strings using the UNIX command pwgen.
"},{"location":"cli/configure/#ssh-authorized-key","title":"SSH Authorized Key","text":"Logging in remotely with SSH is possible by adding a public key to a user. Here we add the authorized key to the admin user, multiple keys are supported.
With SSH keys in place it is possible to disable password login, just remember to verify SSH login and network connectivity before doing so.
admin@host:/config/> edit system authentication user admin \nadmin@host:/config/system/authentication/user/admin/> edit authorized-key example@host\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> show\nalgorithm ssh-rsa;\nkey-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=;\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> leave\n Note: the ssh-keygen program already base64 encodes the public key data, so there is no need to use the text-editor command, set does the job.
"},{"location":"cli/configure/#creating-a-veth-pair","title":"Creating a VETH Pair","text":"The following example creates a veth0a <--> veth0b virtual Ethernet pair which is useful for connecting, e.g., a container to the physical world. Here we also add an IPv4 address to one end of the pair.
admin@host:/config/> edit interface veth0a\nadmin@host:/config/interface/veth0a/> set veth peer veth0b\nadmin@host:/config/interface/veth0a/> set ipv4 address 192.168.0.1 prefix-length 24\nadmin@host:/config/interface/veth0a/> up\nadmin@host:/config/> diff\ninterfaces {\n+ interface veth0a {\n+ type veth;\n+ ipv4 {\n+ address 192.168.0.1 {\n+ prefix-length 24;\n+ }\n+ }\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ }\n}\nadmin@host:/config/> leave\n See the bridging example below for more.
Note: in the CLI you do not have to create the veth0b interface. The system infers this for you. When setting up a VETH pair using NETCONF, however, you must include the veth0b interface.
"},{"location":"cli/configure/#creating-a-bridge","title":"Creating a Bridge","text":"Building on the previous example, we now create a non-VLAN filtering bridge (br0) that forwards any, normally link-local, LLDP traffic between both its bridge ports: eth0 and vet0b.
admin@host:/> configure\nadmin@host:/config/> edit interface br0\nadmin@host:/config/interface/br0/> set bridge ieee-group-forward lldp\nadmin@host:/config/interface/br0/> up\nadmin@host:/config/> set interface eth0 bridge-port bridge br0\nadmin@host:/config/> set interface veth0b bridge-port bridge br0\nadmin@host:/config/> diff\ninterfaces {\n+ interface br0 {\n+ type bridge;\n+ bridge {\n+ ieee-group-forward lldp;\n+ }\n+ }\n interface eth0 {\n+ bridge-port {\n+ bridge br0;\n+ }\n }\n+ interface veth0a {\n+ type veth;\n+ ipv4 {\n+ address 192.168.0.1 {\n+ prefix-length 24;\n+ }\n+ }\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ bridge-port {\n+ bridge br0;\n+ }\n+ }\n}\n Both a physical port eth0 and a virtual port veth0b (bridge side of the VETH pair from the previous example) are now bridged. Any traffic ingressing one port will egress the other. Only reserved IEEE multicast is filtered, except LLDP frames as shown above.
Note: the bridge can be named anything, provided the interface name is not already taken. However, for any name outside the pattern br[0-9]+, you have to set the interface type manually to bridge.
"},{"location":"cli/introduction/","title":"Introduction","text":""},{"location":"cli/introduction/#cli-introduction","title":"CLI Introduction","text":"The command line interface (CLI, see-ell-aye) implements a CISCO-like, or Juniper Networks JunOS-like, CLI. It is the traditional way of interacting with single network equipment like switches and routers. Today more advanced graphical NETCONF-based tools are available that allows for managing entire fleets of installed equipment.
Nevertheless, when it comes to initial deployment and debugging, it is very useful to know how to navigate and use the CLI. This very short guide intends to help you with that.
New users usually get the CLI as the default \"shell\" when logging in, but the default admin user logs in to bash. To access the CLI, type:
admin@host-12-34-56:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@host-12-34-56:/>\n The prompt (beginning of the line) changes slightly. Key commands available in any context are:
admin@host-12-34-56:/> help # Try: Tab or ?\n...\nadmin@host-12-34-56:/> show # Try: Tab or ?\nadmin@host-12-34-56:/> # Try: Tab or ?\n Note: even on an empty command line, you can tap the Tab or ? keys. See help keybindings for more tips!
"},{"location":"cli/introduction/#key-concepts","title":"Key Concepts","text":"The two modes in the CLI are the admin-exec and the configure context.
The top-level context after logging in and starting the CLI is the admin-exec or \"main\" context. It is used for querying system status, managing configuration files/profiles and doing advanced debugging.
Available commands can be seen by pressing ? at the prompt:
admin@host:/>\n configure Create new candidate-config based on running-config\n copy Copy file or configuration, e.g., copy running-config startup-config\n dir List available configuration files\n exit Exit from CLI (log out)\n factory-reset Restore the system to factory default state\n follow Monitor a log file, use Ctrl-C to abort\n help Help system (try also the '?' key)\n logout Alias to exit\n netcalc IP subnet calculator, with subnetting\n password Password tools\n ping Ping a network host or multicast group\n poweroff Poweroff system (system policy may yield reboot)\n reboot Reboot system\n remove Remove a configuration file\n set Set operations, e.g., current date/time\n show Show system status and configuration files\n tcpdump Capture network traffic\n upgrade Install a software update bundle from remote or local file\n The system has three main datastores (or files): factory, startup, and running that can be managed and inspected using the copy, show, and configure commands. The traditional names used in the CLI for these are listed below:
factory-config the default configuration from factory for the device, i.e., what the system returns to after a factory-reset startup-config created from factory-config at first boot after factory reset. Loaded as the system configuration on each boot running-config what is actively running on the system. If no changes have been made since boot, it is the same as startup-config candidate-config is created from running-config when entering the configure context. Any changes made here can be discarded (abort, rollback) or committed (commit, leave) to running-config Edit the running configuration using the configure command. This copies running to candidate, a temporary datastore, where changes are made:
admin@host-12-34-56:/> configure\nadmin@host-12-34-56:/config/> ... # Try: Tab or ?\nadmin@host-12-34-56:/config/> leave\n The leave command activates the changes by issuing a transaction to, essentially, copy the candidate back to running. Depending on the changes made, this can take a few seconds. If the changes are invalid, i.e., not correct according to the underlying YANG models, a warning is shown and the session remains in configure context. Use the abort command to cancel your changes, or investigate further with the diff command (see more below).
To save configuration changes made to the running-config to persistent store, so the system will use them for consecutive reboots, use the copy command:
admin@host-12-34-56:/> copy running-config startup-config\n In configure context the following commands are available:
Command Description set foo bar val Set bar leaf node in foo subcontext to val no foo bar Clear/delete configuration made to bar in foo edit foo baz Enter baz sub-sub-context in foo subcontext change password Start password dialogue to change a user's password text-editor foo Open a text editor to edit binary setting foo abort Abort changes in configuration, return to admin-exec exit Exit one level sub-context, or abort from top-level leave Save changes to running-config, return to admin-exec show [foo] Show configured values (optionally in subcontext) diff [foo] Show uncommitted changes in candidate do command Call admin-exec command: do show log commit"},{"location":"cli/introduction/#example-session","title":"Example Session","text":"Remember to use the TAB and ? keys to speed up your navigation. See help keybindings for more tips!
In this example we enter configure context to add an IPv4 address to interface eth0, then we apply the changes using the leave command.
We inspect the system status to ensure the change took effect. Then we save the changes for the next reboot.
admin@host-12-34-56:/> configure\nadmin@host-12-34-56:/config/> edit interface eth0\nadmin@host-12-34-56:/config/interface/eth0/> set ipv4 <TAB>\n address autoconf bind-ni-name enabled\n forwarding mtu neighbor\nadmin@host-12-34-56:/config/interface/eth0/> set ipv4 address 192.168.2.200 prefix-length 24\nadmin@host-12-34-56:/config/interface/eth0/> show\ntype ethernetCsmacd;\nipv4 address 192.168.2.200 prefix-length 24;\nipv6 enabled true;\nadmin@host-12-34-56:/config/interface/eth0/> diff\ninterfaces {\n interface eth0 {\n+ ipv4 {\n+ address 192.168.2.200 {\n+ prefix-length 24;\n+ }\n+ }\n }\n}\nadmin@host-12-34-56:/config/interface/eth0/> leave\nadmin@host-12-34-56:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo loopback UNKNOWN 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\neth0 ethernet UP 52:54:00:12:34:56\n ipv4 192.168.2.200/24 (static)\n ipv6 fe80::5054:ff:fe12:3456/64 (link-layer)\nadmin@host-12-34-56:/> show ip brief\nlo UNKNOWN 127.0.0.1/8 ::1/128\neth0 UP 192.168.2.200/24 fe80::5054:ff:fe12:3456/64\nadmin@host-12-34-56:/> copy running-config startup-config\n One of the ideas behind a separate running and startup configuration is to be able to verify a configuration change. In case of an inadvertent change that, e.g., breaks networking, it is trivial to revert back by:
admin@host-12-34-56:/> copy startup-config running-config\n Or restart the device, for example if the change to the configuration caused you to lose contact with the system (it happens to the best of us). The system will start up from the last \"save gave\".
Tip: when restoring a backup of a configuration, or having manually edited a config file, you can validate it using system's YANG models, it is not applied if validation is successful:
copy /media/backup/old.cfg running-config validate
"},{"location":"cli/keybindings/","title":"Keybindings","text":""},{"location":"cli/keybindings/#cli-keybindings","title":"CLI Keybindings","text":"Writing CLI commands by hand is very tedious. To make things easier the CLI has several keybindings, most significant first:
Key Cmd/Key Description TAB Complete current command, see below for example ? Show available commands, or arguments, with help Ctrl-c Cancel everything on current line Ctrl-d abort/exit Delete character, or abort/exit on empty line Ctrl-z leave Leave and activate changes in configure context Ctrl-f Right arrow Move cursor forward one character Ctrl-b Left arrow Move cursor back one character Meta-f Ctrl-Right Move cursor forward one word Meta-b Ctrl-Left Move cursor back one word Ctrl-e End Move cursor to end of line Ctrl-a Home Move cursor to beginning of line Ctrl-k Kill (cut) text from cursor to end of line Ctrl-u Delete (cut) entire line Ctrl-y Yank (paste) from kill buffer to cursor Meta-. Yank (paste) last argument from previous line Ctrl-w Meta-Backspace Delete (cut) word to the left Meta-Delete Delete (cut) word to the right Ctrl-l Clear screen and refresh current line Ctrl-p Up arrow History, previous command Ctrl-n Down arrow History, next command Ctrl-r History, reversed interactive search (i-search)"},{"location":"cli/keybindings/#what-is-meta","title":"What is Meta?","text":"The Meta key is called Alt on most modern keyboards. If you have neither, first tap the Esc key instead of holding down Alt/Meta.
"},{"location":"cli/keybindings/#usage","title":"Usage","text":"Complete a word. Start by typing a few characters, then tap the TAB key on your keyboard:
conf<TAB> --> configure\n See possible arguments, with brief help text, to a command:
show ?\nbridge Show bridge (ports/fdb/mdb/vlans)\ndatetime Show current date and time, default RFC2822 format\n...\n Type the command, then tap the ? key.
"},{"location":"cli/netcalc/","title":"Network Calculator","text":""},{"location":"cli/netcalc/#network-calculator","title":"Network Calculator","text":"netcalc <ADDRESS/LEN | NETWORK NETMASK> [split <1-32 | 64-128>]\n "},{"location":"cli/netcalc/#description","title":"Description","text":"netcalc is a network calculator that takes an IP address and a subnet mask, or an IP address and prefix mask in CIDR notation, and then returns information about the subnet. Both IPv4 and IPv6 is supported.
A subnet can be entered in two ways:
192.168.2.0 255.255.255.0: traditional IPv4 'address netmask' style 192.168.2.0/24: modern prefix length, same also for IPv6 An optional split LEN can be given as argument, the new length value must be bigger than the current prefix length. See example below.
"},{"location":"cli/netcalc/#examples","title":"Examples","text":"Its most commonly used features are to understand how many addresses an IP subnet has, what the broadcast address is, first and last usable address.
admin@example:/> netcalc 192.168.2.0/24\nAddress : 192.168.2.0 11000000.10101000.00000010. 00000000\nNetmask : 255.255.255.0 = 24 11111111.11111111.11111111. 00000000\nWildcard : 0.0.0.255 00000000.00000000.00000000. 11111111\n=>\nNetwork : 192.168.2.0/24 11000000.10101000.00000010. 00000000\nHostMin : 192.168.2.1 11000000.10101000.00000010. 00000001\nHostMax : 192.168.2.254 11000000.10101000.00000010. 11111110\nBroadcast: 192.168.2.255 11000000.10101000.00000010. 11111111\nHosts/Net: 254 Class C, Private network (RFC1918)\n Another common use-case is for IP subnetting, i.e., using only as many addresses for an IP subnet as needed. Example, to split the above /24 in four:
admin@example:/> netcalc 192.168.2.0/24 split 26\nAddress : 192.168.2.0 11000000.10101000.00000010. 00000000\nNetmask : 255.255.255.0 = 24 11111111.11111111.11111111. 00000000\nWildcard : 0.0.0.255 00000000.00000000.00000000. 11111111\n=>\nNetwork : 192.168.2.0/24 11000000.10101000.00000010. 00000000\nHostMin : 192.168.2.1 11000000.10101000.00000010. 00000001\nHostMax : 192.168.2.254 11000000.10101000.00000010. 11111110\nBroadcast: 192.168.2.255 11000000.10101000.00000010. 11111111\nHosts/Net: 254 Class C, Private network (RFC1918)\n\n[Split network/26]\nNetwork : 192.168.2.0 - 192.168.2.63 Netmask : 255.255.255.192\nNetwork : 192.168.2.64 - 192.168.2.127 Netmask : 255.255.255.192\nNetwork : 192.168.2.128 - 192.168.2.191 Netmask : 255.255.255.192\nNetwork : 192.168.2.192 - 192.168.2.255 Netmask : 255.255.255.192\n "},{"location":"cli/quick/","title":"Quickstart Guide","text":""},{"location":"cli/quick/#cli-quickstart-guide","title":"CLI Quickstart Guide","text":"The question mark ? key along with the Tab key are your best friends in the command line interface. They provide context help and completion of commands you input. See the table below for a handful of examples.
Command Description help Overview of topics/settings in the current context ? Lists commands + brief help in the current context <Tab> Lists commands available in the current context partial<Tab> Completes a partial command name command <Tab> Lists keywords/arguments associated with a command command keyword ? Lists arguments associated with a keyword + help Explore the following topics for more information. Note, the keybindings are really useful to learn!
Command Description help introduction An introduction to the CLI help configure How to use configure context help text-editor Help with the built-in text-editor command help keybindings Lists available keybindings & other helpful tricks In configure context the help setting command shows the YANG description text for each node and container. To reach the admin exec help from configure context, e.g., do help text-editor
"},{"location":"cli/tcpdump/","title":"Network Monitoring","text":""},{"location":"cli/tcpdump/#network-monitoring","title":"Network Monitoring","text":"tcpdump is useful tool for analyzing and diagnosing network problems. This document presents the limited feature set that exposed is in the CLI. Administrator level users with shell access can use the full feature set, and is not described here.
The following section is useful for understanding how to use the tool. A section called Examples follows that, which may be what you want to scroll down to.
"},{"location":"cli/tcpdump/#hardware-overview","title":"Hardware Overview","text":"Using tcpdump effectively requires an understanding of how the underlying hardware works. For a standard PC, or common single-board computers (SBC), the network cards (NICs) are usually connected directly to the CPU.
.---------.\n | +-- eth0\n | CPU |\n | +-- eth1\n '---------'\n In this setup it is evident that traffic coming in on either eth0 or eth1 reach the CPU, i.e., running tcpdump eth0 captures all traffic.
However, on other types of networking hardware, e.g., dedicated switch core setups depicted below, the flow of network traffic will likely not pass through the CPU. This depends of course on how the switch is set up, for instance if routing between all ports is enabled, each flow will reach the CPU, but in a plain switching setup it will not.
.---------.\n | |\n | CPU |\n | |\n '----+----'\n |\n |\n .----------+---------.\n | |\n ---+ +---\n E1 | SC | E3\n ---+ +---\n E2 | | E4\n '--+--+--+--+--+--+--'\n | | | | | |\n E5 E6 E7 E8 E9 E10\n So, running tcpdump e1 in a switching setup, inside the CPU, the only traffic that will be captured is traffic ingressing port E1 destined for the CPU itself. To analyze traffic going through the switch, you need something called port mirroring, or port monitoring, i.e., setting up the switch core to mirror traffic ingressing and/or egressing a set of ports to another port. On this monitor port you can then run your tcpdump, which means you need an external device (laptop).
A planned feature is to support mirroring traffic to the CPU port, which would be an effective way to log and monitor traffic over a longer period of time. Highly effective for diagnosing intermittent and other rare network issues.
If only \"proof of life\" is required, then sometimes port counters, also called RMON counters, can be very useful too. Seeing counters of a particular type increment means traffic is ingressing or egressing.
"},{"location":"cli/tcpdump/#examples","title":"Examples","text":"Listen to all traffic on an interface:
admin@example:/> tcpdump e1\n Listen to only ping traffic:
admin@example:/> tcpdump e1 expression icmp\n Listen to traffic on a given port:
admin@example:/> tcpdump e1 expression \"port 80\"\n Wait for one ping only:
admin@example:/> tcpdump e1 count 1 expression icmp\n Very verbose output:
admin@example:/> tcpdump e1 verbose\n "},{"location":"cli/text-editor/","title":"Text Editor","text":""},{"location":"cli/text-editor/#text-editor","title":"Text Editor","text":"The CLI built-in text-editor command can be used to edit type binary settings in configure context.
The default editor is a Micro Emacs clone. Users not familiar with terminal based editors may benefit from this introduction.
"},{"location":"cli/text-editor/#escape-metaalt-control-shift","title":"Escape Meta/Alt Control Shift","text":"When starting up, the editor status field at the bottom shows the following shorthand:
C-h q quick help | C-h t tutorial | C-h b key bindings | C = Ctrl | M = Alt\n Key combinations with a - (dash) mean holding down the modifier key. Combinations without a - (dash) mean without any modifier key.
"},{"location":"cli/text-editor/#quick-help-c-h-q","title":"Quick help C-h q","text":" hold down the Ctrl key on tap the h key release Ctrl tap the q key The bottom part of the terminal now shows a \"buffer\" called *quick*:
FILE BUFFER WINDOW MARK/KILL MISC\nC-x C-c exit C-x b switch C-x 0 only other C-space mark C-_ undo\nC-x C-f find C-x k close C-x 1 only this C-w kill-rg C-s search\nC-x C-s save C-x C-b list C-x 2 split two C-k kill-ln C-r r-search\nC-x s save-all C-x h mark C-x ^ enlarge C-y yank M-% replace\nC-x i insert C-x g goto-ln C-x o other win C-x C-x swap M-q reformat\n "},{"location":"cli/text-editor/#save-exit-c-x-c-c","title":"Save & Exit C-x C-c","text":" Hold down the Ctrl key tap X tap c release Ctrl The status field at the bottom asks if you are really sure, and/or if you want to add a final Enter/newline to the file. For binary content that final newline may be important.
"},{"location":"cli/text-editor/#changing-the-editor","title":"Changing the Editor","text":"The system has three different built-in editors:
emacs (Micro Emacs) nano (GNU Nano) vi (Visual Editor) Changing editor is done in configure context, in the system container:
admin@host:/> configure\nadmin@host:/config/> edit system\nadmin@host:/config/system/> set text-editor <TAB>\nemacs nano vi\nadmin@host:/config/system/> set text-editor nano\nadmin@host:/config/system/> leave\nadmin@host:/>\n "},{"location":"cli/upgrade/","title":"Upgrading","text":""},{"location":"cli/upgrade/#upgrading-the-system","title":"Upgrading the System","text":"The admin-exec command upgrade can be used to install software images, or bundles. A bundle is a signed and self-contained package that carries all the information necessary to determine if it holds a bootloader, a Linux image, or even both.
To install a new software image to the currently inactive partition1 , we use the upgrade command and a URI to a ftp/tftp/sftp or http/https server that hosts the file:
admin@host:/> upgrade tftp://192.168.122.1/firmware-x86_64-v23.11.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 20% Determining slot states done.\n 20% Checking bundle\n 20% Verifying signature\n 40% Verifying signature done.\n 40% Checking bundle done.\n 40% Checking manifest contents\n 60% Checking manifest contents done.\n 60% Determining target install group\n 80% Determining target install group done.\n 80% Updating slots\n 80% Checking slot rootfs.1\n 90% Checking slot rootfs.1 done.\n 90% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://192.168.122.1/firmware-x86_64-v23.11.pkg` succeeded\nadmin@host:/>\n The secondary partition (rootfs.1) has now been upgraded and will be used as the active partition on the next boot. Leaving the primary partition, with the version we are currently running, intact in case of trouble.
See Upgrade & Boot Order for more information on upgrading.
It is not possible to upgrade the partition we booted from. Thankfully the underlying \"rauc\" subsystem keeps track of this. Hence, to upgrade both partitions you must reboot to the new version (to verify it works) and then repeat the same command.\u00a0\u21a9
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Introduction","text":""},{"location":"#introduction","title":"Introduction","text":"Welcome to Infix, your immutable, friendly, and secure operating system! On these pages you can find both user and developer documentation.
Most topics on configuring the system include CLI examples, but every setting, as well as status read-back from the operational datastore, is also possible to perform using NETCONF or RESTCONF. In fact, the Infix regression test system solely relies on NETCONF and RESTCONF.
Tip
The CLI documentation is also available from inside the CLI itself using the help command in admin-exec mode.
This document provides an introduction of key concepts, details how the system boots, including failure modes, and provides links to other documents for further study.
"},{"location":"#command-line-interface","title":"Command Line Interface","text":"The command line interface (CLI, see-ell-i) is the traditional way of interacting with single network equipment like switches and routers. Today users have come to expect more advanced graphical GUIs, like a web interface, to manage a device or NETCONF-based tools that allow for managing entire fleets of installed equipment.
Nevertheless, when it comes to initial deployment and debugging, it is very useful to know how to navigate and use the CLI.
Info
For more information, see the CLI Introduction and the CLI Configuration Tutorial.
"},{"location":"#key-concepts","title":"Key Concepts","text":"The two modes in the CLI are the admin-exec and the configure context.
However, when logging in to the system, from the console port or SSH, you land in a standard UNIX shell, Bash. This is for advanced users and remote scripting purposes (production equipment):
Run the command 'cli' for interactive OAM\n\n admin@example:~$\n To enter the CLI, follow the instructions, for interactive Operations, Administration, and Management (OAM), type:
admin@example:~$ cli\n admin@example:/>\n The prompt, constructed from your username and the device's hostname, changes slightly. You are now in the admin-exec context of the CLI. Here you can inspect system status and do operations to debug networking issues, e.g. ping. You can also enter configure context by typing: configure followed by commands to set, edit, apply changes using leave, or abort and return to admin-exec.
Tip
If you haven't already, the CLI Introduction would be useful to skim through at this point.
"},{"location":"#datastores","title":"Datastores","text":"The system has several datastores (or files):
factory-config consists of a set of default configurations, some static and others generated per-device, e.g., a unique hostname and number of ports/interfaces. This file is generated at boot. failure-config is also generated at boot, from the same YANG models as factory-config, and holds the system Fail Secure Mode startup-config is created from factory-config at boot if it does not exist. It is loaded as the system configuration on each boot. running-config is what is actively running on the system. If no changes have been made since the system booted, it is the same as startup-config. candidate-config is created from running-config when entering the configure context. Any changes made here can be discarded (abort, rollback) or committed (commit, leave) to running-config. Tip
Please see the Branding & Releases document for more in-depth information on how factory-config and failure-config can be adapted to different customer requirements. Including how you can override the generated versions of these files with plain per-product ones -- this may even protect against some of the failure modes below.
"},{"location":"boot/","title":"Boot Procedure","text":""},{"location":"boot/#boot-procedure","title":"Boot Procedure","text":"Systems running Infix will typically boot in multiple phases, forming a boot chain. Each link in the chain has three main responsibilities:
Ensuring the integrity of the next link before passing control to it. This avoids silent failures stemming from data corruption. Ensuring the authenticity of the next link before passing control to it, commonly referred to as Secure Boot. This protects against malicious attempts to modify a system's firmware. Preparing the system state according to the requirements of the next link. E.g. the Linux kernel requires the system's RAM to be operational. A typical chain consists of four stages:
.---------.\n | ROM >---. Determine the location of and load the SPL\n '---------' |\n.-----------------'\n| .---------.\n'---> SPL >---. Perform DDR training and load the TPL\n '---------' |\n.-----------------'\n| .---------.\n'---> TPL >---. Load Linux kernel, device tree, and root filesystem\n '---------' |\n.-----------------'\n| .---------.\n'---> Infix | Get down to business\n '---------'\n After a reset, hardware will pass control to a program (ROM) which is almost always programmed into the SoC by the vendor. This program will determine the location of the Secondary Program Loader (SPL), typically by reading a set of Sample at Reset (SaR) pins.
The SPL is sometimes provided by the SoC vendor in binary form, and is sometimes built as a part of the Tertiary Program Loader (TPL) build. Its main responsibility is usually to set up the system's memory controller and perform DDR training, if required, before loading the TPL.
Commonly referred to as the system's bootloader, the TPL is responsible for preparing the execution environment required by the Linux kernel.
This document's focus is to describe the final two phases of the boot chain, as the initial phases are very hardware dependent, better described by existing documentation provided by the SoC vendor.
"},{"location":"boot/#bootloader","title":"Bootloader","text":""},{"location":"boot/#configuration","title":"Configuration","text":"To mitigate the risk of a malicious user being able to circumvent the bootloader's validation procedure, user configuration is kept to a minimum. Two settings are available:
Boot order: Since Infix maintains two copies of its software image, and as some bootloaders support netbooting, the order in which boot sources are considered can be configured. To select the active source, use RAUC:
root@example:~# rauc status mark-active <slot>\n...\n Where <slot> is one of:
<slot> Source rootfs.0 Primary partition rootfs.1 Secondary partition net.0 Netboot (where supported) Debug: By default, the kernel will only output errors to the console during boot. Optionally, this can be altered such that all enabled messages are logged.
On systems using U-Boot, this can be enabled by running fw_setenv DEBUG 1. To restore the default behavior, run fw_setenv DEBUG.
On systems running GRUB, this can be enabled by running:
root@example:~# grub-editenv /mnt/aux/grub/grubenv set DEBUG=1\n To restore the default behavior, run:
root@example:~# grub-editenv /mnt/aux/grub/grubenv unset DEBUG\n "},{"location":"boot/#u-boot","title":"U-Boot","text":"Used on aarch64 based systems. It is able to verify both the integrity and authenticity of an Infix image. As such, it can be used as a part of a Secure Boot chain, given that the preceding links are able to do the same.
Supports booting Infix from a block device using the Disk Image layout. Currently, Virtio and MMC disks are supported.
An FIT Framed Squash Image can be used to boot Infix over the network. DHCP is used to configure the network and TFTP to transfer the image to the system's RAM.
Access to U-Boot's shell is disabled to prevent side-loading of malicious software. To configure the active boot partition, refer to the Bootloader Configuration section.
"},{"location":"boot/#grub","title":"GRUB","text":"Used on x86_64 based systems. Neither the integrity nor the authenticity of the Infix image is verified. It is only intended to provide a way of booting a Disk Image, such that a standard System Upgrade can be performed on virtualized instances.
Access to the GRUB shell is not limited in any way, and the boot partition can be selected interactively at boot using the arrow keys. It is also possible to permanently configure the default partition from Infix using the Bootloader Configuration.
"},{"location":"boot/#system-boot","title":"System Boot","text":"After the system firmware (BIOS or and boot loader start Linux the following happens. The various failure modes, e.g., missing password in VPD, are detailed later in this section.
Before mounting /cfg and /var partitions, hosting read-writable data like startup-config and container images, the system first checks if a factory reset has been requested by the user, if so it wipes the contents of these partitions Linux boots with a device tree which is used for detecting generic make and model of the device, e.g., number of interfaces. It may also reference an EEPROM with Vital Product Data. That is where the base MAC address and per-device password hash is stored. (Generic builds use the same MAC address and password) On every boot the system's factory-config and failure-config are generated from the YANG2 models of the current firmware version. This ensures that a factory reset device can always boot, and that there is a working fail safe, or rather fail secure, mode On first power-on, and after a factory reset, the system does not have a startup-config, in which case factory-config is copied to startup-config -- if a per-product specific version exists it is preferred over the generated one Provided the integrity of the startup-config is OK, a system service loads and activates the configuration "},{"location":"boot/#failure-modes","title":"Failure Modes","text":"So, what happens if any of the steps above fail?
"},{"location":"boot/#vpd-fail","title":"VPD Fail","text":"The per-device password cannot be read, or is corrupt, so the system factory-config and failure-config are not generated:
First boot, or after factory reset: startup-config cannot be created or loaded, and failure-config cannot be loaded. The system ends up in an unrecoverable state, i.e., RMA3 Mode The system has booted (at least) once with correct VPD and password and already has a startup-config. Provided the startup-config is OK (see below), it is loaded and system boots successfully In both cases, external factory reset modes/button will not help, and in the second case will cause the device to fail on the next boot.
Note
The second case does not yet have any warning or event that can be detected from the outside. This is planned for a later release.
"},{"location":"boot/#broken-startup-config","title":"Broken startup-config","text":"If loading startup-config fails for some reason, e.g., invalid JSON syntax, failed validation against the system's YANG model, or a bug in the system's confd service, the Fail Secure Mode is triggered and failure-config is loaded (unless VPD Failure, see above).
Tip
Please see the Branding & Releases document for how to provide per-product failure-config, or factory-config to suit your product's preferences.
Fail Secure Mode is a fail-safe mode provided for debugging the system. The default4 creates a setup of isolated interfaces with communication only to the management CPU, SSH and console login using the device's factory reset password, IP connectivity only using IPv6 link-local, and device discovery protocols: LLDP, mDNS-SD. The login and shell prompt are set to failure-c0-ff-ee, the last three octets of the device's base MAC address.
"},{"location":"boot/#system-upgrade","title":"System Upgrade","text":"Much of the minutiae of software upgrades is delegated to RAUC, which offers lots of benefits out-of-the-box:
Upgrade Bundles are always signed, such that their authenticity can be verified by the running operating system, before the new one is installed. The bureaucracy of interfacing with different bootloaders, manage the boot order, is a simple matter of providing a compatible configuration. Updates can be sourced from the local filesystem (including external media like USB sticks or SD-cards) and from remote servers using FTP or HTTP(S). To initiate a system upgrade from the shell1 , run:
root@example:~# rauc install <file|url>\n...\n Where the file or URL points to a RAUC Upgrade Bundle.
This will upgrade the partition not currently running. After a successful upgrade is completed, you can reboot your system, which will then boot from the newly installed image. Since the partition from which you were originally running is now inactive, running the same upgrade command again will bring both partitions into sync.
"},{"location":"boot/#image-formats","title":"Image Formats","text":""},{"location":"boot/#squashfs-image","title":"SquashFS Image","text":"Canonical Name: rootfs.squashfs
The central read-only filesystem image containing Infix's Linux kernel, device trees, and root filesystem. All other images bundle this image, or is dependent on it, in one way or another.
On its own, it can be used as an initrd to efficiently boot a virtual instance of Infix.
"},{"location":"boot/#fit-framed-squash-image","title":"FIT Framed Squash Image","text":"Canonical Name: rootfs.itb
As the name suggests, this is essentially the Squash FS Image with a Flattened Image Tree (FIT) header. Being a native format to U-Boot, using this framing allows us to verify the integrity and authenticity of the SquashFS image using standard U-Boot primitives.
In contrast to most FIT images, the kernel and device trees are not stored as separate binaries in the image tree. Instead, Infix follows the standard Linux layout where the kernel and related files are stored in the /boot directory of the filesystem.
On disk, this image is then stored broken up into its two components; the FIT header (rootfs.itbh) and the SquashFS image. The header is stored on the Auxiliary Data partition of the Disk Image, while the SquashFS image is stored in one of the Root Filesystem partitions.
When the system boots, U-Boot will concatenate the two parts to validate the SquashFS's contents. This path was chosen because:
Having a separate raw SquashFS means Linux can directly mount it as the root filesystem.
It decouples Infix from U-Boot. If a better way of validating our image is introduced, we can switch to it without major changes to Infix's boot process, as we can still use a regular SquashFS as the root filesystem.
It lets us use standard interfaces to boot Linux, like SYSLINUX. It also plays well with traditional bootloaders, like GRUB.
In its full form, it can be used to netboot Infix, as it contains all the information needed by U-Boot in a single file.
"},{"location":"boot/#rauc-upgrade-bundle","title":"RAUC Upgrade Bundle","text":"Canonical Name: infix-${ARCH}.pkg
Itself a SquashFS image, this bundle (sometimes referred to package) contains the Infix SquashFS Image along with the header of the FIT Framed Squash Image, and some supporting files to let RAUC know how install it on the target system.
When performing a System Upgrade, this is the format to use.
"},{"location":"boot/#disk-image","title":"Disk Image","text":"Canonical Name: disk.img
Infix runs from a block device (e.g. eMMC or virtio disk) with the following layout. The disk is expected to use the GPT partitioning scheme. Partitions marked with an asterisk are optional.
.-----------.\n| GPT Table |\n:-----------:\n| boot* |\n:-----------:\n| aux |\n:-----------:\n| |\n| primary |\n| |\n:-----------:\n| |\n| secondary |\n| |\n:-----------:\n| cfg |\n:-----------:\n| |\n| var* |\n| |\n'-----------'\n "},{"location":"boot/#boot-bootloader","title":"boot - Bootloader","text":"Parameter Value Required No Size 4 MiB Format Raw binary, as dictated by the hardware Optional partition containing the system's bootloader. May also reside in a separate storage device, e.g. a serial FLASH.
On x86_64, this partition holds the EFI system partition, containing the GRUB bootloader.
"},{"location":"boot/#aux-auxiliary-data","title":"aux - Auxiliary Data","text":"Parameter Value Required Yes Size 4 MiB Format EXT4 filesystem Holds information that is shared between Infix and its bootloader, such as image signatures required to validate the chain of trust, bootloader configuration etc.
Typical layout when using U-Boot bootloader:
/\n\u251c primary.itbh\n\u251c secondary.itbh\n\u2514 uboot.env\n During boot, an ITB header along with the corresponding root filesystem image are concatenated in memory, by U-Boot, to form a valid FIT image that is used to verify its integrity and origin before any files are extracted from it.
Note that the bootloader's primary environment is bundled in the binary - uboot.env is only used to import a few settings that is required to configure the boot order.
"},{"location":"boot/#primarysecondary-root-filesystems","title":"primary/secondary - Root Filesystems","text":"Parameter Value Required Yes Size >= 256 MiB Format Squash filesystem Holds the SquashFS Image. Two copies exist so that an incomplete upgrade does not brick the system, and to allow fast rollbacks when upgrading to a new version.
"},{"location":"boot/#cfg-configuration-data","title":"cfg - Configuration Data","text":"Parameter Value Required Yes Size >= 16 MiB Format EXT4 filesystem Non-volatile storage of the system configuration and user data. Concretely, user data is everything stored under /root and /home.
"},{"location":"boot/#var-variable-data","title":"var - Variable Data","text":"Parameter Value Required No Size >= 16 MiB Format EXT4 filesystem Persistent storage for everything under /var. This is maintained as a separate filesystem from the data in cfg, because while the system can funtion reasonably well without a persistent /var, loosing /cfg or /etc is much more difficult.
If var is not available, Infix will still persist /var/lib using cfg as the backing storage.
See Upgrade & Boot Order for more information.\u00a0\u21a9
YANG is a modeling language from IETF, replacing that used for SNMP (MIB), used to describe the subsystems and properties of the system.\u00a0\u21a9
Return Merchandise Authorization (RMA), i.e., broken beyond repair by end-user and eligible for return to manufacturer.\u00a0\u21a9
Customer specific builds can define their own failure-config. It may be the same as factory-config, with the hostname set to failure, or a dedicated configuration that isolates interfaces, or even disables ports, to ensure that the device does not cause any security problems on the network. E.g., start forwarding traffic between previously isolated VLANs.\u00a0\u21a9
"},{"location":"branding/","title":"Branding & Releases","text":""},{"location":"branding/#branding-releases","title":"Branding & Releases","text":"This document is for projects using Infix as a br2-external, i.e., OEMs that want to create their own \"Spin\" of Infix.
"},{"location":"branding/#branding","title":"Branding","text":"Branding is done in menuconfig, there are several settings affecting it, most are in the Infix external subsection called \"Branding\", but there is also BR2_TARGET_GENERIC_HOSTNAME, which deserves a special mention.
The hostname is used for the system default /etc/hostname, which is the base name for the \"unique:ified\" hostname + the last three octets of the base MAC1 address, e.g., infix-c0-ff-ee. This in turn is the hostname that is set at first boot and also advertised by device discovery protocols like mDNS-SD and LLDP.
See the help texts for the Infix Branding settings to understand which ones are mandatory and which are optional, menuconfig does not check this for you and you may end up with odd results.
Verify the result after a build by inspecting:
output/images/*: names, missing prefix, etc. output/target/etc/os-release: this file is sourced by other build scripts, e.g., mkgns3a.sh. For reference, see os-release(5) Important
To get a proper GIT revision (hash) from your OS spin, remember to set in menuconfig INFIX_OEM_PATH. When unset, the Infix post-build.sh script defaults to the Infix base path. The revision is stored in the file /etc/os-release as BUILD_ID, also in the file /etc/version. See below for more info.
"},{"location":"branding/#factory-failure-config","title":"Factory & Failure Config","text":"To support booting the same image (CPU architecture) on multiple boards, Infix by default generates the device's initial configuration every time at boot. This also ensures the device can always be restored to a known state after a factory reset, since the factory-config is guaranteed to be compatible with the YANG models for the given software version. (For more information on how the system boots, please see the section Key Concepts in the Introduction document.)
However, for custom builds of Infix it is possible to override this with a single static /etc/factory-config.cfg (and failure-config) in your rootfs overlay -- with a VPD you can even support several!
"},{"location":"branding/#variables-format-specifiers","title":"Variables & Format Specifiers","text":"Parts of the configuration you likely always want to generated, like the SSH hostkey used by SSH server and NETCONF, a unique hostname, or the admin user's unique (per-device with a VPD) password hash. This section lists the available keywords, see the next section for examples of how to use them:
Default password hash: $factory$ (from VPD, .dtb, or built-in) XPath: /ietf-system:system/authentication/user/password Default SSH and NETCONF hostkey: genkey (regenerated at factory reset) XPath: /ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey'] Hostname format specifiers: XPath: /ietf-system:system/hostname %i: OS ID, from /etc/os-release, from Menuconfig branding %h: Default hostname, from /etc/os-release, from branding %m: NIC specific part of base MAC, e.g., to c0-ff-ee %%: Literal % "},{"location":"branding/#static-files","title":"Static Files","text":"Caution
Maintaining a static factory-config and failure-config may seem like an obvious choice, but as YANG models evolve (even the IETF models get upgraded), you may need to upgrade your static files.
First, for one-off builds (one image per product), the simplest way is to override the location where the system looks for the files, /etc already at build time. This can be done using a Buildroot rootfs overlay providing, e.g., /etc/factory-config.cfg. Example: NanoPi R2S in ${INFIX}/board/aarch64/r2s/rootfs/etc/factory-config.cfg.
Second, to support multiple products in a single image, we can employ another method to install a /etc/factory-config.cfg override -- at runtime. This relies on the very early system probe that detects the specific product from VPD data.
The probe consists of several sequential steps that currently run from ${INFIX}/board/common/rootfs/usr/libexec/infix/init.d/. One of them check if /usr/share/product/<PRODUCT> exists, and if so attempts to copy the entire contents to /. Here, <PRODUCT> is determined from the VPD, which is available in /run/system.json as \"product-name\", after 00-probe has run. The lower case version of the string is used.
I.e., create a rootfs overlay that provides any combination of:
/usr/share/product/<PRODUCT>/etc/factory-config.cfg /usr/share/product/<PRODUCT>/etc/failure-config.cfg "},{"location":"branding/#dynamically-generated","title":"Dynamically Generated","text":"The generated factory-config and failure-config files consist of both static JSON files and part generated files at runtime for each device. The resulting files are written to the RAM disk in /run:
/run/confd/factory-config.gen /run/confd/failure-config.gen Provided no custom overrides (see above) have been installed already, these files are then copied to:
/etc/factory-config.cfg /etc/failure-config.cfg ... where the bootstrap process expects them to be in the next step.
Examples of generated contents are the SSH hostkey and hostname. The latter is constructed from the file /etc/hostname, appended with the last three octets of the system's base MAC address. To override the base hostname, set BR2_TARGET_GENERIC_HOSTNAME in your defconfig.
The static files are installed by Infix confd in /usr/share/confd/ at build time. It contains two subdirectories:
/usr/share/confd/\n |- factory.d/\n | |- 10-foo.json\n | |- 10-bar.json\n | `- 10-qux.json\n `- failure.d/\n |- 10-xyzzy.json\n `- 10-garply.json\n To override, or extend, these files in you br2-external, set up a rootfs overlay and add it last in BR2_ROOTFS_OVERLAY. Your overlay can look something like this:
./board/common/rootfs/\n |- etc/\n | |- confdrc # See below\n | `- confdrc.local\n `- usr/\n `- share/\n `- confd/\n |- 10-foo.json # Override Infix foo\n |- 30-bar.json # Extend, probably 10-bar.json\n `- 30-fred.json # Extend, your own defaults\n Using the same filename in your overlay, here 10-foo.json, completely replaces the contents of the same file provided by Infix. If you just want to extend, or replace parts of an Infix default, use 30-....json. Here the file 30-bar.json is just a helpful hit to maintainers of your br2-external that it probably extends Infix' 10-bar.json.
The reason for the jump in numbers is that 20 is reserved for files generated by Infix' gen-function scripts. Your br2-external can provide a few custom ones that the bootstrap knows about, e.g., gen-ifs-custom that overrides 20-interfaces.json. See the bootstrap script for more help, and up-to-date information.
Tip
You may not need to provide your own /etc/confdrc for your spin. The one installed by confd is usually enough. However, if you want to adjust the behavior of bootstrap you may want to override it. There is also confdrc.local, which usually is enough to change arguments to scripts like gen-interfaces, e.g., to create a bridge by default, you may want to look into GEN_IFACE_OPTS.
"},{"location":"branding/#example-snippets","title":"Example Snippets","text":""},{"location":"branding/#ietf-system","title":"IETF System","text":" \"ietf-system:system\": {\n \"hostname\": \"example-%m\",\n \"ntp\": {\n \"enabled\": true,\n \"server\": [\n {\n \"name\": \"ntp.org\",\n \"udp\": {\n \"address\": \"pool.ntp.org\"\n }\n }\n ]\n },\n \"authentication\": {\n \"user\": [\n {\n \"name\": \"admin\",\n \"password\": \"$factory$\",\n \"infix-system:shell\": \"bash\"\n }\n ]\n },\n \"infix-system:motd-banner\": \"Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=\"\n }, # <---- REMEMBER COMMA SEPARATORS IN SNIPPETS!\n # <---- ... and no comments.\n The motd-banner is a binary type, which is basically a Base64 encoded text file without line breaks (-w0):
$ echo \"Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCAtLSBhIE5ldHdvcmsgT3BlcmF0aW5nIFN5c3RlbQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQuZ2l0aHViLmlvCictJy0tLSctJwo=\" \\\n | base64 -d\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.github.io\n'-'---'-'\n "},{"location":"branding/#ietf-keystore","title":"IETF Keystore","text":"Notice how both the public and private keys are left empty here, this cause them to be always automatically regenerated after each factory reset. Keeping the factory-config snippet like this means we can use the same file on multiple devices, without risking them sharing the same host keys. Sometimes you may want the same host keys, but that is the easy use-case and not documented here.
\"ietf-keystore:keystore\": {\n \"asymmetric-keys\": {\n \"asymmetric-key\": [\n {\n \"name\": \"genkey\",\n \"public-key-format\": \"ietf-crypto-types:ssh-public-key-format\",\n \"public-key\": \"\",\n \"private-key-format\": \"ietf-crypto-types:rsa-private-key-format\",\n \"cleartext-private-key\": \"\",\n \"certificates\": {}\n }\n ]\n }\n },\n "},{"location":"branding/#ietf-netconf-server","title":"IETF NETCONF Server","text":" \"ietf-netconf-server:netconf-server\": {\n \"listen\": {\n \"endpoints\": {\n \"endpoint\": [\n {\n \"name\": \"default-ssh\",\n \"ssh\": {\n \"tcp-server-parameters\": {\n \"local-address\": \"::\"\n },\n \"ssh-server-parameters\": {\n \"server-identity\": {\n \"host-key\": [\n {\n \"name\": \"default-key\",\n \"public-key\": {\n \"central-keystore-reference\": \"genkey\"\n }\n }\n ]\n }\n }\n }\n }\n ]\n }\n }\n },\n "},{"location":"branding/#infix-services","title":"Infix Services","text":" \"infix-services:ssh\": {\n \"enabled\": true,\n \"hostkey\": [\n \"genkey\"\n ],\n \"listen\": [\n {\n \"name\": \"ipv4\",\n \"address\": \"0.0.0.0\",\n \"port\": 22\n },\n {\n \"name\": \"ipv6\",\n \"address\": \"::1\",\n \"port\": 22\n }\n ]\n }\n "},{"location":"branding/#integration","title":"Integration","text":"When integrating your software stack with Infix there may be protocols that want to change system settings like hostname and dynamically set IP address and default gateway, e.g. PROFINET. This section detail a few recommendations for maintaining co-existence in this scenario of the multiple producers problem.
First, there's a clear difference between \"singleton\" like hostsname and an interface IP address. Consider the case of a static IP and a DHCP assigned IP, these can co-exist because of the proto NUM field available in iproute2. This is used in Infix so that static addresses can be flushed independently of DHCP addresses. The same can be done by other \"address providers\", e.g., PROFINET.
Changing properties like hostname should be done by injecting a change into Infix, by for example calling sysrepocfg -Ediff.xml. Here is an example of how to get the current hostname and apply an XML diff:
root@infix-00-00-00:~# sysrepocfg -X -x \"/system/hostname\" > hostnm.xml\nroot@infix-00-00-00:~# cat hostnm.xml\n<system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>infix-00-00-00</hostname>\n</system>\nroot@infix-00-00-00:~# edit hostnm.xml\nroot@infix-00-00-00:~# sysrepocfg -Ehostnm.xml\nroot@example:~#\n Second, perform all changes on running-config, the running datastore. That way you have a clear state to return to if your application needs to do a factory reset. E.g., in PROFINET a type 1/2 factory reset will reset only the PROFINET specific settings. That way you can actually have your system startup-config disable all physical ports and the PROFINET application enables only ports that are not deactivated. (On factory reset it will not know of any ports to deactivate so it will activate all.)
You can consider the system composed of two entities:
NETCONF starts up the system using startup-config, then Hands over control to your application at runtime Infix is prepared for this by already having two \"runlevels\" for these two states. The startup-config is applied in runlevel S (bootstrap) and the system then enters runlevel 2 for normal operation.
This allow you to keep a set of functionality that is provided by the underlying system, and another managed by your application. You can of course in your br2-external provide a sysrepo plugin that block operations on certain datastores when your application is enabled. E.g., to prevent changes to startup after initial deployment. In that case a proper factory reset would be needed to get back to a \"pre-deployment\" state where you can reconfigure your baseline.
"},{"location":"branding/#releases","title":"Releases","text":"A release build requires the global variable INFIX_RELEASE to be set. It can be derived from GIT, if the source tree is kept in GIT VCS. First, let us talk about versioning in general.
"},{"location":"branding/#versioning","title":"Versioning","text":"Two popular scheme for versioning a product derived from Infix:
Track Infix major.minor, e.g. Foobar v23.08.z, where z is your patch level. I.e., Foobar v23.08.0 could be based on Infix v23.08.0, or v23.08.12, it is up to you. Maybe you based it on v23.08.12 and then back ported changes from v23.10.0, but it was the first release you made to your customer(s). Start from v1.0.0 and step the major number every time you sync with a new Infix release, or every time Infix bumps to the next Buildroot LTS. The important thing is to be consistent, not only for your own sake, but also for your end customers. The major.minor.patch style is the most common and often recommended style, which usually maps well to other systems, e.g. PROFINET GSDML files require this (VX.Y.Z). But you can of course use only two numbers, major.minor, as well.
Warning
What could be confusing, however, is if you use the name Infix with your own versioning scheme.
"},{"location":"branding/#specifying-versioning-information","title":"Specifying Versioning Information","text":"Two optional environment variables control the version information recorded in images. Both of these must be a lower-case string (no spaces or other characters outside of 0\u20139, a\u2013z, '.', '_' and '-') identifying the operating system version, excluding any OS name information or release code name, and suitable for processing by scripts or usage in generated filenames.
"},{"location":"branding/#infix_build_id","title":"INFIX_BUILD_ID","text":"Used for BUILD_ID in /etc/os-release.
Default: $(git describe --always --dirty --tags), from the top directory. By default, the top directory refers to the root of the Infix source tree, but this can be changed by setting the branding variable INFIX_OEM_PATH, e.g. in a defconfig file or via make menuconfig, to the path of an enclosing br2-external.
"},{"location":"branding/#infix_release","title":"INFIX_RELEASE","text":"Used for VERSION and VERSION_ID in /etc/os-release and generated file names like disk images, etc.
Default: ${INFIX_BUILD_ID}
The base MAC address is defined in the device's Vital Product Data (VPD) EEPROM, or similar, which is used by the kernel to create the system interfaces. This MAC address is usually also printed on a label on the device.\u00a0\u21a9
"},{"location":"container/","title":"Docker Containers","text":""},{"location":"container/#docker-container-support","title":"Docker Container Support","text":"Infix comes with native support for Docker containers using podman. The YANG model describes the current level of support, complete enough to run both system and application containers.
Key design features of Infix, like using Linux switchdev, allow users to assign switch ports directly to containers, not just bridged VETH pairs. This is a rare and in many cases unique feature of Infix.
All network specific settings are done using the IETF interfaces YANG model, with augments for containers to ensure smooth integration with container networking in podman.
Important
Even though the podman command can be used directly from a shell prompt, we strongly recommend using the CLI commands instead. They employ the services of a wrapper container script which handles the integration of Docker containers in the system.
"},{"location":"container/#caution","title":"Caution","text":"A word of warning. Containers can run on your system in privileged mode, as root, giving them full access to devices on your system. Even though containers are fenced from the host with Linux namespaces, resource limited using cgroups, and normally run with capped privileges, a privileged container is relatively easy to break out of. A trivial example is given in the Advanced section of this document.
We recommend avoiding privileged containers, if possible (they do have valid use-cases) and instead use capabilities.
Remember:
If the system is compromised, containers can be used to easily install malicious software in your system and over the network Your system is as secure as anything you run in the container If you run containers, there is no security guarantee of any kind Running 3rd party container images on your system could open a security hole/attack vector/surface An expert with knowledge how to build exploits will be able to jailbreak/elevate to root even if best practices are followed This being said, a system suspected of being compromised can always be restored to a safe state with a factory reset. Provided, of course, that it has secure boot enabled.
"},{"location":"container/#getting-started","title":"Getting Started","text":"In the CLI, containers can be run in one of two ways:
container run IMAGE [COMMAND], or enter configure context, then edit container NAME The first is useful mostly for testing, or running single commands in an image. It is a wrapper for podman run -it --rm ....
The second creates a read-only container that by default automatically start at every boot. It basically wraps podman create ....
When non-volatile storage is needed two complementary options exist:
Volumes: data stored in a volume is persisted until explicitly removed from the configuration, i.e., across host reboots and container upgrades Content Mounts: where the content of a file mounted into the container is kept along with the container configuration in the device's startup-config Podman ensures (using tmpfs) all containers have writable directories for certain critical file system paths: /dev, /dev/shm, /run, /tmp, and /var/tmp. Meaning, what you most often need is writable volumes for /var/lib and /etc, or only file mounts for a few files in /etc. The actual requirements depend on your container image and application to run.
Important
When running containers from public registries, double-check that they support the CPU architecture of your host system. Remember, unlike virtualization, containers reuse the host's CPU and kernel.
"},{"location":"container/#example-hello-world","title":"Example: Hello World","text":"Classic Hello World:
admin@example:/> container run docker://hello-world\nStarting docker://hello-world :: use Ctrl-p Ctrl-q to detach\nTrying to pull docker.io/library/hello-world:latest...\nGetting image source signatures\nCopying blob c1ec31eb5944 done\nCopying config d2c94e258d done\nWriting manifest to image destination\nStoring signatures\n\nHello from Docker!\nThis message shows that your installation appears to be working correctly.\n "},{"location":"container/#example-web-server","title":"Example: Web Server","text":"A web server with nginx, using standard docker bridge. Podman will automatically create a VETH pair for us, connecting the container to the docker0 bridge:
admin@example:/> configure\nadmin@example:/config/> edit interface docker0\nadmin@example:/config/interface/docker0/> set container-network\nadmin@example:/config/interface/docker0/> end\nadmin@example:/config/> edit container web\nadmin@example:/config/container/web/> set image docker://nginx:alpine\nadmin@example:/config/container/web/> set network publish 8080:80\nadmin@example:/config/container/web/> set network interface docker0\nadmin@example:/config/container/web/> set volume cache target /var/cache\nadmin@example:/config/container/web/> leave\nadmin@example:/> show container\n Exit to the shell and verify the service with curl, or try to attach to your device's IP address using your browser:
admin@example:~$ curl http://localhost:8080\n or connect to port 8080 of your running Infix system with a browser. See the following sections for how to add more interfaces and manage your container at runtime.
"},{"location":"container/#container-images","title":"Container Images","text":"The underlying podman project supports importing and fetching images in a variety of ways, the most common ones are also supported by Infix. In this section we present how to use them and in the next section we show how to upgrade to a newer base image.
The CLI help shows:
admin@example:/config/container/system/> help image\nNAME\n image <string>\n\nDESCRIPTION\n Docker image for the container: [transport]name[:tag|@digest]\n\n quay.io/username/myimage -- Pull myimage:latest\n docker://busybox -- Pull busybox:latest from Docker Hub\n docker://ghcr.io/usr/img -- Pull img:latest from GitHub packages\n dir:/media/usb/myimage:1.1 -- Use myimage v1.1 from USB media\n docker-archive:/tmp/archive -- Use archive:latest from tarball\n oci-archive:/lib/oci/archive -- Use archive:latest from OCI archive\n May be in .tar or .tar.gz format\n\n Additionally, the following URIs are also supported for setups\n that do not use a HUB or similar. Recommend using 'checksum'!\n\n ftp://addr/path/to/archive -- Downloaded using wget\n http://addr/path/to/archive -- Downloaded using curl\n https://addr/path/to/archive -- Downloaded using curl\n\n Note: if a remote repository cannot be reached, the creation of the\n container will be put on a queue that retries pull every time\n there is a route change in the host's system.\n Tip
The built-in help system in the CLI is generated from the YANG model, so the same information is also available for remote NETCONF users.
The two most common variants are docker:// and oci-archive:/.
The former requires a working Docker registry and the latter operates on a plain OCI archive. Infix does not come with a built-in registry, so the docker:// option is best used with external services, which in turn require networking to be up. In a deployment phase the easiest may be to set up a single interface on your host system with DHCP client.
The default method is docker://, so when setting the image for your container, you can omit the docker:// prefix. You can also use the admin-exec command container pull docker://..., and when configuring a container podman will check first if it has the image before trying to download anything. (See also the upgrade section, below.)
The oci-archive:/ is interesting since many users may not have, or do not want to, publish their images in a registry. Use the Docker OCI exporter or any other tool that supports generating OCI Image format. Infix supports loading both .tar or .tar.gz formats.
Here we show a simple example of fetching an OCI image to the system, but many others exist, tools like wget, curl, and scp come to mind.
Shell OCI Example:
admin@example:~$ cd /var/tmp/\nadmin@example:/var/tmp$ sudo wget https://github.com/kernelkit/curiOS/releases/download/edge/curios-oci-amd64.tar.gz\nConnecting to github.com (140.82.121.3:443)\nwget: note: TLS certificate validation not implemented\nConnecting to objects.githubusercontent.com (185.199.109.133:443)\nsaving to 'curios-oci-amd64.tar.gz'\ncurios-oci-amd64.tar 100% |*********************************| 7091k 0:00:00 ETA\n'curios-oci-amd64.tar.gz' saved\nadmin@example:/var/tmp$ ll\ntotal 7104\ndrwxr-xr-x 3 root root 4096 Mar 27 14:22 ./\ndrwxr-xr-x 14 root root 4096 Mar 27 11:57 ../\n-rw-r--r-- 1 root root 7261785 Mar 27 14:22 curios-oci-amd64.tar.gz\ndrwx------ 6 frr frr 4096 Mar 27 11:57 frr/\n Importing the image into Podman can be done either from the CLI admin-exec context ...
admin@example:/var/tmp$ cli\nadmin@example:/> container load /var/tmp/curios-oci-amd64.tar.gz name curios:edge\n Tip
The name curios:edge is the tag you give the imported (raw) archive which you can then reference in your container image configuration: set image curios:edge.
... or by giving the container configuration the full path to the OCI archive, which helps greatly with container upgrades (see below):
admin@example:/config/container/system/> set image oci-archive:/var/tmp/curios-oci-amd64.tar.gz\n Checksum Example:
admin@example:/> configure\nadmin@example:/config/> edit container sys\nadmin@example:/config/container/sys/> set hostname sys\nadmin@example:/config/container/sys/> set image ftp://192.168.122.1/curios-oci-amd64-v24.05.0.tar.gz\nadmin@example:/config/container/sys/> set checksum\n md5 sha256 sha512\nadmin@example:/config/container/sys/> set checksum sha256 4f01077036527498ed910f1a3e80645ae3eff629d10043cf80ebc6850c99c629\nadmin@example:/config/container/sys/> leave\nadmin@example:/> copy running-config startup-config\nadmin@example:/> show container\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\nb02e945c43c9 localhost/curios-oci-amd64-v24.05.0:latest 5 seconds ago Up 5 seconds sys\n\nadmin@example:/> show log\n...\nNov 20 07:24:56 example container[5040]: Fetching ftp://192.168.122.1/curios-oci-amd64-v24.05.0.tar.gz\nNov 20 07:24:56 example container[5040]: curios-oci-amd64-v24.05.0.tar.gz downloaded successfully.\nNov 20 07:24:56 example container[5040]: curios-oci-amd64-v24.05.0.tar.gz checksum verified OK.\nNov 20 07:24:57 example container[5040]: Cleaning up extracted curios-oci-amd64-v24.05.0\nNov 20 07:24:57 example container[5040]: podman create --name sys --conmon-pidfile=/run/container:sys.pid --read-only --replace --quiet --cgroup-parent=containers --restart=always --systemd=false --tz=local --hostname sys --log-driver k8s-file --log-opt path=/run/containers/sys.fifo --network=none curios-oci-amd64-v24.05.0\nNov 20 07:24:57 example container[3556]: b02e945c43c9bce2c4be88e31d6f63cfdb1a3c8bdd02179376eb059a49ae05e4\n "},{"location":"container/#understanding-image-tags","title":"Understanding Image Tags","text":"Docker images use tags to identify different versions of the same image. Understanding the difference between mutable and immutable tags is important for managing container upgrades effectively.
"},{"location":"container/#mutable-tags","title":"Mutable Tags","text":"Tags like :latest, :edge, or :stable are mutable \u2014 they point to different images over time as new versions are published to the registry.
Advantages:
Convenient: upgrade without changing configuration Simple: use the CLI command container upgrade NAME to get the latest version, there is even a convenient RPC for controlling the remotely Good for: development, testing, and systems that auto-update Trade-offs:
Less reproducible: different systems may run different versions Less predictable: upgrades happen when you pull, not when you plan Harder to rollback: previous version may no longer be available Example mutable tags:
docker://nginx:latest # Always points to newest release\ndocker://myapp:edge # Development/bleeding edge version\noci-archive:/var/tmp/app.tar # Local archive that may be replaced\n "},{"location":"container/#immutable-tags","title":"Immutable Tags","text":"Version-specific tags like :v1.0.1, :24.11.0, or digest references like @sha256:abc123... are immutable \u2014 they always reference the exact same image content.
Advantages:
Reproducible: all systems run identical versions Predictable: upgrades only happen when you change configuration Auditable: clear history of what ran when Good for: production, compliance, and controlled deployments Trade-offs:
More explicit: must update configuration to upgrade Requires planning: need to know which version to use Example immutable tags:
docker://nginx:1.25.3 # Specific version number\ndocker://myapp:v2.1.0 # Semantic version tag\ndocker://nginx@sha256:abc123 # Cryptographic digest (most immutable)\n Tip
Best practice for production: Use specific version tags (:v1.0.1) rather than mutable tags (:latest). This ensures all your systems run identical software and upgrades happen only when you decide.
"},{"location":"container/#upgrading-container-images","title":"Upgrading Container Images","text":"The applications in your container are an active part of the system as a whole, so make it a routine to keep your container images up-to-date!
"},{"location":"container/#how-container-lifecycle-works","title":"How Container Lifecycle Works","text":"Infix intelligently manages container lifecycles to provide a smooth experience while minimizing unnecessary work:
At first setup: When you configure a container for the first time, Infix fetches the image (if needed) and creates the container instance.
At boot time: Infix checks if the container needs to be recreated by comparing checksums for:
The image archive that the container was built from The container configuration script When configuration changes: If you modify any container settings (network, volumes, environment, etc.), the container is automatically recreated with the new configuration.
When explicitly upgraded: Using the container upgrade command forces a fresh pull of the image and recreates the container.
This means that in most cases, containers persist across reboots and are only recreated when actually necessary. Your container's state stored in volumes is preserved across recreations. Since Infix containers use a read-only root filesystem, any changes written outside of volumes or the writable paths provided by Podman (/dev, /dev/shm, /run, /tmp, /var/tmp) will be lost when the container is recreated.
"},{"location":"container/#method-1-upgrading-immutable-tags","title":"Method 1: Upgrading Immutable Tags","text":"When using version-specific tags, you upgrade by explicitly changing the image reference in your configuration:
admin@example:/> configure\nadmin@example:/config/> edit container web\nadmin@example:/config/container/web/> set image docker://nginx:1.25.3\nadmin@example:/config/container/web/> leave\n What happens:
Podman pulls the new image in the background (if not already present) Your container is automatically stopped The container is recreated with the new image The container is started with your existing volumes intact Example: Upgrading from one version to another:
admin@example:/> configure\nadmin@example:/config/> edit container system\nadmin@example:/config/container/system/> show image\nimage ghcr.io/kernelkit/curios:v24.11.0;\nadmin@example:/config/container/system/> set image ghcr.io/kernelkit/curios:v24.12.0\nadmin@example:/config/container/system/> leave\nadmin@example:/> show log\n...\nDec 13 14:32:15 example container[1523]: Pulling ghcr.io/kernelkit/curios:v24.12.0...\nDec 13 14:32:18 example container[1523]: Stopping old container instance...\nDec 13 14:32:19 example container[1523]: Creating new container with updated image...\nDec 13 14:32:20 example container[1523]: Container system started successfully\n "},{"location":"container/#method-2-upgrading-mutable-tags","title":"Method 2: Upgrading Mutable Tags","text":"For images using mutable tags like :latest or :edge, use the container upgrade command:
admin@example:/> container upgrade NAME\n This command:
Stops the running container Pulls the latest version of the image from the registry Recreates the container with the new image Starts the container automatically Example using registry:
admin@example:/> container upgrade system\nsystem\nTrying to pull ghcr.io/kernelkit/curios:edge...\nGetting image source signatures\nCopying blob 07bfba95fe93 done\nCopying config 0cb6059c0f done\nWriting manifest to image destination\nStoring signatures\n0cb6059c0f4111650ddbc7dbc4880c64ab8180d4bdbb7269c08034defc348f17\nsystem: not running.\n59618cc3c84bef341c1f5251a62be1592e459cc990f0b8864bc0f5be70e60719\n Example using local OCI archive:
An OCI archive image can be upgraded in a similar manner. First, get the new archive onto the system (see Container Images section above), then, provided the oci-archive:/path/to/archive format is used in your configuration, call the upgrade command:
admin@example:/> container upgrade system\nUpgrading container system with local archive: oci-archive:/var/tmp/curios-oci-amd64.tar.gz ...\n7ab4a07ee0c6039837419b7afda4da1527a70f0c60c0f0ac21cafee05ba24b52\n OCI archives can also be fetched from ftp/http/https URLs. In that case, the upgrade works the same way as a registry image \u2014 Infix downloads the new archive and recreates the container.
"},{"location":"container/#embedded-container-images","title":"Embedded Container Images","text":"Tip
Containers running from OCI images embedded in the operating system, e.g., /lib/oci/mycontainer.tar.gz, are automatically kept in sync with the Infix system image version.
How it works: When you build a custom Infix image with embedded OCI archives, those containers will be upgraded whenever you upgrade the Infix operating system itself. At boot, Infix checks if the embedded image has changed and automatically recreates the container if needed.
Example: default builds of Infix include a couple of OCI images for reference, one is /lib/oci/curios-nftables-v24.11.0.tar.gz, but there is also a symlink called curios-nftables-latest.tar.gz in the same directory, which is what the Infix regression tests use in the image configuration of the container. When the system is upgraded and the embedded image changes, the test containers are automatically recreated with the new version.
This approach ensures your embedded containers always match your system version without any manual intervention.
"},{"location":"container/#capabilities","title":"Capabilities","text":"An unprivileged container works for almost all use-cases, but there are occasions where they are too restricted and users start looking for the privileged flag. Capabilities offers a middle ground.
For example, a system container from which ping does not work:
admin@example:/config/container/system/> edit capabilities\nadmin@example:/config/container/system/capabilities/> set add net_raw\nadmin@example:/config/container/system/capabilities/> end\nadmin@infix-00-00-00:/config/container/system/> show\n...\ncapabilities {\n add net_raw;\n}\n...\n Infix supports a subset of all capabilities that are relevant for containers. Please note, that this is an advanced topic that require time and analysis of your container application to figure out which capabilities you need.
"},{"location":"container/#networking-and-containers","title":"Networking and Containers","text":"By default, unlike other systems, persistent2 containers have no networking enabled. All network access has to be set up explicitly. Currently two types of of container networks are supported:
host: an managed host interface, e.g., one end of a VETH pair, or even a physical interface bridge: an IP masquerading bridge In the former the interface is delegated to (moved into) the container, while in the latter a VETH pair is automatically created by Podman and one end delegated to the container, while the other end is assigned to the bridge (see the next section).
Tip
For more information on VETH pairs, see the Networking Guide.
"},{"location":"container/#container-bridge","title":"Container Bridge","text":"A container bridge is what most container setups use and users want.
The difference from a regular bridge is that the container runtime fully manages them -- connecting containers with automatically created VETH pairs (look at the bridge port names) and setting up firewall rules between the host and other containers, as well as managing port forwarding. This transparent background management is what makes container use seem to be so simple.
All interface configuration is done in configure context.
admin@example:/> configure\nadmin@example:/config> edit interface docker0\nadmin@example:/config/interface/docker0/> set container-network\nadmin@example:/config/interface/docker0/> leave\n There is more to this story. When using the CLI, and sticking to common interface nomenclature, Infix helps you with some of the boring stuff. E.g., creating a new interface with a name like brN or dockerN automatically infers the interface types, which you would otherwise have to set manually:
admin@example:/config/interface/docker0/> set type bridge\nadmin@example:/config/interface/docker0/> set container-network type bridge\n Important
When configuring the system via an API such as NETCONF or RESTCONF, no settings are inferred. Instead it is up to the caller to fully define the desired setup. This makes the CLI very useful for first setup and then extracting the resulting XML from the shell using the cfg -X command.
We have to declare the interface as a container network, ensuring the interface cannot be used by the system for any other purpose. E.g., a container host interface is supposed to be used by a container, by declaring it as such we can guarantee that it would never accidentally be added as a bridge or lag port. Hence, to move an interface currently set as a bridge-port it must be removed from the bridge before being given to a container.
The default subnet for a container bridge is 172.17.0.0/16, the bridge takes the .1 address and hand out the rest of the range to containers in a round-robin like fashion. A container with this network get an automatically created VETH pair connection to the bridge and a lot of other networking parameters (DNS, default route) are set up.
Some of the defaults of a container bridge can be changed, e.g., instead of set container-network type bridge, above, do:
admin@example:/config/interface/docker0/> edit container-network\nadmin@example:/config/interface/docker0/container-network/> set type bridge\nadmin@example:/config/interface/docker0/container-network/> edit subnet 192.168.0.0/16\nadmin@example:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> set gateway 192.168.255.254\nadmin@example:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> end\nadmin@example:/config/interface/docker0/container-network/> edit route 10.0.10.0/24\nadmin@example:/config/interface/docker0/container-network/route/10.0.10.0/24/> set gateway 192.168.10.254\nadmin@example:/config/interface/docker0/container-network/route/10.0.10.0/24/> end\nadmin@example:/config/interface/docker0/container-network/> end\nadmin@example:/config/interface/docker0/> leave\n Other network settings, like DNS and domain, use built-in defaults, but can be overridden from each container. Other common settings per container is the IP address and name of the network interface inside the container. The default, after each stop/start cycle, or reboot of the host, is to name the interfaces eth0, eth1, in the order they are given in the network list, and to give the container the next address in a bridge. Below an example of a system container calls set network interface docker0, here we show how to set options for that network:
admin@example:/config/container/ntpd/> edit network interface docker0\nadmin@example:/config/container/ntpd/network/interface/docker0/>\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option\n<string> Options for masquerading container bridges.\nadmin@example:/config/container/ntpd/network/interface/docker0/> help option\nNAME\n option <string>\n\nDESCRIPTION\n Options for masquerading container bridges.\n\n Example: ip=1.2.3.4 -- request a specific IP (IPv4 or IPv6)\n mac=00:01:02:c0:ff:ee -- set fixed MAC address in container\n interface_name=foo0 -- set interface name inside container\n\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option ip=172.17.0.2\nadmin@example:/config/container/ntpd/network/interface/docker0/> set option interface_name=wan\nadmin@example:/config/container/ntpd/network/interface/docker0/> leave\n "},{"location":"container/#container-host-interface","title":"Container Host Interface","text":"Another common use-case is to move network interfaces into the network namespace of a container1 . This of course works with plain Ethernet interfaces as well, but here we will use one end of a VETH pair as an example.
The network option setting is available also for this case, but only the interface_name=foo0 option works. Which is still very useful. To set:
IP address, use IPv4/IPv6 settings in the interface settings MAC address, to use the custom-phys-address in the interface settings For an example of both, see the next section.
"},{"location":"container/#routed-setup","title":"Routed Setup","text":"In this routed setup we reserve 192.168.0.0/24 for the network between the host and the ntpd container.
Configuration is a straight-forward VETH pair setup where we name the container-end of pair ntpd. This is just a convenience for us when reading the configuration later. The real action happens on the last line where we declare the ntpd end as a container network interface:
admin@example:/config/> edit interface veth0\nadmin@example:/config/interface/veth0/> set veth peer ntpd\nadmin@example:/config/interface/veth0/> set ipv4 address 192.168.0.1 prefix-length 24\nadmin@example:/config/interface/veth0/> end\nadmin@example:/config/> edit interface ntpd\nadmin@example:/config/interface/ntpd/> set ipv4 address 192.168.0.2 prefix-length 24\nadmin@example:/config/interface/ntpd/> set custom-phys-address static 00:c0:ff:ee:00:01\nadmin@example:/config/interface/ntpd/> set container-network\n Tip
Notice how you can also set a custom MAC address at the same time.
Adding the interface to the container is the same as before, but since everything for host interfaces is set up in the interfaces context, we can take a bit of a shortcut.
admin@example:/config/container/ntpd/> set network interface ntpd\nadmin@example:/config/container/ntpd/> leave\n Tip
Use the set network interface ntpd option interface_name=foo0 to set the name of the interface inside the container to foo0.
The point of the routed case is that port forwarding from the container in this case is limited to a single interface, not all interfaces as is the default in the masquerading container bridge setup.
"},{"location":"container/#bridged-setup","title":"Bridged Setup","text":"A perhaps more common case is to bridge the other end of the VETH pair with other physical ports. In this section we show how to add a new pair to give our container two interfaces:
We start by adding the second VETH pair:
admin@example:/config/> edit interface veth1a\nadmin@example:/config/interface/veth1a/> set veth peer veth1\nadmin@example:/config/interface/veth1a/> set ipv4 address 192.168.1.2 prefix-length 24\n Note
The LAN bridge (br1) in this example has IP address 192.168.1.1.
When a container has multiple host interfaces it can often be useful to have a default route installed. This can be added from the host with a 0.0.0.0/0 route on one of the interfaces:
admin@example:/config/interface/veth1a/> set container-network route 0.0.0.0/0 gateway 192.168.1.1\nadmin@example:/config/interface/veth1a/> show\ntype veth;\ncontainer-network {\n type host;\n route 0.0.0.0/0 {\n gateway 192.168.1.1;\n }\n}\nveth {\n peer veth1;\n}\nadmin@example:/config/interface/veth1a/> end\nadmin@example:/config/> set interface veth1 bridge-port bridge br1\n Please note, container network routes require the base interface also have a static IP address set. Setting only the route, but no address, means the route is skipped.
"},{"location":"container/#host-networking","title":"Host Networking","text":"The third use-case is host networking, this is where a container share the network namespace of the host. An example here could be a nftables or ntpd container -- single applications which add core functionality to the host operating system.
The host networking setup cannot be combined with any other network.
For an example, see below.
"},{"location":"container/#mounts-and-volumes","title":"Mounts and Volumes","text":"It is possible to mount files, directories, and even files matching a glob, into a container. This gives precise control over the container's file system:
admin@example:/config/container/system/> edit mount leds\nadmin@example:/config/container/system/mount/leds> set source /sys/class/leds\nadmin@example:/config/container/system/mount/leds> set target /sys/class/leds\nadmin@example:/config/container/system/mount/leds> end\nadmin@example:/config/container/system/>\n Any type of file can be bind mounted into the container, just watch out for permissions though. In the example above, /sys/class/leds is not writable from a container unless it runs in privileged mode. For plain configuration files you get more freedom, and your container can rely on, e.g., inotify events to trigger reloading its services when you change the file on the host.
So it depends on the container, and indeed your overall setup, what to use. An intriguing option is Content Mounts, which when changed also trigger a container restart.
Other times volumes are a better fit. A volume is an automatically created read-writable entity that follows the life of your container.
admin@example:/config/container/ntpd/> set volume varlib target /var/lib\n Volumes are persistent across both reboots and upgrades of the base image. They are created by Podman when the container first starts up, unlike a regular bind mount it synchronizes with the contents of the underlying container image's path at first use. I.e., \"bind-mount, if empty: then rsync\".
Note
Infix support named volumes (only), and it is not possible to share a volume between containers. All the tricks possible with volumes may be added in a later release.
"},{"location":"container/#volume-management","title":"Volume Management","text":"Volumes are persistent storage that survive container restarts and image upgrades, making them ideal for application data. However, this also means they are not automatically removed when a container is deleted from the configuration.
This design choice prevents accidental data loss, especially in scenarios where:
A container is temporarily removed and re-added with the same name A container is replaced with a different configuration but same name System upgrades or configuration changes affect container definitions To clean up unused volumes and reclaim disk space, use the admin-exec command:
admin@example:/> container prune\nDeleted Images\n...\nDeleted Volumes\nntpd-varlib\nsystem-data\n\nTotal reclaimed space: 45.2MB\n The container prune command safely removes:
Unused container images Volumes not attached to any container (running or stopped) Other unused container resources Tip
You can monitor container resource usage with the command:
admin@example:/> show container usage\n This displays disk space used by images, containers, and volumes, helping you decide when to run the prune command.
To see which volumes exist and which containers use them:
admin@example:/> show container volumes\n "},{"location":"container/#content-mounts","title":"Content Mounts","text":"Content mounts are a special type of file mount where the file contents is stored with the container configuration. This can be very useful when deploying similar systems at multiple sites. When the host loads its startup-config (or even factory-config) a temporary file is created using the decoded base64 data from the content node.
admin@example:/config/container/ntpd/> edit mount ntpd.conf\nadmin@example:/config/container/ntpd/mount/ntpd.conf> text-editor content\n... interactive editor starts up ...\nadmin@example:/config/container/ntpd/mount/ntpd.conf> set target /etc/ntpd.conf\nadmin@example:/config/container/ntpd/mount/ntpd.conf> end\nadmin@example:/config/container/ntpd/>\n The editor is a small Emacs clone called Mg, see the built-in help text, or press Ctrl-x Ctrl-c to exit and save. When the editor exits the contents are base64 encoded and stored in the candidate datastore.
Note
Since these files are always recreated when the host is restarted, changes made by the container are not preserved, or saved back to the host's startup-config.
Infix has three different text editors available. For more information, see CLI Text Editor.
"},{"location":"container/#example-containers","title":"Example Containers","text":""},{"location":"container/#system-container","title":"System Container","text":"Let's try out what we've learned by setting up a system container, a container providing multiple services, using the docker0 interface we created previously:
admin@example:/> configure\nadmin@example:/config> edit container system\nadmin@example:/config/container/system/> set image ghcr.io/kernelkit/curios:edge\nadmin@example:/config/container/system/> set network interface docker0\nadmin@example:/config/container/system/> set publish 222:22\nadmin@example:/config/container/system/> leave\n Note
Ensure you have a network connection to the registry. If the image cannot be pulled, creation of the container will be put in a queue and be retried every time there is a change in the routing table, e.g., default route is added, and every 60 seconds.
Provided the image is downloaded successfully, a new system container now runs behind the docker0 interface, forwarding container port 22 to port 222 on all of the host's interfaces. (See help publish in the container configuration context for the full syntax.)
Available containers can be accessed from admin-exec:
admin@example:/> show container\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n439af2917b44 ghcr.io/kernelkit/curios:edge 41 hours ago Up 16 hours 0.0.0.0:222->222/tcp system\n This is a system container, so you can \"attach\" to it by starting a shell (or logging in with SSH):
admin@example:/> container shell system\nroot@439af2917b44:/#\n Notice how the hostname inside the container changes. By default the container ID (hash) is used, but this can be easily changed:
root@439af2917b44:/# exit\nadmin@example:/> configure\nadmin@example:/config/> edit container system\nadmin@example:/config/container/system/> set hostname sys101\nadmin@example:/config/container/system/> leave\nadmin@example:/> container shell system\nroot@sys101:/#\n In fact, the container hostname setting supports the same format specifiers as the host's hostname setting:
%i: OS ID, from /etc/os-release, from Menuconfig branding %h: Default hostname, from /etc/os-release, from branding %m: NIC specific part of base MAC, e.g., to c0-ff-ee %%: Literal % The most useful combination is probably \"container-name-%m\", which in this example give the container hostname container-name-c0-ff-ee.
"},{"location":"container/#application-container-nftables","title":"Application Container: nftables","text":"Infix currently does not have a native firewall configuration, and even when it does it will never expose the full capabilities of nftables. For advanced setups, the following is an interesting alternative.
admin@example:/> configure\nadmin@example:/config> edit container nftables\nadmin@example:/config/container/nftables/> set image ghcr.io/kernelkit/curios-nftables:edge\nadmin@example:/config/container/nftables/> set network host\nadmin@example:/config/container/nftables/> set privileged\nadmin@example:/config/container/nftables/> edit mount nftables.conf\nadmin@example:/config/container/nftables/mount/nftables.conf/> set target /etc/nftables.conf\nadmin@example:/config/container/nftables/mount/nftables.conf/> text-editor content\n... interactive editor starts up where you can paste your rules ...\nadmin@example:/config/container/nftables/mount/nftables.conf/> leave\n Notice how we set network host, so the container can see and act on all the host's interfaces, and that we also have to run the container in privileged mode.
"},{"location":"container/#application-container-ntpd","title":"Application Container: ntpd","text":"The default NTP server/client in Infix is Chrony, a fully working and capable workhorse for most use-cases. However, it does not support a feature like multicasting, for that you need ISC ntpd.
As we did with nftables, previously, we can use host networking and set up a read-only config file that is bind-mounted into the container's file system and store in the host's startup-config. However, ntpd also saves clock drift information in /var/lib/ntpd, so we will also use volumes in this example.
admin@example:/> configure\nadmin@example:/config> edit container ntpd\nadmin@example:/config/container/ntpd/> set image ghcr.io/kernelkit/curios-ntpd:edge\nadmin@example:/config/container/ntpd/> set network interface ntpd # From veth0 above\nadmin@example:/config/container/ntpd/> edit mount ntp.conf\nadmin@example:/config/container/ntpd/mount/ntp.conf/> set target /etc/ntp.conf\nadmin@example:/config/container/ntpd/mount/ntp.conf/> text-editor content\n... interactive editor starts up where you can paste your rules ...\nadmin@example:/config/container/ntpd/mount/ntp.conf/> end\nadmin@example:/config/container/ntpd/> edit volume varlib\nadmin@example:/config/container/ntpd/volume/varlib/> set target /var/lib\nadmin@example:/config/container/ntpd/volume/varlib/> leave\nadmin@example:/> copy running-config startup-config\n The ntp.conf file is stored in the host's startup-config and any state data in the container's /var/lib is retained between reboots and across image upgrades.
"},{"location":"container/#advanced","title":"Advanced","text":"This section covers advanced, and sometimes dangerous, topics. Please read any warnings and always consider the security aspects.
"},{"location":"container/#running-host-commands-from-container","title":"Running Host Commands From Container","text":"SSH login with keys is very handy, both remote scripting friendly and secure, but it does require a few extra configuration steps. The way to set it up is covered in part in SSH Authorized Key.
Another insecure approach is to access the host system directly, bypassing the namespaces that make up the boundary between host and container.
Caution
Please note, this completely demolishes the isolation barrier between container and host operating system. It is only suitable in situations where the container serves more as a unit of distribution rather than as a separate component of the system. Strongly recommended to use this only in trusted setups! Consider also limiting the time frame in which this is active!
First, enable Privileged mode, this unlocks the door and allows the container to manage resources on the host system. An example is the nftables container mentioned previously.
admin@example:/config/container/system/> set privileged\n Second, mount the host's /proc/1 directory to somewhere inside your container. Here we pick /1:
admin@example:/config/container/system/> edit mount host\nadmin@example:/config/container/system/mount/host/> set source /proc/1\nadmin@example:/config/container/system/mount/host/> set target /1\nadmin@example:/config/container/system/mount/host/> leave\n Third, from inside the container, use the host's PID 1 namespaces with the nsenter3 command to slide through the container's walls. Here we show two example calls to hostname, first the container's own name and then asking what the hostname is on the host:
root@sys101:/# hostname\nsys101\nroot@sys101:/# nsenter -m/1/ns/mnt -u/1/ns/uts -i/1/ns/ipc -n/1/ns/net hostname\nexample\n One use-case for this method is when extending Infix with a management container that connects to other systems. For some tips on how to control an Infix system this way, see Scripting Infix.
"},{"location":"container/#container-requirements","title":"Container Requirements","text":"In addition to general best practices for container images, there are a few more things to consider when targeting embedded systems:
Ensure the image targets the CPU architecture of the target system, learn more about Multi-platform Builds Follow best practices for naming and tagging, e.g., :latest vs :1.0 Follow OCI recommendations and layout, learn more about OCI and Docker Exporters If the Docker documentation is not enough, there are plenty of guides online with examples on how to create your own container image. For the more advanced, please see the next section.
"},{"location":"container/#advanced-users","title":"Advanced Users","text":"Most people prefer their system containers small, often based on Alpine Linux, or similar, with only a few small applications, including their own, and an SSH server perhaps. For some developers, even this is too big, so they roll their own from source. This section is for you.
Depending on your needs, here is a checklist:
you need something that can forward signals, e.g., tini Bash only, or BusyBox init, a classic most embedded developers know, but read on ... a system container only need the bare necessities of a system bringup E.g., BusyBox's init, but not everything Some of the networking is set up by Podman and CNI for you, but you may want to run a DHCP client? Do not rename interfaces inside the container, use the dedicated interface_name option in the configuration instead Remember, Podman provides a tmpfs for all critical system paths: /dev, /dev/shm, /run, /tmp, and /var/tmp, so you don't need to clean or set up any of these mount points Examples using tini and BusyBox init are available from the KernelKit curiOS project. It is a small Buildroot based container image builder that generates OCI compatible image tarballs without any tools from Docker or Podman -- ready-made images exist for testing on both AMD64 and ARM64 targets, as well as docker pull images and and OCI tarballs with SHA256 checksums for integrity checking.
Finally, if you build your own version of Infix, and embed OCI tarballs in the system image, then see the tip at the end of Upgrading a Container Image (above).
Something which the container bridge network type does behind the scenes with one end of an automatically created VETH pair.\u00a0\u21a9
this does not apply to the admin-exec command container run. This command is intended to be used for testing and evaluating container images. Such containers are given a private network behind an IP masquerading bridge.\u00a0\u21a9
The nsenter program is available from either the util-linux package in Debian/Ubuntu/Mint, or in BusyBox. Note, however, it may not be enabled by default in BusyBox.\u00a0\u21a9
"},{"location":"developers-guide/","title":"Developer's Guide","text":""},{"location":"developers-guide/#developers-guide","title":"Developer's Guide","text":"Please note, by default the root account is disabled in Infix NETCONF builds. Meaning, the only way to access the system is with the admin account, which is created based on credentials found in the VPD area -- for Qemu devices this is emulated using qemu_fw_cfg.
For developers this can be quite frustrating to be blocked from logging in to debug the system. So we recommend enabling the root account in the Buildroot make menuconfig system.
make menuconfig\n -> System configuration\n -> [*]Enable root login with password\n Important
Please see the Contributing section, below, for details on how to fork and clone when contributing to Infix.
"},{"location":"developers-guide/#cloning","title":"Cloning","text":"When pre-built releases are not enough, for instance when you want to add or modify some Open Source components, you can clone the Infix tree to your PC:
$ mkdir ~/Projects; cd ~/Projects\n$ git clone https://github.com/kernelkit/infix.git\n..\n$ cd infix/\n$ git submodule update --init\n..\n "},{"location":"developers-guide/#customer-builds","title":"Customer Builds","text":"Customer builds add product specific device trees, more OSS packages, e.g., Frr and podman, and sometimes integrates proprietary software. What's important to remember, however, is that they are all made by setting up Infix as a GIT submodule, similar to how Infix set up a GIT submodule for Buildroot.
So, in addition to using the customer's specific defconfig(s), one must also make sure to update all submodules, otherwise you will likely end up with a broken build.
$ ...\n$ git submodule update --init --recursive\n ~~~~~~~~~~~\n Other caveats should be documented in the customer specific trees.
"},{"location":"developers-guide/#building","title":"Building","text":"Tip
For more details, see the Getting Started and System Requirements sections of the excellent Buildroot manual.
Buildroot is almost stand-alone, it needs a few locally installed tools to bootstrap itself. The most common ones are usually part of the base install of the OS, but specific ones for building need the following. The instructions here are for Debian/Ubuntu based systems (YMMV):
$ sudo apt install bc binutils build-essential bzip2 cpio \\\n diffutils file findutils git gzip \\\n libncurses-dev libssl-dev perl patch \\\n python3 rsync sed tar unzip wget \\\n autopoint bison flex autoconf automake \\\n mtools\n To build an Infix image; select the target and then make:
make x86_64_defconfig\nmake\n Online help is available:
make help\n To see available defconfigs for supported targets, use:
make list-defconfigs\n "},{"location":"developers-guide/#test","title":"Test","text":"Working with the regression test framework, Infamy, a few more tools and services are required on your system:
$ sudo apt install jq graphviz qemu-system-x86 qemu-system-arm \\\n ethtool gdb-multiarch tcpdump tshark\n..\n To be able to build the test specification you also need:
$ sudo apt-get install python3-graphviz ruby-asciidoctor-pdf\n..\n "},{"location":"developers-guide/#documentation","title":"Documentation","text":"The documentation is written in Markdown, with GitHub extensions, and published using MkDocs, material theme. This means some features require MkDocs hinting which may not render fully when previewing on GitHub -- this is OK.
MkDocs is packaged and available to install via apt, but not all of the plugins and extensions we rely on are available, so instead we do recommend using pipx to install the necessary tooling:
$ sudo apt install pipx\n$ pipx install mkdocs\n$ pipx inject mkdocs mkdocs-material pymdown-extensions mkdocs-callouts mike mkdocs-to-pdf\n The last two packages, mike and mkdocs-to-pdf, are used for online versioning and PDF generation by GitHub Actions, but since they are in the mkdocs.yml file, everyone who wants to preview the documentation have to install all the tooling.
Preview with:
$ cd ~/src/infix/\n$ mkdocs serve\n "},{"location":"developers-guide/#development","title":"Development","text":"Developing with Infix is the same as developing with Buildroot. When working with a package, be it locally kept sources, or when using local.mk, you only want to rebuild the parts you have modified:
make foo-rebuild\n or
make foo-reconfigure\n or, as a last resort when nothing seems to bite:
make foo-dirclean foo-rebuild\n As shown here, you can combine multiple build targets and steps in one go, like this:
make foo-rebuild bar-rebuild all run\n This rebuilds (and installs) foo and bar, the all target calls on Buildroot to finalize the target filesystem and generate the images. The final run argument is explained below.
"},{"location":"developers-guide/#yang-model","title":"YANG Model","text":"When making changes to the confd and statd services, you will often need to update the YANG models. If you are adding a new YANG module, it's best to follow the structure of an existing one. However, before making any changes, always discuss them with the Infix core team. This helps avoid issues later in development and makes pull request reviews smoother.
"},{"location":"developers-guide/#confd","title":"confd","text":"The Infix src/confd/ is the engine of the system. Currently it is a plugin for systemd-plugind and contains XPath subscriptions to all the supported YANG models.
There are essentially two ways of adding support for a new YANG model:
The sysrepo way, or The Infix way, using libsrx (the lydx_*() functions) The former is well documented in sysrepo, and the latter is best taught by example, e.g., src/confd/src/infix-dhcp.c. Essentially libsrx is a way of traversing the libyang tree instead of fetching changes by XPath.
When working with confd you likely want to enable full debug mode, this is how you do it:
Open the file package/confd/confd.conf Uncomment the first line set DEBUG=1 Change the following line to add -v3 at the end[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon\n to:
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -v3 -- Configuration daemon\n Now you can rebuild confd, just as described above, and restart Infix:
make confd-rebuild all run\n "},{"location":"developers-guide/#statd","title":"statd","text":"The Infix status daemon, src/statd, is responsible for populating the sysrepo operational datastore. Like confd, it uses XPath subscriptions, but unlike confd, it relies entirely on yanger, a Python script that gathers data from local linux services and feeds it into sysrepo.
To apply changes, rebuild the image:
make python-statd-rebuild statd-rebuild all\n Rebuilding the image and testing on target for every change during development process can be tedious. Instead, yanger allows remote execution, running the script directly on the host system (test container):
infamy0:test # ../src/statd/python/yanger/yanger -x \"../utils/ixll -A ssh d3a\" ieee802-dot1ab-lldp\n ixll is a utility script that lets you run network commands using an interface name instead of a hostname. It makes operations like ssh, scp, and network discovery easier.
Normally, yanger runs commands locally to retrieve data (e.g., lldpcli when handling ieee802-dot1ab-lldp). However, when executed with -x \"../utils/ixll -A ssh d3a\" it redirects these commands to a remote system connected to the local d3a interface via SSH. This setup is used for running yanger in an interactive test environment. The yanger script runs on the host system, but key commands are executed on the target system.
For debugging or testing, you can capture system command output and replay it later without needing a live system.
To capture:
infamy0:test # ../src/statd/python/yanger/yanger -c /tmp/capture ieee802-dot1ab-lldp\n To replay:
infamy0:test # ../src/statd/python/yanger/yanger -r /tmp/capture ieee802-dot1ab-lldp\n This is especially useful when working in isolated environments or debugging issues without direct access to the DUT.
"},{"location":"developers-guide/#upgrading-packages","title":"Upgrading Packages","text":""},{"location":"developers-guide/#buildroot","title":"Buildroot","text":"The Kernelkit team maintains an internal fork of Buildroot, with branches following the naming scheme YYYY.MM.patch-kkit e.g. 2025.02.1-kkit, which means a new branch should be created whenever Buildroot is updated. These branches should contain only changes to existing packages (but no new patches), modifications to Buildroot itself or upstream backports.
The team tracks the latest Buildroot LTS (Long-Term Support) release and updates. The impact of minor LTS release upgrades is expected to have a very low impact and should be done as soon there is a patch release of a Buildroot LTS available.
Depending on your setup, follow the appropriate steps below.
"},{"location":"developers-guide/#repo-locally-cloned-already","title":"Repo locally cloned already","text":" Navigate to the Buildroot directory
cd buildroot/\n Pull the latest changes from KernelKit
git pull\n Fetch the latest tags from upstream
git fetch upstream --tags\n "},{"location":"developers-guide/#no-local-repo-yet","title":"No local repo yet","text":" Clone the Kernelkit Buildroot repository
git clone git@github.com:kernelkit/buildroot.git\n Add the upstream remote
git remote add upstream https://gitlab.com/buildroot.org/buildroot.git\n Checkout old KernelKit branch
git checkout 2025.02.1-kkit\n Note
Below, it is not allowed to rebase the branch when bumped in Infix.
"},{"location":"developers-guide/#continue-here","title":"Continue Here","text":" Create a new branch based on the previous KernelKit Buildroot release (e.g. 2025.02.1-kkit) and name it according to the naming scheme (e.g. 2025.02.2-kkit)
git checkout -b 2025.02.2-kkit\n Rebase the new branch onto the corresponding upstream release
git rebase 2025.02.2\n Push the new branch and tags
git push origin 2025.02.2-kkit --tags\n In Infix, checkout new branch of Buildroot
cd buildroot\ngit fetch\ngit checkout 2025.02.2-kkit\n Commit and push the changes. Remember to update the ChangeLog!
Create a pull request.
Note
Remember to set the pull request label to ci:main to ensure full CI coverage.
"},{"location":"developers-guide/#linux-kernel","title":"Linux kernel","text":"The KernelKit team maintains an internal fork of Linux kernel, with branches following the naming scheme kkit-linux-[version].y, e.g. kkit-6.12.y, which means a new branch should be created whenever the major kernel version is updated. This branch should contain all kernel patches used by Infix.
The team tracks the latest Linux kernel LTS (Long-Term Support) release and updates. The upgrade of LTS minor releases is expected to have low impact and should be done as soon as a patch release of the LTS Linux kernel is available.
"},{"location":"developers-guide/#repo-locally-cloned-already_1","title":"Repo locally cloned already","text":" ./utils/kernel-upgrade.sh /path/to/linux/tree Update Changelog push and create a pull request Note
Remember to set the pull request label to ci:main to ensure full CI coverage.
"},{"location":"developers-guide/#testing","title":"Testing","text":"Manual testing can be done using Qemu by calling make run, see also Infix in Virtual Environments, or on a physical device by upgrading to the latest build or \"netbooting\" and running the image from RAM. The latter is how most board porting work is done -- much quicker change-load-test cycles.
The Infix automated test suite is built around Qemu and Qeneth, see:
Regression Testing with Infamy Docker Image With any new feature added to Infix, it is essential to include relevant test case(s). See the Test Development section for guidance on adding test cases.
"},{"location":"developers-guide/#reviewing","title":"Reviewing","text":"While reviewing a pull request, you might find yourself wanting to play around with a VM running that exact version. For such occasions, gh-dl-artifact.sh is your friend in need! It employs the GitHub CLI (gh) to locate a prebuilt image from our CI workflow, download it, and prepare a local output directory from which you can launch both make run instances, and run regression tests with make test and friends.
For example, if you are curious about how PR 666 behaves in some particular situation, you can use gh to switch to that branch, from which gh-dl-artifact.sh can then download and prepare the corresponding image for execution with our normal tooling:
gh pr checkout 666\n./utils/gh-dl-artifact.sh\ncd x-artifact-a1b2c3d4-x86_64\nmake run\n Note
CI artifacts are built from a merge commit of the source and target branches. Therefore, the version in the Infix banner will not match the SHA of the commit you have checked out.
"},{"location":"developers-guide/#contributing","title":"Contributing","text":"Infix is built from many components, when contributing you need to set up your own fork, create a local branch for your change, push to your fork, and then use GitHub to create a Pull Reqeuest.
For this to work as painlessly as possible for everyone involved:
Fork Infix to your own user or organization1 Fork all the Infix submodules, e.g., kernelkit/buildroot to your own user or organization as well Clone your fork of Infix to your laptop/workstation Deactivate the Actions you don't want in your fork Please read the Contributing Guidelines as well! $ cd ~/Projects\n$ git clone https://github.com/YOUR_USER_NAME/infix.git\n...\n$ cd infix/\n$ git submodule update --init\n...\n Note
When updating/synchronizing with upstream Infix changes you may have to synchronize your forks as well. GitHub have a Sync fork button in the GUI for your fork for this purpose. A cronjob on your server of choice can do this for you with the GitHub CLI tool.
Organizations should make sure to lock the main (or master) branch of their clones to ensure members do not accidentally merge changes there. Keeping these branches in sync with upstream Infix is highly recommended as a baseline and reference. For integration of local changes another company-specific branch can be used instead.\u00a0\u21a9
"},{"location":"dhcp/","title":"DHCP Server","text":""},{"location":"dhcp/#dhcp-server","title":"DHCP Server","text":"The DHCPv4 server provides automatic IP address assignment and network configuration for clients. It supports address pools, static host assignments, and customizable DHCP options. It also serves as a DNS proxy for local subnets and can even forward queries to upstream DNS servers1 .
Note
When using the CLI, the system automatically enables essential options like DNS servers and default gateway based on the system's network configuration. These options can be disabled, changed or overridden, at any level: global, subnet, or per-host.
"},{"location":"dhcp/#basic-configuration","title":"Basic Configuration","text":"The following example configures a DHCP server for subnet 192.168.2.0/24 with an address pool:
admin@example:/> configure\nadmin@example:/config/> edit dhcp-server subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> set pool start-address 192.168.2.100 end-address 192.168.2.200\nadmin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> leave\n When setting up the server from the CLI, the system automatically adds a few default DHCP options that will be sent to clients: both DNS server and default gateway will use the system address on the matching interface.
admin@example:/> show running-config\n \"infix-dhcp-server:dhcp-server\": {\n \"subnet\": [\n {\n \"subnet\": \"192.168.2.0/24\",\n \"option\": [\n {\n \"id\": \"dns-server\",\n \"address\": \"auto\"\n },\n {\n \"id\": \"router\",\n \"address\": \"auto\"\n }\n ],\n \"pool\": {\n \"start-address\": \"192.168.2.100\",\n \"end-address\": \"192.168.2.200\"\n }\n }\n ]\n }\n Important
Remember to set up an interface in this subnet, avoid using addresses in the DHCP pool, or reserved for static hosts. In Class C networks the router usually has address .1. Depending on the use-case, you may also want to set up routing.
"},{"location":"dhcp/#static-host-assignment","title":"Static Host Assignment","text":"To reserve specific IP addresses for clients based on their MAC address, hostname, or client ID:
admin@example:/config/dhcp-server/\u2026/192.168.2.0/24/> edit host 192.168.2.10\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> set match mac-address 00:11:22:33:44:55\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> set hostname printer\nadmin@example:/config/dhcp-server/\u2026/192.168.2.10/> leave\n Match hosts using a client identifier instead of MAC address:
admin@example:/config/dhcp-server/\u2026/192.168.1.0/24/> edit host 192.168.1.50\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> edit match\nadmin@example:/config/dhcp-server/\u2026/match/> set client-id hex c0:ff:ee\nadmin@example:/config/dhcp-server/\u2026/match/> leave\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> set lease-time infinite\nadmin@example:/config/dhcp-server/\u2026/192.168.1.50/> leave\n The hex prefix here ensures matching of client ID is done using the hexadecimal octets c0:ff:ee, three bytes. Without the prefix the ASCII string \"c0:ff:ee\", eight bytes, is used.
Note
The DHCP server is fully RFC conformant, in the case of option 61 this means that using the hex prefix will require the client to set the htype field of the option to 00. See RFC 2132 for details.
"},{"location":"dhcp/#custom-dhcp-options","title":"Custom DHCP Options","text":"Configure additional DHCP options globally, per subnet, or per host:
admin@example:/config/dhcp-server/> edit subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> edit option dns-server\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/dns-server/> set address 8.8.8.8\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/dns-server/> leave\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> edit option ntp-server\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/ntp-server/> set address 192.168.2.1\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/option/ntp-server/> leave\n When configuring, e.g., dns-server, or router options with the value auto, the system uses the IP address from the interface matching the subnet. For example:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 192.168.1.1/24 (static)\neth1 ethernet UP 02:00:00:00:00:01\n ipv4 192.168.2.1/24 (static)\n\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> edit option dns-server\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/option/dns-server/> set address auto\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/option/dns-server/> leave\n In this case, clients in subnet 192.168.1.0/24 will receive 192.168.1.1 as their DNS server address.
"},{"location":"dhcp/#multiple-subnets","title":"Multiple Subnets","text":"Configure DHCP for multiple networks:
admin@example:/> configure\nadmin@example:/config/> edit dhcp-server\nadmin@example:/config/dhcp-server/> edit subnet 192.168.1.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> set pool start-address 192.168.1.100 end-address 192.168.1.200\nadmin@example:/config/dhcp-server/subnet/192.168.1.0/24/> leave\nadmin@example:/config/dhcp-server/> edit subnet 192.168.2.0/24\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> set pool start-address 192.168.2.100 end-address 192.168.2.200\nadmin@example:/config/dhcp-server/subnet/192.168.2.0/24/> leave\n "},{"location":"dhcp/#monitoring","title":"Monitoring","text":"View active leases and server statistics:
admin@example:/> show dhcp-server\nIP ADDRESS MAC HOSTNAME CLIENT ID EXPIRES\n192.168.2.22 00:a0:85:00:02:05 00:c0:ff:ee 3591s\n192.168.1.11 00:a0:85:00:04:06 foo 01:00:a0:85:00:04:06 3591s\n\nadmin@example:/> show dhcp-server statistics\nDHCP offers sent : 6\nDHCP ACK messages sent : 5\nDHCP NAK messages sent : 0\nDHCP decline messages received : 0\nDHCP discover messages received : 6\nDHCP request messages received : 5\nDHCP release messages received : 6\nDHCP inform messages received : 6\n This requires the system DNS resolver to be configured.\u00a0\u21a9
"},{"location":"discovery/","title":"Device Discovery","text":""},{"location":"discovery/#device-discovery","title":"Device Discovery","text":"Infix advertises itself via the mDNS-SD and LLDP discovery protocols. mDNS-SD has good client support in Windows, macOS and on Linux systems. More on these protocols later.
An even simpler method is available when directly attached to an Infix device:
.----. Ethernet .--------.\n| PC +---------------------+ Device |\n'----' if1 e1 '--------'\n With IPv6 you can ping the all-hosts address (ff02::1), the device's link-local IPv6 address is then seen in the response. In the following example, the PC here uses tap0 as if1, Infix responds with address fe80::ff:fec0:ffed.
linux-pc:# ping -6 -L -c 3 ff02::1%tap0\nPING ff02::1%tap0(ff02::1%tap0) 56 data bytes\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=1 ttl=64 time=0.558 ms\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=2 ttl=64 time=0.419 ms\n64 bytes from fe80::ff:fec0:ffed%tap0: icmp_seq=3 ttl=64 time=0.389 ms\n\n--- ff02::1%tap0 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2043ms\nrtt min/avg/max/mdev = 0.389/0.455/0.558/0.073 ms\nlinux-pc:# \n Tip
The -L option ignores local responses from the PC.
This address can then be used to connect to the device, e.g., using SSH. Notice the syntax username@address%interface:
linux-pc:# ssh admin@fe80::ff:fec0:ffed%tap0\nadmin@fe80::ff:fec0:ffed%tap0's password: admin\nadmin@infix-c0-ff-ee:~$ \n "},{"location":"discovery/#lldp","title":"LLDP","text":"Infix supports LLDP (IEEE 802.1AB). For a device with factory default settings, the link-local IPv6 address can be read from the Management Address TLV using tcpdump or other sniffing tools1 :
linux-pc:# tcpdump -i tap0 -Qin -v ether proto 0x88cc\ntcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n15:51:52.061071 LLDP, length 193\n Chassis ID TLV (1), length 7\n Subtype MAC address (4): 02:00:00:c0:ff:ee (oui Unknown)\n Port ID TLV (2), length 7\n Subtype MAC address (3): 02:00:00:c0:ff:ee (oui Unknown)\n Time to Live TLV (3), length 2: TTL 120s\n System Name TLV (5), length 14: infix-c0-ff-ee\n System Description TLV (6), length 91\n Infix by KernelKit Linux 5.19.17 #1 SMP PREEMPT_DYNAMIC Wed Jun 7 08:47:23 CEST 2023 x86_64\n System Capabilities TLV (7), length 4\n System Capabilities [Bridge, WLAN AP, Router, Station Only] (0x009c)\n Enabled Capabilities [Station Only] (0x0080)\n Management Address TLV (8), length 24\n Management Address length 17, AFI IPv6 (2): fe80::ff:fec0:ffed\n Interface Index Interface Numbering (2): 2\n Port Description TLV (4), length 4: eth0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n Link aggregation Subtype (3)\n aggregation status [supported], aggregation port ID 0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n MAC/PHY configuration/status Subtype (1)\n autonegotiation [none] (0x00)\n PMD autoneg capability [unknown] (0x8000)\n MAU type Unknown (0x0000)\n End TLV (0), length 0\n^C\n1 packet captured\nlinux-pc:# \n If the device has an IPv4 address assigned, it is shown in an additional Management Address TLV.
Note
The Management Addresses shown by LLDP are not necessarily associated with the port transmitting the LLDP message.
In the example below, the IPv4 address (10.0.1.1) happens to be assigned to eth0, while the IPv6 address (2001:db8::1) is not.
linux-pc:# sudo tcpdump -i tap0 -Qin -v ether proto 0x88cc\ntcpdump: listening on tap0, link-type EN10MB (Ethernet), snapshot length 262144 bytes\n15:46:07.908665 LLDP, length 207\n Chassis ID TLV (1), length 7\n Subtype MAC address (4): 02:00:00:c0:ff:ee (oui Unknown)\n Port ID TLV (2), length 7\n Subtype MAC address (3): 02:00:00:c0:ff:ee (oui Unknown)\n Time to Live TLV (3), length 2: TTL 120s\n System Name TLV (5), length 14: infix-c0-ff-ee\n System Description TLV (6), length 91\n Infix by KernelKit Linux 5.19.17 #1 SMP PREEMPT_DYNAMIC Wed Jun 7 08:47:23 CEST 2023 x86_64\n System Capabilities TLV (7), length 4\n System Capabilities [Bridge, WLAN AP, Router, Station Only] (0x009c)\n Enabled Capabilities [Station Only] (0x0080)\n Management Address TLV (8), length 12\n Management Address length 5, AFI IPv4 (1): 10.0.1.1\n Interface Index Interface Numbering (2): 2\n Management Address TLV (8), length 24\n Management Address length 17, AFI IPv6 (2): 2001:db8::1\n Interface Index Interface Numbering (2): 3\n Port Description TLV (4), length 4: eth0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n Link aggregation Subtype (3)\n aggregation status [supported], aggregation port ID 0\n Organization specific TLV (127), length 9: OUI IEEE 802.3 Private (0x00120f)\n MAC/PHY configuration/status Subtype (1)\n autonegotiation [none] (0x00)\n PMD autoneg capability [unknown] (0x8000)\n MAU type Unknown (0x0000)\n End TLV (0), length 0\n^C\n1 packet captured\n2 packets received by filter\n0 packets dropped by kernel\nlinux-pc:#\n The following capabilities are available via NETCONF/RESTCONF or the Infix CLI.
"},{"location":"discovery/#lldp-enabledisable","title":"LLDP Enable/Disable","text":"The LLDP service can be disabled using the following commands.
admin@infix-c0-ff-ee:/> configure\nadmin@infix-c0-ff-ee:/config/> no lldp \nadmin@infix-c0-ff-ee:/config/> leave\nadmin@infix-c0-ff-ee:/> \n To reenable it from the CLI config mode:
admin@test-00-01-00:/config/> set lldp enabled \nadmin@test-00-01-00:/config/> leave\n "},{"location":"discovery/#lldp-message-transmission-interval","title":"LLDP Message Transmission Interval","text":"By default, LLDP uses a message-tx-interval of 30 seconds, as defined by the IEEE standard. Infix allows this value to be customized. To change it using the CLI:
admin@test-00-01-00:/config/> set lldp message-tx-interval 1\nadmin@test-00-01-00:/config/> leave\n "},{"location":"discovery/#lldp-administrative-status-per-interface","title":"LLDP Administrative Status per Interface","text":"Infix supports configuring the LLDP administrative status on a per-port basis. The default mode is tx-and-rx, but the following options are also supported:
rx-only \u2013 Receive LLDP packets only tx-only \u2013 Transmit LLDP packets only disabled \u2013 Disable LLDP on the interface Example configuration:
admin@test-00-01-00:/config/> set lldp port e8 dest-mac-address 01:80:C2:00:00:0E admin-status disabled\nadmin@test-00-01-00:/config/> set lldp port e5 dest-mac-address 01:80:C2:00:00:0E admin-status rx-only \nadmin@test-00-01-00:/config/> set lldp port e6 dest-mac-address 01:80:C2:00:00:0E admin-status tx-only\nadmin@test-00-01-00:/config/> leave\n Note
The destination MAC address must be the standard LLDP multicast address: 01:80:C2:00:00:0E.
"},{"location":"discovery/#displaying-lldp-neighbor-information","title":"Displaying LLDP Neighbor Information","text":"In CLI mode, Infix also provides a convenient show lldp command to list LLDP neighbors detected on each interface:
admin@test-00-01-00:/> show lldp \nINTERFACE REM-IDX TIME CHASSIS-ID PORT-ID \ne5 1 902 00:a0:85:00:04:01 00:a0:85:00:04:07 \ne6 3 897 00:a0:85:00:03:01 00:a0:85:00:03:07 \ne8 2 901 00:a0:85:00:02:01 00:a0:85:00:02:05\n "},{"location":"discovery/#mdns-sd","title":"mDNS-SD","text":"DNS-SD/mDNS-SD can be used to discover Infix devices and services. By default, Infix use the .local domain for advertising services. Some networks use .lan instead, so this configurable:
admin@infix-c0-ff-ee:/> configure\nadmin@infix-c0-ff-ee:/config/> edit mdns\nadmin@infix-c0-ff-ee:/config/mdns/> set domain lan\n Other available settings include limiting the interfaces mDNS responder acts on:
admin@infix-c0-ff-ee:/config/> set interfaces allow e1\n or
admin@infix-c0-ff-ee:/config/> set interfaces deny wan\n The allow and deny settings are complementary, deny always wins.
In Linux, tools such as avahi-browse or mdns-scan2 can be used to search for devices advertising their services via mDNS.
linux-pc:# avahi-browse -ar\n+ tap0 IPv6 infix-c0-ff-ee SFTP File Transfer local\n+ tap0 IPv4 infix-c0-ff-ee SFTP File Transfer local\n+ tap0 IPv6 infix-c0-ff-ee SSH Remote Terminal local\n+ tap0 IPv4 infix-c0-ff-ee SSH Remote Terminal local\n= tap0 IPv4 infix-c0-ff-ee SFTP File Transfer local\n hostname = [infix-c0-ff-ee.local]\n address = [10.0.1.1]\n port = [22]\n txt = []\n= tap0 IPv4 infix-c0-ff-ee SSH Remote Terminal local\n hostname = [infix-c0-ff-ee.local]\n address = [10.0.1.1]\n port = [22]\n txt = []\n= tap0 IPv6 infix-c0-ff-ee SFTP File Transfer local\n hostname = [infix-c0-ff-ee.local]\n address = [fe80::ff:fec0:ffed]\n port = [22]\n txt = []\n= tap0 IPv6 infix-c0-ff-ee SSH Remote Terminal local\n hostname = [infix-c0-ff-ee.local]\n address = [fe80::ff:fec0:ffed]\n port = [22]\n txt = []\n^C\nlinux-pc:#\n Tip
The -t option is also very useful, it stops browsing automatically when a \"more or less complete list\" has been printed. However, some devices on the LAN may be in deep sleep so run the command again if you cannot find the device you are looking for.
Additionally, avahi-resolve-host-name can be used to verify domain name mappings for IP addresses. By default, it translates from IPv4 addresses. This function allows users to confirm that addresses are mapped correctly.
linux-pc:# avahi-resolve-host-name infix-c0-ff-ee.local\ninfix-c0-ff-ee.local 10.0.1.1\nlinux-pc:#\n Thanks to mDNS we can use the advertised name instead of the IP address for operations like ping and ssh as shown below:
linux-pc:# ping infix-c0-ff-ee.local -c 3\nPING infix-c0-ff-ee.local (10.0.1.1) 56(84) bytes of data.\n64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.852 ms\n64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=1.12 ms\n64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=1.35 ms\n\n--- infix-c0-ff-ee.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 0.852/1.105/1.348/0.202 ms\n\nlinux-pc:# ssh admin@infix-c0-ff-ee.local\n(admin@infix-c0-ff-ee.local) Password: \n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-\n\nRun the command 'cli' for interactive OAM\n\nlinux-pc:#\n To disable mDNS/mDNS-SD, type the commands:
admin@infix-c0-ff-ee:/> configure \nadmin@infix-c0-ff-ee:/config/> no mdns\nadmin@infix-c0-ff-ee:/config/> leave\n "},{"location":"discovery/#human-friendly-hostname-alias","title":"Human-Friendly Hostname Alias","text":"Each Infix deviuce advertise itself as infix.local, in addition to its full hostname (e.g., infix-c0-ff-ee.local or foo.local). This alias works seamlessly on a network with a single Infix device, and makes it easy to connect when the exact hostname is not known in advance. The examples below show how the alias can be used for actions such as pinging or establishing an SSH connection:
linux-pc:# ping infix.local -c 3\nPING infix.local (10.0.1.1) 56(84) bytes of data.\n64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.751 ms\n64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=2.28 ms\n64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=1.42 ms\n\n--- infix.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2003ms\nrtt min/avg/max/mdev = 0.751/1.482/2.281/0.626 ms\n\nlinux-pc:# ssh admin@infix.local\n(admin@infix.local) Password: \n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-c0-ff-ee:~$\n When multiple Infix devices are present on the LAN the alias will not uniquely identify a device; infix.local will refer to any of the Infix devices, likely the one that first appeared.
Note
When multiple Infix devices are present on the LAN, use the full name, e.g., infix-c0-ff-ee.local or foo.local rather than the alias infix.local to deterministically connect to the device.
"},{"location":"discovery/#browse-network-using-networklocal","title":"Browse Network Using network.local","text":"Another mDNS alias that all Infix devices advertise is network.local. This is a web service which basically runs avahi-browse and displays a table of other Infix devices and their services.
With multiple Infix devices on the LAN, one will take the role of your portal to access all others, if it goes down another takes its place.
To disable the netbrowse service, and the network.local alias, the following commands can be used:
admin@infix-c0-ff-ee:/> configure \nadmin@infix-c0-ff-ee:/config/> edit web\nadmin@infix-c0-ff-ee:/config/web/> no netbrowse\nadmin@infix-c0-ff-ee:/config/web/> leave\n E.g., lldpd which includes the lldpcli too, handy to sniff and display LLDP packets.\u00a0\u21a9
mdns-scan: a tool for scanning for mDNS/DNS-SD services on the local network.\u00a0\u21a9
"},{"location":"eth-counters/","title":"RMON Counters","text":""},{"location":"eth-counters/#rmon-counters","title":"RMON Counters","text":"This document show the mapping between YANG and Linux / Ethtool counters.
YANG Linux / Ethtool in-total-octets FramesReceivedOK, FrameCheckSequenceErrors, FramesLostDueToIntMACRcvError, AlignmentErrors, etherStatsOversizePkts, etherStatsJabbers in-frames FramesReceivedOK in-multicast-frames MulticastFramesReceivedOK in-broadcast-frames BroadcastFramesReceivedOK in-error-fcs-frames FrameCheckSequenceErrors in-error-undersize-frames undersize_pkts in-error-oversize-frames etherStatsJabbers, etherStatsOversizePkts in-error-mac-internal-frames FramesLostDueToIntMACRcvError out-frames FramesTransmittedOK out-multicast-frames MulticastFramesXmittedOK out-broadcast-frames BroadcastFramesXmittedOK infix-eth:out-good-octets OctetsTransmittedOK infix-eth:in-good-octets OctetsReceivedOK"},{"location":"firewall/","title":"Firewall Configuration","text":""},{"location":"firewall/#firewall-documentation","title":"Firewall Documentation","text":""},{"location":"firewall/#introduction","title":"Introduction","text":"A zone-based firewall aims to simplify network security. Instead of complex per-interface rules, you work with zones and policies. Briefly, zones define a level of trust for all interfaces or networks assigned to it, and policies regulate the traffic flow between zones.
Figure 1. Zones group interfaces, policies control traffic flows.
Three distinct traffic flows exist: traffic destined for the host itself, traffic between interfaces within the same zone (intra-zone), and traffic between different zones (inter-zones).
The zone approach is not just more intuitive and maintainable, it allows you to think more in terms of trust relationships:
\"internal networks can access the Internet\" \"Internet cannot access my internal network, except this port forward\" When you add new interfaces to existing zones, they automatically inherit the established security policies. The amount of actual rules that matter to you is kept to a minimum.
Impatient and ready to get going?
Fast forward to the Examples: End Device, Home/Office Router, Enterprise Gateway
"},{"location":"firewall/#visual-overview","title":"Visual Overview","text":"Use the zone matrix to quickly audit your firewall configuration and identify potential security gaps. It provides an overview and shows the relationship between zones and the policies that connect them. Each cell in the matrix represents a potential traffic flow, with rows indicating the ingress zone and columns the egress zone.
Figure 2. Example output from show firewall command.
The matrix uses visual indicators to show the status of each zone and policy:
\u2713 Green checkmark \u2014 traffic is explicitly allowed by policy \u2717 Red cross \u2014 traffic is blocked (default behavior) \u26a0 Yellow warning \u2014 watch out! Some traffic allowed, such as port forwarding rules
This visualization helps you quickly understand your firewall's behavior and identify any unintended gaps or overly permissive rules in your configuration.
Use the ? key in the CLI
This admin-exec (top-level) CLI command has sub-commands that you can use to drill down on the operational data. Tap the ? key once to see an overview after show firewall, or just use the classic UNIX Tab key to complete everything until you've found your command.
"},{"location":"firewall/#zones","title":"Zones","text":"Zones are logical groupings of network interfaces or IP networks that share the same trust level. Each zone has a default action that determines what happens to traffic destined for the host itself (INPUT chain). A LAN zone may have this set to accept, while a DMZ zone may be set to reject by default and only allow a subset of available services (e.g., DHCP, DNS, SSH) that devices in the DMZ can use to reach the host.
Important
Interfaces and networks are mutually exclusive in zones \u2014 attempting to configure both will result in a validation error. When setting up port forwarding from one zone to another, the destination network must be declared in a zone.
"},{"location":"firewall/#default-zone","title":"Default Zone","text":"You must specify a default zone. This serves as a safety net: any interface not explicitly assigned to a zone automatically belongs to the default zone, ensuring network interfaces remain protected by the firewall. This automatic assignment is particularly useful when configuring new interfaces (e.g., VLANs, bridges, or hotplugged devices).
Choose your default zone carefully \u2014 it should be the most restrictive zone appropriate for unmanaged interfaces. For routers, this is typically the wan zone, but you can of course also set up a dedicated block zone. In the CLI, when first enabling the firewall, a public zone is created. See more about this in the example below.
Remember IP forwarding on interfaces!
Firewall policies only control whether traffic is allowed on input, to be forwarded, or blocked (default). For the actual routing between interfaces to work, you must also enable IP forwarding on the relevant interfaces.
"},{"location":"firewall/#intra-zone-traffic","title":"Intra-Zone Traffic","text":"Traffic between different interfaces, or networks, in the same zone is not forwarded by default. In most cases, if devices on separate interfaces need to communicate, they should be in different zones with a policy between them. Alternatively, if you want true LAN-like behavior, bridge the interfaces at layer-2 instead of routing between them at layer-3.
Intra-zone forwarding \u2014 routing traffic within a single zone \u2014 is rarely needed. But if you do require it, create a policy where both ingress and egress are set to the same zone, e.g., lan \u2192 lan.
"},{"location":"firewall/#port-forwarding","title":"Port Forwarding","text":"Port forwarding, also known as destination NAT (DNAT), redirects inbound traffic to another address and/or port. This allows external access to internal services. See Enterprise Gateway for an example.
Each zone can have port forwarding rules that apply to traffic arriving at that zone's interfaces or matching its networks. The forwarded traffic must then be allowed by appropriate policies to reach the destination zone.
The Zone Matrix shows a \u26a0 conditional warning flag, coloring the cell yellow, when exceptions like port forwarding are active.
"},{"location":"firewall/#policies","title":"Policies","text":"In short, policy rules control traffic between zones. By default all inter-zone (and intra-zone) traffic is rejected. Meaning you must explicitly allow the traffic flows you intend.
Figure 3. Example of common traffic flows (policies) between zones.
IP masquerading (SNAT) is a policy setting that applies to traffic egressing a target zone. (Essential for Internet access from private networks.)
A policy, like zones, have a default action. If it is not set to accept you must specify which services on the host any zone interface and network are allowed access to.
Note
Policy rules apply in-order, the first matching rule with action drop will terminate the traffic flow. Use action continue to allow processing to go to the next rule, until the last (implicit) default-drop rule at the end.
The CLI currently does not support reordering rules. As a workaround, save your running-config to startup-config, then exit to the shell and edit the file with edit /cfg/startup-config.cfg.
See the examples below for how to set up a policy. The built-in help system can also be useful:
admin@example:/config/firewall/policy/lan-to-dmz/> help masquerade\nNAME\n masquerade \nDESCRIPTION\n Enable masquerading (SNAT) for traffic matching this policy.\n Matching traffic will have their source IP address changed on egress,\n using the IP address of the interface the traffic egresses.\";\nadmin@example:/config/firewall/policy/lan-to-dmz/>\n
"},{"location":"firewall/#symbolic-names","title":"Symbolic Names","text":"The symbolic names HOST and ANY are available for use in both ingress and egress zones. In fact, the CLI uses inference when first enabling the firewall to inject a default policy to allow automatic IPv6 address assignment.
HOST \u2192 ANY: Control device to any outbound connection (default: allowed) ANY \u2192 HOST: Control what can reach device services (uncommon, usually per-zone) Zone \u2192 HOST: Allow specific zone to access device services "},{"location":"firewall/#custom-filters","title":"Custom Filters","text":"For more advanced firewall scenarios custom filters can be used. Currently only various ICMP type traffic control is supported. Enough to support the built-in allow-host-ipv6 policy and allow certain ICMP types on input or forward.
You can inspect this built-in (locked) policy from admin-exec level with the command: show firewall policy allow-host-ipv6.
"},{"location":"firewall/#default-behavior","title":"Default Behavior","text":"ICMP messages (particularly echo-request and echo-reply) can be used to reveal network information for malicious purposes. Therefore, the firewall blocks ICMP requests by default. This applies unless the zone's default action is accept.
To enable echo-request (IPv4) for any interface, or per zone when action is set to drop or reject, set up a dedicated policy with ingress ANY and egress HOST that use a custom filter to accept that ICMP type. Make this policy the first rule in your list of policies, and remember to use continue for non-matching traffic.
Another built-in behavior is automatically allowing \"established,related\" return traffic flows. This uses connection tracking - the firewall remembers outbound connections you initiate and automatically allows the corresponding inbound response traffic. This means you only need to configure rules for new connections; the firewall handles return traffic automatically without additional rules.
"},{"location":"firewall/#services","title":"Services","text":"Several pre-defined services exist, that cover most use-cases, but you can also define custom services for applications not covered by the built-in ones.
The firewall includes over 100 pre-defined services, such as:
ssh \u2014 Secure Shell (port 22/tcp) http \u2014 Web traffic (port 80/tcp) https \u2014 Secure web traffic (port 443/tcp) dns \u2014 Domain Name System (port 53/tcp and 53/udp) dhcp \u2014 DHCP server (port 67/udp) dhcpv6-client \u2014 DHCPv6 client traffic netconf \u2014 Network Configuration Protocol (port 830/tcp) restconf \u2014 REST-based Network Configuration Protocol (port 443/tcp) Use the ? key in the CLI
See the YANG model for the full list, or tap the ? key when setting up an allowed host service in a zone set service
"},{"location":"firewall/#examples","title":"Examples","text":""},{"location":"firewall/#end-device-protection","title":"End Device Protection","text":"This is the default firewall setup, useful for end devices on untrusted networks. It provides maximum protection while allowing essential connectivity.
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> show\ndefault public;\nzone public {\n action reject;\n description \"Public, unknown network. Only SSH and DHCPv6 client allowed.\";\n service dhcpv6-client;\n service ssh;\n}\nadmin@example:/config/firewall/> leave\n
The reject action differs from drop in that it responds to ICMP messages, although maybe not how you may think. Pinging the device we may1 see this:
$ ping 192.168.122.161\nFrom 192.168.122.161 icmp_seq=1 Packet filtered\n
If we run tcpdump it shows us why:
$ tcpdump -lni eth0\n20:10:40.245707 IP 192.168.122.1 > 192.168.122.161: ICMP echo request, id 56838, seq 1, length 64\n20:10:40.245961 IP 192.168.122.161 > 192.168.122.1: ICMP host 192.168.122.161 unreachable - admin prohibited filter, length 92\n
The key here is that, yes the device responds, but not with ICMP reply but ICMP unreachable, and a little helpful message.
The default zone is public, so all interfaces that are not explicitly assigned to another zone will be operationally placed in this zone as a safeguard. Inspect this from admin-exec context with show firewall, as can be seen in the below screenshot, the only interface e1 has been assigned automatically to the public zone. This information is also saved to the system log.
Figure 4. Zone matrix and firewall overview from show firewall.
Important
These defaults are inferred for interactive CLI users. Enabling the firewall using NETCONF/RESTCONF will not yield the same results.
"},{"location":"firewall/#homeoffice-router","title":"Home/Office Router","text":"For typical routers that need to protect internal devices while providing internet access. The LAN zone trusts internal devices, while the WAN zone blocks external threats.
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> set default wan\nadmin@example:/config/firewall/> edit zone lan\nadmin@example:/config/firewall/\u2026/lan/> set description \"Internal LAN network - trusted\"\nadmin@example:/config/firewall/\u2026/lan/> set action accept\nadmin@example:/config/firewall/\u2026/lan/> set interface eth1\nadmin@example:/config/firewall/\u2026/lan/> set service ssh\nadmin@example:/config/firewall/\u2026/lan/> set service dhcp\nadmin@example:/config/firewall/\u2026/lan/> set service dns\nadmin@example:/config/firewall/\u2026/lan/> end\nadmin@example:/config/firewall/> edit zone wan\nadmin@example:/config/firewall/\u2026/wan/> set description \"External WAN interface - untrusted\"\nadmin@example:/config/firewall/\u2026/wan/> set action drop\nadmin@example:/config/firewall/\u2026/wan/> set interface eth0\nadmin@example:/config/firewall/\u2026/wan/> end\nadmin@example:/config/firewall/> edit policy loc-to-wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set description \"Allow LAN traffic to WAN with SNAT\"\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set ingress lan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set egress wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set action accept\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set masquerade\nadmin@example:/config/firewall/\u2026/loc-to-wan/> leave\n
"},{"location":"firewall/#enterprise-gateway","title":"Enterprise Gateway","text":"For businesses that need to host public services while protecting internal resources. We can build upon the Home/Office Router example above and add a DMZ zone with additional policies for controlled access.
admin@example:/> configure\nadmin@example:/config/> edit firewall zone dmz\nadmin@example:/config/firewall/\u2026/dmz/> set description \"Semi-trusted public services\"\nadmin@example:/config/firewall/\u2026/dmz/> set action drop\nadmin@example:/config/firewall/\u2026/dmz/> set interface eth1\nadmin@example:/config/firewall/\u2026/dmz/> set service ssh\nadmin@example:/config/firewall/\u2026/dmz/> end\nadmin@example:/config/firewall/> edit policy loc-to-wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set description \"Allow local networks (LAN+DMZ) to WAN with SNAT\"\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set ingress dmz\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set egress wan\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set action accept\nadmin@example:/config/firewall/\u2026/loc-to-wan/> set masquerade\nadmin@example:/config/firewall/\u2026/loc-to-wan/> end\nadmin@example:/config/firewall/> edit policy lan-to-dmz\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set description \"Allow LAN to manage DMZ services\"\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set ingress lan\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set egress dmz\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> set action accept\nadmin@example:/config/firewall/\u2026/lan-to-dmz/> end\nadmin@example:/config/firewall/> edit zone wan port-forward 8080 tcp\nadmin@example:/config/firewall/\u2026/tcp/> set to addr 192.168.2.10\nadmin@example:/config/firewall/\u2026/tcp/> set to port 80\nadmin@example:/config/firewall/\u2026/tcp/> leave\n
This adds a DMZ zone for public services, updates the internet access policy to include DMZ traffic, allows LAN management of DMZ services, and forwards external web traffic to the DMZ server.
"},{"location":"firewall/#logging-and-monitoring","title":"Logging and Monitoring","text":"Different log levels are available to monitor and debug firewall behavior. Configure logging using the CLI:
admin@example:/> configure\nadmin@example:/config/> edit firewall\nadmin@example:/config/firewall/> set logging all\nadmin@example:/config/firewall/> leave\n
Firewall logs help you understand traffic patterns and security events. The CLI admin-exec command show firewall shows the last 10 log messages in the overview:
Figure 5. Summary of recent logs at the end of show firewall.
Use the command show log firewall.log to display the full logfile (remember, the syslog daemon rotates and zips too big log files). You can also use the follow firewall.log command to continuously monitor firewall log messages.
"},{"location":"firewall/#netfilter-integration","title":"Netfilter Integration","text":"The Infix firewall operates through Linux netfilter hooks. Understanding how the zones and policy concepts map to these hooks will hopefully help you understand the firewall's behavior and ease troubleshooting.
"},{"location":"firewall/#packet-flow","title":"Packet Flow","text":"Figure 6. Linux netfilter hooks in layer-3 traffic flow.
Netfilter Hook Function Description prerouting ZONE Classification of incoming traffic, match interfaces/networks with zones prerouting ZONE Port forwarding (DNAT) from zone configuration input ZONE Host input filtering (services) input ZONE Default action for non-matching services (action) forward POLICY Allow traffic between zones (inter-zone rules) postrouting POLICY Masquerade (SNAT) when traffic egresses a zone"},{"location":"firewall/#prerouting-hook","title":"PREROUTING Hook","text":" Zone Classification: Traffic is tagged based on ingress interface or source network Port Forwarding: DNAT from zone configuration occurs before routing decisions Connection Tracking: Early state establishment for stateful filtering "},{"location":"firewall/#input-hook","title":"INPUT Hook","text":" ANY-to-HOST Policies: Enforces policy rules for traffic destined to the host itself Zone Services: Allows configured services (SSH, HTTP, etc.) based on zone trust level Zone Action: Applies a default action (accept/reject/drop) for unmatched traffic "},{"location":"firewall/#forward-hook","title":"FORWARD Hook","text":" Policy Enforcement: Primary location for inter-zone traffic filtering Custom Filters: ICMP and other protocol-specific rules within policies Service Matching: Allows or denies services based on policy configuration "},{"location":"firewall/#postrouting-hook","title":"POSTROUTING Hook","text":" Masquerading: Source NAT for outbound traffic when policies enable masquerading "},{"location":"firewall/#emergency-lockdown","title":"Emergency Lockdown","text":"For security emergencies (active breaches, suspicious activity), the firewall supports an immediate lockdown mode that blocks ALL traffic.
Danger
This will immediately terminate all network connections, including SSH. Physical console access is required to restore normal operation. It is also possible to restore normal operation by power-cycling the device.
To activate emergency lockdown:
~$ curl -kX POST -u admin:admin -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"infix-firewall:input\": {\"operation\": \"now\"}}' \\\n https://example.local/restconf/operations/infix-firewall:firewall/lockdown-mode\n To cancel lockdown mode (requires console access):
~$ curl -kX POST -u admin:admin -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"infix-firewall:input\": {\"operation\": \"cancel\"}}' \\\n https://example.local/restconf/operations/infix-firewall:firewall/lockdown-mode\n You can check the current lockdown state:
~$ curl -kX GET -u admin:admin -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/infix-firewall:firewall/lockdown\n{\n \"infix-firewall:firewall\": {\n \"lockdown\": false\n }\n}\n The output from ping clients differ A LOT. Some do not consider ICMP unreachable to be a proper response and it will appear as if the device is not responding at all. Use tcpdump or wireshark to get to the bottom of network mysteries.\u00a0\u21a9
"},{"location":"hardware/","title":"Hardware Info & Status","text":""},{"location":"hardware/#hardware-information-and-status","title":"Hardware Information and Status","text":"The hardware infomation and status is handled by the YANG model IETF hardware, with deviations and augmentations in infix-hardware.
"},{"location":"hardware/#usb-ports","title":"USB Ports","text":"For Infix to be able to control USB port(s), a device tree modification is needed (see alder.dtsi for full example).
chosen {\n infix {\n usb-ports = <&cp0_usb3_1>;\n usb-port-names = \"USB\";\n };\n };\n Two USB ports are also exposed in QEMU for test purpose.
All USB ports in the system will be disabled during boot due to the file board/common/rootfs/etc/modprobe.d/usbcore.conf. If you do not want Infix to control USB port(s), remove the file or manually enable the USB bus, here is an example:
# Enable the bus\necho 1 > /sys/bus/usb/devices/usb1/authorized\n And then enable sub-devices (e.g. USB memory)
# Enable a device plugged into usb1\necho 1 > /sys/bus/usb/devices/usb1/1-1/authorized\n "},{"location":"hardware/#current-status","title":"Current status","text":"admin@example:/> show hardware\n USB PORTS\n NAME STATE\n USB unlocked\n An USB port can be in two states unlocked and locked. When a port is locked, all connected devices will get power, but never authorized by Linux to use.
"},{"location":"hardware/#configure-usb-port","title":"Configure USB port","text":"Note
You can only configure USB ports known to the system. See the CLI command show hardware in admin-exec context. (Use do prefix in configure context.)
admin@example:/> configure\nadmin@example:/config/> set hardware component USB state admin-state unlocked\nadmin@example:/config/> leave\nadmin@example:/>\n "},{"location":"hardware/#using-a-usb-stick","title":"Using a USB Stick","text":"With the USB port unlocked, a memory stick can be used to expand the storage of the device. Plug it in, either directly, or via a USB hub. All partitions of VFAT or exFAT type are automatically mounted to the /media/<LABLEL> directory, e.g., /media/memorystick, or if a partition does not have a label, using the kernel device name, e.g., /media/sda.
Depending on the partition type the the media is either mounted in flush or sync mode to ensure files are properly flushed to the backing store of the memory stick. This to protect against users yanking out a device before file(s) have been written to it.
The only way currently to safely \"eject\" a USB memory stick is to use umount command from a UNIX shell, which explicitly synchronizes any cached data to disk before returning the prompt:
admin@example:~$ sudo umount /media/log\n "},{"location":"license/","title":"Origin & Licensing","text":""},{"location":"license/#origin-licensing","title":"Origin & Licensing","text":"Infix is entirely built on Open Source components (packages). Most of them, as well as the build system with its helper scripts and tools, is from Buildroot, which is distributed under the terms of the GNU General Public License (GPL). See the file COPYING for details.
Some files in Buildroot may contain a different open source license statement. Those files are licensed under the license contained in the file itself.
Buildroot and Infix also bundle patch files, which are applied to the sources of the various packages. Those patches are not covered by the license of Buildroot or Infix. Instead, they are covered by the license of the software to which the patches are applied. When said software is available under multiple licenses, the patches are only provided under the publicly accessible licenses.
Infix releases include the license information covering all Open Source packages. This is extracted automatically at build time using the tool make legal-info. Any proprietary software built on top of Infix, or Buildroot, would need separate auditing to ensure it does not link with any GPL1 licensed library.
Infix image builds use GNU libc (GLIBC) which is covered by the LGPL. The LGPL does allow proprietary software, as long as said software is linking dynamically, not statically, to GLIBC.\u00a0\u21a9
"},{"location":"management/","title":"Management","text":""},{"location":"management/#system-management","title":"System Management","text":"The system utilizes YANG models for keeping configuration and operational data. These databases can be managed through different interfaces such as NETCONF, RESTCONF, and CLI via SSH or Console.
"},{"location":"management/#ssh-management","title":"SSH Management","text":"An SSH server (SSHv2) is provided for remote management. It can be enabled/disabled as shown below.
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> set enabled\nadmin@example:/config/ssh/>\n By default the SSH server accepts connections to port 22 on all its IP addresses, but this can be adjusted using the listen command. To make the server (only) listen for incoming connections to IP address 192.168.1.1 and port 12345 the following commands can be used.
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> show\nenabled true;\nhostkey genkey;\nlisten ipv4 {\n address 0.0.0.0;\n port 22;\n}\nlisten ipv6 {\n address ::;\n port 22;\n}\nadmin@example:/config/ssh/> no listen ipv6\nadmin@example:/config/ssh/> edit listen ipv4\nadmin@example:/config/ssh/listen/ipv4/> set address 192.168.1.1\nadmin@example:/config/ssh/listen/ipv4/> set port 12345\nadmin@example:/config/ssh/listen/ipv4/>\n The default SSH hostkey is generated on first boot and is used in both SSH and NETCONF (SSH transport). Custom keys can be added to the configuration in ietf-keystore. The only supported hostkey type is 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
"},{"location":"management/#use-your-own-ssh-hostkeys","title":"Use your own SSH hostkeys","text":"Hostkeys can be generated with OpenSSL:
openssl genpkey -quiet -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform PEM > mykey\nopenssl rsa -RSAPublicKey_out < mykey > mykey.pub\n Store the keys in ietf-keystore without the header and footer information created by OpenSSL.
After the key has been stored in the keystore and given the name mykey it can be added to SSH configuration:
admin@example:/> configure\nadmin@example:/config/> edit ssh\nadmin@example:/config/ssh/> set hostkey mykey\n "},{"location":"management/#console-port","title":"Console Port","text":"For units with a console port, it is possible for users to login to shell/CLI with functionality similar to what is provided via SSH.
The type and setup for your console port is product specific. For instance, it can be a USB-C port connected to the CPU serial port using a USB-to-serial converter. To connect you would need a USB-C cable connected to the console port of the device. The serial port is typically setup to run at 115200 baud, 8N1.
Infix OS \u2014 Immutable.Friendly.Secure v24.11.1 (ttyS0)\nexample login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$\n The resize command can be used to update terminal settings to the size of your terminal window.
admin@example:~$ resize\nCOLUMNS=115;LINES=59;export COLUMNS LINES;\nadmin@example:~$\n CLI can be entered from shell in the same way as for SSH.
admin@example:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet LOWER-DOWN 00:53:00:06:03:01\ne2 ethernet LOWER-DOWN 00:53:00:06:03:02\n...\nadmin@example:/>\n "},{"location":"management/#web-web-console-and-restconf","title":"Web, Web-console and RESTCONF","text":"The system provides a set of Web services:
a rudimentary Web server, currently limited to an information page a RESTCONF server with equivalent management capabilities as NETCONF a Web console service, where the shell/CLI can be accessed via HTTPS, similar to connecting via a console port or SSH There is also a Netbrowse Web service presenting information about the unit's neighbors, collected via mDNS (see Discovery for more details).
admin@example:/> configure\nadmin@example:/config/> edit web\nadmin@example:/config/web/> help\n enabled Enable or disable on all web services.\n console Web console interface.\n netbrowse mDNS Network Browser.\n restconf IETF RESTCONF Server.\nadmin@example:/config/web/>\n "},{"location":"management/#enabledisable-web-service-and-server","title":"Enable/disable Web Service and Server","text":"The Web service can be enabled as shown below.
admin@example:/> configure\nadmin@example:/config/> edit web\nadmin@example:/config/web/> set enabled\nadmin@example:/config/web/> \n Enabling the Web service implies that a Web server is enabled. Currently this Web server provides generic Infix information, as well as a link to a Web console. The Web server uses HTTPS; any HTTP request is redirected to HTTPS.
The enabled setting for the Web service acts as a global enable/disable setting for the other Web services (Web console, RESTCONF and Netbrowse).
"},{"location":"management/#enabledisable-web-console","title":"Enable/disable Web Console","text":"The Web console service provides a terminal service similar to Console or SSH. The Web console is secured via HTTPS on port 7861.
The Web console has its own enable/disable setting, but will only be activated if the Web service is enabled. The example below shows how to disable the Web console.
admin@example:/config/web/> edit console\nadmin@example:/config/web/console/> no enabled\nadmin@example:/config/web/console/>\n "},{"location":"management/#enabledisable-restconf-service","title":"Enable/disable RESTCONF Service","text":"Alternatively, the system can be managed remotely using RESTCONF. Meaning you can curl it instead of using a dedicated NETCONF client.
The RESTCONF service has its own enable/disable setting, but will only be activated if the Web service is enabled. The example below shows how to disable the RESTCONF service.
admin@example:/config/web/> edit restconf\nadmin@example:/config/web/restconf/> no enabled\nadmin@example:/config/web/restconf/>\n "},{"location":"management/#system-upgrade","title":"System Upgrade","text":"See Upgrade & Boot Order for information on upgrading.
"},{"location":"netboot/","title":"Netboot HowTo","text":""},{"location":"netboot/#netboot-howto","title":"Netboot HowTo","text":"This document describes how to set up network booting U-Boot devices on a LAN, e.g., when working with an evaluation board or other embedded system. The most secure way to do this is with a local LAN between a PC and the device.
Instead of setting up everything in U-Boot to download the Linux Image, device tree, and initramfs, we will let U-Boot download a script with instructions to run. When you have multiple systems (boards) this quickly becomes a lot easier to manage.
Note
Instructions in this HowTo assume a Debian based development system, e.g., Ubuntu or Linux Mint.
"},{"location":"netboot/#network-interface-setup","title":"Network Interface Setup","text":"For two dedicated network interfaces, here eth2 and eth3 (ymmv), we create an old-style interfaces config1 with the following content:
# /etc/network/interfaces.d/gimli\nauto eth2\niface eth2 inet static\n address 192.168.0.1\n netmask 255.255.255.0\n\nauto eth3\niface eth3 inet manual\n Tip
Use configuration file names in .d/ directories that make sense and can easily be remembered. Here we use the hostname of the PC.
"},{"location":"netboot/#dhcptftp-server-setup","title":"DHCP/TFTP Server Setup","text":"The examples given here use dnsmasq, which provides both DHCP and TFTP server support. The same can be achieved with other implementations.
Similar to interfaces.d, dnsmasq has an /etc/dnsmasq.d directory so we can use \"snippets\" instead of modifying /etc/dnsmasq.conf directly. Add a file called /etc/dnsmasq.d/gimli.
Initial content:
# Remember IP address handed out to BOOTP clients\nbootp-dynamic\n# Disable check-if-ip-address-is-already-used\nno-ping\n\n# Enable TFTP server, use /srv/ftp, same as any FTP server, useful\n# when using the same images for system upgrade as for netbooting.\nenable-tftp\ntftp-root=/srv/ftp\n Caution
First of all, make sure you DO NOT accidentally set up dnsmasq so that it starts acting as a DHCP server also on your office LAN! Follow the instructions below for more details.
If you have many interfaces used for lab equipment and only one office LAN interface, then use something like this:
# Disable DHCP server on loopback and office LAN (eth0)\nexcept-interface=lo\nexcept-interface=eth0\n To further lock this down, we only run the DHCP server on each of the interfaces used for lab equipment, with a dedicated IP range as well:
# Currently I have an imx8mp-evk on eth2, so on my system I have a\n# symlink bootfile-eth2 -> netboot.scr\ninterface=eth2\ndhcp-range=192.168.0.100,192.168.0.199,1h\ndhcp-boot=tag:eth2,bootfile-eth2\n "},{"location":"netboot/#bootfile-netbootscr","title":"Bootfile netboot.scr","text":"The bootfile U-Boot retrieves from the TFTP server is a script that looks like this, netboot.sh:
setenv ramdisk_addr_r 0x58000000\nsetenv fdt_addr_r 0x50400000\n\nsetenv autoboot off\ntftp ${fdt_addr_r} imx8mp-evk/imx8mp-evk.dtb\ntftp ${kernel_addr_r} imx8mp-evk/Image\ntftp ${ramdisk_addr_r} imx8mp-evk/rootfs.squashfs\n\nsetenv bootargs console=ttymxc1,115200 root=/dev/ram0 brd.rd_size=500000 rauc.slot=net\nbooti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}\n U-Boot cannot read script files directly, so we need to wrap it with a FIT format header, this is done by first converting it on the PC:
$ mkimage -T script -d netboot.sh netboot.scr\n The output is netboot.scr which we symlink to above in the dnsmasq setup step.
"},{"location":"netboot/#u-boot-commands","title":"U-Boot Commands","text":"U-Boot is a maze of environment variables, some with values, some wrap commands, and most are undocumented. We will use a prefix for our variables to ensure we do not overwrite anything you may want to use later.
==> setenv ixboot 'dhcp && source \\${fileaddr}'\n==> saveenv\n To prevent NetworkManager from automatically managing the interfaces.\u00a0\u21a9
"},{"location":"networking/","title":"Network Configuration","text":""},{"location":"networking/#network-configuration","title":"Network Configuration","text":"Infix aims to support all Linux Networking constructs. The YANG models used to describe the system are chosen to fit well and leverage the underlying Linux kernel's capabilities. The ietf-interfaces.yang model forms the base, extended with ietf-ip.yang and other layer-3 IETF models. The layer-2 bridge and aggregate models are defined by Infix to exploit the unique features not available in IEEE models.
Important
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"networking/#interface-lego","title":"Interface LEGO\u00ae","text":"The network building blocks available in Linux are akin to the popular LEGO\u00ae bricks.
There are two types of relationships that can link two blocks together:
Lower-to-upper: Visually represented by an extruding square connected upwards to a square socket. An interface can only have a single lower-to-upper relationship, i.e., it can be attached to a single upper interface like a bridge or a LAG. In iproute2 parlance, this corresponds to the interface's master setting Upper-to-lower: Visually represented by an extruding semicircle connected downwards to a semicircle socket. The lower interface in these relationships accepts multiple upper-to-lower relationships from different upper blocks. E.g., multiple VLANs and IP address blocks can be connected to the same lower interface
An interface may simultaneously have a lower-to-upper relation to some other interface, and be the target of one or more upper-to-lower relationships. It is valid, for example, for a physical port to be attached to a bridge, but also have a VLAN interface stacked on top of it. In this example, traffic assigned to the VLAN in question would be diverted to the VLAN interface before entering the bridge, while all other traffic would be bridged as usual.
Type Yang Model Description bridge infix-if-bridge SW implementation of an IEEE 802.1Q bridge ip ietf-ip, infix-ip IP address to the subordinate interface vlan infix-if-vlan Capture all traffic belonging to a specific 802.1Q VID lag infix-if-lag Link aggregation, static and IEEE 802.3ad (LACP) lo ietf-interfaces Software loopback interface eth ieee802-ethernet-interface Physical Ethernet device/port. infix-ethernet-interface veth infix-if-veth Virtual Ethernet pair, typically one end is in a container common ietf-interfaces, Properties common to all interface types infix-interfaces"},{"location":"networking/#data-plane","title":"Data Plane","text":"The blocks you choose, and how you connect them, defines your data plane. Here we see an example of how to bridge a virtual port with a physical LAN.
Depending on the (optional) VLAN filtering of the bridge, the container may have full or limited connectivity with outside ports, as well as the internal CPU.
In fact the virtual port connected to the bridge can be member of several VLANs, with each VLAN being an interface with an IP address inside the container.
Thanks to Linux, and technologies like switchdev, that allow you to split a switching fabric into unique (isolated) ports, the full separation and virtualization of all Ethernet layer properties are possible to share with a container. Meaning, all the building blocks used on the left hand side can also be used freely on the right hand side as well.
"},{"location":"networking/#general","title":"General","text":"General interface settings include type, enable, custom MAC address, and text description. Other settings have their own sections, below.
The type is important to set when configuring devices remotely because unlike the CLI, a NETCONF or RESTCONF session cannot guess the interface type for you. The operating system provides an override of the available interface types.
An enabled interface can be inspected using the operational datastore, nodes admin-state and oper-state show the status, . Possible values are listed in the YANG model.
The custom-phys-address can be used to set an interface's MAC address. This is an extension to the ietf-interfaces YANG model, which defines phys-address as read-only4 . The following shows the different configuration options.
The description is saved as Linux ifalias on an interface. It is a free-form string, useful for describing purpose or just adding comments for remote debugging, e.g., using the operational datastore.
Caution
There is no validation or safety checks performed by the system when using custom-phys-address. In particular the offset variant can be dangerous to use -- pay attention to the meaning of bits in the upper-most octet: local bit, multicast/group, etc.
"},{"location":"networking/#fixed-custom-mac","title":"Fixed custom MAC","text":"admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address static 00:ab:00:11:22:33\n\n=> 00:ab:00:11:22:33\n "},{"location":"networking/#chassis-mac","title":"Chassis MAC","text":"Chassis MAC, sometimes also referred to as base MAC. In these two examples it is 00:53:00:c0:ff:ee.
admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address chassis\n\n=> 00:53:00:c0:ff:ee\n "},{"location":"networking/#chassis-mac-with-offset","title":"Chassis MAC, with offset","text":"When constructing a derived address it is recommended to set the locally administered bit. Same chassis MAC as before.
admin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set custom-phys-address chassis offset 02:00:00:00:00:02\n\n=> 02:53:00:c0:ff:f0\n "},{"location":"networking/#bridging","title":"Bridging","text":"This is the most central part of the system. A bridge is a switch, and a switch is a bridge. In Linux, setting up a bridge with ports connected to physical switch fabric, means you manage the actual switch fabric!
"},{"location":"networking/#mac-bridge","title":"MAC Bridge","text":"In Infix ports are by default not switch ports, unless the customer specific factory config sets it up this way. To enable switching, with offloading if you have a switch chipset, between ports you create a bridge and then add ports to that bridge. Like this:
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> up\nadmin@example:/config/> set interface eth0 bridge-port bridge br0\nadmin@example:/config/> set interface eth1 bridge-port bridge br0\nadmin@example:/config/> leave\n Here we add two ports to bridge br0: eth0 and eth1.
Tip
The CLI has several built-in helpers governed by convention. E.g., naming bridges brN, where N is a number, the type is inferred automatically and unlocks all bridge features. Other conventions are vethNA, where N is a number and A is a letter ('a' for access port and 'b' for bridge side is common), and ethN.M for VLAN M on top of ethN, or dockerN for a IP masquerading container bridge.
Note, this inference only works with the CLI, configuring networking over NETCONF or RESTCONF requires setting the type explicitly.
It is possible to create multiple MAC bridges, however, it is currently5 not recommended to use more than one MAC bridge on products with Marvell LinkStreet switching ASICs. A VLAN filtering bridge should be used instead.
"},{"location":"networking/#vlan-filtering-bridge","title":"VLAN Filtering Bridge","text":"By default bridges in Linux do not filter based on VLAN tags. This can be enabled when creating a bridge by adding a port to a VLAN as a tagged or untagged member. Use the port default VID (PVID) setting to control VLAN association for traffic ingressing a port untagged (default PVID: 1).
admin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> up\nadmin@example:/config/> set interface eth0 bridge-port bridge br0\nadmin@example:/config/> set interface eth0 bridge-port pvid 10\nadmin@example:/config/> set interface eth1 bridge-port bridge br0\nadmin@example:/config/> set interface eth1 bridge-port pvid 20\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 10 untagged eth0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 20 untagged eth1\n This sets eth0 as an untagged member of VLAN 10 and eth1 as an untagged member of VLAN 20. Switching between these ports is thus prohibited.
To terminate a VLAN in the switch itself, either for switch management or for routing, the bridge must become a (tagged) member of the VLAN.
admin@example:/config/interface/br0/> set bridge vlans vlan 10 tagged br0\nadmin@example:/config/interface/br0/> set bridge vlans vlan 20 tagged br0\n To route or to manage via a VLAN, a VLAN interface needs to be created on top of the bridge, see section VLAN Interfaces below for more on this topic.
Note
In some use-cases only a single management VLAN on the bridge is used. For the example above, if the bridge itself is an untagged member only in VLAN 10, IP addresses can be set directly on the bridge without the need for dedicated VLAN interfaces on top of the bridge.
"},{"location":"networking/#multicast-filtering-and-snooping","title":"Multicast Filtering and Snooping","text":"Multicast filtering in the bridge is handled by the bridge itself. It can filter both IP multicast and MAC multicast. For IP multicast it also supports \"snooping\", i.e., IGMP and MLD, to automatically reduce the broadcast effects of multicast. See the next section for a summary of the terminology used.
Important
Currently there is no way to just enable multicast filtering without also enabling snooping. This may change in the future, in which case a filtering enabled setting will be made available along with the existing snooping setting.
When creating your bridge you must decide if you need a VLAN filtering bridge or a plain bridge (see previous section). Multicast filtering is supported for either, but take note that it must be enabled and set up per VLAN when VLAN filtering is enabled -- there are no global multicast settings in this operating mode.
In the following example we have a regular 8-port bridge without VLAN filtering. We focus on the multicast specific settings:
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> set bridge multicast snooping\nadmin@example:/config/interface/br0/> set ipv4 address 192.168.2.1 prefix-length 24\nadmin@example:/config/interface/br0/> leave\nadmin@example:/> copy running-config startup-config\n Here we enable snooping and set a static IPv4 address so that the switch can take part in IGMP querier elections. (MLD querier election currently not supported.) We can inspect the current state:
admin@example:/> show ip multicast\nMulticast Overview\nQuery Interval (default): 125 sec\nRouter Timeout : 255\nFast Leave Ports :\nRouter Ports :\nFlood Ports : e0, e1, e2, e3, e4, e5, e6, e7\n\nInterface VID Querier State Interval Timeout Ver\nbr0 192.168.2.1 Up 125 None 3\n\nBridge VID Multicast Group Ports\nbr0 224.1.1.1 e3, e2\nbr0 ff02::6a br0\n This is a rather small LAN, so our bridge has already become the elected IGMP querier. We see it is ours because the timeout is None, and we recognize the IP address the system has detected, as ours. We can also see two ports that have joined the same IPv4 multicast group, 224.1.1.1, and one join from the system itself for the IPv6 group ff02::6a.
Now, let us see what happens when we add another bridge, this time with VLAN filtering enabled. We skip the boring parts about how to move ports e4-e7 to br1 and assign them to VLANs, and again, focus on the multicast bits only:
admin@example:/> configure\nadmin@example:/config/> edit interface br1\nadmin@example:/config/interface/br1/> set bridge vlans vlan 1 multicast snooping\nadmin@example:/config/interface/br1/> set bridge vlans vlan 2 multicast snooping\nadmin@example:/config/interface/br1/> leave\nadmin@example:/> copy running-config startup-config\n Let us see what we get:
admin@example:/> show ip multicast\nMulticast Overview\nQuery Interval (default): 125 sec\nRouter Timeout : 255\nFast Leave Ports : e5\nRouter Ports : e1, e2, e5, e6, e7\nFlood Ports : e1, e2, e3, e4, e5, e6, e7, e8\n\nInterface VID Querier State Interval Timeout Ver\nbr0 192.168.2.1 Up 125 None 3\nbr1 1 0.0.0.0 Up 125 None 3\nbr1 2 0.0.0.0 Up 125 None 3\n\nBridge VID Multicast Group Ports\nbr0 224.1.1.1 e2\nbr0 ff02::fb br0\nbr0 ff02::6a br0\nbr0 ff02::1:ff00:0 br0\nbr1 1 224.1.1.1 e5\nbr1 2 224.1.1.1 e7\nbr1 1 ff02::fb br1\nbr1 1 ff02::1:ff00:0 br1\n In this setup we have a lot more going on. Multiple multicast router ports have been detected, and behind the scenes someone has also added an IGMP/MLD fast-leave port.
"},{"location":"networking/#terminology-abbreviations","title":"Terminology & Abbreviations","text":" IGMP: Internet Group Membership Protocol, multicast subscription for IPv4, for details see RFC3376 MLD: Multicast Listener Discovery (Protocol), multicast subscription for IPv6, for details see RFC3810 Unknown/Unregistered multicast: multicast groups that are not in the multicast forwarding database (MDB) Known/Registered multicast: multicast groups that are in the multicast forwarding database (MDB) MDB: the multicast forwarding database, consists of filters for multicast groups, directing where multicast is allowed to egress. A filter entry consists of a group and a port list. The bridge filters with a unique database per VLAN, in the same was as the unicast FDB Join/Leave: the terminology used in earlier versions of the two protocols to subscribe and unsubscribe to a multicast group. For more information, see Membership Report Membership Report A membership report is sent by end-devices and forwarded by switches to the elected querier on the LAN. They consist of multiple \"join\" and \"leave\" operations on groups. They can also, per group, list which senders to allow or block. Switches usually only support the group subscription, and even more common also only support filtering on the MAC level3 Querier election: the process of determining who is the elected IGMP/MLD querier on a LAN. Lowest numerical IP address wins, the special address 0.0.0.0 (proxy querier) never wins Proxy querier: when no better querier exists on a LAN, one or more devices can send proxy queries with source address 0.0.0.0 (or :: for IPv6). See Query Interval, below, why this is a good thing Query interval: the time in seconds between two queries from an IGMP/MLD querier. It is not uncommon that end-devices do not send their membership reports unless they first hear a query Fast Leave: set on a bridge port to ensure multicast is pruned as quickly as possible when a \"leave\" membership report is received. In effect, this option marks the port as directly connected to an end-device. When not set (default), a query with timeout is first sent to ensure no unintentional loss of multicast is incurred Router port: can be both configured statically and detected at runtime based on connected devices, usually multicast routers. On a router port all multicast is forwarded, both known and unknown Flood port: set on a bridge port (default: enabled) to ensure all unknown multicast is forwarded Router timeout: the time in seconds until a querier is deemed to have been lost and another device (switch/router) takes over. In the tables shown above, a None timeout is declared when the current device is the active querier Tip
The reason why multicast flooding is enabled by default is to ensure safe co-existence with MAC multicast, which is common in industrial networks. It also allows end devices that do not know of IGMP/MLD to communicate over multicast as long as the group they have chosen is not used by other IGMP/MLD aware devices on the LAN.
As soon as an IGMP/MLD membership report to \"join\" a group is received the group is added to the kernel MDB and forwarding to other ports stop. The only exception to this rule is multicast router ports.
If your MAC multicast forwarding is not working properly, it may be because an IP multicast group maps to the same MAC address. Please see RFC 1112 for details. Use static multicast router ports, or static multicast MAC filters, to mitigate.
"},{"location":"networking/#forwarding-of-ieee-reserved-group-addresses","title":"Forwarding of IEEE Reserved Group Addresses","text":"Addresses in the range 01:80:C2:00:00:0X are used by various bridge signaling protocols, and are not forwarded by default. Still, it is sometimes useful to let the bridge forward such packets, this can be done by specifying protocol names or the last address nibble as decimal value 0..15:
admin@example:/config/> edit interface br0 bridge\nadmin@example:/config/interface/br0/bridge/> set ieee-group-forward # Tap the ? ley for alternatives\n [0..15] List of IEEE link-local protocols to forward, e.g., STP, LLDP\n dot1x 802.1X Port-Based Network Access Control.\n lacp 802.3 Slow Protocols, e.g., LACP.\n lldp 802.1AB Link Layer Discovery Protocol (LLDP).\n stp Spanning Tree (STP/RSPT/MSTP).\nadmin@example:/config/interface/br0/bridge/> set ieee-group-forward\n The following example configures bridge br0 to forward LLDP packets.
admin@example:/config/interface/br0/bridge/> set ieee-group-forward lldp\nadmin@example:/config/interface/br0/bridge/>\n "},{"location":"networking/#link-aggregation","title":"Link Aggregation","text":"A link aggregate, or lag, allows multiple physical interfaces to be combined into a single logical interface, providing increased bandwidth (in some cases) and redundancy (primarily). Two modes of qualifying lag member ports are available:
static: Active members selected based on link status (carrier) lacp: IEEE 802.3ad Link Aggregation Control Protocol In LACP mode, LACPDUs are exchanged by the link partners to qualify each lag member, while in static mode only carrier is used. This additional exchange in LACP ensures traffic can be forwarded in both directions.
Traffic distribution, for both modes, across the active lag member ports is determined by the hash policy1 . It uses an XOR of the source, destination MAC addresses and the EtherType field. This, IEEE 802.3ad-compliant, algorithm will place all traffic to a particular network peer on the same link. Meaning there is no increased bandwidth for communication between two specific devices.
Tip
Similar to other interface types, naming your interface lagN, where N is a number, allows the CLI to automatically infer the interface type as LAG.
"},{"location":"networking/#basic-configuration","title":"Basic Configuration","text":"Creating a link aggregate interface and adding member ports:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode static\nadmin@example:/config/interface/lag0/> end\nadmin@example:/config/> set interface eth7 lag-port lag lag0\nadmin@example:/config/> set interface eth8 lag-port lag lag0\nadmin@example:/config/> leave\n A static lag responds only to link (carrier) changes of member ports. E.g., in this example egressing traffic is continuously distributed over the two links until link down on one link is detected, triggering all traffic to be steered to the sole remaining link.
"},{"location":"networking/#lacp-configuration","title":"LACP Configuration","text":"LACP mode provides dynamic negotiation of the link aggregate. Key settings include:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode lacp\nadmin@example:/config/interface/lag0/> set lag lacp mode passive\nadmin@example:/config/interface/lag0/> set lag lacp rate fast\nadmin@example:/config/interface/lag0/> set lag lacp system-priority 100\n LACP mode supports two operational modes:
active: Initiates negotiation by sending LACPDUs (default) passive: Waits for peer to initiate negotiation Note
At least one end of the link must be in active mode for negotiation to occur.
The LACP rate setting controls protocol timing:
slow: LACPDUs sent every 30 seconds, with 90 second timeout (default) fast: LACPDUs sent every second, with 3 second timeout "},{"location":"networking/#link-flapping","title":"Link Flapping","text":"To protect against link flapping, debounce timers can be configured to delay link qualification. Usually only the up delay is needed:
admin@example:/config/interface/lag0/lag/link-monitor/> edit debounce\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set up 500\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set down 200\n "},{"location":"networking/#operational-status-overview","title":"Operational Status, Overview","text":"Like other interfaces, link aggregates are also available in the general interfaces overview in the CLI admin-exec context. Here is the above static mode aggregate:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\n.\n.\n.\nlag0 lag UP static: balance-xor, hash: layer2\n\u2502 ethernet UP 00:a0:85:00:02:00\n\u251c eth7 lag ACTIVE\n\u2514 eth8 lag ACTIVE\n Same aggregate, but in LACP mode:
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\n.\n.\n.\nlag0 lag UP lacp: active, rate: fast (1s), hash: layer2\n\u2502 ethernet UP 00:a0:85:00:02:00\n\u251c eth7 lag ACTIVE active, short_timeout, aggregating, in_sync, collecting, distributing\n\u2514 eth8 lag ACTIVE active, short_timeout, aggregating, in_sync, collecting, distributing\n "},{"location":"networking/#operational-status-detail","title":"Operational Status, Detail","text":"In addition to basic status shown in the interface overview, detailed LAG status can be inspected:
admin@example:/> show interface lag0\nname : lag0\nindex : 25\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag mode : static\nlag type : balance-xor\nlag hash : layer2\nlink debounce up : 0 msec\nlink debounce down : 0 msec\nipv4 addresses :\nipv6 addresses :\nin-octets : 0\nout-octets : 2142\n Same aggregate, but in LACP mode:
admin@example:/> show interface lag0\nname : lag0\nindex : 24\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag mode : lacp\nlag hash : layer2\nlacp mode : active\nlacp rate : fast (1s)\nlacp aggregate id : 1\nlacp system priority: 65535\nlacp actor key : 9\nlacp partner key : 9\nlacp partner mac : 00:a0:85:00:03:00\nlink debounce up : 0 msec\nlink debounce down : 0 msec\nipv4 addresses :\nipv6 addresses :\nin-octets : 100892\nout-octets : 111776\n Member ports provide additional status information:
Link failure counter: number of detected link failures LACP state flags: various states of LACP negotiation: active: port is actively sending LACPDUs short_timeout: using fast rate (1s) vs. slow rate (30s) aggregating: port is allowed to aggregate in this LAG in_sync: port is synchronized with partner collecting: port is allowed to receive traffic distributing: port is allowed to send traffic defaulted: using default partner info (partner not responding) expired: partner info has expired (no LACPDUs received) Aggregator ID: unique identifier for this LAG group Actor state: LACP state flags for this port (local) Partner state: LACP state flags from the remote port Example member port status:
admin@example:/> show interface eth7\nname : eth7\nindex : 8\nmtu : 1500\noperational status : up\nphysical address : 00:a0:85:00:02:00\nlag member : lag0\nlag member state : active\nlacp aggregate id : 1\nlacp actor state : active, short_timeout, aggregating, in_sync, collecting, distributing\nlacp partner state : active, short_timeout, aggregating, in_sync, collecting, distributing\nlink failure count : 0\nipv4 addresses :\nipv6 addresses :\nin-octets : 473244\nout-octets : 499037\n "},{"location":"networking/#example-switch-uplink-with-lacp","title":"Example: Switch Uplink with LACP","text":"LACP mode provides the most robust operation, automatically negotiating the link aggregate and detecting configuration mismatches.
A common use case is connecting a switch to an upstream device:
admin@example:/> configure\nadmin@example:/config/> edit interface lag0\nadmin@example:/config/interface/lag0/> set lag mode lacp\n Enable fast LACP for quicker fail-over:
admin@example:/config/interface/lag0/> set lag lacp rate fast\n Add uplink ports
admin@example:/config/interface/lag0/> end\nadmin@example:/config/> set interface eth7 lag-port lag lag0\nadmin@example:/config/> set interface eth8 lag-port lag lag0\n Enable protection against \"link flapping\".
admin@example:/config/interface/lag0/> edit lag link-monitor\nadmin@example:/config/interface/lag0/lag/link-monitor/> edit debounce\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set up 500\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> set down 200\nadmin@example:/config/interface/lag0/lag/link-monitor/debounce/> top\n Add to bridge for switching
admin@example:/config/interface/lag0/lag/link-monitor/debounce/> end\nadmin@example:/config/> set interface lag0 bridge-port bridge br0\nadmin@example:/config/> leave\n "},{"location":"networking/#vlan-interfaces","title":"VLAN Interfaces","text":"Creating a VLAN can be done in many ways. This section assumes VLAN interfaces created atop another Linux interface. E.g., the VLAN interfaces created on top of the Ethernet interface or bridge in the picture below.
A VLAN interface is basically a filtering abstraction. When you run tcpdump on a VLAN interface you will only see the frames matching the VLAN ID of the interface, compared to all the VLAN IDs if you run tcpdump on the lower-layer interface.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0.20\nadmin@example:/config/interface/eth0.20/> show\ntype vlan;\nvlan {\n tag-type c-vlan;\n id 20;\n lower-layer-if eth0;\n}\nadmin@example:/config/interface/eth0.20/> leave\n The example below assumes bridge br0 is already created, see VLAN Filtering Bridge.
admin@example:/> configure\nadmin@example:/config/> edit interface vlan10\nadmin@example:/config/interface/vlan10/> set vlan id 10\nadmin@example:/config/interface/vlan10/> set vlan lower-layer-if br0\nadmin@example:/config/interface/vlan10/> leave\n As conventions, a VLAN interface for VID 20 on top of an Ethernet interface eth0 is named eth0.20, and a VLAN interface for VID 10 on top of a bridge interface br0 is named vlan10.
Note
If you name your VLAN interface foo0.N or vlanN, where N is a number, the CLI infers the interface type automatically.
"},{"location":"networking/#physical-ethernet-interfaces","title":"Physical Ethernet Interfaces","text":""},{"location":"networking/#ethernet-settings-and-status","title":"Ethernet Settings and Status","text":"Physical Ethernet interfaces provide low-level settings for speed/duplex as well as packet status and statistics.
By default, Ethernet interfaces defaults to auto-negotiating speed/duplex modes, advertising all speed and duplex modes available. In the example below, the switch would by default auto-negotiate speed 1 Gbit/s on port eth1 and 100 Mbit/s on port eth4, as those are the highest speeds supported by H1 and H2 respectively.
The speed and duplex status for the links can be listed as shown below, assuming the link operational status is 'up'.
admin@example:/> show interface eth1\nname : eth1\nindex : 2\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 1000\nphysical address : 00:53:00:06:11:01\nipv4 addresses :\nipv6 addresses : \nin-octets : 75581\nout-octets : 43130\n...\nadmin@example:/> show interface eth4\nname : eth4\nindex : 5\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 100\nphysical address : 00:53:00:06:11:04\nipv4 addresses :\nipv6 addresses : \nin-octets : 75439\nout-octets : 550704\n...\nadmin@example:/>\n "},{"location":"networking/#configuring-fixed-speed-and-duplex","title":"Configuring fixed speed and duplex","text":"Auto-negotiation of speed/duplex mode is desired in almost all use-cases, but it is possible to disable auto-negotiation and specify a fixed speed and duplex mode.
Important
When setting a fixed speed and duplex mode, ensure both sides of the link have matching configuration. If speed does not match, the link will not come up. If duplex mode does not match, the result is reported collisions and/or bad throughput.
The example below configures port eth3 to fixed speed 100 Mbit/s half-duplex mode.
admin@example:/> configure\nadmin@example:/config/> edit interface eth3 ethernet\nadmin@example:/config/interface/eth3/ethernet/> set speed 0.1\nadmin@example:/config/interface/eth3/ethernet/> set duplex half\nadmin@example:/config/interface/eth3/ethernet/> set auto-negotiation enable false\nadmin@example:/config/interface/eth3/ethernet/> show\nauto-negotiation {\n enable false;\n}\nduplex half;\nspeed 0.1;\nadmin@example:/config/interface/eth3/ethernet/> leave\nadmin@example:/> \n Speed metric is in Gbit/s. Auto-negotiation needs to be disabled in order for fixed speed/duplex to apply. Only speeds 0.1(100 Mbit/s) and 0.01 (10 Mbit/s) can be specified. 1 Gbit/s and higher speeds require auto-negotiation to be enabled.
"},{"location":"networking/#ethernet-statistics","title":"Ethernet statistics","text":"Ethernet packet statistics6 can be listed as shown below.
admin@example:/> show interface eth1\nname : eth1\nindex : 2\nmtu : 1500\noperational status : up\nauto-negotiation : on\nduplex : full\nspeed : 1000\nphysical address : 00:53:00:06:11:0a\nipv4 addresses :\nipv6 addresses : \nin-octets : 75581\nout-octets : 43130\n\neth-in-frames : 434\neth-in-multicast-frames : 296\neth-in-broadcast-frames : 138\neth-in-error-fcs-frames : 0\neth-in-error-oversize-frames : 0\neth-out-frames : 310\neth-out-multicast-frames : 310\neth-out-broadcast-frames : 0\neth-out-good-octets : 76821\neth-in-good-octets : 60598\nadmin@example:/> \n "},{"location":"networking/#veth-pairs","title":"VETH Pairs","text":"A Virtual Ethernet (VETH) pair is basically a virtual Ethernet cable. A cable can be \"plugged in\" to a bridge and the other end can be given to a container, or plugged into another bridge.
The latter example is useful if you have multiple bridges in the system with different properties (VLAN filtering, IEEE group forwarding, etc.), but still want some way of communicating between these domains.
admin@example:/> configure\nadmin@example:/config/> edit interface veth0a\nadmin@example:/config/interface/veth0a/> set veth peer veth0b\nadmin@example:/config/interface/veth0a/> end\nadmin@example:/config/> diff\ninterfaces {\n+ interface veth0a {\n+ type veth;\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ }\n}\nadmin@example:/config/>\n Tip
This is another example of the automatic inference of the interface type from the name. Any name can be used, but then you have to set the interface type to veth manually.
"},{"location":"networking/#management-plane","title":"Management Plane","text":"This section details IP Addresses And Other Per-Interface IP settings.
Infix support several network interface types, each can be assigned one or more IP addresses, both IPv4 and IPv6 are supported. (There is no concept of a \"primary\" address.)
"},{"location":"networking/#ipv4-address-assignment","title":"IPv4 Address Assignment","text":"Multiple address assignment methods are available:
Type Yang Model Description static ietf-ip Static assignment of IPv4 address, e.g., 10.0.1.1/24 link-local infix-ip Auto-assignment of IPv4 address in 169.254.x.x/16 range dhcp infix-dhcp-client Assignment of IPv4 address by DHCP server, e.g., 10.0.1.1/24 Note
The DHCP address method is only available for LAN interfaces (Ethernet, virtual Ethernet (veth), bridge, link aggregates, etc.)
Supported DHCP (request) options, configurability (Cfg) and defaults, are listed below. Configurable options can be disabled on a per client interface basis, some options, like clientid and option 81, are possible to set the value of as well.
Opt Name Cfg Description 1 netmask No Request IP address and netmask 3 router Yes Default route(s), see also option 121 and 249 6 dns-server Yes DNS server(s), static ones take precedence 12 hostname Yes DHCP cannot set hostname, only for informing server 15 domain Yes Default domain name, for name resolution 28 broadcast Yes Broadcast address, calculated if disabled 42 ntp-server Yes NTP server(s), static ones take precedence 50 address Yes Request (previously cached) address 61 client-id Yes Default MAC address (and option 12) 81 fqdn Yes Similar to option 12, request FQDN update in DNS 119 search Yes Request domain search list 121 classless-static-route Yes Classless static routes 249 ms-classless-static-route Yes Microsoft static route, same as option 121 Default: router, dns-server, domain, broadcast, ntp-server, search, address, classless-static-route, ms-classless-static-route
When configuring a DHCP client, ensure that the NTP client is enabled for the ntp-server DHCP option to be processed correctly. If the NTP client is not enabled, any NTP servers provided by the DHCP server will be ignored. For details on how to enable the NTP client, see the NTP Client Configuration section.
Important
Per RFC3442, if the DHCP server returns both a Classless Static Routes option (121) and Router option (3), the DHCP client must ignore the latter.
"},{"location":"networking/#ipv6-address-assignment","title":"IPv6 Address Assignment","text":"Multiple address assignment methods are available:
Type Yang Model Description static ietf-ip Static assignment of IPv6 address, e.g., 2001:db8:0:1::1/64 link-local ietf-ip2 (RFC4862) Auto-configured link-local IPv6 address (fe80::0 prefix + interface identifier, e.g., fe80::ccd2:82ff:fe52:728b/64) global auto-conf ietf-ip (RFC4862) Auto-configured (stateless) global IPv6 address (prefix from router + interface identifier, e.g., 2001:db8:0:1:ccd2:82ff:fe52:728b/64 dhcp infix-dhcpv6-client Assignment of IPv6 address by DHCPv6 server, e.g., 2001:db8::42/128 Both for link-local and global auto-configuration, it is possible to auto-configure using a random suffix instead of the interface identifier.
Note
The DHCPv6 address method is only available for LAN interfaces (Ethernet, virtual Ethernet (veth), bridge, link aggregates, etc.)
Supported DHCPv6 (request) options, configurability (Cfg) and defaults, are listed below. Configurable options can be disabled on a per client interface basis, some options, like client-id and client-fqdn, are possible to set the value of as well.
Opt Name Cfg Description 1 client-id Yes Client identifier (DUID), auto-generated by default 2 server-id Yes Server identifier (DUID) 23 dns-server Yes DNS recursive name servers, static ones take precedence 24 domain-search Yes Domain search list 25 ia-pd Yes Prefix delegation for downstream networks 31 sntp-server Yes Simple Network Time Protocol servers 32 information-refresh-time Yes Refresh time for stateless DHCPv6 39 client-fqdn Yes Client FQDN, request DNS update from server 56 ntp-server Yes NTP time servers, static ones take precedence Default: dns-server, domain-search, ntp-server
DHCPv6 supports both stateful (address assignment) and stateless (information-only) modes:
Stateful DHCPv6: The server assigns IPv6 addresses to clients. This is the default mode when enabling the DHCPv6 client. Stateless DHCPv6: Used with SLAAC (Stateless Address Autoconfiguration) when only configuration information (DNS, NTP, etc.) is needed. Enable with the information-only setting. When configuring a DHCPv6 client, ensure that the NTP client is enabled for the ntp-server DHCPv6 option to be processed correctly. If the NTP client is not enabled, any NTP servers provided by the DHCPv6 server will be ignored. For details on how to enable the NTP client, see the NTP Client Configuration section.
"},{"location":"networking/#examples","title":"Examples","text":"admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n To illustrate IP address configuration, the examples below uses a switch with a single Ethernet interface (eth0) and a loopback interface (lo). As shown above, these examples assume eth0 has an IPv6 link-local address and lo has static IPv4 and IPv6 addresses by default.
"},{"location":"networking/#static-and-link-local-ipv4-addresses","title":"Static and link-local IPv4 addresses","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4\nadmin@example:/config/interface/eth0/ipv4/> set address 10.0.1.1 prefix-length 24\nadmin@example:/config/interface/eth0/ipv4/> set autoconf\nadmin@example:/config/interface/eth0/ipv4/> diff\n+interfaces {\n+ interface eth0 {\n+ ipv4 {\n+ address 10.0.1.1 {\n+ prefix-length 24;\n+ }\n+ autoconf;\n+ }\n+ }\n+}\nadmin@example:/config/interface/eth0/ipv4/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 169.254.1.3/16 (random)\n ipv4 10.0.1.1/24 (static)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n As shown, the link-local IPv4 address is configured with set autoconf. The presence of the autoconf container enables IPv4 link-local address assignment. The resulting address (169.254.1.3/16) is of type random (ietf-ip.yang).
The IPv4LL client also supports a request-address setting which can be used to \"seed\" the client's starting address. If the address is free it will be used, otherwise it falls back to the default algorithm.
admin@example:/config/interface/eth0/ipv4/> edit autoconf\nadmin@example:/config/interface/eth0/ipv4/autoconf/> set request-address 169.254.1.2\nadmin@example:/config/interface/eth0/ipv4/autoconf/> leave\n "},{"location":"networking/#use-of-dhcp-for-ipv4-address-assignment","title":"Use of DHCP for IPv4 address assignment","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4\nadmin@example:/config/interface/eth0/ipv4/> set dhcp\nadmin@example:/config/interface/eth0/ipv4/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv4 10.1.2.100/24 (dhcp)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n The resulting address (10.1.2.100/24) is of type dhcp.
To configure DHCP client options, such as sending a specific hostname to the server, you can specify options with values:
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv4 dhcp\nadmin@example:/config/interface/eth0/ipv4/dhcp/> set option hostname value myhost\nadmin@example:/config/interface/eth0/ipv4/dhcp/> show\noption hostname {\n value myhost;\n}\nadmin@example:/config/interface/eth0/ipv4/dhcp/> leave\nadmin@example:/>\n Tip
The special value auto can be used with the hostname option to automatically use the configured system hostname.
Other useful DHCP options include:
client-id - Send a specific client identifier to the server route-preference - Set the administrative distance for DHCP-learned routes (default: 5) For advanced usage with vendor-specific options, see the YANG model.
"},{"location":"networking/#use-of-dhcpv6-for-ipv6-address-assignment","title":"Use of DHCPv6 for IPv6 address assignment","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set dhcp\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8::42/128 (dhcp)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n The resulting address (2001:db8::42/128) is of type dhcp.
To configure DHCPv6 client options, such as requesting prefix delegation for downstream networks, you can specify options:
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6 dhcp\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set option ia-pd\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set option dns-server\nadmin@example:/config/interface/eth0/ipv6/dhcp/> show\noption dns-server;\noption ia-pd;\nadmin@example:/config/interface/eth0/ipv6/dhcp/> leave\nadmin@example:/>\n For stateless DHCPv6 (used with SLAAC to get only configuration information):
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6 dhcp\nadmin@example:/config/interface/eth0/ipv6/dhcp/> set information-only true\nadmin@example:/config/interface/eth0/ipv6/dhcp/> show\ninformation-only true;\noption dns-server;\noption domain-search;\nadmin@example:/config/interface/eth0/ipv6/dhcp/> leave\nadmin@example:/>\n Other useful DHCPv6 options include:
duid - Set a specific DHCPv6 Unique Identifier (auto-generated by default) client-fqdn - Request the server to update DNS records with client's FQDN route-preference - Set the administrative distance for DHCPv6-learned routes (default: 5) For advanced usage with vendor-specific options, see the YANG model.
"},{"location":"networking/#disabling-ipv6-link-local-addresses","title":"Disabling IPv6 link-local address(es)","text":"The (only) way to disable IPv6 link-local addresses is by disabling IPv6 on the interface.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set enabled false\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#static-ipv6-address","title":"Static IPv6 address","text":"admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set address 2001:db8::1 prefix-length 64\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8::1/64 (static)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#stateless-auto-configuration-of-global-ipv6-address","title":"Stateless Auto-configuration of Global IPv6 Address","text":"Stateless address auto-configuration of global addresses is enabled by default. The address is formed by concatenating the network prefix advertised by the router (here 2001:db8:0:1::0/64) and the interface identifier. The resulting address is of type link-layer, as it is formed based on the interface identifier (ietf-ip.yang).
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:0:ff:fe00:0/64 (link-layer)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Disabling auto-configuration of global IPv6 addresses can be done as shown below.
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set autoconf create-global-addresses false\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"networking/#random-link-identifiers-for-ipv6-stateless-autoconfiguration","title":"Random Link Identifiers for IPv6 Stateless Autoconfiguration","text":"By default, the auto-configured link-local and global IPv6 addresses are formed from a link-identifier based on the MAC address.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:0:ff:fe00:0/64 (link-layer)\n ipv6 fe80::ff:fe00:0/64 (link-layer)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n To avoid revealing identity information in the IPv6 address, it is possible to specify use of a random identifier (ietf-ip.yang and RFC8981).
admin@example:/> configure\nadmin@example:/config/> edit interface eth0 ipv6\nadmin@example:/config/interface/eth0/ipv6/> set autoconf create-temporary-addresses true\nadmin@example:/config/interface/eth0/ipv6/> leave\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\neth0 ethernet UP 02:00:00:00:00:00\n ipv6 2001:db8:0:1:b705:8374:638e:74a8/64 (random)\n ipv6 fe80::ad3d:b274:885a:9ffb/64 (random)\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Both the link-local address (fe80::) and the global address (2001:) have changed type to random.
"},{"location":"networking/#ipv4-forwarding","title":"IPv4 forwarding","text":"To be able to route (static or dynamic) on the interface it is required to enable forwarding. This setting controls if packets received on this interface can be forwarded.
admin@example:/config/> edit interface eth0\nadmin@example:/config/interface/eth0/> set ipv4 forwarding\nadmin@example:/config/interface/eth0/> leave\nadmin@example:/>\n "},{"location":"networking/#ipv6-forwarding","title":"IPv6 forwarding","text":"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.
The following table shows the system IPv6 features that the forwarding setting control when it is Enabled or *Disabled:
IPv6 Feature Enabled Disabled IsRouter set in Neighbour Advertisements Yes No Transmit Router Solicitations No Yes Router Advertisements are ignored Yes Yes Accept Redirects No Yes admin@example:/config/> edit interface eth0\nadmin@example:/config/interface/eth0/> set ipv6 forwarding\nadmin@example:/config/interface/eth0/> leave\nadmin@example:/>\n "},{"location":"networking/#routing-support","title":"Routing support","text":"Currently supported YANG models:
YANG Model Description ietf-routing Base model for all other models ietf-ipv4-unicast-routing Static IPv4 unicast routing ietf-ipv6-unicast-routing Static IPv6 unicast routing ietf-ospf OSPF routing infix-routing Infix deviations and extensions The base model, ietf-routing, is where all the other models hook in. It is used to set configuration and read operational status (RIB tables) in the other models.
Note
The standard IETF routing models allows multiple instances, but Infix currently only support one instance per routing protocol! In the examples presented here, the instance name default is used.
"},{"location":"networking/#ipv4-static-routes","title":"IPv4 Static routes","text":"The standard IETF model for static routes reside under the static control plane protocol. For our examples we use the instance name default, you can use any name.
For a route with destination 192.168.200.0/24 via 192.168.1.1:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv4\nadmin@example:/config/routing/\u2026/ipv4/> set route 192.168.200.0/24 next-hop next-hop-address 192.168.1.1\nadmin@example:/config/routing/\u2026/ipv4/> leave\nadmin@example:/>\n For a \"floating\" static route with destination 10.0.0.0/16 via a backup router 192.168.1.1, using the highest possible distance:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv4\nadmin@example:/config/routing/\u2026/ipv4/> set route 10.0.0.0/16 next-hop next-hop-address 192.168.1.1 route-preference 254\nadmin@example:/config/routing/\u2026/ipv4/> leave\nadmin@example:/>\n Tip
Remember to enable IPv4 forwarding for the interfaces you want to route between.
"},{"location":"networking/#ipv6-static-routes","title":"IPv6 Static routes","text":"admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol static name default ipv6\nadmin@example:/config/routing/\u2026/ipv6/> set route 2001:db8:3c4d:200::/64 next-hop next-hop-address 2001:db8:3c4d:1::1\nadmin@example:/config/routing/\u2026/ipv6/> leave\nadmin@example:/>\n "},{"location":"networking/#ospfv2-routing","title":"OSPFv2 Routing","text":"The system supports OSPF dynamic routing for IPv4, i.e., OSPFv2. To enable OSPF and set one active interface in area 0:
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface e0 enabled\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n Tip
Remember to enable IPv4 forwarding for all the interfaces you want to route between.
"},{"location":"networking/#ospf-area-types","title":"OSPF area types","text":"In addition to regular OSPF areas, area types NSSA and Stub are also supported. To configure an NSSA area with summary routes:
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.1 area-type nssa-area\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.1 summary true\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n "},{"location":"networking/#bidirectional-forwarding-detection-bfd","title":"Bidirectional Forwarding Detection (BFD)","text":"It is possible to enable BFD per OSPF interface to speed up detection of link loss.
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface e0 bfd enabled true\nadmin@example:/config/routing/\u2026/ospf/> leave\nadmin@example:/>\n "},{"location":"networking/#ospf-interface-settings","title":"OSPF interface settings","text":"We have already seen how to enable OSPF per interface (enabled true) and BFD for OSPF per interface (bfd enabled true). These and other OSPF interface settings are done in context of an OSFP area, e.g., area 0.0.0.0. Available commands can be listed using the ? mark.
admin@example:/config/routing/\u2026/> edit ospf area 0.0.0.0\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/> edit interface e0\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set ?\n bfd BFD interface configuration.\n cost Interface's cost.\n dead-interval Interval after which a neighbor is declared down\n enabled Enables/disables the OSPF protocol on the interface.\n hello-interval Interval between Hello packets (seconds). It must\n interface-type Interface type.\n passive Enables/disables a passive interface. A passive\n retransmit-interval Interval between retransmitting unacknowledged Link\n transmit-delay Estimated time needed to transmit Link State Update\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set\n For example, setting the OSPF interface type to point-to-point for an Ethernet interface can be done as follows.
admin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/> set interface-type point-to-point\nadmin@example:/config/routing/\u2026/ospf/area/0.0.0.0/interface/e0/>\n "},{"location":"networking/#ospf-global-settings","title":"OSPF global settings","text":"In addition to area and interface specific settings, OSPF provides global settings for route redistribution and OSPF router identifier.
admin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@example:/config/routing/\u2026/ospf/> set ?\n area List of OSPF areas.\n default-route-advertise Distribute default route to network\n explicit-router-id Defined in RFC 2328. A 32-bit number\n redistribute Redistribute protocols into OSPF\nadmin@example:/config/routing/\u2026/ospf/> set\n Explicit router ID: By default the router will pick an IP address from one of its OSPF interfaces as OSPF router ID. An explicit ID is used to get a deterministic behavior, e.g., set explicit-router-id 1.1.1.1. Redistribution: set redistribute static and set redistribute connected can be used to include static or connected routes into the OSPF routing domain. These routes are redistributed as external type-2 (E2) routes. Advertising default route: An OSPF router can be made to distribute a default route into the OSPF domain by command set default-route-advertise enabled. This route is distributed as long as the router itself has an active default route in its routing table. By adding command set default-route-advertise always the router will distribute a default route even when it lacks a default route. The default route will be distributed as an external type-2 (E2) route. "},{"location":"networking/#debug-ospfv2","title":"Debug OSPFv2","text":"Using NETCONF and the YANG model ietf-routing it is possible to read the OSPF routing table, neighbors and more, that may be useful for debugging the OSPFv2 setup. The CLI has various OSPF status commands such as show ospf neighbor, show ospf interface and show ospf routes.
admin@example:/> show ospf neighbor\n\nNeighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL\n10.1.1.2 1 Full/- 3h46m59s 30.177s 10.1.1.2 e0:10.1.1.1 0 0 0\n10.1.1.3 1 Full/- 3h46m55s 34.665s 10.1.1.3 e1:10.1.1.1 0 0 0\n\nadmin@example:/>\n For more detailed troubleshooting, OSPF debug logging can be enabled to capture specific protocol events. Debug messages are written to the routing log file (/var/log/routing).
Caution
Debug logging significantly increases log output and may impact performance. Only enable debug categories needed for troubleshooting, and disable them when done.
To enable specific OSPF debug categories:
admin@example:/> configure\nadmin@example:/config/> edit routing control-plane-protocol ospfv2 name default ospf debug\nadmin@example:/config/routing/\u2026/ospf/debug/> set bfd true\nadmin@example:/config/routing/\u2026/ospf/debug/> set nsm true\nadmin@example:/config/routing/\u2026/ospf/debug/> leave\nadmin@example:/>\n Available debug categories include:
bfd: BFD (Bidirectional Forwarding Detection) events packet: Detailed packet debugging (all OSPF packets) ism: Interface State Machine events nsm: Neighbor State Machine events default-information: Default route origination nssa: Not-So-Stubby Area events All debug options are disabled by default. Refer to the infix-routing YANG model for the complete list of available debug options.
To view current debug settings:
admin@example:/> show running-config routing control-plane-protocol\n To disable all debug logging, simply delete the debug settings or set all options back to false:
admin@example:/> configure\nadmin@example:/config/> delete routing control-plane-protocol ospfv2 name default ospf debug\nadmin@example:/config/> leave\nadmin@example:/>\n "},{"location":"networking/#view-routing-table","title":"View routing table","text":"The routing table can be inspected from the operational datastore, XPath /routing/ribs, using sysrepocfg, NETCONF/RESTCONF, or using the CLI.
"},{"location":"networking/#ipv4-routing-table","title":"IPv4 routing table","text":"This CLI example shows the IPv4 routing table with a few connected routes and some routes learned from OSPF. See the next section for an explanation of route preferences (PREF).
The > at the start of a line marks a selected route (in the IETF YANG model referred to as active), if there are more than one route with the same destination the * marks the next-hop used and installed in the kernel FIB (the YANG model refers to this as installed).
admin@example:/> show ip route\n DESTINATION PREF NEXT-HOP PROTO UPTIME\n>* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 4h2m43s\n>* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 4h2m43s\n 10.0.0.3/32 110/0 lo ospfv2 4h2m57s\n>* 10.0.0.3/32 0/0 lo direct 4h2m58s\n 10.0.13.0/30 110/2000 e5 ospfv2 4h2m57s\n>* 10.0.13.0/30 0/0 e5 direct 4h2m58s\n 10.0.23.0/30 110/1 e6 ospfv2 4h2m57s\n>* 10.0.23.0/30 0/0 e6 direct 4h2m58s\n 192.168.3.0/24 110/1 e2 ospfv2 4h2m57s\n>* 192.168.3.0/24 0/0 e2 direct 4h2m58s\nadmin@example:/>\n "},{"location":"networking/#ipv6-routing-table","title":"IPv6 routing table","text":"This CLI example show the IPv6 routing table.
admin@example:/> show ipv6 route\n DESTINATION PREF NEXT-HOP PROTO UPTIME\n>* ::/0 1/0 2001:db8:3c4d:50::1 static 0h1m20s\n>* 2001:db8:3c4d:50::/64 0/0 e6 direct 0h1m20s\n>* 2001:db8:3c4d:200::1/128 0/0 lo direct 0h1m20s\n * fe80::/64 0/0 e7 direct 0h1m20s\n * fe80::/64 0/0 e6 direct 0h1m20s\n * fe80::/64 0/0 e5 direct 0h1m20s\n * fe80::/64 0/0 e4 direct 0h1m20s\n * fe80::/64 0/0 e3 direct 0h1m20s\n * fe80::/64 0/0 e2 direct 0h1m20s\n>* fe80::/64 0/0 e1 direct 0h1m20s\nadmin@example:/>\n "},{"location":"networking/#route-preference","title":"Route Preference","text":"The operating system leverages FRRouting (Frr) as routing engine for both static and dynamic routing. Even routes injected from a DHCP client, and IPv4 link-local (IPv4) routes, are injected into Frr to let it weigh all routes before installing them into the kernel routing table (sometimes referred to as FIB).
Routes have different weights made up from a distance and a metric. The kernel routing table only talks about metric, which unfortunately is not the same -- this is one of the reasons why the term route preference is used instead. It is recommended to use the CLI, or any of the other previously mentioned YANG based front-ends, to inspect the routing table.
Default distances used (lower numeric value wins):
Distance Protocol 0 Kernel routes, i.e., connected routes 1 Static routes 5 DHCP routes 110 OSPF 254 IPv4LL (ZeroConf) device routes 255 Route will not be used or redistributed Hence, a route learned from OSPF may be overridden by a static route set locally. By default, even a route to the same destination, but with a different next-hop, learned from a DHCP server wins over an OSPF route.
The distance used for static routes and DHCP routes can be changed by setting a different routing preference value.
Note
The kernel metric is an unsigned 32-bit value, which is read by Frr as (upper) 8 bits distance and 24 bits metric. But it does not write it back to the kernel FIB this way, only selected routes are candidates to be installed in the FIB by Frr.
"},{"location":"networking/#source-protocol","title":"Source protocol","text":"The source protocol describes the origin of the route.
Protocol Description kernel Added when setting a subnet address on an interface static User created, learned from DHCP, or IPv4LL ospfv2 Routes learned from OSPFv2 The YANG model ietf-routing support multiple ribs but only two are currently supported, namely ipv4 and ipv6.
(source MAC XOR destination MAC XOR EtherType) MODULO num_links \u21a9
Link-local IPv6 addresses are implicitly enabled when enabling IPv6. IPv6 can be enabled/disabled per interface in the ietf-ip YANG model.\u00a0\u21a9
For example, IPv4 groups are mapped to MAC multicast addresses by mapping the low-order 23-bits of the IP address in the low-order 23 bits of the Ethernet address 01:00:5E:00:00:00. Meaning, more than one IP multicast group maps to the same MAC multicast group.\u00a0\u21a9
A YANG deviation was previously used to make it possible to set phys-address, but this has been replaced with the more flexible custom-phys-address.\u00a0\u21a9
MAC bridges on Marvell Linkstreet devices are currently limited to a single MAC database, this may be a problem if the same MAC address appears in different MAC bridges.\u00a0\u21a9
Ethernet counters are described in ieee802-ethernet-interface.yang and infix-ethernet-interface.yang. There is a dedicated document on Ethernet Counters that provide additional details on the statistics support.\u00a0\u21a9
"},{"location":"override-package/","title":"Developing with Buildroot","text":""},{"location":"override-package/#package-override","title":"Package Override","text":"This guide demonstrates how the local.mk file is utilized to override a Buildroot package, which can be very useful when fixing bugs, adding features, or evaluating new releases of packages. As an example we use tcpdump to illustrate this process.
Tip
For a comprehensive guide to utilizing Buildroot during development, including the <pkg>_OVERRIDE_SRCDIR mechanism, shown below, please see Using Buildroot during development in the official docs.
"},{"location":"override-package/#setup","title":"Setup","text":"Since the output/ directory is often wiped for rebuilds, make sure you keep the file local.mk in the top directory and only symlink it to your build directory:
~$ cd infix/\n~/infix(main)$ touch local.mk\n~/infix/output(main)$ ln -s ../local.mk .\n~/infix/output(main)$ cd ..\n "},{"location":"override-package/#override","title":"Override","text":"Now edit the file:
~/infix(main)$ editor local.mk\n Add an override for tcpdump. The file is a Makefile snippet so you can add a lot of things. Comment out lines with the UNIX comment # character if needed:
TCPDUMP_OVERRIDE_SRCDIR = /path/to/tcpdump/repo\n "},{"location":"override-package/#building","title":"Building","text":"The execution of make tcpdump-rebuild all triggers a process where Buildroot synchronizes the tcpdump source code from the specified override directory to output/build/tcpdump-custom, followed by the rebuilding of the entire project.
~/infix$(main)$ make tcpdump-rebuild all\n Buildroot follows a process of downloading and processing tarballs: extraction, configuration, compilation, and installation. The source for each package is extracted to a temporary build directory:
output/build/<package>-<version> # e.g., tcpdump-4.99.4\n Let's have a look at what we got:
~/infix$(main)$ ll /output/build/ | grep tcpdump\ndrwxr-xr-x 7 group user 20480 Nov 10 18:26 tcpdump-4.99.4/\ndrwxr-xr-x 7 group user 12288 Nov 10 18:28 tcpdump-custom/\n As long as your local override is in place, Buildroot will use your custom version.
Note
Remember, the build directory is ephemeral, so be careful to change any of the files therein. It can be useful though during debugging, but just make sure to learn the difference between the various Buildroot commands to build, clean, reconfigure, etc.
"},{"location":"qos/","title":"Quality of Service","text":""},{"location":"qos/#quality-of-service","title":"Quality of Service","text":"On occasion, most networks will experience congestion due to some extraordinary load being placed upon it. If the load is transient, switches and routers may be able to absorb such bursts of traffic by queuing packets in internal memories. However, if the load is sustained over long periods of time, queues will fill up and packets will start to be dropped. When such situations arise, it is the job of the network's Quality of Service (QoS) policy to define which packets to drop and which ones to prioritize, such that critical services remain operational.
"},{"location":"qos/#hardware-forwarded-traffic","title":"Hardware Forwarded Traffic","text":"The default QoS policy for flows which are offloaded to a switching ASIC is defined by the hardware defaults of the device in question.
"},{"location":"qos/#marvell-linkstreet","title":"Marvell LinkStreet","text":"This family of devices, sometimes also referred to as SOHO, are managed by the mv88e6xxx driver in the Linux kernel. While older chips in this family where limited to 4 output queues per port, this documentation is only valid for newer generations with 8 output queues per port.
The picture illustrates packets having their priority determined at ingress, here interface e1 and e3. In this example, both packets are forwarded to the same outgoing interface (e2), subject to output queueing. The sections below provides more information on these topics.
"},{"location":"qos/#default-policy","title":"Default Policy","text":""},{"location":"qos/#queueing","title":"Queueing","text":"Both layer 2 (VLAN PCP) and layer 3 (IP DSCP) priority marks are considered when selecting the output queue of an incoming frame. PCP to queue mapping is done 1:1. For IP packets, the 3 most significant bits of the DSCP is used to select the queue:
PCP DSCP \u21d2 Queue Weight 0 0-7 \u21d2 0 1 1 8-15 \u21d2 1 2 2 16-23 \u21d2 2 3 3 24-31 \u21d2 3 6 4 32-39 \u21d2 4 12 5 40-47 \u21d2 5 17 6 48-55 \u21d2 6 25 7 56-63 \u21d2 7 33 For packets containing both a VLAN tag and an IP header, PCP priority takes precedence over DSCP priority. In cases where neither are available, packets are always assigned to queue 0.
Each port's set of 8 egress queues operate on a Weighted Round Robin (WRR) schedule, using the weights listed in the table above. The sum of all weights adds up to 99, meaning that the weight of any given queue is roughly equivalent to the percentage of the available bandwidth reserved for it.
"},{"location":"qos/#marking","title":"Marking","text":"Any priority marks available on ingress are left unmodified when the frame egresses an output port. In the case when an IP packet ingresses without a VLAN tag, and is to egress with a VLAN tag, its PCP is set to the 3 most significant bits of it. If no priority information is available in the frame on ingress (i.e. untagged non-IP), then packets will egress out of tagged ports with PCP set to 0.
"},{"location":"qos/#software-forwarded-traffic","title":"Software Forwarded Traffic","text":"For packets which are processed by a CPU, i.e. typically routed traffic, and bridged traffic between interfaces that do not belong to the same hardware switching domain, an nftables container can be used to define a QoS policy.
For VLAN interfaces, Infix provides support for mapping the Priority Code Point (PCP) to internal priority on ingress, and the reverse on egress.
These ingress-qos and egress-qos settings are done per VLAN, both defaulting to '0'. The example below shows how to keep the PCP priority for packets being routed between two VLAN interfaces.
admin@example:/config/> edit interface e1.10\nadmin@example:/config/interface/e1.10/> set vlan ingress-qos priority from-pcp \nadmin@example:/config/interface/e1.10/> up\nadmin@example:/config/> edit interface e1.20\nadmin@example:/config/interface/e1.20/> set vlan egress-qos pcp from-priority \nadmin@example:/config/interface/e1.20/> leave\nadmin@example:/> \n "},{"location":"qos/#a-complex-example","title":"A complex example","text":"The picture below shows a packet flow being subject both to software forwarding and hardware offloading.
"},{"location":"scripting-netconf/","title":"With NETCONF","text":""},{"location":"scripting-netconf/#scripting-with-netconf","title":"Scripting with NETCONF","text":"NETCONF (Network Configuration Protocol) provides a standardized mechanism for managing network devices using XML-based RPC operations over SSH (port 830). This guide shows practical examples for interacting with Infix using NETCONF.
NETCONF offers robust capabilities for network automation:
Transactional operations: Validate before commit, rollback on error Fine-grained locking: Prevent concurrent configuration conflicts Structured data: XML with YANG schema validation Standardized operations: Get, edit-config, copy-config, etc. "},{"location":"scripting-netconf/#netconf-vs-restconf","title":"NETCONF vs RESTCONF","text":"Both protocols use the same YANG data models, but differ in approach:
Feature NETCONF RESTCONF Transport SSH HTTPS Encoding XML JSON/XML Operations RPC-based REST/HTTP methods Best for Automation scripts Web integration, simple queries Choose NETCONF when you need:
Transactional configuration changes Configuration validation before commit Locking to prevent concurrent changes Integration with existing NETCONF tooling Choose RESTCONF for:
Simple queries and updates Web-based applications When you prefer JSON over XML RESTful API patterns "},{"location":"scripting-netconf/#quick-start-with-netopeer2-cli","title":"Quick Start with netopeer2-cli","text":"netopeer2-cli is an interactive NETCONF client, useful for learning and testing. Install it:
~$ sudo apt install netopeer2-cli\n Connect to your Infix device:
~$ netopeer2-cli\n> connect --host example.local --login admin\nadmin@example.local password:\n> status\nCurrent NETCONF session:\n ID : 1\n Host : example.local\n Port : 830\n Transport : SSH\n Capabilities: 35\n "},{"location":"scripting-netconf/#basic-operations-in-netopeer2-cli","title":"Basic Operations in netopeer2-cli","text":"Get entire configuration:
> get-config --source running\n Get specific subtree (hostname):
> get-config --source running --filter-xpath /system/hostname\n<data xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>example</hostname>\n </system>\n</data>\n Get operational state:
> get --filter-xpath /interfaces\n Edit configuration:
> edit-config --target candidate --config=/tmp/config.xml\n> commit\n Disconnect:
> disconnect\n> quit\n "},{"location":"scripting-netconf/#discovery-common-patterns","title":"Discovery & Common Patterns","text":"Before working with specific configuration items, you often need to discover what exists on the system. This section shows common discovery patterns and practical workflows.
"},{"location":"scripting-netconf/#discovering-available-interfaces","title":"Discovering Available Interfaces","text":"List all interface names:
<rpc message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name/>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Using netopeer2-cli:
> get --filter-xpath /interfaces/interface/name\n This returns all interface names, useful for iterating through interfaces in scripts.
"},{"location":"scripting-netconf/#get-all-yang-capabilities","title":"Get All YANG Capabilities","text":"Discover which YANG modules and features are available:
> status\n Or programmatically via the <hello> message capabilities received during connection establishment.
"},{"location":"scripting-netconf/#get-entire-running-configuration","title":"Get Entire Running Configuration","text":"Useful for exploration or backup:
<rpc message-id=\"2\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get-config>\n <source>\n <running/>\n </source>\n </get-config>\n</rpc>\n Using netopeer2-cli:
> get-config --source running\n "},{"location":"scripting-netconf/#common-workflow-patterns","title":"Common Workflow Patterns","text":""},{"location":"scripting-netconf/#pattern-1-find-interface-by-ip-address","title":"Pattern 1: Find interface by IP address","text":"Get all interfaces with their IPs, then filter:
> get --filter-xpath /interfaces\n Parse the XML output to find which interface has the desired IP.
"},{"location":"scripting-netconf/#pattern-2-check-which-interfaces-are-down","title":"Pattern 2: Check which interfaces are down","text":"> get --filter-xpath /interfaces/interface/oper-status\n Look for interfaces with <oper-status>down</oper-status>.
"},{"location":"scripting-netconf/#pattern-3-get-interface-statistics-for-monitoring","title":"Pattern 3: Get interface statistics for monitoring","text":"<rpc message-id=\"3\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <statistics/>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Returns in-octets, out-octets, in-errors, out-errors for all interfaces.
"},{"location":"scripting-netconf/#scripting-with-netconf-client","title":"Scripting with netconf-client","text":"netconf-client is a lightweight Python-based NETCONF client designed for scripting and automation.
"},{"location":"scripting-netconf/#installation","title":"Installation","text":"~$ pip install netconf-client\n Or clone and install from source:
~$ git clone https://github.com/wires-se/netconf-client.git\n~$ cd netconf-client\n~$ pip install .\n "},{"location":"scripting-netconf/#basic-usage","title":"Basic Usage","text":"The client provides a simple command-line interface:
~$ netconf-client --host example.local --user admin --password admin <operation>\n Common operations:
get-config - Retrieve configuration edit-config - Modify configuration get - Retrieve operational state copy-config - Copy between datastores lock/unlock - Lock datastores commit - Commit candidate configuration "},{"location":"scripting-netconf/#python-api","title":"Python API","text":"You can also use netconf-client as a Python library:
from netconf_client.client import NetconfClient\n\n# Connect\nclient = NetconfClient(\n host='example.local',\n username='admin',\n password='admin'\n)\n\n# Get configuration\nconfig = client.get_config(source='running')\nprint(config)\n\n# Edit configuration\nxml_config = \"\"\"\n<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\n\"\"\"\nclient.edit_config(target='candidate', config=xml_config)\nclient.commit()\n\n# Close connection\nclient.close()\n "},{"location":"scripting-netconf/#configuration-examples","title":"Configuration Examples","text":""},{"location":"scripting-netconf/#read-hostname","title":"Read Hostname","text":"XML request:
<rpc message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get-config>\n <source>\n <running/>\n </source>\n <filter type=\"subtree\">\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname/>\n </system>\n </filter>\n </get-config>\n</rpc>\n Using netopeer2-cli:
> get-config --source running --filter-xpath /system/hostname\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get-config --source running --xpath /system/hostname\n Response:
<rpc-reply message-id=\"1\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <data>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>example</hostname>\n </system>\n </data>\n</rpc-reply>\n "},{"location":"scripting-netconf/#set-hostname","title":"Set Hostname","text":"XML request:
<rpc message-id=\"2\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <edit-config>\n <target>\n <candidate/>\n </target>\n <config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n </config>\n </edit-config>\n</rpc>\n Using netopeer2-cli:
Save the config to /tmp/hostname.xml:
<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\n Then apply:
> edit-config --target candidate --config=/tmp/hostname.xml\n> commit\n Using netconf-client:
~$ cat > hostname.xml <<EOF\n<config>\n <system xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">\n <hostname>newhostname</hostname>\n </system>\n</config>\nEOF\n\n~$ netconf-client --host example.local --user admin \\\n edit-config --target candidate --config hostname.xml\n\n~$ netconf-client --host example.local --user admin commit\n "},{"location":"scripting-netconf/#add-ip-address-to-interface","title":"Add IP Address to Interface","text":"Save the config to ip-config.xml:
<config>\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name>eth0</name>\n <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n <address>\n <ip>192.168.1.100</ip>\n <prefix-length>24</prefix-length>\n </address>\n </ipv4>\n </interface>\n </interfaces>\n</config>\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n edit-config --target candidate --config ip-config.xml\n~$ netconf-client --host example.local --user admin commit\n "},{"location":"scripting-netconf/#copy-running-to-startup","title":"Copy Running to Startup","text":"XML request:
<rpc message-id=\"3\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <copy-config>\n <source>\n <running/>\n </source>\n <target>\n <startup/>\n </target>\n </copy-config>\n</rpc>\n Using netopeer2-cli:
> copy-config --source running --target startup\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n copy-config --source running --target startup\n "},{"location":"scripting-netconf/#operational-data-examples","title":"Operational Data Examples","text":""},{"location":"scripting-netconf/#read-interface-state","title":"Read Interface State","text":"XML request:
<rpc message-id=\"4\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n <get>\n <filter type=\"subtree\">\n <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n <interface>\n <name>eth0</name>\n </interface>\n </interfaces>\n </filter>\n </get>\n</rpc>\n Using netopeer2-cli:
> get --filter-xpath /interfaces/interface[name='eth0']\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/interfaces/interface[name='eth0']\"\n This returns operational state including admin/oper status, statistics, MAC address, MTU, and IP addresses.
"},{"location":"scripting-netconf/#read-all-interfaces","title":"Read All Interfaces","text":"Using netopeer2-cli:
> get --filter-xpath /interfaces\n Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath /interfaces\n "},{"location":"scripting-netconf/#read-routing-table","title":"Read Routing Table","text":"Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/routing/ribs/rib[name='ipv4-default']\"\n "},{"location":"scripting-netconf/#read-ospf-neighbors","title":"Read OSPF Neighbors","text":"Using netconf-client:
~$ netconf-client --host example.local --user admin \\\n get --xpath \"/routing/control-plane-protocols/control-plane-protocol[type='ietf-ospf:ospfv2'][name='default']/ietf-ospf:ospf\"\n "},{"location":"scripting-netconf/#advanced-scripting","title":"Advanced Scripting","text":""},{"location":"scripting-netconf/#python-script-backup-configuration","title":"Python Script: Backup Configuration","text":"#!/usr/bin/env python3\nfrom netconf_client.client import NetconfClient\nfrom datetime import datetime\nimport sys\n\ndef backup_config(host, user, password, output_file):\n try:\n # Connect\n client = NetconfClient(host=host, username=user, password=password)\n\n # Get running config\n config = client.get_config(source='running')\n\n # Save to file with timestamp\n timestamp = datetime.now().strftime('%Y%m%d-%H%M%S')\n filename = f\"{output_file}-{timestamp}.xml\"\n\n with open(filename, 'w') as f:\n f.write(config)\n\n print(f\"Configuration backed up to {filename}\")\n\n client.close()\n return 0\n\n except Exception as e:\n print(f\"Error: {e}\", file=sys.stderr)\n return 1\n\nif __name__ == '__main__':\n if len(sys.argv) != 5:\n print(f\"Usage: {sys.argv[0]} <host> <user> <password> <output_prefix>\")\n sys.exit(1)\n\n sys.exit(backup_config(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]))\n Usage:
~$ chmod +x backup.py\n~$ ./backup.py example.local admin admin config-backup\nConfiguration backed up to config-backup-20250102-143022.xml\n "},{"location":"scripting-netconf/#python-script-monitor-interface-statistics","title":"Python Script: Monitor Interface Statistics","text":"#!/usr/bin/env python3\nfrom netconf_client.client import NetconfClient\nimport xml.etree.ElementTree as ET\nimport time\nimport sys\n\ndef get_interface_stats(client, interface):\n \"\"\"Get interface statistics\"\"\"\n xpath = f\"/interfaces/interface[name='{interface}']/statistics\"\n data = client.get(filter_xpath=xpath)\n\n # Parse XML to extract counters\n root = ET.fromstring(data)\n ns = {'if': 'urn:ietf:params:xml:ns:yang:ietf-interfaces'}\n\n stats = {}\n for stat in root.findall('.//if:statistics/*', ns):\n stats[stat.tag.split('}')[1]] = int(stat.text)\n\n return stats\n\ndef monitor_interface(host, user, password, interface, interval=5):\n \"\"\"Monitor interface statistics\"\"\"\n client = NetconfClient(host=host, username=user, password=password)\n\n print(f\"Monitoring {interface} on {host} (Ctrl-C to stop)\")\n print(f\"{'Time':<20} {'RX Packets':<15} {'TX Packets':<15} {'RX Bytes':<15} {'TX Bytes':<15}\")\n print(\"-\" * 80)\n\n try:\n while True:\n stats = get_interface_stats(client, interface)\n timestamp = time.strftime('%Y-%m-%d %H:%M:%S')\n\n print(f\"{timestamp:<20} \"\n f\"{stats.get('in-unicast-pkts', 0):<15} \"\n f\"{stats.get('out-unicast-pkts', 0):<15} \"\n f\"{stats.get('in-octets', 0):<15} \"\n f\"{stats.get('out-octets', 0):<15}\")\n\n time.sleep(interval)\n\n except KeyboardInterrupt:\n print(\"\\nMonitoring stopped\")\n finally:\n client.close()\n\nif __name__ == '__main__':\n if len(sys.argv) < 5:\n print(f\"Usage: {sys.argv[0]} <host> <user> <password> <interface> [interval]\")\n sys.exit(1)\n\n interval = int(sys.argv[5]) if len(sys.argv) > 5 else 5\n monitor_interface(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], interval)\n Usage:
~$ chmod +x monitor.py\n~$ ./monitor.py example.local admin admin eth0 2\nMonitoring eth0 on example.local (Ctrl-C to stop)\nTime RX Packets TX Packets RX Bytes TX Bytes\n--------------------------------------------------------------------------------\n2025-01-02 14:35:10 12453 8932 1847392 892341\n2025-01-02 14:35:12 12489 8967 1851204 895673\n...\n "},{"location":"scripting-netconf/#shell-script-batch-configuration","title":"Shell Script: Batch Configuration","text":"#!/bin/bash\n# Apply configuration to multiple devices\n\nDEVICES=\"device1.local device2.local device3.local\"\nUSER=\"admin\"\nPASSWORD=\"admin\"\nCONFIG_FILE=\"$1\"\n\nif [ -z \"$CONFIG_FILE\" ]; then\n echo \"Usage: $0 <config.xml>\"\n exit 1\nfi\n\nfor device in $DEVICES; do\n echo \"Configuring $device...\"\n\n # Edit candidate\n netconf-client --host \"$device\" --user \"$USER\" --password \"$PASSWORD\" \\\n edit-config --target candidate --config \"$CONFIG_FILE\"\n\n if [ $? -eq 0 ]; then\n # Commit if edit succeeded\n netconf-client --host \"$device\" --user \"$USER\" --password \"$PASSWORD\" commit\n echo \" \u2713 $device configured successfully\"\n else\n echo \" \u2717 $device configuration failed\"\n fi\ndone\n "},{"location":"scripting-netconf/#other-netconf-tools","title":"Other NETCONF Tools","text":""},{"location":"scripting-netconf/#ncclient-python","title":"ncclient (Python)","text":"Popular Python library for NETCONF:
~$ pip install ncclient\n Example:
from ncclient import manager\n\nwith manager.connect(host='example.local', port=830,\n username='admin', password='admin',\n hostkey_verify=False) as m:\n # Get config\n c = m.get_config(source='running')\n print(c)\n "},{"location":"scripting-netconf/#ansible","title":"Ansible","text":"Ansible includes NETCONF modules for automation:
- name: Get interface config\n netconf_get:\n source: running\n filter: <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/>\n "},{"location":"scripting-netconf/#cisco-tools","title":"Cisco Tools","text":" NSO (Network Services Orchestrator): Enterprise orchestration platform YANG Suite: Web-based YANG exploration and testing tool Cisco pyATS: Network test automation "},{"location":"scripting-netconf/#troubleshooting","title":"Troubleshooting","text":""},{"location":"scripting-netconf/#enable-netconf-debugging","title":"Enable NETCONF Debugging","text":"For netconf-client, use verbose mode:
~$ netconf-client --host example.local --user admin --verbose get-config\n For netopeer2-cli, enable debug output:
> debug 1\n> get-config --source running\n "},{"location":"scripting-netconf/#common-issues","title":"Common Issues","text":"Connection refused:
Verify SSH is running on port 830: ssh -p 830 admin@example.local Check firewall rules Authentication failed:
Verify credentials Check user has NETCONF access permissions Operation not supported:
Verify NETCONF capability: netopeer2-cli \u2192 status \u2192 check capabilities Some operations require specific YANG modules "},{"location":"scripting-netconf/#references","title":"References","text":" NETCONF Protocol (RFC 6241) netconf-client on GitHub netopeer2 Documentation YANG Data Modeling Language (RFC 7950) ietf-interfaces YANG module ietf-system YANG module ncclient Documentation "},{"location":"scripting-prod/","title":"Production Testing","text":""},{"location":"scripting-prod/#scripting-for-production-tests","title":"Scripting for Production Tests","text":"This document shows how to set up and remotely script devices with a focus on production testing.
"},{"location":"scripting-prod/#vlan-snake","title":"VLAN Snake","text":"As part of production tests, verification of Ethernet ports is usually expected. A common way for devices with multiple bridged Ethernet ports is to connect a test PC to two ports and send a ping traversing all ports. This can be achieved by using VLANs, on the switch, as described in this section. The resulting configuration file can be applied to the running configuration of the produced unit, e.g, use config file restore as described previously.
In this example we assume a 10 port switch, with ports e1-e10. The following VLAN configuration and cable connections will be used:
VLAN & Ports Connect VLAN 10: e1 & e2 e2 <=> e3 VLAN 20: e3 & e4 e4 <=> e5 VLAN 30: e5 & e6 e6 <=> e7 VLAN 40: e7 & e8 e8 <=> e9 VLAN 50: e9 & e10 The test PC is connected to e1 and e10 via different interfaces (alternatively, two different PCs are used).
Tip
Configuration here is done via console. When configuring remotely over SSH, remember to keep one IP address (the one used for the SSH connection)! I.e., set a static IP address first, then perform the VLAN configuration step.
"},{"location":"scripting-prod/#configuration-at-start","title":"Configuration at Start","text":"Starting out, we assume a configuration where all ports are network interfaces (possibly with IPv6 enabled).
admin@example:/> show interfaces\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet LOWER-DOWN 00:53:00:06:11:01\ne2 ethernet LOWER-DOWN 00:53:00:06:11:02\ne3 ethernet LOWER-DOWN 00:53:00:06:11:03\ne4 ethernet LOWER-DOWN 00:53:00:06:11:04\ne5 ethernet LOWER-DOWN 00:53:00:06:11:05\ne6 ethernet LOWER-DOWN 00:53:00:06:11:06\ne7 ethernet LOWER-DOWN 00:53:00:06:11:07\ne8 ethernet LOWER-DOWN 00:53:00:06:11:08\ne9 ethernet LOWER-DOWN 00:53:00:06:11:09\ne10 ethernet UP 00:53:00:06:11:0a\n ipv6 fe80::0053:00ff:fe06:110a/64 (link-layer)\nadmin@example:/>\n "},{"location":"scripting-prod/#creating-bridge-and-adding-ports","title":"Creating Bridge and Adding Ports","text":"The following example creates a bridge and adds all Ethernet ports to it. On a device with layer-2 offloading (switch fabric), this sets all ports in the same VLAN. The next section sets up VLAN isolation.
admin@example:/> configure\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> end\nadmin@example:/config/> set interface e1 bridge-port bridge br0\nadmin@example:/config/> set interface e2 bridge-port bridge br0\nadmin@example:/config/> set interface e3 bridge-port bridge br0\nadmin@example:/config/> set interface e4 bridge-port bridge br0\nadmin@example:/config/> set interface e5 bridge-port bridge br0\nadmin@example:/config/> set interface e6 bridge-port bridge br0\nadmin@example:/config/> set interface e7 bridge-port bridge br0\nadmin@example:/config/> set interface e8 bridge-port bridge br0\nadmin@example:/config/> set interface e9 bridge-port bridge br0\nadmin@example:/config/> set interface e10 bridge-port bridge br0\nadmin@example:/config/>\n The interface status can be viewed using show interfaces after leaving configuration context. When configuring via SSH, first assign an IP address to br0 before leaving configuration context, e.g.
admin@example:/config/> set interface br0 ipv6 enabled\n This enables IPv6 SLAAC, auto-configured address. Or skip leave and stay in configuration context until you have completed all the device setup, including setting IP address.
admin@example:/config/> leave\nadmin@example:/>\nadmin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge LOWER-DOWN\n\u251c e2 bridge LOWER-DOWN\n\u251c e3 bridge LOWER-DOWN\n\u251c e4 bridge LOWER-DOWN\n\u251c e5 bridge LOWER-DOWN\n\u251c e6 bridge LOWER-DOWN\n\u251c e7 bridge LOWER-DOWN\n\u251c e8 bridge LOWER-DOWN\n\u251c e9 bridge LOWER-DOWN\n\u2514 e10 bridge FORWARDING\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"scripting-prod/#assign-vlans-to-ports","title":"Assign VLANs to Ports","text":"Now, configure VLANs as outlined previously: default VID for ingress (PVID), which is done per port, and egress mode (untagged), which is done at the bridge level. See the VLAN bridges section for more information.
admin@example:/>\nadmin@example:/> configure\nadmin@example:/config/> set interface e1 bridge-port pvid 10\nadmin@example:/config/> set interface e2 bridge-port pvid 10\nadmin@example:/config/> set interface e3 bridge-port pvid 20\nadmin@example:/config/> set interface e4 bridge-port pvid 20\nadmin@example:/config/> set interface e5 bridge-port pvid 30\nadmin@example:/config/> set interface e6 bridge-port pvid 30\nadmin@example:/config/> set interface e7 bridge-port pvid 40\nadmin@example:/config/> set interface e8 bridge-port pvid 40\nadmin@example:/config/> set interface e9 bridge-port pvid 50\nadmin@example:/config/> set interface e10 bridge-port pvid 50\nadmin@example:/config/> edit interface br0\nadmin@example:/config/interface/br0/> edit bridge vlans\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e1\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e2\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e3\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e4\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e5\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e6\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e7\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e8\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e9\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e10\nadmin@example:/config/interface/br0/bridge/vlans/> leave\nadmin@example:/>\n Interface status would now should something like the following
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge LOWER-DOWN vlan:10u pvid:10\n\u251c e2 bridge LOWER-DOWN vlan:10u pvid:10\n\u251c e3 bridge LOWER-DOWN vlan:20u pvid:20\n\u251c e4 bridge LOWER-DOWN vlan:20u pvid:20\n\u251c e5 bridge LOWER-DOWN vlan:30u pvid:30\n\u251c e6 bridge LOWER-DOWN vlan:30u pvid:30\n\u251c e7 bridge LOWER-DOWN vlan:40u pvid:40\n\u251c e8 bridge LOWER-DOWN vlan:40u pvid:40\n\u251c e9 bridge LOWER-DOWN vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n "},{"location":"scripting-prod/#connect-cables-and-test","title":"Connect Cables and Test","text":"We can now connect the PC to e1 and e10, while the other ports are patched according to above. We should see link up and FORWARDING on all ports in the bridge.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge FORWARDING vlan:10u pvid:10\n\u251c e2 bridge FORWARDING vlan:10u pvid:10\n\u251c e3 bridge FORWARDING vlan:20u pvid:20\n\u251c e4 bridge FORWARDING vlan:20u pvid:20\n\u251c e5 bridge FORWARDING vlan:30u pvid:30\n\u251c e6 bridge FORWARDING vlan:30u pvid:30\n\u251c e7 bridge FORWARDING vlan:40u pvid:40\n\u251c e8 bridge FORWARDING vlan:40u pvid:40\n\u251c e9 bridge FORWARDING vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nadmin@example:/>\n Here we use IPv6 ping all hosts (ff02::1) on PC interface eth1 to check reachability to the other interface of the PC.
Tip
We recommend using network namespaces (Linux only) on the PC to ensure that traffic is actually sent out to the switch, rather than being looped back internally. Alternatively, use two separate PCs.
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.496 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.514 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.473 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.736 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.563 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.507 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n6 packets transmitted, 6 received, 0% packet loss, time 5108ms\nrtt min/avg/max/mdev = 0.473/0.548/0.736/0.088 ms\n~ $\n We can verify that traffic goes through the switch by disconnecting one of the patch cables, e.g., between e4 and e5
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.510 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.448 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.583 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.515 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.521 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.495 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=7 ttl=64 time=0.743 ms\n... Disconnecting patch cable, thus losing packets\n... and reconnecting again. Connectivity resumes.\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=16 ttl=64 time=0.961 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=17 ttl=64 time=0.513 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=18 ttl=64 time=0.794 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=19 ttl=64 time=0.755 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=20 ttl=64 time=0.779 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n20 packets transmitted, 12 received, 40% packet loss, time 19432ms\nrtt min/avg/max/mdev = 0.448/0.634/0.961/0.156 ms\n~ $\n "},{"location":"scripting-prod/#set-ip-address","title":"Set IP Address","text":"The configuration so far does not provide a means to connect to the switch management via SSH or NETCONF, as the switch has no IP address. The example below shows how to add the switch to VLAN 10 (as used for ports e1 and e2) and enables IPv6.
admin@example:/config/> edit interface vlan10\nadmin@example:/config/interface/vlan10/> set vlan lower-layer-if br0\nadmin@example:/config/interface/vlan10/> set ipv6 enabled\nadmin@example:/config/interface/vlan10/> show\ntype vlan;\nipv6 {\n enabled true;\n}\nvlan {\n tag-type c-vlan;\n id 10;\n lower-layer-if br0;\n}\nadmin@example:/config/interface/vlan10/>\nadmin@example:/config/interface/vlan10/> end\nadmin@example:/config/> edit interface br0 bridge vlans\nadmin@example:/config/interface/br0/bridge/vlans/> set vlan 10 tagged br0\nadmin@example:/config/interface/br0/bridge/vlans/> leave\nadmin@example:/>\n Interface vlan10 with an auto-configured IPv6 address should appear.
admin@example:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nbr0 bridge vlan:10t\n\u2502 ethernet UP 00:53:00:06:11:01\n\u251c e1 bridge FORWARDING vlan:10u pvid:10\n\u251c e2 bridge FORWARDING vlan:10u pvid:10\n\u251c e3 bridge FORWARDING vlan:20u pvid:20\n\u251c e4 bridge FORWARDING vlan:20u pvid:20\n\u251c e5 bridge FORWARDING vlan:30u pvid:30\n\u251c e6 bridge FORWARDING vlan:30u pvid:30\n\u251c e7 bridge FORWARDING vlan:40u pvid:40\n\u251c e8 bridge FORWARDING vlan:40u pvid:40\n\u251c e9 bridge FORWARDING vlan:50u pvid:50\n\u2514 e10 bridge FORWARDING vlan:50u pvid:50\nlo ethernet UP 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\nvlan10 ethernet UP 00:53:00:06:11:01\n\u2502 ipv6 fe80::0053:00ff:fe06:1101/64 (link-layer)\n\u2514 br0 ethernet UP 00:53:00:06:11:01\nadmin@example:/>\n When pinging \"IPv6 all hosts\" from the PC, there should be two responses for every ping, one from the switch and one from the PC attached to e10.
~ $ ping -L ff02::1%eth1\nPING ff02::1%eth1(ff02::1%eth1) 56 data bytes\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.508 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=1 ttl=64 time=0.968 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.866 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=2 ttl=64 time=0.867 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=3 ttl=64 time=0.467 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.469 ms\n64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.452 ms\n64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=4 ttl=64 time=0.453 ms\n^C\n--- ff02::1%eth1 ping statistics ---\n4 packets transmitted, 4 received, +4 duplicates, 0% packet loss, time 3031ms\nrtt min/avg/max/mdev = 0.452/0.631/0.968/0.211 ms\n~ $\n It should now be possible to access the switch from the PC via SSH (or NETCONF).
~ $ ssh admin@fe80::0053:00ff:fe06:1101%eth1\nadmin@fe80::0053:00ff:fe06:1101%eth1's password:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$ exit\n~ $\n See previous sections on backup and restore of the created configuration.
"},{"location":"scripting-restconf/","title":"With RESTCONF","text":""},{"location":"scripting-restconf/#scripting-with-restconf","title":"Scripting with RESTCONF","text":"RESTCONF provides a programmatic interface to both configuration and operational data over HTTPS. This guide shows practical examples using curl to interact with the RESTCONF API.
All examples use the following conventions:
Host: example.local (replace with your device hostname/IP) Credentials: admin:admin (default username:password) HTTPS: Self-signed certificates require -k flag in curl "},{"location":"scripting-restconf/#helper-script","title":"Helper Script","text":"To simplify RESTCONF operations, create a curl.sh wrapper script:
#!/bin/sh\n# RESTCONF CLI wrapper for curl\n\n# Show usage and exit\nusage()\n{\n cat <<-EOF >&2\n Usage: $0 [-h HOST] [-d DATASTORE] [-u USER:PASS] METHOD PATH [CURL_ARGS...]\n\n Options:\n -h HOST Target host (default: infix.local)\n -d DS Datastore: running, operational, startup (default: running)\n -u CREDS Credentials as user:pass (default: admin:admin)\n\n Methods: GET, POST, PUT, PATCH, DELETE\n EOF\n exit \"$1\"\n}\n\n# Default values\nHOST=${HOST:-infix.local}\nDATASTORE=running\nAUTH=admin:admin\n\n# Parse options\nwhile getopts \"h:d:u:\" opt; do\n case $opt in\n h) HOST=\"$OPTARG\" ;;\n d) DATASTORE=\"$OPTARG\" ;;\n u) AUTH=\"$OPTARG\" ;;\n *) usage 1 ;;\n esac\ndone\nshift $((OPTIND - 1))\n\n# Validate required arguments\nif [ $# -lt 2 ]; then\n echo \"Error: METHOD and PATH are required\" >&2\n usage 1\nfi\n\nMETHOD=$1\nPATH=$2\nshift 2\n\n# Ensure PATH starts with /\ncase \"$PATH\" in\n /*) ;;\n *) PATH=\"/$PATH\" ;;\nesac\n\n# Build URL based on datastore\ncase \"$DATASTORE\" in\n running|startup)\n URL=\"https://${HOST}/restconf/data${PATH}\"\n ;;\n operational)\n URL=\"https://${HOST}/restconf/data${PATH}\"\n ;;\n *)\n echo \"Error: Invalid datastore '$DATASTORE'. Use: running, operational, or startup\" >&2\n exit 1\n ;;\nesac\n\n# Execute curl with all remaining arguments passed through\nexec /usr/bin/curl \\\n --insecure \\\n --user \"${AUTH}\" \\\n --request \"${METHOD}\" \\\n --header \"Content-Type: application/yang-data+json\" \\\n --header \"Accept: application/yang-data+json\" \\\n \"$@\" \\\n \"${URL}\"\n Make it executable:
~$ chmod +x curl.sh\n This wrapper handles authentication, headers, SSL certificates, and URL construction, making commands much cleaner. You can override defaults with command-line options or environment variables:
# Using command-line options\n~$ ./curl.sh -h 192.168.1.10 -d operational -u admin:secret GET /ietf-interfaces:interfaces\n\n# Using environment variables\n~$ HOST=192.168.1.10 ./curl.sh GET /ietf-system:system\n The examples below show both raw curl commands and the equivalent using curl.sh where applicable.
"},{"location":"scripting-restconf/#discovery-common-patterns","title":"Discovery & Common Patterns","text":"Before working with specific configuration items, you often need to discover what exists on the system. This section shows common discovery patterns and practical workflows.
"},{"location":"scripting-restconf/#discovering-available-interfaces","title":"Discovering Available Interfaces","text":"List all interface names:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][].name'\nlo\ne0\ne1\n This is essential for automation - interface names vary by platform (eth0, e1, enp0s3, etc.), so scripts should discover them rather than hardcode.
"},{"location":"scripting-restconf/#get-api-capabilities","title":"Get API Capabilities","text":"Discover what YANG modules are available:
~$ curl -kX GET -u admin:admin \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/ietf-yang-library:yang-library\n This returns all supported YANG modules, revisions, and features.
"},{"location":"scripting-restconf/#get-entire-running-configuration","title":"Get Entire Running Configuration","text":"Useful for exploration or backup:
~$ ./curl.sh -h example.local GET / -o backup.json\n "},{"location":"scripting-restconf/#common-workflow-patterns","title":"Common Workflow Patterns","text":""},{"location":"scripting-restconf/#pattern-1-find-interface-by-ip-address","title":"Pattern 1: Find interface by IP address","text":"Get all interfaces with IPs and search:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | select(.[\"ietf-ip:ipv4\"][\"address\"][]?.ip == \"192.168.1.100\") | .name'\n "},{"location":"scripting-restconf/#pattern-2-list-all-interfaces-that-are-down","title":"Pattern 2: List all interfaces that are down","text":"~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | select(.[\"oper-status\"] == \"down\") | .name'\n "},{"location":"scripting-restconf/#pattern-3-get-statistics-for-all-interfaces","title":"Pattern 3: Get statistics for all interfaces","text":"~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][] | \"\\(.name): RX \\(.statistics[\"in-octets\"]) TX \\(.statistics[\"out-octets\"])\"'\n Output:
lo: RX 29320 TX 29320\ne0: RX 1847392 TX 892341\ne1: RX 0 TX 0\n "},{"location":"scripting-restconf/#pattern-4-check-if-interface-exists-before-configuring","title":"Pattern 4: Check if interface exists before configuring","text":"~$ if ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q \"ietf-interfaces:interface\"; then\n echo \"Interface eth0 exists\"\n else\n echo \"Interface eth0 not found\"\n fi\n "},{"location":"scripting-restconf/#configuration-operations","title":"Configuration Operations","text":""},{"location":"scripting-restconf/#read-hostname","title":"Read Hostname","text":"Example of fetching JSON configuration data:
Using curl directly:
~$ curl -kX GET -u admin:admin \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/data/ietf-system:system/hostname\n{\n \"ietf-system:system\": {\n \"hostname\": \"foo\"\n }\n}\n Using curl.sh:
~$ ./curl.sh -h example.local GET /ietf-system:system/hostname\n{\n \"ietf-system:system\": {\n \"hostname\": \"foo\"\n }\n}\n "},{"location":"scripting-restconf/#set-hostname","title":"Set Hostname","text":"Example of updating configuration with inline JSON data:
Using curl directly:
~$ curl -kX PATCH -u admin:admin \\\n -H 'Content-Type: application/yang-data+json' \\\n -d '{\"ietf-system:system\":{\"hostname\":\"bar\"}}' \\\n https://example.local/restconf/data/ietf-system:system\n Using curl.sh:
~$ ./curl.sh -h example.local PATCH /ietf-system:system \\\n -d '{\"ietf-system:system\":{\"hostname\":\"bar\"}}'\n "},{"location":"scripting-restconf/#add-ip-address-to-interface","title":"Add IP Address to Interface","text":"Add an IP address to the loopback interface:
~$ ./curl.sh -h example.local POST \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \\\n -d '{ \"prefix-length\": 32 }'\n "},{"location":"scripting-restconf/#delete-ip-address-from-interface","title":"Delete IP Address from Interface","text":"Remove an IP address from the loopback interface:
~$ ./curl.sh -h example.local DELETE \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254\n "},{"location":"scripting-restconf/#copy-running-to-startup","title":"Copy Running to Startup","text":"No copy command available yet to copy between datastores, and the Rousette back-end also does not support \"write-through\" to the startup datastore.
To save running-config to startup-config, fetch running to a local file and then update startup with it:
Using curl directly:
~$ curl -kX GET -u admin:admin -o running-config.json \\\n -H 'Accept: application/yang-data+json' \\\n https://example.local/restconf/ds/ietf-datastores:running\n\n~$ curl -kX PUT -u admin:admin -d @running-config.json \\\n -H 'Content-Type: application/yang-data+json' \\\n https://example.local/restconf/ds/ietf-datastores:startup\n Using curl.sh:
~$ ./curl.sh -h example.local GET / -o running-config.json\n~$ ./curl.sh -h example.local -d startup PUT / -d @running-config.json\n "},{"location":"scripting-restconf/#operational-data","title":"Operational Data","text":""},{"location":"scripting-restconf/#read-interface-configuration","title":"Read Interface Configuration","text":"Get the running configuration for the loopback interface:
~$ ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=lo\n "},{"location":"scripting-restconf/#read-interface-operational-state","title":"Read Interface Operational State","text":"Get operational data (state, statistics, etc.) for an interface:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=lo\n This includes administrative and operational state, MAC address, MTU, and statistics counters.
"},{"location":"scripting-restconf/#read-interface-statistics","title":"Read Interface Statistics","text":"Extract specific statistics using jq:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][0][\"statistics\"][\"in-octets\"]'\n "},{"location":"scripting-restconf/#list-all-interfaces","title":"List All Interfaces","text":"Get operational data for all interfaces:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces\n "},{"location":"scripting-restconf/#read-routing-table","title":"Read Routing Table","text":"Get the IPv4 routing table:
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/ribs/rib=ipv4-default\n "},{"location":"scripting-restconf/#read-ospf-state","title":"Read OSPF State","text":"Get OSPF operational data (neighbors, routes, etc.):
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default\n Or get just the neighbor information:
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default/ietf-ospf:ospf/areas/area=0.0.0.0/interfaces\n "},{"location":"scripting-restconf/#system-operations-rpcs","title":"System Operations (RPCs)","text":""},{"location":"scripting-restconf/#factory-reset","title":"Factory Reset","text":"Reset the system to factory defaults:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n https://example.local/restconf/operations/ietf-factory-default:factory-reset\ncurl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0\n Note: The connection error is expected - the device resets immediately.
"},{"location":"scripting-restconf/#system-reboot","title":"System Reboot","text":"Reboot the system:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n https://example.local/restconf/operations/ietf-system:system-restart\n "},{"location":"scripting-restconf/#set-date-and-time","title":"Set Date and Time","text":"Example of an RPC that takes input/arguments:
~$ curl -kX POST -u admin:admin \\\n -H \"Content-Type: application/yang-data+json\" \\\n -d '{\"ietf-system:input\": {\"current-datetime\": \"2024-04-17T13:48:02-01:00\"}}' \\\n https://example.local/restconf/operations/ietf-system:set-current-datetime\n Verify the change with SSH:
~$ ssh admin@example.local 'date'\nWed Apr 17 14:48:12 UTC 2024\n "},{"location":"scripting-restconf/#advanced-examples","title":"Advanced Examples","text":""},{"location":"scripting-restconf/#makefile-for-common-operations","title":"Makefile for Common Operations","text":"Create a Makefile to simplify common operations:
HOST ?= infix.local\n\nlo-running:\n ./curl.sh -h $(HOST) GET /ietf-interfaces:interfaces/interface=lo\n\nlo-operational:\n ./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=lo\n\nlo-add-ip:\n ./curl.sh -h $(HOST) POST \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \\\n -d '{ \"prefix-length\": 32 }'\n\nlo-del-ip:\n ./curl.sh -h $(HOST) DELETE \\\n /ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254\n\n%-stats:\n @./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=$* 2>/dev/null \\\n | jq -r '.[\"ietf-interfaces:interfaces\"][\"interface\"][0][\"statistics\"][\"in-octets\"]'\n\n%-monitor:\n while sleep 0.2; do make -s HOST=$(HOST) $*-stats; done \\\n | ttyplot -t \"$(HOST):$* in-octets\" -r\n Usage examples:
# Get loopback operational state\n~$ make lo-operational\n\n# Add IP to loopback\n~$ make lo-add-ip\n\n# Get eth0 statistics\n~$ make eth0-stats\n\n# Monitor eth0 traffic in real-time (requires ttyplot)\n~$ make eth0-monitor\n You can override the host:
~$ make HOST=192.168.1.10 lo-operational\n "},{"location":"scripting-restconf/#monitoring-interface-traffic","title":"Monitoring Interface Traffic","text":"The %-monitor target demonstrates real-time monitoring by polling interface statistics and piping to ttyplot for visualization. Install ttyplot with:
~$ sudo apt install ttyplot\n Then monitor any interface:
~$ make eth0-monitor\n This creates a live ASCII graph of incoming octets on eth0.
"},{"location":"scripting-restconf/#references","title":"References","text":" RESTCONF Protocol (RFC 8040) YANG Data Modeling Language (RFC 7950) ietf-interfaces YANG module ietf-routing YANG module ietf-system YANG module "},{"location":"scripting-sysrepocfg/","title":"Legacy Scripting","text":"Note
This method is a legacy \"simple and human-friendly\" way to manage the system. These days we strongly recommend using RESTCONF instead.
"},{"location":"scripting-sysrepocfg/#legacy-scripting","title":"Legacy Scripting","text":"Although not the primary interface for Infix, it is possible to interact with the system using raw sysrepocfg commands. This way you get to interact directly with the YANG models when logged in to Infix. Thus, a set config, read config, read status and an RPC can be conducted using sysrepocfg for any supported YANG model.
See sysrepocfg for more information. Examples below will utilize:
sysrepocfg -I FILE -fjson -d DATABASE to import/write a JSON formatted configuration file to the specified database. sysrepocfg -E FILE -fjson -d DATABASE to edit/merge JSON formatted configuration in FILE with the specified database. sysrepocfg -R FILE -fjson to execute remote procedure call (RPC) defined in FILE (JSON formatted). sysrepocfg -X -fjson -d DATABASE -x xpath to read configuration or status from specified database. For importing (-I) and editing (-E), -d running is typically used in examples below. Specify -d startup to apply changes to startup configuration. Exporting (-X) could operate on configuration (e.g., -d running) or status (-d operational).
Some commands require a file as input. In the examples below we assume it has been transferred to the device in advance, e.g. using scp:
~$ cat file.json\n{\n \"ietf-factory-default:factory-reset\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$\n "},{"location":"scripting-sysrepocfg/#factory-reset","title":"Factory Reset","text":"~$ cat file.json\n{\n \"ietf-factory-default:factory-reset\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n^C\n~$\n See Factory Reset for another (simpler) alternative.
If it is only wished to copy factory config to running config the following RPC is available
~$ cat file.json\n{\n \"infix-factory-default:factory-default\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n^C\n~$\n "},{"location":"scripting-sysrepocfg/#system-reboot","title":"System Reboot","text":"~$ cat /tmp/file.json\n{\n \"ietf-system:system-restart\": {\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n~$\n See System Reboot for another (simpler) alternative.
"},{"location":"scripting-sysrepocfg/#set-date-and-time","title":"Set Date and Time","text":"~$ ssh admin@example.local 'date'\nSun Nov 20 10:20:23 UTC 2005\n~$ cat file.json\n{\n \"ietf-system:set-current-datetime\": {\n \"current-datetime\": \"2024-04-17T13:48:02-01:00\"\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -fjson -R /tmp/file.json'\n~$ ssh admin@example.local 'date'\nWed Apr 17 14:48:12 UTC 2024\n~$\n See Set Date and Time for another (simpler) alternative.
"},{"location":"scripting-sysrepocfg/#remote-control-of-ethernet-ports","title":"Remote Control of Ethernet Ports","text":"Reading administrative status of interface e0 of running configuration.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running -e report-all \\\n -x \\\"/ietf-interfaces:interfaces/interface[name='e0']/enabled\\\"'\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"enabled\": true\n }\n ]\n }\n}\n~$\n Note
Without -e report-all argument the line \"enabled: true would not be shown as true is default.
~$ ssh admin@example.local \"sysrepocfg -X -fjson -d running \\\n -x \\\"/ietf-interfaces:interfaces/interface[name='e0']/enabled\\\"\"\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\"\n }\n ]\n }\n}\n~$\n Setting the administrative status of interface e0 of running configuration.
$ cat file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"enabled\": false\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n "},{"location":"scripting-sysrepocfg/#enabledisable-dhcpv4-client","title":"Enable/Disable DHCPv4 client","text":"Enabling DHCPv4 client on interface e0, with current default options.
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv4\": {\n \"infix-dhcp-client:dhcp\": {}\n }\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n Disabling DHCPv4 client on interface e0 (remove the dhcp container).
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv4\": {}\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$\n To fully remove the DHCPv4 client configuration, remove the infix-dhcp-client:dhcp container from the interface's ipv4 configuration.
"},{"location":"scripting-sysrepocfg/#enabledisable-ipv6","title":"Enable/Disable IPv6","text":"IPv6 is typically enabled on all interfaces by default. The example below shows IPv4 and IPv6 addresses assigned on e0.
~$ ssh admin@example.local 'ip addr show dev e0'\n2: e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 02:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff\n inet 10.0.2.15/24 scope global proto dhcp e0\n valid_lft forever preferred_lft forever\n inet6 fec0::ff:fe00:0/64 scope site dynamic mngtmpaddr proto kernel_ra\n valid_lft 86380sec preferred_lft 14380sec\n inet6 fe80::ff:fe00:0/64 scope link proto kernel_ll\n valid_lft forever preferred_lft forever\n~$\n IPv6 is enabled/disabled per interface. The example below disables IPv6 on interface e0.
~$ cat /tmp/file.json\n{\n \"ietf-interfaces:interfaces\": {\n \"interface\": [\n {\n \"name\": \"e0\",\n \"ietf-ip:ipv6\": {\n \"enabled\": false\n }\n }\n ]\n }\n}\n~$ scp file.json admin@example.local:/tmp/file.json\n~$ ssh admin@example.local 'sysrepocfg -E /tmp/file.json -fjson -d running'\n~$ ssh admin@example.local 'ip addr show dev e0'\n2: e0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 02:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff\n inet 10.0.2.15/24 scope global proto dhcp e0\n valid_lft forever preferred_lft forever\n~$\n "},{"location":"scripting-sysrepocfg/#change-a-binary-setting","title":"Change a Binary Setting","text":"A YANG binary type setting is Base64 encoded and requires a little bit more tricks. We take the opportunity to showcase a shell script helper: /usr/bin/text-editor, which works just like the text-editor command in the CLI, but this one takes an XPath argument to the binary leaf to edit.
Stripped down, it looks something like this:
if tmp=$(sysrepocfg -G \"$xpath\"); then\n file=$(mktemp)\n\n echo \"$tmp\" | base64 -d > \"$file\"\n if edit \"$file\"; then\n tmp=$(base64 -w0 < \"$file\")\n sysrepocfg -S \"$xpath\" -u \"$tmp\"\n fi\n\n rm -f \"$file\"\nelse\n echo \"Failed to retrieve value for $xpath\"\n exit 1\nfi\n An example container configuration, with an embedded file that is mounted to /var/www/index.html can look like this:
\"infix-containers:containers\": {\n \"container\": [\n {\n \"name\": \"web\",\n \"image\": \"oci-archive:/lib/oci/curios-httpd-latest.tar.gz\",\n \"hostname\": \"web\",\n \"network\": {\n \"interface\": [\n {\n \"name\": \"veth-sys0\"\n }\n ]\n },\n \"mount\": [\n {\n \"name\": \"index.html\",\n \"content\": \"PCFET0NUWVBFIGh0bWwjibberish.shortened.down==\",\n \"target\": \"/var/www/index.html\"\n }\n ]\n }\n ]\n }\n The command to edit this file, and restart the container with the new contents, look like this:
admin@infix:~$ cfg edit \"/infix-containers:containers/container[name='web']/mount[name='index.html']/content\"\n "},{"location":"scripting-sysrepocfg/#backup-configuration","title":"Backup Configuration","text":"Displaying running or startup configuration is possible with sysrepocfg -X, as shown below.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running'\n{\n \"ieee802-dot1ab-lldp:lldp\": {\n \"infix-lldp:enabled\": true\n...\n~$\n An example for backing up startup configuration from remote PC.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d startup > /tmp/backup.json'\n~$ scp admin@example.local:/tmp/backup.json .\n~$\n Or possibly skip intermediate storage of file
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d startup' > backup.json\n~$\n A final example is to only use scp. This is simpler, but only works to backup the startup configuration (not running).
~$ scp admin@example.local:/cfg/startup-config.cfg backup.json\n~$\n "},{"location":"scripting-sysrepocfg/#restore-configuration","title":"Restore Configuration","text":"To restore a backup configuration to startup, the simplest way is to use scp and reboot as shown below
~$ scp admin@example.local:/cfg/startup-config.cfg backup.json\n~$ ssh admin@example.local 'reboot'\nConnection to switch.local closed by remote host.\n~$\n An alternative method to restore a backup configuration is to use the sysrepocfg -I FILE (import) command.
The example below imports the backup configuration to startup, and reboots the unit.
~$ scp backup.json admin@example.local:/tmp/\n~$ ssh admin@example.local 'sudo sysrepocfg -I /tmp/backup.json -fjson -d startup'\n~$ ssh admin@example.local 'reboot'\nConnection to switch.local closed by remote host.\n~$\n Note
The login credentials (hash) for the admin user are stored as part of the configuration file. When replacing a switch and applying the backed up configuration from the former switch, the password on the replacement unit will also change.
"},{"location":"scripting-sysrepocfg/#copy-running-to-startup","title":"Copy Running to Startup","text":"The following command reads out the running config via sysrepocfg -X and writes the result to the startup configuration.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running > /cfg/startup-config.cfg'\n~$\n An alternative is to write it to a temporary file, and use sysrepocfg -I to import it to startup.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d running > /tmp/running.json'\n~$ ssh admin@example.local 'sysrepocfg -I /tmp/running.json -fjson -d startup'\n~$\n "},{"location":"scripting-sysrepocfg/#read-hardware-information","title":"Read Hardware Information","text":"The IETF Hardware YANG model has been augmented for ONIE formatted production data stored in EEPROMs, if available. For details, see the VPD documentation and the ietf-hardware and infix-hardware YANG models.
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d operational -x /ietf-hardware:hardware'\n{\n \"ietf-hardware:hardware\": {\n \"component\": [\n {\n \"name\": \"product\",\n \"class\": \"infix-hardware:vpd\",\n \"serial-num\": \"12345\",\n \"model-name\": \"Switch2010\",\n \"mfg-date\": \"2024-01-30T16:42:37+00:00\",\n \"infix-hardware:vpd-data\": {\n \"product-name\": \"Switch2010\",\n \"part-number\": \"ABC123-001\",\n \"serial-number\": \"007\",\n \"mac-address\": \"00:53:00:01:23:45\",\n \"manufacture-date\": \"01/30/2024 16:42:37\",\n \"num-macs\": 11,\n \"manufacturer\": \"ACME Production\",\n \"vendor\": \"SanFran Networks\"\n }\n },\n {\n \"name\": \"USB\",\n \"class\": \"infix-hardware:usb\",\n \"state\": {\n \"admin-state\": \"unlocked\",\n \"oper-state\": \"enabled\"\n }\n }\n ]\n }\n}\n~$\n "},{"location":"scripting/","title":"Introduction","text":""},{"location":"scripting/#scripting-infix","title":"Scripting Infix","text":"In some situations a user cannot, or does not want to, use the NETCONF API for interacting with Infix. Examples include production tasks and simpler remote scripting jobs to one or more remote devices.
This document assumes you have the password for the admin user, and that you can connect to the device. Please see Finding my Device for help on locating it.
Furthermore, the example commands shown here that are execute from a PC to a remote device over SSH, use Linux/UNIX. With advances lately in both Windows and macOS, many of the user friendly tools previously only available in Linux are now available there too.
The shell prompt for the PC laptop side:
~$\n The shell prompt when logged in to an Infix device:
admin@example:~$\n Note
The shell script commands used here are the raw variants which the CLI usually wraps in a warm and snugly blanket. Meaning they may change over time, while the CLI wrappers do not. That being said, please let us know if you find any inconsistencies.
"},{"location":"scripting/#tips","title":"Tips","text":" Ensure the admin user does not have clish as login shell Enable SSH key authentication Deploy same SSH public key to many Infix devices Secure your private SSH key using, e.g., ssh-agent The ssh-keygen command, used to create the private/public key pair, asks for a passphrase and although this is technically optional it is highly recommended to set one. For ease of use, in particular when scripting, use ssh-agent to avoid retyping the passphrase for every command.
Useful links on SSH, keys, and using ssh-agent:
https://en.wikipedia.org/wiki/Ssh-agent https://www.cyberciti.biz/faq/how-to-use-ssh-agent-for-authentication-on-linux-unix/ https://goteleport.com/blog/how-to-use-ssh-agent-safely/ "},{"location":"scripting/#admin-user-not-authorized","title":"Admin User Not Authorized","text":"All system services and critical configuration files are owned by the locked-down root user. It is not possible to activate the root user account for remote logins. Instead, use admin user and the sudo command prefix.
Here we are logged in to an example device:
admin@example:~$ cp /cfg/startup-config.cfg /cfg/backup-config.cfg\ncp: can't create '/cfg/backup-config.cfg': Permission denied\nadmin@example:~$ sudo cp /cfg/startup-config.cfg /cfg/backup-config.cfg\n "},{"location":"scripting/#examples","title":"Examples","text":"The following example commands are run from the PC over SSH. The following is a very brief introduction.
The notation is ssh username@address, where address can be an IPv4 or IPv6 address, a DNS name, or an mDNS name, e.g. infix.local. In the case of IPv6: address%interface, where interface differs between operating systems. On Linux and macOS the interface name is used, but on Windows the interface index1 is used.
"},{"location":"scripting/#logging-in-to-a-device","title":"Logging in to a device","text":"~$ ssh admin@fe80::ff:fe00:0%eth0\nThe authenticity of host 'fe80::ff:fe00:0%eth0 (fe80::ff:fe00:0%eth0)' can't be established.\nED25519 key fingerprint is SHA256:5/9mw64jhmYyD8MD+SwrsG3RXMBbP48pDe2T8bg14RQ.\nThis key is not known by any other names\nAre you sure you want to continue connecting (yes/no/[fingerprint])? yes\nWarning: Permanently added 'fe80::ff:fe00:0%eth0' (ED25519) to the list of known hosts.\nadmin@fe80::ff:fe00:0%eth0's password: *****\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-c0-ff-ee:/>\n "},{"location":"scripting/#running-a-remote-command","title":"Running a remote command","text":"~$ ssh admin@fe80::ff:fe00:0%eth0 echo hej\nadmin@fe80::ff:fe00:0%eth0's password: *****\nhej\n "},{"location":"scripting/#made-easy-with-mdns","title":"Made Easy with mDNS","text":"Connecting to networked devices using IP addresses is the way many people are used to. The above example with IPv6 tend to scare off people, so for the rest of this document we'll use the mDNS name instead:
~$ ssh admin@infix.local%eth0\nThe authenticity of host 'infix.local%eth0' can't be established.\nAre you sure you want to continue connecting (yes/no/[fingerprint])? yes\n...\nadmin@infix:/>\n "},{"location":"scripting/#factory-reset","title":"Factory Reset","text":"The command option -y disables any \"are you sure?\" interaction and immediately triggers a factory reset and reboot of the device. It is when the device boots up it erases all writable storage.
~$ ssh admin@infix.local%eth0 factory -y\nadmin@infix.local%eth0's password: *****\n "},{"location":"scripting/#system-reboot","title":"System Reboot","text":"~$ ssh admin@infix.local%eth0 reboot\nadmin@infix.local%eth0's password: *****\n "},{"location":"scripting/#set-date-and-time","title":"Set Date and Time","text":"Devices running Infix may have their system time completely off and this can cause problems for upgrading and when accessing the web interface over HTTPS (certificate validation looks at start and end dates).
To set the device's system time, and sync that with the RTC: use the PCs current time as argument:
~$ ssh admin@infix.local%eth0 \"sudo date -s '2024-03-20 18:14+01:00' && \\\n sudo hwclock -w -u\"\nadmin@infix.local%eth0's password: *****\n Tip
The -u option ensures saving system time to the RTC in UTC time.
Verify that the change took:
~$ ssh admin@infix.local%eth0 date\nadmin@infix.local%eth0's password: *****\nWed Mar 20 17:14:47 UTC 2024\n "},{"location":"scripting/#remote-control-of-ethernet-ports","title":"Remote Control of Ethernet Ports","text":"There are two ways to do it:
Change the configuration without saving it to startup-config Change the operational state The first involves sending a NETCONF command/config in XML. The second we will cover here. We start by querying available interfaces (ports) on the remote system:
~$ ssh admin@infix.local%qtap0 ip -br a\nadmin@infix.local%qtap0's password:\nlo UP 127.0.0.1/8 ::1/128\ne0 UP fe80::ff:fe00:0/64\ne1 UP\ne2 UP\ne3 UP\ne4 UP\ne5 UP fe80::ff:fe00:5/64\ne6 UP fe80::ff:fe00:6/64\ne7 UP fe80::ff:fe00:7/64\ne8 UP fe80::ff:fe00:8/64\ne9 UP 192.168.2.200/24 fe80::ff:fe00:9/64\nbr0 UP\n Here we see a loopback interface (lo), ten Ethernet ports (e0-e9) and a bridge (br0). From this quick glance we can guess that the ports e1-e4 are bridged (you can verify this with the remote command bridge link) because they do not have a link-local IPv6 address.
I know it's port e6 that I want to take down:
~$ ssh admin@infix.local%qtap0 ip link set e6 down\nadmin@infix.local%qtap0's password:\nRTNETLINK answers: Operation not permitted\n~$ ssh admin@infix.local%qtap0 sudo ip link set e6 down\nadmin@infix.local%qtap0's password:\n Changing the operational link state of a port is a privileged command, so we have to prefix our command with sudo.
Inspecting the link state again show the port is now down:
~$ ssh admin@infix.local%qtap0 ip -br a\nadmin@infix.local%qtap0's password:\nlo UP 127.0.0.1/8 ::1/128\ne0 UP fe80::ff:fe00:0/64\ne1 UP\ne2 UP\ne3 UP\ne4 UP\ne5 UP fe80::ff:fe00:5/64\ne6 DOWN\ne7 UP fe80::ff:fe00:7/64\ne8 UP fe80::ff:fe00:8/64\ne9 UP 192.168.2.200/24 fe80::ff:fe00:9/64\nbr0 UP\n "},{"location":"scripting/#check-network-connectivity","title":"Check Network Connectivity","text":"Say you want to perform a [System Upgrade][#system-upgrade] and it just does not work, a good place to start troubleshooting is ensuring the device can actually reach the upgrade server.
We run the following ping command remotely on the device:
~$ ssh admin@infix.local%eth0 ping -c 3 server.local\nadmin@infix.local%qtap0's password: *****\nPING server.local (192.168.2.42) 56(84) bytes of data.\n64 bytes from server.local: icmp_seq=1 ttl=64 time=0.201 ms\n64 bytes from server.local: icmp_seq=2 ttl=64 time=0.432 ms\n64 bytes from server.local: icmp_seq=3 ttl=64 time=0.427 ms\n\n--- server.local ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 2050ms\nrtt min/avg/max/mdev = 0.201/0.353/0.432/0.107 ms\n Here we get a reply, so whatever is the issue with the upgrade was not hiding behind a connectivity issue at least.
"},{"location":"scripting/#system-upgrade","title":"System Upgrade","text":"The underlying software that handles upgrades is called RAUC. To trigger an upgrade you (currently) need an FTP/TFTP or HTTP/HTTPS server where RAUC can fetch the upgrade from. In this example we use an FTP server to upgrade the currently inactive \"slot\":
~$ ssh admin@infix.local%eth0 rauc install ftp://server.local/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\ninstalling\n 0% Installing\n 0% Determining slot states\n 20% Determining slot states done.\n 20% Checking bundle\n 20% Verifying signature\n 40% Verifying signature done.\n 40% Checking bundle done.\n 40% Checking manifest contents\n 60% Checking manifest contents done.\n 60% Determining target install group\n 80% Determining target install group done.\n 80% Updating slots\n 80% Checking slot rootfs.1\n 90% Checking slot rootfs.1 done.\n 90% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nidle\nInstalling `ftp://server.local/infix-aarch64-24.06.0.pkg` succeeded\n~$\n The inactive slot is now marked active and will be used on the next boot. To upgrade the partition we booted from, we must first reboot.
For more information, see the Boot Procedure document.
Alternative:
If you know your device has sufficient storage, eMMC or RAM disk (check with the remote command df -h), you can also copy the .pkg file to the device instead of having to set up an FTP/TFTP or HTTP/HTTPS server.
Create an upload directory where admin has write permission:
~$ ssh admin@infix.local%eth0 \"sudo mkdir /var/tmp/upload; \\\n sudo chown admin /var/tmp/upload\"\nadmin@infix.local%eth0's password:\n Copy the file with secure copy, first we show the nasty IPv6 version of the command:
~$ scp infix-aarch64-24.06.0.pkg admin@\\[fe80::ff:fe00:0%eth0\\]:/var/tmp/upload/\nadmin@fe80::ff:fe00:0%eth0's password:\ninfix-aarch64-24.06.0.pkg 100% 296 601.4KB/s 00:00\n And the upgrade command itself:
~$ ssh admin@infix.local%eth0 rauc install /var/tmp/upload/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\n.\n.\n.\n Remember to remove the file from the upload directory when you are done, this can be done before or after the reboot to activate the upgrade. If you want to upgrade both \"slots\", then you can of course keep the file until you are done (provided the upload directory was created on persistent storage).
~$ ssh admin@infix.local%eth0 rm /var/tmp/upload/infix-aarch64-24.06.0.pkg\nadmin@infix.local%eth0's password: *****\n~$\n "},{"location":"scripting/#controlling-leds","title":"Controlling LEDs","text":"As part of production testing, LED verification is often expected to be performed. Infix uses standard Linux support for LED management, where LEDs appear in the file system under /sys/class/leds and can be controlled using echo command. sudo privileges are required.
When interacting with LEDs this way, first disable the Infix iitod daemon to avoid conflicting LED control.
~$ ssh admin@example.local 'initctl stop iitod'\n Then run the test, e.g., visually control that a red LED labeled 'LAN' is working.
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\\:lan/trigger'\n~$ ssh admin@example.local 'echo 1 | sudo tee /sys/class/leds/red\\:lan/brightness'\n To turn off the same LED, run the following commands.
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\\:lan/trigger'\n~$ ssh admin@example.local 'echo 0 | sudo tee /sys/class/leds/red\\:lan/brightness'\n When done with LED testing, enable Infix iitod daemon again.
~$ ssh admin@example.local 'initctl start iitod'\n "},{"location":"scripting/#reading-power-feed-status","title":"Reading Power Feed Status","text":"As part of production tests, verification of Power Feed sensors is often expected to be performed. Infix uses standard Linux support for Power management, where power sources appear in the file system under /sys/class/power_supply. The following example reads status of two power supplies named pwr1 and pwr2.
~$ ssh admin@example 'cat /sys/class/power_supply/pwr1/online'\n1\n~$ ssh admin@example 'cat /sys/class/power_supply/pwr2/online'\n0\n~$\n Here, only pwr1 have power.
Press Win-r to bring up the Run dialog, enter cmd.exe and press enter. Then type in ipconfig /all to list all interfaces, their status, as well as interface index.\u00a0\u21a9
"},{"location":"support/","title":"Support Data","text":""},{"location":"support/#support-data-collection","title":"Support Data Collection","text":"When troubleshooting issues or seeking support, the support command provides a convenient way to collect comprehensive system diagnostics. This command gathers configuration files, logs, network state, and other system information into a single compressed archive.
"},{"location":"support/#collecting-support-data","title":"Collecting Support Data","text":"To collect support data and save it to a file:
admin@host:~$ support collect > support-data.tar.gz\n(admin@host) Password: ***********\nStarting support data collection from host...\nThis may take up to a minute. Please wait...\nTailing /var/log/messages for 30 seconds (please wait)...\nLog tail complete.\nCollection complete. Creating archive...\nadmin@host:~$ ls -l support-data.tar.gz\n-rw-rw-r-- 1 admin admin 508362 nov 30 13:05 support-data.tar.gz\n The command can also be run remotely via SSH from your workstation:
$ ssh admin@host support collect > support-data.tar.gz\n...\n The collection process may take up to a minute depending on system load and the amount of logging data. Progress messages are shown during the collection process.
"},{"location":"support/#encrypted-collection","title":"Encrypted Collection","text":"For secure transmission of support data, the archive can be encrypted with GPG using a password:
admin@host:~$ support collect -p mypassword > support-data.tar.gz.gpg\nStarting support data collection from host...\nThis may take up to a minute. Please wait...\n...\nCollection complete. Creating archive...\nEncrypting with GPG...\n The support collect command even supports omitting mypassword and will then prompt interactively for the password. This works over SSH too, but the local ssh client may then echo the password.
Tip
To hide the encryption password for an SSH session, the script supports reading from stdin: echo \"$MYSECRET\" | ssh user@device support collect -p > file.tar.gz.gpg
After transferring the resulting file to your workstation, decrypt it with the password:
$ gpg -d support-data.tar.gz.gpg > support-data.tar.gz\n$ tar xzf support-data.tar.gz\n...\n or
$ gpg -d support-data.tar.gz.gpg | tar xz\n...\n Important
Make sure to share mypassword out-of-band from the encrypted data with the recipient of the data. I.e., avoid sending both in the same plain-text email for example.
"},{"location":"support/#what-is-collected","title":"What is Collected","text":"The support archive includes:
System identification (hostname, uptime, kernel version) Running and operational configuration (sysrepo datastores) System logs (/var/log directory and live tail of messages log) Network configuration and state (interfaces, routes, neighbors, bridges) FRRouting information (OSPF, BFD status) Container information (podman containers and their configuration) System resource usage (CPU, memory, disk, processes) Hardware information (PCI, USB devices, network interfaces) "},{"location":"syslog/","title":"Syslog Support","text":""},{"location":"syslog/#syslog-support","title":"Syslog Support","text":"The system comes with native logging to internal storage, /var/log/, or /log for a shortcut. Depending on the device, this may be a RAM disk, meaning logs are not retained across reboots. This document details how to log to external media or remote syslog servers.
It is also possible to set up the device to act as a syslog server (log sink), this is covered briefly at the very end of this document.
Note
The default logging setup in the system cannot be modified, only the log file rotation. Please see the dir admin-exec command for a listing of existing log files.
"},{"location":"syslog/#log-to-file","title":"Log to File","text":"Logging to a local file is useful when combined with an external media. E.g., a USB stick with a log partition (named/labeled: \"log\"). Below is an example.
For a list of available log facilities, see the table in a later section.
admin@example:/> configure\nadmin@example:/config/> edit syslog actions log-file file:/media/log/mylog\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list\n all audit auth authpriv console cron cron2 daemon ftp kern\n local0 local1 local2 local3 local4 local5 local6 local7 lpr mail\n news ntp syslog user uucp\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list all severity\n alert all critical debug emergency error info none notice warning\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list all severity critical\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> set facility-list mail severity warning\nadmin@example:/config/syslog/\u2026/file:/media/log/mylog/> leave\nadmin@example:/>\n Important
The log-file syntax requires the leading prefix file:. If the path is not absolute, e.g., file:mylog, the file is saved to the system default path, i.e., /log/mylog. In this case, verify that the filename is not already in use.
"},{"location":"syslog/#log-rotation","title":"Log Rotation","text":"By default log files are allowed to grow to a size of 1 MiB after which they are \"rotated\". The whole reason for this is to not fill up the disk with outdated logs. A rotated file is saved in stages and older ones are also compressed (using gzip). Use the show log command in admin-exec context to start the log file viewer:
admin@example:/config/syslog/> do show log\nlog log.0 log.1.gz log.2.gz log.3.gz log.4.gz log.5.gz\nadmin@example:/config/syslog/> do show log log.1.gz\n Tip
Use the Tab key on your keyboard list available log files. The do prefix is also very useful in configure context to access commands from admin-exec context.
By default 10 compressed older files are saved. Here the oldest is log.5.gz and the most recently rotated one is log.0.
Log file rotation can be configured both globally and per log file. Here we show the global settings, the set up is the same for per log file, which if unset inherit the global settings:
admin@example:/> configure\nadmin@example:/config/> edit syslog file-rotation\nadmin@example:/config/syslog/file-rotation/> show\nadmin@example:/config/syslog/file-rotation/>\n The defaults are not shown. We can inspect them by asking the YANG model for the help texts:
admin@example:/config/syslog/file-rotation/> help\n max-file-size number-of-files\nadmin@example:/config/syslog/file-rotation/> help max-file-size \nNAME\n max-file-size kilobytes\n\nDESCRIPTION\n Maximum log file size (kiB), before rotation.\n\nDEFAULT\n 1024\nadmin@example:/config/syslog/file-rotation/> help number-of-files \nNAME\n number-of-files [0..4294967295]\n\nDESCRIPTION\n Maximum number of log files retained.\n\nDEFAULT\n 10\n To change the defaults to something smaller, 512 kiB and 20 (remember everything after .0 is compressed, and text compresses well):
admin@example:/config/syslog/file-rotation/> set max-file-size 512\nadmin@example:/config/syslog/file-rotation/> set number-of-files 20\nadmin@example:/config/syslog/file-rotation/> show\nnumber-of-files 20;\nmax-file-size 512;\nadmin@example:/config/syslog/file-rotation/> leave\nadmin@example:/>\n "},{"location":"syslog/#log-format","title":"Log Format","text":"There are three major syslog log formats, the default is RFC3164 for log files and BSD for remote logging. Depending on time synchronization and remote log server capabilities, or policies, the RFC5424 format is often preferred since it not only has better time resolution but also supports structured logging:
BSD : myproc[8710]: Kilroy was here.\nRFC3164 : Aug 24 05:14:15 192.0.2.1 myproc[8710]: Kilroy was here.\nRFC5424 : 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - Kilroy was here.\n The BSD format is only applicable to remote logging. It remains the default for compatibility reasons, and is recommended since the device may not have proper time, making it better for the remote log server to perform time stamping at the time of arrival.
Configuring the log format is the same for log files and remotes:
admin@example:/config/> edit syslog actions log-file file:foobar\nadmin@example:/config/syslog/\u2026/file:foobar/> set log-format\n bsd rfc3164 rfc5424\nadmin@example:/config/syslog/\u2026/file:foobar/> set log-format rfc5424\nadmin@example:/config/syslog/\u2026/file:foobar/> leave\nadmin@example:/>\n "},{"location":"syslog/#log-to-remote-server","title":"Log to Remote Server","text":"Logging to a remote syslog server is the recommended way of supervising the system. This way all login attempts (console, SSH, or web) and any configuration changes can be traced, even in cases of a remote attacker tries to cover their traces by deleting logs.
The recommended setup involves using a fixed IP address, default BSD log format, and the default Internet port (514). This is the most reliable, because your device may not have DNS set up or even available, and some remote syslog servers do not support receiving time stamped log messages -- this is of course entirely dependent on how the remote server is set up, as well as local policy.
admin@example:/config/> edit syslog\n actions file-rotation server\nadmin@example:/config/> edit syslog actions destination moon\nadmin@example:/config/syslog/\u2026/moon/> set\n facility-list log-format udp\nadmin@example:/config/syslog/\u2026/moon/> set udp\n address port\nadmin@example:/config/syslog/\u2026/moon/> set udp address 192.168.0.12\nadmin@example:/config/syslog/\u2026/moon/> set facility-list container severity all\nadmin@example:/config/syslog/\u2026/moon/> leave\nadmin@example:/>\n Tip
The alternatives shown below each prompt in the example above can be found by tapping the Tab key.
"},{"location":"syslog/#acting-as-a-log-server","title":"Acting as a Log Server","text":"The syslog server can act as a log sink for other devices on a LAN. For this to work you need a static IP address, here we use 10.0.0.1/24.
admin@example:/> configure\nadmin@example:/config/> edit syslog server\nadmin@example:/config/syslog/server/> set enabled true\nadmin@example:/config/syslog/server/> set listen udp 514 address 10.0.0.1\nadmin@example:/config/syslog/server/> leave\nadmin@example:/>\n See the above Log to File section on how to set up filtering of received logs to local files. Advanced filtering based on hostname and message properties is also available, see the next section for details.
"},{"location":"syslog/#advanced-filtering","title":"Advanced Filtering","text":"The syslog subsystem supports several advanced filtering options that allow fine-grained control over which messages are logged. These can be combined with facility and severity filters to create sophisticated logging rules.
"},{"location":"syslog/#pattern-matching","title":"Pattern Matching","text":"Messages can be filtered using regular expressions (POSIX extended regex) on the message content. This is useful when you want to log only messages containing specific keywords or patterns:
admin@example:/config/> edit syslog actions log-file file:errors\nadmin@example:/config/syslog/\u2026/file:errors/> set pattern-match \"ERROR|CRITICAL|FATAL\"\nadmin@example:/config/syslog/\u2026/file:errors/> set facility-list all severity info\nadmin@example:/config/syslog/\u2026/file:errors/> leave\nadmin@example:/>\n This will log all messages containing ERROR, CRITICAL, or FATAL.
"},{"location":"syslog/#advanced-severity-comparison","title":"Advanced Severity Comparison","text":"By default, severity filtering uses \"equals-or-higher\" comparison, meaning a severity of error will match error, critical, alert, and emergency messages. You can change this behavior:
admin@example:/config/> edit syslog actions log-file file:daemon-errors\nadmin@example:/config/syslog/\u2026/file:daemon-errors/> set facility-list daemon\nadmin@example:/config/syslog/\u2026/daemon/> set severity error\nadmin@example:/config/syslog/\u2026/daemon/> set advanced-compare compare equals\nadmin@example:/config/syslog/\u2026/daemon/> leave\nadmin@example:/>\n This will log only error severity messages, not higher severities.
You can also block specific severities:
admin@example:/config/syslog/\u2026/daemon/> set advanced-compare action block\n This will exclude error messages from the log.
"},{"location":"syslog/#hostname-filtering","title":"Hostname Filtering","text":"When acting as a log server, you can filter messages by hostname. This is useful for directing logs from different devices to separate files:
admin@example:/config/> edit syslog actions log-file file:router1\nadmin@example:/config/syslog/\u2026/file:router1/> set hostname-filter router1\nadmin@example:/config/syslog/\u2026/file:router1/> set facility-list all severity info\nadmin@example:/config/syslog/\u2026/file:router1/> leave\nadmin@example:/>\n Multiple hostnames can be added to the filter list.
"},{"location":"syslog/#property-based-filtering","title":"Property-Based Filtering","text":"For more advanced filtering, you can match on specific message properties using various comparison operators:
admin@example:/config/> edit syslog actions log-file file:myapp\nadmin@example:/config/syslog/\u2026/file:myapp/> edit property-filter\nadmin@example:/config/syslog/\u2026/property-filter/> set property programname\nadmin@example:/config/syslog/\u2026/property-filter/> set operator isequal\nadmin@example:/config/syslog/\u2026/property-filter/> set value myapp\nadmin@example:/config/syslog/\u2026/property-filter/> leave\nadmin@example:/>\n Available properties: - msg: Message body - msgid: RFC5424 message identifier - programname: Program/tag name - hostname: Source hostname - source: Alias for hostname - data: RFC5424 structured data
Available operators: - contains: Substring match - isequal: Exact equality - startswith: Prefix match - regex: Basic regular expression - ereregex: Extended regular expression (POSIX ERE)
The comparison can be made case-insensitive:
admin@example:/config/syslog/\u2026/property-filter/> set case-insensitive true\n Or negated to exclude matching messages:
admin@example:/config/syslog/\u2026/property-filter/> set negate true\n "},{"location":"syslog/#facilities","title":"Facilities","text":"Code Facility Description 0 kern Kernel log messages 1 user User-level messages 2 mail Mail system 3 daemon General system daemons 4 auth Security/authorization messages 5 syslog Messages generated by syslogd 6 lpr Line printer subsystem 7 news Network news subsystem 8 uucp UNIX-to-UNIX copy 9 cron Clock/cron daemon (BSD, Linux) 10 authpriv Security/authorization messages (private) 11 ftp FTP daemon 12 ntp NTP subsystem 13 audit Log audit (security) 14 console Log alert 15 cron2 Clock/cron daemon (Solaris) 16 rauc* local0, reserved for RAUC 17 container* local1, reserved for containers 18 local2 Currently unused 19 local3 Currently unused 20 local4 Currently unused 21 local5 Currently unused 22 reserved* local6, reserved for industrial Ethernet 23 web* local7, reserved for nginx web server Facilities marked * are local augments to the model.
"},{"location":"system/","title":"Configuration","text":""},{"location":"system/#system-configuration","title":"System Configuration","text":"System settings in Infix are provided by the ietf-system YANG model, augmented with Linux specific extensions in infix-system, like Message of the Day (login message) and user login shell. More on this later on in this document.
For the sake of brevity, the hostname in the following examples has been shortened to host. The default hostname is composed from a product specific string followed by the last three octets of the system base MAC address, e.g., switch-12-34-56. An example of how to change the hostname is included below.
Note
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"system/#changing-password","title":"Changing Password","text":"User management, including passwords, SSH keys, remote authentication is available in the system authentication configuration context.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> change password\nNew password:\nRetype password:\nadmin@host:/config/system/authentication/user/admin/> leave\n The change password command starts an interactive dialogue that asks for the new password, with a confirmation, and then salts and encrypts the password with sha512crypt.
It is also possible to use the set password ... command. This allows setting an already hashed password. To manually hash a password, use the do password encrypt command. This launches the admin-exec command to hash, and optionally salt, your password. This encrypted string can then be used with set password ....
Tip
If you are having trouble thinking of a password, there is a nifty password generate command in admin-exec context which generates random passwords using the UNIX command pwgen. Use the do prefix when inside any configuration context to access admin-exec commands.
"},{"location":"system/#ssh-authorized-key","title":"SSH Authorized Key","text":"Logging in remotely with SSH is possible by adding a public key to a user. Here we add the authorized key to the admin user, multiple keys are supported.
With SSH keys in place it is possible to disable password login, just remember to verify SSH login and network connectivity before doing so.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> edit authorized-key example@host\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> show\nalgorithm ssh-rsa;\nkey-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=;\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> leave\n Note
The ssh-keygen program already base64 encodes the public key data, so there is no need to use the text-editor command, set does the job.
"},{"location":"system/#multiple-users","title":"Multiple Users","text":"The system supports multiple users and multiple user levels, or groups, that a user can be a member of. Access control is entirely handled by the NETCONF \"NACM\" YANG model, which provides granular access to configuration, data, and RPC commands over NETCONF.
By default the system ships with a single group, admin, which the default user admin is a member of. The broad permissions granted by the admin group is what gives its users full system administrator privileges. There are no restrictions on the number of users with administrator privileges, nor is the admin user reserved or protected in any way -- it is completely possible to remove the default admin user from the configuration. However, it is recommended to keep at least one user with administrator privileges in the system, otherwise the only way to regain full access is to perform a factory reset.
"},{"location":"system/#adding-a-user","title":"Adding a User","text":"Similar to how to change password, adding a new user is done using the same set of commands:
admin@host:/config/> edit system authentication user jacky\nadmin@host:/config/system/authentication/user/jacky/> change password\nNew password:\nRetype password:\nadmin@host:/config/system/authentication/user/jacky/> leave\n An authorized SSH key is added the same way as presented previously.
"},{"location":"system/#adding-a-user-to-the-admin-group","title":"Adding a User to the Admin Group","text":"The following commands add user jacky to the admin group.
admin@host:/config/> edit nacm group admin\nadmin@host:/config/nacm/group/admin/> set user-name jacky\nadmin@host:/config/nacm/group/admin/> leave\n "},{"location":"system/#security-aspects","title":"Security Aspects","text":"The NACM user levels apply primarily to NETCONF, with exception of the admin group which is granted full system administrator privileges to the underlying UNIX system with the following ACL rules:
...\n \"module-name\": \"*\",\n \"access-operations\": \"*\",\n \"action\": \"permit\",\n ...\n A user in the admin group is allowed to also use a POSIX login shell and use the sudo command to perform system administrative commands. This makes it possible to use all the underlying UNIX tooling, which to many can be very useful, in particular when debugging a system, but please remember to use with care -- the system is not built to require managing from the shell. The tools available in the CLI and automated services, started from the system's configuration, are the recommended way of using the system, in addition to NETCONF tooling.
"},{"location":"system/#changing-hostname","title":"Changing Hostname","text":"Notice how the hostname in the prompt does not change until the change is committed by issuing the leave command.
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname example\nadmin@host:/config/system/> leave\nadmin@example:/>\n The hostname is advertised over mDNS-SD in the .local domain. If another device already has claimed the example.local CNAME, in our case, mDNS will advertise a \"uniqified\" variant, usually suffixing with an index, e.g., example-1.local. Use an mDNS browser to scan for available devices on your LAN.
In some cases you may want to set the device's domain name as well. This is handled the same way:
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname foo.example.com\nadmin@host:/config/system/> leave\nadmin@foo:/>\n Both host and domain name are stored in the system files /etc/hosts and /etc/hostname. The latter is exclusively for the host name. The domain may be used by the system DHCP server when handing out leases to clients, it is up to the clients to request the domain name option.
Note
Critical services like syslog, mDNS, LLDP, and similar that advertise the hostname, are restarted when the hostname is changed.
"},{"location":"system/#changing-login-banner","title":"Changing Login Banner","text":"The motd-banner setting is an Infix augment and an example of a binary type setting that can be changed interactively with the built-in text-editor command.
Tip
See the next section for how to change the editor used to something you may be more familiar with.
admin@host:/config/> edit system\nadmin@host:/config/system/> text-editor motd-banner\nadmin@host:/config/system/> leave\nadmin@host:/>\n Log out and log back in again to inspect the changes.
"},{"location":"system/#changing-the-editor","title":"Changing the Editor","text":"The system has three different built-in editors that can be used as the text-editor command:
emacs (Micro Emacs) nano (GNU Nano) vi (Visual Editor) To change the editor to GNU Nano:
admin@host:/> configure\nadmin@host:/config/> edit system\nadmin@host:/config/system/> set text-editor nano\nadmin@host:/config/system/> leave\nadmin@host:/>\n Important
Configuration changes only take effect after issuing the leave command. I.e., you must change the editor first, and then re-enter configure context to use your editor of choice.
"},{"location":"system/#dns-resolver-configuration","title":"DNS Resolver Configuration","text":"The system supports both static and dynamic (DHCP) DNS setup. The locally configured (static) server is preferred over any acquired from a DHCP client.
admin@host:/> configure\nadmin@host:/config/> edit system dns-resolver\nadmin@host:/config/system/dns-resolver/> set server google udp-and-tcp address 8.8.8.8\nadmin@host:/config/system/dns-resolver/> show\nserver google {\n udp-and-tcp {\n address 8.8.8.8;\n }\n}\nadmin@host:/config/system/dns-resolver/> leave\n It is also possible to configure resolver options like timeout and retry attempts. See the YANG model for details, or use the built-in help system in the CLI.
Note
When acting as a DHCP server and DNS proxy for other devices, any local DNS server configured here is automatically used as upstream DNS server.
"},{"location":"system/#ntp-client-configuration","title":"NTP Client Configuration","text":"Below is an example configuration for enabling NTP with a specific server and the iburst option for faster initial synchronization.
admin@host:/> configure\nadmin@host:/config/> edit system ntp\nadmin@host:/config/system/ntp/> set enabled\nadmin@host:/config/system/ntp/> set server ntp-pool\nadmin@host:/config/system/ntp/> set server ntp-pool udp address pool.ntp.org\nadmin@host:/config/system/ntp/> set server ntp-pool iburst\nadmin@host:/config/system/ntp/> set server ntp-pool prefer\nadmin@host:/config/system/ntp/> leave\n This configuration enables the NTP client and sets the NTP server to pool.ntp.org with the iburst and prefer options. The iburst option ensures faster initial synchronization, and the prefer option designates this server as preferred.
prefer false: The NTP client will choose the best available source based on several factors, such as network delay, stratum, and other metrics (default config). prefer true: The NTP client will try to use the preferred server as the primary source unless it becomes unreachable or unusable. "},{"location":"system/#show-ntp-sources","title":"Show NTP Sources","text":"The status for NTP sources is availble in YANG and accessable with CLI/NETCONF/RESTCONF.
To view the sources being used by the NTP client, run:
admin@target:/> show ntp\nADDRESS MODE STATE STRATUM POLL-INTERVAL\n192.168.1.1 server candidate 1 6\n192.168.2.1 server candidate 1 6\n192.168.3.1 server selected 1 6\n
"},{"location":"system/#show-ntp-status","title":"Show NTP Status","text":"To check the status of NTP synchronization (only availble in CLI), use the following command:
admin@host:/> show ntp tracking\nReference ID : C0248F86 (192.36.143.134)\nStratum : 2\nRef time (UTC) : Mon Oct 21 10:06:45 2024\nSystem time : 0.000000001 seconds slow of NTP time\nLast offset : -3845.151367188 seconds\nRMS offset : 3845.151367188 seconds\nFrequency : 4.599 ppm slow\nResidual freq : +1293.526 ppm\nSkew : 12.403 ppm\nRoot delay : 1.024467230 seconds\nRoot dispersion : 0.273462683 seconds\nUpdate interval : 0.0 seconds\nLeap status : Normal\nadmin@host:/>\n This output provides detailed information about the NTP status, including reference ID, stratum, time offsets, frequency, and root delay.
Tip
The system uses chronyd Network Time Protocol (NTP) daemon. The output shown here is best explained in the Chrony documentation.
"},{"location":"test-arch/","title":"Test System Architecture","text":""},{"location":"test-arch/#test-system-architecture","title":"Test System Architecture","text":""},{"location":"test-arch/#tenets","title":"Tenets","text":" Keep overhead to a minimum. Tests should be fast to both write and run. Ideally, the developer should want to add tests early in the development cycle because they instinctively feel that that is the quickest route to arrive at a correct and robust implementation.
Both physical and virtual hardware matters. Infix is primarily deployed on physical hardware, so being able to run the test suite on real devices is crucial to guarantee a high quality product. At the same time, there is much value in running the same suite on virtual hardware, as it makes it easy to catch regressions early. It is also much more practical and economical to build large virtual networks than physical ones.
Avoid CLI scripting & scraping. Reliably interacting with a DUT over a serial line in a robust way is very hard to get right. Given that we have a proper API (RESTCONF), we should leverage that when testing. Front-ends can be tested by other means.
"},{"location":"test-arch/#overview","title":"Overview","text":"The test system is made up of several independent components, which are typically used in concert to run a full test suite.
"},{"location":"test-arch/#test-cases","title":"Test Cases","text":"A test case is an executable, receiving the physical topology as a positional argument, which produces TAP compliant output on its stdout. I.e., it is executed in the following manner:
test-case [OPTS] <physical-topology>\n Test cases are typically written in Python, using the Infamy library. Ultimately though, it can be implemented in any language, as long as it matches the calling convention above.
"},{"location":"test-arch/#infamy","title":"Infamy","text":"Rather than having each test case come up with its own implementation of how to map topologies, how to push NETCONF data to a device, etc., we provide a library of functions to take care of all that, dubbed \"Infamy\". When adding a new test case, ask yourself if any parts of it might belong in Infamy as a generalized component that can be reused by other tests.
Some of the core functions provided by Infamy are:
Mapping a logical topology to a physical one Finding and attaching to a device over an Ethernet interface, using NETCONF Pushing/pulling NETCONF data to/from a device Generating TAP compliant output "},{"location":"test-arch/#9pm","title":"9PM","text":"To run multiple tests, we employ 9PM. It let's us define test suites as simple YAML files. Suites can also be hierarchically structured, with a suite being made up of other suites, etc.
It also validates the TAP output, making sure to catch early exits from a case, and produces a nice summary report.
"},{"location":"test-arch/#testenv","title":"/test/env","text":"A good way to ensure that nobody ever runs the test suite is to make it really hard to do so. /test/env's job is instead to make it very easy to create a reproducible environment in which tests can be executed.
Several technologies are leveraged to accomplish this:
Containers: The entire execution is optionally done inside a standardized docker container environment. This ensures that the software needed to run the test suite is always available, no matter which distribution the user is running on their machine.
Python Virtual Environments: To make sure that the expected versions of all Python packages are available, the execution is wrapped inside a venv. This is true for containerized executions, where the container comes with a pre-installed environment, but it can also be sourced from the host system when running outside of the container.
Virtual Test Topology: Using Qeneth, the environment can optionally be started with a virtual topology of DUTs to run the tests on.
docker is the only supported container environment when running tests in the host's network namespace. When running on a virtual Qeneth topology, podman may also be used by installing the podman-docker package from your host system's distro.
"},{"location":"test-arch/#physical-and-logical-topologies","title":"Physical and Logical Topologies","text":"Imagine that we want to create a test with three DUTs; one acting as a DHCP server, and the other two as DHCP clients - with all three having a management connection to the host PC running the test. In other words, the test requires a logical topology like the one below.
graph \"dhcp-client-server\" {\n host [\n label=\"host | { <c1> c1 | <srv> srv | <c2> c2 }\",\n kind=\"controller\",\n ];\n\n server [\n label=\"{ <mgmt> mgmt } | server | { <c1> c1 | <c2> c2 }\",\n kind=\"infix\",\n ];\n client1 [\n label=\"{ <mgmt> mgmt } | client1 | { <srv> srv }\",\n kind=\"infix\",\n ];\n client2 [\n label=\"{ <mgmt> mgmt } | client2 | { <srv> srv }\",\n kind=\"infix\",\n ];\n\n host:srv -- server:mgmt\n host:c1 -- client1:mgmt\n host:c2 -- client2:mgmt\n\n server:c1 -- client1:srv;\n server:c2 -- client2:srv;\n}\n When running in a virtualized environment, one could simply create a setup that matches the test's logical topology. But in scenarios when devices are physical systems, connected by real copper cables, this is not possible (unless you have some wicked L1 relay matrix thingy).
Instead, the test implementation does not concern itself with the exact nodes used to run the test, only that the logical topology can be mapped to some subset of the physical topology. In mathematical terms, the physical topology must contain a subgraph that is isomorphic to the logical topology.
Standing on the shoulders of giants (i.e. people with mathematics degrees), we can deploy well-known algorithms to find such subgraphs. Continuing our example, let's say we want to run our DHCP test on the physical topology below.
graph \"quad-ring\" {\n host [\n label=\"host | { <d1a> d1a | <d1b> d1b | <d1c> d1c | <d2a> d2a | <d2b> d2b | <d2c> d2c | <d3a> d3a | <d3b> d3b | <d3c> d3c | <d4a> d4a | <d4b> d4b | <d4c> d4c }\",\n kind=\"controller\",\n ];\n\n dut1 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut1 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut2 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut2 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut3 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut3 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n dut4 [\n label=\"{ <e1> e1 | <e2> e2 | <e3> e3 } | dut4 | { <e4> e4 | <e5> e5 }\",\n kind=\"infix\",\n ];\n\n host:d1a -- dut1:e1\n host:d1b -- dut1:e2\n host:d1c -- dut1:e3\n\n host:d2a -- dut2:e1\n host:d2b -- dut2:e2\n host:d2c -- dut2:e3\n\n host:d3a -- dut3:e1\n host:d3b -- dut3:e2\n host:d3c -- dut3:e3\n\n host:d4a -- dut4:e1\n host:d4b -- dut4:e2\n host:d4c -- dut4:e3\n\n dut1:e5 -- dut2:e4\n dut2:e5 -- dut3:e4\n dut3:e5 -- dut4:e4\n dut4:e5 -- dut1:e4\n}\n Our test (in fact, all tests) receives the physical topology as an input parameter, and then maps the desired logical topology onto it, producing a mapping from logical nodes and ports to their physical counterparts.
{\n \"client1\": \"dut1\",\n \"client1:mgmt\": \"dut1:e1\",\n \"client1:srv\": \"dut1:e4\",\n \"client2\": \"dut3\",\n \"client2:mgmt\": \"dut3:e2\",\n \"client2:srv\": \"dut3:e5\",\n \"host\": \"host\",\n \"host:c1\": \"host:d1a\",\n \"host:c2\": \"host:d3b\",\n \"host:srv\": \"host:d4c\",\n \"server\": \"dut4\",\n \"server:c1\": \"dut4:e5\",\n \"server:c2\": \"dut4:e4\",\n \"server:mgmt\": \"dut4:e3\"\n}\n With this information, the test knows that, in this particular environment, the server should be managed via the port called d4c on the node called host; that the port connected to the server on client1 is e4 on dut1, etc. Thereby separating the implementation of the test from any specific physical setup.
Testcases are not required to use a logical topology; they may choose to accept whatever physical topology its given, and dynamically determine the DUTs to use for testing. As an example, an STP test could accept an arbitrary physical topology, run the STP algorithm on it offline, enable STP on all DUTs, and then verify that the resulting spanning tree matches the expected one.
"},{"location":"test-arch/#integration-to-infix","title":"Integration to Infix","text":"When the test environment is started with Qeneth, it doesn't use the base image directly. Instead, it creates a copy and inserts a test-mode flag into it. During the bootstrap phase, the system checks for the presence of the test-mode flag (file).
If the flag exists, a 'test-config.cfg' file is generated. In the following step, the system loads the 'test-config' instead of the standard startup-config (or factory-config). This configuration is simple and safe, equivalent to the one used in 'Secure Mode' (also known as 'failure-config').
Additionally, the configuration enables extra RPCs related to system restart and configuration overrides, allowing tests to be run even on systems where the factory configuration may potentially create L2 loops.
"},{"location":"testing/","title":"Regression Testing","text":""},{"location":"testing/#regression-testing-with-infamy","title":"Regression Testing with Infamy","text":"Infix comes with a test suite that is intended to provide end-to-end verification of supported features. Generally speaking, this means that one or more DUTs are configured over NETCONF or RESTCONF; the resulting network is then black-box tested by injecting and inspecting network traffic at various points.
This document is intended to be a practical guide on how to run, develop and debug tests. There is a separate document describing the Test System Architecture.
"},{"location":"testing/#modes-of-testing","title":"Modes of Testing","text":""},{"location":"testing/#virtual-devices","title":"Virtual Devices","text":"By default, tests are run on a topology made up of virtual Infix nodes using Qeneth. To run the full regression test suite: build Infix for x86_64:
$ make x86_64_defconfig\n...\n$ make\n...\n$ make test\n...\n "},{"location":"testing/#physical-devices","title":"Physical Devices","text":"To run the tests on a preexisting topology from the host's network namespace, specify the host TEST_MODE:
$ make TEST_MODE=host test\n...\n This typically used when testing on physical hardware. By default the topology will be sourced from /etc/infamy.dot, but this can be overwritten by setting the TOPOLOGY variable:
$ make TEST_MODE=host TOPOLOGY=~/my-topology.dot test\n...\n "},{"location":"testing/#make-run-devices","title":"make run Devices","text":"Some tests only require a single DUT. These can therefore be run against an Infix image started from make run. This requires that the instance is configured to use TAP networking.
When the instance is running, you can open a separate terminal and run the subset of the test suite that can be mapped to it:
$ make TEST_MODE=run test\n...\n "},{"location":"testing/#interactive-usage","title":"Interactive Usage","text":"When developing and debugging tests, the overhead of repeatedly setting up and tearing down the test environment can quickly start to weigh you down. In these situation, you can start an interactive test environment:
$ make test-sh\nInfo: Generating topology\nInfo: Generating node YAML\nInfo: Generating executables\nInfo: Launching dut1\nInfo: Launching dut2\nInfo: Launching dut3\nInfo: Launching dut4\n11:42:52 infamy0:test #\n The example above uses the default mode (qeneth), but the host and run modes also support the test-sh target.
It takes a little while to start up, but then we have a shell prompt inside the container running Infamy. It's a very limited environment, but it has enough to easily run single tests, connect to the virtual devices, and step your code.
"},{"location":"testing/#running-subsets-of-tests","title":"Running Subsets of Tests","text":"Each test case is a separate executable, which can be run without arguments:
11:42:53 infamy0:test # ./case/dhcp/dhcp_basic.py\n To run a suite of tests, e.g., only the DHCP client tests, pass the suite as an argument to 9PM:
11:42:53 infamy0:test # ./9pm/9pm.py case/dhcp/dhcp.yaml\n To run the suite of all tests:
11:42:53 infamy0:test # ./9pm/9pm.py case/all.yaml\n "},{"location":"testing/#connecting-to-infamy","title":"Connecting to Infamy","text":"The test system runs in a Docker container, so to get a shell prompt in another terminal you need to connect to that container. Infamy comes with a helper script for this:
$ ./test/shell\n11:42:53 infamy0:test #\n By default it connect to the latest started Infamy instance. If you for some reason run multiple instances of Infamy the shell script takes an optional argument \"system\", which is the hostname of the container you want to connect to:
$ ./test/shell infamy2\n11:42:53 infamy2:test #\n "},{"location":"testing/#connecting-to-a-dut","title":"Connecting to a DUT","text":"Tip
The console script uses telnet to connect to a port forwarded to localhost by the Docker container. To exit Telnet, use Ctrl-] and then 'q' followed by enter. This can be customized in ~/.telnetrc
All DUTs in a virtual Infamy topology are emulated in Qemu instances managed by qeneth. If you want to watch what's happening on one of the target systems, e.g., tail a log file or run tcpdump during the test, there is another helper script in Infamy for this:
$ ./test/console 1\nTrying 127.0.0.1...\nConnected to 127.0.0.1.\n\nInfix OS \u2014 Immutable.Friendly.Secure v23.11.0-226-g0c144da (console)\ninfix-00-00-00 login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@infix-00-00-00:~$\n From here we can observe dut1 freely while running tests.
Like the shell script, console takes an optional \"system\" argument in case you run multiple instances of Infamy:
$ ./test/console 1 infamy2\n...\n You can also connect to the console of a DUT from within a shell:
$ ./test/shell\n11:42:54 infamy0:test # qeneth status\n11:42:54 infamy0:test # qeneth console dut1\nlogin: admin\npassword: *****\nadmin@infix-00-00-00:~$\n Tip
Disconnect from the qeneth console by bringing up the old Telnet \"menu\" using Ctrl-], compared to standard Telnet this is the BusyBox version so you press 'e' + enter instead of 'q' to quit.
"},{"location":"testing/#attaching-to-macvlans","title":"Attaching to MACVLANs","text":"To fully isolate the host's interfaces from one another, many tests will stack a MACVLAN on an interface, which is then placed in a separate network namespace.
It is often useful to attach to those namespaces, so that you can interactively inject traffic into the test setup.
First, connect to the Infamy instance in question:
$ ./test/shell\n11:43:19 infamy0:test #\n Then, attach to the MACVLAN namespace by running the nsenter helper script from the test directory, supplying the base interface name as the first argument:
11:43:19 infamy0:test # ./nsenter d1b\n11:43:20 infamy0(d1b):test #\n By default, an interactive shell is started, but you can also supply another command:
11:43:19 infamy0:test # ./nsenter d1b ip -br addr\nlo UNKNOWN 127.0.0.1/8 ::1/128\niface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64\n You can now freely debug the network activity of your test and the responses from the DUT.
"},{"location":"testing/#using-the-python-debugger","title":"Using the Python Debugger","text":"The built in breakpoint() function in Python is very useful when you want to run a test case to a certain point at which you might want to interactively inspect either the test's or the device's state.
Simply insert a call to breakpoint() at the point of interest in your test and run it as normal. Once Python executes the call, it will drop you into the Python debugger:
11:42:58 infamy0:test # ./case/dhcp/dhcp_basic.py\n# Starting (2024-02-10 11:42:59)\n# Probing dut1 on port d1a for IPv6LL mgmt address ...\n# Connecting to mgmt IP fe80::ff:fe00:0%d1a:830 ...\nok 1 - Initialize\n> /home/jocke/src/infix/test/case/dhcp/dhcp_basic.py(44)<module>()\n(Pdb)\n At this point you have full access to the test's state, but it is also an opportunity to inspect the state of the DUTs (e.g. via their console or over SSH).
It is also possible to run a test under Pdb from the get-go, if you want to setup breakpoints without modifying the source, or simply step through the code:
11:42:58 infamy0:test # python -m pdb case/dhcp/dhcp_basic.py\n "},{"location":"testing/#deterministic-topology-mappings","title":"Deterministic Topology Mappings","text":"By default, mappings from logical to physical topologies are not stable across test case executions. This can be very frustrating when debugging a failing test, since logical nodes are suffled around between phyical nodes. In such cases, supplying a PYTHONHASHSEED variable (set to any 32-bit unsigned integer) when launching the test environment will make sure that topology mappings are deterministic:
$ make PYTHONHASHSEED=0 test-sh\n If a seed is not supplied, a random value is chosen. This seed is logged by the meta/reproducible.py test case when running a test suite:
$ make test\nInfo: Generating topology\nInfo: Generating node YAML\nInfo: Generating executables\nInfo: Launching dut1\nInfo: Launching dut2\nInfo: Launching dut3\nInfo: Launching dut4\n9PM - Simplicity is the ultimate sophistication\n\nStarting test 0002-reproducible.py\n2024-05-03 10:40:30 # Starting (2024-05-03 10:40:30)\n2024-05-03 10:40:30 # Specify PYTHONHASHSEED=3773822171 to reproduce this test environment\n2024-05-03 10:40:30 ok 1 - $PYTHONHASHSEED is set\n2024-05-03 10:40:30 # Exiting (2024-05-03 10:40:30)\n2024-05-03 10:40:30 1..1\n...\n This is useful because this value can then be used to rerun a test (or the whole suite) with identical topology mappings:
$ make PYTHONHASHSEED=3773822171 TESTS=case/system/hostname.py test\n "},{"location":"testing/#deterministic-transport-protocol","title":"Deterministic Transport Protocol","text":"By default, the communication transport protocol (NETCONF/RESTCONF) is chosen randomly. If you supply a PYTHONHASHSEED as described above, you get the same protocol used for that hash. But if you want to choose the protocol, add extra arguments to Infamy:
$ make INFAMY_EXTRA_ARGS=\"--transport=restconf\" TESTS=case/system/hostname.py test\n or, when running interactively:
$ make test-sh\n09:08:17 infamy0:test # ./9pm/9pm.py -o\"--transport=restconf\" case/system/hostname.py\n "},{"location":"testing/#test-specification","title":"Test specification","text":"The test specification is automaticly generated from the test cases, this puts some constraints on the test cases:
All test cases must be written so that it can be understandable with only the steps so the result can be reproduced manually. E.g., instead of set ip address on the interface it should be more human readable, for example:
test.step(\"Set IPv4 address on the interface dut:cross\")\ntest.step(\"Verify the IP address is set on dut:cross\")\n where dut, cross and the IPv4 address could be found inside the logical topology for the test.
Each test case must begin with the test name followed by a empty line and the description of the test. For example:
\"\"\"\nSet hostname\n\nVerify that it it possible to change hostname\n\"\"\"\n The test specifaction can be genererated with:
$ make test-spec\n...\n "},{"location":"testing/#test-development","title":"Test Development","text":"For adding a new test to the automated regression test suite, it's best to start by reviewing an existing test case.
All tests are located in the infix/test/case repository and are grouped by the features they verify. For example, infix/test/case/services contains tests for various Infix services, such as LLDP and mDNS.
While test grouping is flexible, each test should be placed in a logically relevant category.
When creating a new test group, add it to infix/test/case/all.yaml, to enable it to run as a subset of the test suite:
- name: infix-services\n suite: services/services.yaml\n A new test (e.g., lldp_enable_disable) should be added to the corresponding test group .yaml file, such as infix/test/cases/services.yaml:
- name: lldp_enable_disable\n case: lldp_enable_disable/test.py\n It is necessary to include the test in infix/test/case/services/Readme.adoc to ensure proper test specification generation:
include::lldp_enable_disable/Readme.adoc[]\n Each test case should have its own directory under, infix/test/case/services, containing:
test.py - the test script topology.dot - the logical topology definition. When the test specification is generated, topology.svg and Readme.adoc should also be created.
"},{"location":"tunnels/","title":"Tunneling (L2/L3)","text":""},{"location":"tunnels/#tunnel-configuration","title":"Tunnel Configuration","text":"Infix supports multiple tunnel encapsulation protocols for connecting remote networks or devices across an IP backbone. Tunnels encapsulate packets within IP datagrams, allowing traffic to traverse intermediate networks transparently.
Important
When issuing leave to activate your changes, remember to also save your settings, copy running-config startup-config. See the CLI Introduction for a background.
"},{"location":"tunnels/#generic-routing-encapsulation-gre","title":"Generic Routing Encapsulation (GRE)","text":"GRE tunnels provide a simple and efficient method to encapsulate various network layer protocols over IP networks. Infix supports both IPv4 and IPv6 tunnels in two modes:
GRE (Layer 3): Point-to-point IP tunnel for routing protocols and routed traffic GRETAP (Layer 2): Ethernet tunnel for bridging Layer 2 networks Tip
If you name your tunnel interface greN or gretapN, where N is a number, the CLI infers the interface type automatically.
"},{"location":"tunnels/#basic-gre-configuration","title":"Basic GRE Configuration","text":"A basic GRE tunnel for routing between two sites:
admin@example:/> configure\nadmin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre local 192.168.3.1 remote 192.168.3.2\nadmin@example:/config/interface/gre0/> set ipv4 address 10.255.0.1 prefix-length 30\nadmin@example:/config/interface/gre0/> leave\nadmin@example:/>\n This creates a Layer 3 tunnel between 192.168.3.1 and 192.168.3.2 using the outer IP addresses, with the tunnel itself using 10.255.0.0/30 for the inner IP addressing.
"},{"location":"tunnels/#gretap-configuration","title":"GRETAP Configuration","text":"GRETAP tunnels operate at Layer 2, allowing bridging across the tunnel:
admin@example:/> configure\nadmin@example:/config/> edit interface gretap0\nadmin@example:/config/interface/gretap0/> set type gretap\nadmin@example:/config/interface/gretap0/> set gre local 192.168.3.1 remote 192.168.3.2\nadmin@example:/config/interface/gretap0/> leave\nadmin@example:/>\n GRETAP interfaces can be added to a bridge, bridging local and remote Ethernet segments. See the Bridge Configuration for more on bridges.
"},{"location":"tunnels/#ospf-over-gre","title":"OSPF over GRE","text":"GRE tunnels are commonly used to carry dynamic routing protocols like OSPF across networks that don't support multicast or where you want to create a virtual topology different from the physical network.
Example topology: Two sites connected via a GRE tunnel, running OSPF to exchange routes.
Site A configuration:
admin@siteA:/> configure\nadmin@siteA:/config/> edit interface gre0\nadmin@siteA:/config/interface/gre0/> set gre local 203.0.113.1 remote 203.0.113.2\nadmin@siteA:/config/interface/gre0/> set ipv4 address 10.255.0.1 prefix-length 30\nadmin@siteA:/config/interface/gre0/> set ipv4 forwarding\nadmin@siteA:/config/interface/gre0/> end\nadmin@siteA:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@siteA:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface gre0\nadmin@siteA:/config/routing/\u2026/ospf/> leave\nadmin@siteA:/>\n Site B configuration:
admin@siteB:/> configure\nadmin@siteB:/config/> edit interface gre0\nadmin@siteB:/config/interface/gre0/> set gre local 203.0.113.2 remote 203.0.113.1\nadmin@siteB:/config/interface/gre0/> set ipv4 address 10.255.0.2 prefix-length 30\nadmin@siteB:/config/interface/gre0/> set ipv4 forwarding\nadmin@siteB:/config/interface/gre0/> end\nadmin@siteB:/config/> edit routing control-plane-protocol ospfv2 name default ospf\nadmin@siteB:/config/routing/\u2026/ospf/> set area 0.0.0.0 interface gre0\nadmin@siteB:/config/routing/\u2026/ospf/> leave\nadmin@siteB:/>\n Once configured, OSPF will establish a neighbor relationship through the tunnel and exchange routes between the sites. For more info on OSPF configuration, see OSPFv2 Routing.
Note
Consider adjusting MTU on the tunnel interface to account for GRE overhead (typically 24 bytes for IPv4, 44 bytes for IPv6) to avoid fragmentation issues.
"},{"location":"tunnels/#advanced-tunnel-settings","title":"Advanced Tunnel Settings","text":"All tunnel types support common parameters for controlling tunnel behavior and performance.
"},{"location":"tunnels/#time-to-live-ttl","title":"Time To Live (TTL)","text":"The TTL setting controls the Time To Live value for the outer tunnel packets. By default, tunnels use a fixed TTL of 64, which allows packets to traverse multiple hops between tunnel endpoints.
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre ttl 255\nadmin@example:/config/interface/gre0/> leave\n Valid values are 1-255, or the special value inherit which copies the TTL from the encapsulated packet.
Important
The inherit mode can cause problems with routing protocols like OSPF that use TTL=1 for their packets. For tunnels carrying routing protocols, always use a fixed TTL value (typically 64 or 255).
"},{"location":"tunnels/#type-of-service-tos","title":"Type of Service (ToS)","text":"The ToS setting controls QoS marking for tunnel traffic:
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre tos 0x10\nadmin@example:/config/interface/gre0/> leave\n Valid values are 0-255 for fixed ToS/DSCP marking, or inherit (default) to copy the ToS value from the encapsulated packet.
"},{"location":"tunnels/#path-mtu-discovery-gre-only","title":"Path MTU Discovery (GRE only)","text":"The pmtu-discovery setting can be used to control the Path MTU Discovery on GRE tunnels. When enabled (default), the tunnel respects the Don't Fragment (DF) bit and performs PMTU discovery:
admin@example:/config/> edit interface gre0\nadmin@example:/config/interface/gre0/> set gre pmtudisc false\nadmin@example:/config/interface/gre0/> leave\n Disabling PMTU discovery may be necessary in networks with broken ICMP filtering but can lead to suboptimal performance and fragmentation.
"},{"location":"tunnels/#virtual-extensible-local-area-network-vxlan","title":"Virtual eXtensible Local Area Network (VXLAN)","text":"VXLAN is a network virtualization technology that encapsulates Layer 2 Ethernet frames within Layer 4 UDP datagrams. It uses a 24-bit segment ID, termed VXLAN Network Identifier (VNI), allowing up to 16 million isolated networks.
Infix supports both IPv4 and IPv6 for VXLAN tunnel endpoints.
"},{"location":"tunnels/#basic-vxlan-configuration","title":"Basic VXLAN Configuration","text":"Tip
If you name your VXLAN interface vxlanN, where N is a number, the CLI infers the interface type automatically.
admin@example:/> configure\nadmin@example:/config/> edit interface vxlan100\nadmin@example:/config/interface/vxlan100/> set vxlan local 192.168.3.1\nadmin@example:/config/interface/vxlan100/> set vxlan remote 192.168.3.2\nadmin@example:/config/interface/vxlan100/> set vxlan vni 100\nadmin@example:/config/interface/vxlan100/> leave\nadmin@example:/>\n The VNI uniquely identifies the VXLAN segment and must match on both tunnel endpoints.
"},{"location":"tunnels/#vxlan-with-custom-udp-port","title":"VXLAN with Custom UDP Port","text":"The default VXLAN UDP destination port is 4789 (IANA assigned). In some cases you may need to use a different port:
admin@example:/> configure\nadmin@example:/config/> edit interface vxlan100\nadmin@example:/config/interface/vxlan100/> set vxlan local 192.168.3.1\nadmin@example:/config/interface/vxlan100/> set vxlan remote 192.168.3.2\nadmin@example:/config/interface/vxlan100/> set vxlan vni 100\nadmin@example:/config/interface/vxlan100/> set vxlan remote-port 8472\nadmin@example:/config/interface/vxlan100/> leave\nadmin@example:/>\n The remote-port setting allows interoperability with systems using non-standard VXLAN ports.
Note
VXLAN tunnels also support the ttl and tos settings described in the Advanced Tunnel Settings section above.
"},{"location":"upgrade/","title":"Upgrade","text":""},{"location":"upgrade/#upgrading-boot-order","title":"Upgrading & Boot Order","text":"For resilience purposes, Infix maintains two software images referred to as the primary and secondary partition image. In addition, some bootloaders support netbooting.
The boot order defines which image is tried first, and is listed with the CLI show software command. It also shows Infix version installed per partition, and which image was used when booting (STATE booted).
admin@example:/> show software\nBOOT ORDER\nprimary secondary net\n\nNAME STATE VERSION DATE\nprimary booted v25.01.0 2025-04-25T10:15:00+00:00\nsecondary inactive v25.01.0 2025-04-25T10:07:20+00:00\nadmin@example:/>\n YANG support for upgrading Infix, inspecting and modifying the boot-order, is defined in infix-system-software.
"},{"location":"upgrade/#changing-boot-order","title":"Changing Boot Order","text":"The boot order can be manually changed using the set boot-order command. This is useful for rolling back to a previous version or changing the preferred boot source.
The command accepts one to three boot targets as separate arguments, in the desired boot order. Valid boot targets are:
primary - The primary partition secondary - The secondary partition net - Network boot (if supported by bootloader) The CLI provides tab-completion for boot targets, making it easy to enter valid values.
Example: View current boot order and change it:
admin@example:/> show boot-order\nprimary secondary net\nadmin@example:/> set boot-order secondary primary net\nadmin@example:/> show boot-order\nsecondary primary net\nadmin@example:/>\n Example: Set boot order to only try primary partition:
admin@example:/> show boot-order\nsecondary primary net\nadmin@example:/> set boot-order primary\nadmin@example:/> show boot-order\nprimary\nadmin@example:/>\n Example: Using tab-completion (press TAB to see available options):
admin@example:/> set boot-order <TAB>\nnet primary secondary\nadmin@example:/> set boot-order secondary <TAB>\nnet primary secondary\nadmin@example:/> set boot-order secondary primary\nadmin@example:/> show boot-order\nsecondary primary\nadmin@example:/>\n The new boot order takes effect on the next reboot and can be verified with show boot-order or show software:
admin@example:/> show software\nBOOT ORDER\nsecondary primary\n\nNAME STATE VERSION DATE\nprimary booted v25.01.0 2025-04-25T10:15:00+00:00\nsecondary inactive v25.01.0 2025-04-25T10:07:20+00:00\nadmin@example:/>\n Note
The boot order is automatically updated when performing an upgrade. The newly installed image will be set as the first boot target.
Duplicate boot targets are not allowed. The CLI will reject attempts to specify the same target multiple times.
"},{"location":"upgrade/#upgrading","title":"Upgrading","text":"Upgrading Infix is done one partition at a time. If the system has booted from one partition, an upgrade will apply to the other (inactive) partition.
Download and unpack the release to install. Make the image pkg bundle available at some URL2 (Optional) Backup the startup configuration Assume the unit has booted the primary image. Then running the upgrade command installs a new image on the secondary partition As part of a successful upgrade, the boot-order is implictly changed to boot the newly installed image Reboot the unit The unit now runs the new image. To upgrade the remaining partition (primary), run the same upgrade command again, and (optionally) reboot to verify the upgrade Caution
During boot (step 5), the unit may migrate the startup configuration for any syntax changes. It is therefore important that you make sure to upgrade the other partition as well after reboot, of course after having verified your setup.
The CLI example below shows steps 2-5.
Backup startup configuration: It is recommended to backup the startup configuration before performing an upgrade. The backup is useful if the upgrade fails, and makes a later downgrade a smoother process.
admin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg\n\nadmin@example:/> copy /cfg/startup-config.cfg /cfg/v25.01.0-startup-config.cfg\nadmin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg v25.01.0-startup-config.cfg\n\nadmin@example:/> \n Upgrade: Here the image pkg bundle was made available via TFTP.
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.03.1.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n...\n 98% Copying image to rootfs.1\n 99% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.03.1.pkg` succeeded\nadmin@example:/>\n Reboot: The unit will boot on the other partition, with the newly installed image. The Loading startup-config step conducts migration of startup configuration if applicable.
admin@example:/> reboot\n[ OK ] Stopping Static routing daemon\n[ OK ] Stopping Zebra routing daemon\n...\n[ OK ] Loading startup-config\n[ OK ] Verifying self-signed https certificate\n[ OK ] Update DNS configuration\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.03.1 (ttyS0)\nexample login: admin\nPassword:\n.-------.\n| . . | Infix OS \u2014 Immutable.Friendly.Secure\n|-. v .-| https://kernelkit.org\n'-'---'-'\n\nRun the command 'cli' for interactive OAM\n\nadmin@example:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@example:/> show software\nBOOT ORDER\nsecondary primary net\n\nNAME STATE VERSION DATE\nprimary inactive v25.01.0 2025-04-25T10:15:00+00:00\nsecondary booted v25.03.1 2025-04-25T10:24:31+00:00\nadmin@example:/>\n As shown, the boot order has been updated, so that secondary is now the preferred boot source.
To upgrade the remaining partition (primary), run the upgrade URL command again, and (optionally) reboot.
"},{"location":"upgrade/#configuration-migration","title":"Configuration Migration","text":"The example above illustrated an upgrade from Infix v25.01.0 to v25.03.1. Inbetween these versions, YANG configuration definitions changed slightly (more details given below).
During boot, Infix inspects the version meta information within the startup configuration file to determine if configuration migration is needed. In this specific case, the configuration file has version 1.4 while the booted software expects version 1.5 (the configuration version numbering differs from the Infix image version numbering). The startup configuration is migrated to 1.5 definitions and stored, while a backup previous startup configuration is stored in directory /cfg/backup/.
admin@example:/> dir /cfg/backup/\n/cfg/backup/ directory\nstartup-config-1.4.cfg\n\nadmin@example:/>\n The modifications made to the startup configuration can be viewed by comparing the files from the shell. An example is shown below.
admin@example:/> exit\nadmin@example:~$ diff /cfg/backup/startup-config-1.4.cfg /cfg/startup-config.cfg\n--- /cfg/backup/startup-config-1.4.cfg\n+++ /cfg/startup-config.cfg\n...\n- \"public-key-format\": \"ietf-crypto-types:ssh-public-key-format\",\n+ \"public-key-format\": \"infix-crypto-types:ssh-public-key-format\",\n...\n- \"private-key-format\": \"ietf-crypto-types:rsa-private-key-format\",\n+ \"private-key-format\": \"infix-crypto-types:rsa-private-key-format\",\n...\n- \"version\": \"1.4\"\n+ \"version\": \"1.5\"\n...\nadmin@example:~$\n "},{"location":"upgrade/#downgrading","title":"Downgrading","text":"Downgrading to an earlier version is possible, however, downgrading is not guaranteed to work smoothly. In particular, when the unit boots up with the downgraded version, it may fail to apply the startup config, and instead apply its failure config.
We consider two cases: downgrading with and without applying a backup startup configuration before rebooting.
In both cases we start out with a unit running Infix v25.03.1, and wish to downgrade to v25.01.0.
admin@example:/> show software\nBOOT ORDER\nprimary secondary net\n\nNAME STATE VERSION DATE\nprimary booted v25.03.1 2025-04-25T11:36:26+00:00\nsecondary inactive v25.03.1 2025-04-25T10:24:31+00:00\nadmin@example:/>\n "},{"location":"upgrade/#with-backup-startup-config","title":"With Backup startup-config","text":"This is the recommended approach to downgrade, given that you have a backup configuration available. The objective is to avoid ending up with the unit in failure config.
Find the backup configuration file Run upgrade URL to install Infix image to downgrade to Copy backup startup configuration to current startup configuration (from shell) Reboot Find the backup configuration file:
Assume you have a backup startup config for the Infix version to downgrade to (here Infix v25.01.0, config version 1.4).
The preferred approach is to use a startup configuration backed up when running Infix v25.01.0 on the unit. See section Upgrading above for more information. In the following example, there is a backup file available named v25.01.0-startup-config.cfg:
admin@example:/> dir /cfg\n/cfg directory\nbackup/ ssl/ startup-config.cfg v25.01.0-startup-config.cfg\n\nadmin@example:/> \n The alternative is to use a startup config implicitly backed up by the system as part of Configuration Migration.
admin@example:/> dir /cfg/backup/\n/cfg/backup/ directory\nstartup-config-1.4.cfg\n\nadmin@example:/>\n Caution
Using a backup configuration file stored when the unit was running the old version (e.g., v25.01.0-startup-config.cfg) is preferred. Although backup files stored due to configuration migration (e.g., startup-config-1.4.cfg) usually works too if the configuration file version (1.4) matches, there are situations when the system may fail to apply it as described below.
The configuration file version (1.4) is only incremented when changes in YANG configuration syntax mandates it to handle upgrading. Say the next Infix version includes a new feature setting, it can still have version 1.4, as upgrading to it would not need migration. If a user then enables the new feature setting, the new configuration will no longer be compatible with the previous Infix version. A downgrade after enabling new features risks ending up with the unit in failure config.
Use upgrade command to downgrade:
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.01.0.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n ...\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.01.0.pkg` succeeded\nadmin@example:/>\n Apply the backup configuration file:
It is recommended to use a backup configuration file for the Infix version to downgrade to, if there is one available.
admin@example:/> copy /cfg/v25.01.0-startup-config.cfg /cfg/startup-config.cfg\nOverwrite existing file /cfg/startup-config.cfg (y/N)? y\nadmin@example:/>\n An alternative is to use a backup file stored when the system conducted a configuration migration. See the caution note above.
admin@example:/> copy /cfg/backup/startup-config-1.4.cfg /cfg/startup-config.cfg\nOverwrite existing file /cfg/startup-config.cfg (y/N)? y\nadmin@example:/>\n Reboot:
The unit will come up with the applied backup configuration.
admin@example:/> reboot\n[ OK ] Saving system clock to file\n[ OK ] Stopping Software update service\n[ OK ] Stopping Status daemon\n...\n[ OK ] Bootstrapping YANG datastore\n[ OK ] Starting Configuration daemon\n[ OK ] Loading startup-config\n[ OK ] Update DNS configuration\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\nexample login:\n Note
If the unit despite these measures ends up in failure config, see the next section for more information on how to recover.
"},{"location":"upgrade/#without-a-backup-startup-config","title":"Without a Backup startup-config","text":"This procedure assumes you have access to the unit's console port and its default login credentials1 .
Downgrade Reboot Login with unit's default credentials Conduct factory reset (Then go on configure the unit as you wish) Use upgrade command to downgrade:
admin@example:/> upgrade tftp://198.18.117.1/infix-aarch64-25.01.0.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 10% Determining slot states done.\n ...\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://198.18.117.1/infix-aarch64-25.01.0.pkg` succeeded\nadmin@example:/>\n Reboot:
Conduct a reboot. During boot, the unit fails to apply the existing startup configuration (config version 1.5 while software expects version 1.4 or earlier), and instead applies its failure config. This is what is seen on the console when this situation occurs. Note that the login prompt displays failed as part of the hostname.
admin@example:/> reboot\n[ OK ] Saving system clock to file\n[ OK ] Stopping Software update service\n[ OK ] Stopping Status daemon\n...\n[ OK ] Verifying SSH host keys\n[ OK ] Bootstrapping YANG datastore\n[ OK ] Starting Configuration daemon\n[FAIL] Loading startup-config\n[ OK ] Loading failure-config\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\n\nERROR: Corrupt startup-config, system has reverted to default login credentials\nfailed-00-00-00 login:\n To remedy a situation like this, you can login with the unit's default login credentials, preferrably via a console port. The unit's default credentials are typically printed on a sticker on the unit.
failed-00-00-00 login: admin\nPassword:\n\nRun the command 'cli' for interactive OAM\n\nadmin@failed-00-00-00:~$\n When it is safe from a network operations perspective, you can conduct a factory reset and reboot. It is recommended to remove the unit from any production network before doing this, as a factory reset may enable undesired connectivity between the unit's ports.
admin@failed-00-00-00:~$ factory\nFactory reset device (y/N)? y\nfactory: scheduled factory reset on next boot.\nReboot now to perform reset, (y/N)? y\n[ OK ] Saving system time (UTC) to RTC\n[ OK ] Stopping mDNS alias advertiser\n...\n[ OK ] Starting Configuration daemon\n[ OK ] Loading startup-config\n[ OK ] Update DNS configuration\n[ OK ] Verifying self-signed https certificate\n[ OK ] Starting Status daemon\n[ OK ] Starting Status daemon\n\n\nPlease press Enter to activate this console.\n\nInfix OS \u2014 Immutable.Friendly.Secure v25.01.0 (ttyS0)\nexample login:\n Continued configuration is done as with any unit after factory reset.
In failure config, Infix puts all Ethernet ports as individual interfaces. With direct access, one can connect with e.g., SSH, using link local IPv6 addresses. This as an alternative to connecting via a console port.\u00a0\u21a9
Set up an FTP/TFTP/SFTP or HTTP/HTTPS server on the same LAN.\u00a0\u21a9
"},{"location":"virtual/","title":"Virtual Environments","text":""},{"location":"virtual/#virtual-environments","title":"Virtual Environments","text":"Infix primarily targets real hardware, deployment to the cloud is not a priority at the moment. However, for development and testing purposes there is an x86_64 build that runs in Qemu.
These images also work with the Graphical Network Simulator (GNS3), which is a very user-friendly tool for playing around with simple to complex network setups, verifying interoperability between vendors, etc.
"},{"location":"virtual/#qemu","title":"QEMU","text":"Tip
Installation for Debian/Ubuntu based systems can be done by \"simply\": sudo apt install virt-manager -- dependencies ensure the relevant Qemu packages are pulled in as well. This trick, installing virt-manager, helps set up Qemu networking on your system.
A virtualized Infix x86_64 instance can easily be launched from a Linux system, with Qemu installed, by issuing:
$ ./qemu.sh\n...\n from an unpacked release tarball. From a built source tree of Infix the same functionality is bundled as:
$ make run\n...\n To change settings, e.g. networking, make run-menuconfig, or from a pre-built Infix release tarball, using ./qemu.sh -c
The Infix test suite is built around Qemu and Qeneth, see:
Regression Testing with Infamy Infamy Docker Image "},{"location":"virtual/#gns3","title":"GNS3","text":"Download the latest build of the x86_64, or aarch64 if your host machine is Arm. Unpack the tarball in a dedicated directory and use \"Import Appliance\" to install the .gns3a file into GNS3.
Infix is in the \"Router\" category, it comes with 10 interfaces available by default for use as switch ports or routing.
"},{"location":"vpd/","title":"Vital Product Data (VPD)","text":""},{"location":"vpd/#vital-product-data","title":"Vital Product Data","text":"Infix can source Vital Product Data (VPD) from EEPROMs containing an ONIE TLV. The load bearing information from VPDs, used by Infix by default, is:
Base (chassis) MAC address of the system Factory default admin password hash Additionally, every VPD is listed as a separate component in the ietf-hardware model, in which Infix exports all available attributes to the user. This is useful to read out exact hardware revisions of each board making up a system, figure out if a system belongs to a particular production batch, etc.
"},{"location":"vpd/#json-encoding","title":"JSON Encoding","text":"To make EEPROM binary generation less cumbersome, Infix defines a JSON encoding of an ONIE TLV, and provides a tool, onieprom, to translate from one encoding to another. I.e. a JSON object can be translated to its binary equivalent, and vice versa.
A mapping between TLV attribute IDs and JSON keys is defined, using the Linux kernel driver as a reference:
TLV ID Description Key Type 0x21 Product Name \"product-name\" String 0x22 Part Number \"part-number\" String 0x23 Serial Number \"serial-number\" String 0x24 MAC #1 Base \"mac-address\" String (\"c0:ff:ee:00:00:00\") 0x25 Manufacture Date \"manufacture-date\" String (\"MM/DD/YYYY HH:NN:SS\") 0x26 Device Version \"device-version\" Integer (u8) 0x27 Label Revision \"label-revision\" String 0x28 Platform Name \"platform-name\" String 0x29 ONIE Version \"onie-version\" String 0x2a Num MACs \"num-macs\" Integer (u16) 0x2b Manufacturer \"manufacturer\" String 0x2c Country Code \"country-code\" String (ISO 3166-1 2-byte code) 0x2d Vendor \"vendor\" String 0x2e Diag Version \"diag-version\" String 0x2f Service Tag \"service-tag\" String 0xfd Vendor Extension \"vendor-extension\" List of extensions (see below) The JSON encoding of a TLV is a single JSON object with an arbitrary subset of the attributes from the list above.
Example:
{\n \"product-name\": \"Wacky Widget\",\n \"serial-number\": \"#1\",\n \"manufacture-date\": \"02/13/2024 11:29:52\"\n}\n
"},{"location":"vpd/#vendor-extensions","title":"Vendor Extensions","text":"As the specification explicitly mentions the option of supplying multiple vendor extensions, possibly multiple ones of the same type, the JSON chosen encoding is a list, where each element is itself a list of exactly two elements:
Element # Type 1 Integer (u32, IANA enterprise number) 2 String (UTF-8 encoded extension data) The format of the extension data is defined by the entity indicated by the IANA enterprise number. Restricting extension data to UTF-8 encoded strings is a decision imposed by Infix; the ONIE specification allows for arbitrary binary data.
Example:
{\n ...\n \"vendor-extension\": [\n [ 12345, \"my extension data\" ]\n ]\n}\n "},{"location":"vpd/#infix-specific-extensions","title":"Infix Specific Extensions","text":"Kernelkit's IANA enterprise number is 61046, under which any extensions required by Infix are stored. The only valid extension data that may be stored under this number is documented in this section. If other device specific data needs to be stored in a VPD, you must associate that with an enterprise number under our control.
Every Kernelkit extension must be a valid JSON object containing an arbitrary subset of the following attributes:
Key Value Description \"pwhash\" String (output of mkpasswd(1)) Factory default password for admin Since the extension is itself stored in a JSON document, it has to be appropriately quoted.
Example:
{\n ...\n \"vendor-extension\": [\n [ 61046, \"{\\\"pwhash\\\":\\\"$6$9rufAxdqCrxrwfQR$G0l9cTVlu/vOhxgo/uMKfRDOmZRd5XWF3vKr5da6qYoxuTJBS/Pl9K.5lrabWoWFFc.71yFMaSlZz0O8FtAtl.\\\"}\" ]\n ]\n}\n "},{"location":"vpd/#creating-and-parsing-onie-eeprom-binaries","title":"Creating and Parsing ONIE EEPROM Binaries","text":"usage: onieprom [-h] [-e] [-d] [infile] [outfile]\n\npositional arguments:\n infile\n outfile\n\noptions:\n -h, --help show this help message and exit\n -e, --encode Encode JSON input to binary output\n -d, --decode Decode binary input to JSON output\n To convert a compatible JSON document (using the first example above) to its binary equivalent, we ask onieprom to encode it for us:
~$ onieprom -e example.json >example.bin\n~$ hexdump -C example.bin\n00000000 54 6c 76 49 6e 66 6f 00 01 00 2d 25 13 30 32 2f |TlvInfo...-%.02/|\n00000010 31 33 2f 32 30 32 34 20 31 31 3a 32 39 3a 35 32 |13/2024 11:29:52|\n00000020 21 0c 57 61 63 6b 79 20 57 69 64 67 65 74 23 02 |!.Wacky Widget#.|\n00000030 23 31 fe 04 dd 69 88 97 |#1...i..|\n00000038\n We can also run the process in reverse, to inspect the contents of a binary in its equivalent JSON representation:
~$ onieprom -d example.bin >example-again.json\n~$ jq . example-again.json\n{\n \"manufacture-date\": \"02/13/2024 11:29:52\",\n \"product-name\": \"Wacky Widget\",\n \"serial-number\": \"#1\"\n}\n "},{"location":"wifi/","title":"Wireless LAN (WiFi)","text":""},{"location":"wifi/#wi-fi-wireless-lan","title":"Wi-Fi (Wireless LAN)","text":"Infix includes built-in Wi-Fi client support for connecting to wireless networks. When a compatible Wi-Fi adapter is detected, the system automatically begins scanning for available networks.
"},{"location":"wifi/#current-limitations","title":"Current Limitations","text":" Only client mode is supported (no access point functionality) USB hotplug is not supported - adapters must be present at boot Interface naming may be inconsistent with multiple USB Wi-Fi adapters "},{"location":"wifi/#supported-wi-fi-adapters","title":"Supported Wi-Fi Adapters","text":"Wi-Fi support is primarily tested with Realtek chipset-based adapters.
"},{"location":"wifi/#known-working-chipsets","title":"Known Working Chipsets","text":" RTL8821CU Other Realtek chipsets may work but are not guaranteed Some Realtek chipsets require proprietary drivers not included in the standard kernel
Firmware requirements vary by chipset\n Check kernel logs if your adapter is not detected\n "},{"location":"wifi/#configuration","title":"Configuration","text":"Add a supported Wi-Fi network device. To verify that it has been detected, look for wifi0 in show interface
admin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\nlo loopback UP\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet UP 02:00:00:00:00:01\n ipv6 fe80::ff:fe00:1/64 (link-layer)\n ipv6 fec0::ff:fe00:1/64 (link-layer)\nwifi0 ethernet DOWN f0:09:0d:36:5f:86\n wifi ssid: ------, signal: ------\n Add the new Wi-Fi interface to the configuration to start scanning. admin@example:/config/> set interface wifi0\nadmin@example:/config/> leave\n Now the system will now start scanning in the background. To see the result read the operational datastore for interface wifi0 or use the CLI
admin@infix-00-00-00:/> show interface wifi0\nname : wifi0\ntype : wifi\nindex : 3\nmtu : 1500\noperational status : down\nphysical address : f0:09:0d:36:5f:86\nipv4 addresses :\nipv6 addresses :\nSSID : ----\nSignal : ----\n\nSSID ENCRYPTION SIGNAL\nssid1 WPA2-Personal excellent\nssid2 WPA2-Personal excellent\nssid3 WPA2-Personal excellent\nssid4 WPA2-Personal good\nssid5 WPA2-Personal good\nssid6 WPA2-Personal good\n In the CLI, signal strength is reported as: excellent, good, poor or bad. For precise values, use NETCONF or RESTCONF, where the RSSI (in dBm) is available in the operational datastore.
Configure your Wi-Fi secret in the keystore, it should be between 8 and 63 characters
admin@example:/> configure\nadmin@example:/config/> edit keystore symmetric-key example\nadmin@example:/config/keystore/\u2026/example/> set key-format wifi-preshared-key-format\nadmin@example:/config/keystore/\u2026/example/> set cleartext-key mysecret\nadmin@example:/config/keystore/\u2026/example/> leave\nadmin@example:/>\n Configure the Wi-Fi settings, set secret to the name selected above for the symmetric key, in this case example.
WPA2 or WPA3 encryption will be automatically selected based on what the access point supports. No manual selection is required unless connecting to an open network. No support for certificate based authentication yet.
Unencrypted network is also supported, to connect to an unencrypted network (generally not recommended):
admin@example:/config/interface/wifi0/> set wifi encryption disabled\n
A valid country-code is also required for regulatory compliance, the valid codes are documented in the YANG model infix-wifi-country-codes
admin@example:/> configure\nadmin@example:/config/> edit interface wifi0\nadmin@example:/config/interface/wifi0/>\nadmin@example:/config/interface/wifi0/> set wifi ssid ssid1\nadmin@example:/config/interface/wifi0/> set wifi secret example\nadmin@example:/config/interface/wifi0/> set wifi country-code SE\nadmin@example:/config/interface/wifi0/> leave\n The Wi-Fi negotiation should now start immediately, provided that the SSID and pre-shared key are correct. You can verify the connection by running show interface again.
admin@example:/> show interface\nINTERFACE PROTOCOL STATE DATA\nlo loopback UP\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\ne1 ethernet UP 02:00:00:00:00:01\n ipv6 fe80::ff:fe00:1/64 (link-layer)\n ipv6 fec0::ff:fe00:1/64 (link-layer)\nwifi0 ethernet UP f0:09:0d:36:5f:86\n wifi ssid: ssid1, signal: excellent\n\nadmin@example:/>\n "},{"location":"wifi/#troubleshooting-connection-issues","title":"Troubleshooting Connection Issues","text":"Use show wifi scan wifi0 and show interface to verify signal strength and connection status. If issues arise, try the following troubleshooting steps:
Verify signal strength: Check that the target network shows \"good\" or \"excellent\" signal Check credentials: Verify the preshared key in ietf-keystore Review logs: Check system logs with show log for Wi-Fi related errors Regulatory compliance: Ensure the country-code matches your location Hardware detection: Confirm the adapter appears in show interface If issues persist, check the system log for specific error messages that can help identify the root cause.
"},{"location":"cli/configure/","title":"Configuration","text":""},{"location":"cli/configure/#cli-configure-context","title":"CLI Configure Context","text":"Enter the configure context from admin-exec by typing configure followed by Enter. Available commands, press ? at the prompt:
admin@host:/> configure\nadmin@host:/config/>\n abort Abandon candidate\n change Interactively change setting, e.g. password\n check Validate candidate\n commit Commit current candidate to running-config\n delete Delete configuration setting(s)\n diff Summarize uncommitted changes\n do Execute operational mode command\n edit Descend to the specified configuration node\n end Alias to up, leave this subsection/node\n exit Ascend to the parent configuration node, or abort (from top)\n leave Finalize candidate and apply to running-config\n no Alias for delete\n rollback Restore candidate to running-config\n set Set configuration setting\n show Show configuration\n text-editor Modify binary content in a text editor\n top Ascend to the configuration root\n up Ascend to the parent configuration node\n The edit command lets you change to a sub-configure context, e.g.:
admin@host:/config/> edit interface eth0\nadmin@host:/config/interface/eth0/>\n Use up to go up one level.
admin@host:/config/interface/eth0/> up\nadmin@host:/config/>\n Note: commands in configure context are automatically generated from the system's YANG models, hence different products likely have a different set of commands. However, both the ietf-interfaces.yang and ietf-ip.yang models, for instance, that provide the networking support are common to all systems.
"},{"location":"cli/configure/#set-ip-address-on-an-interface","title":"Set IP Address on an Interface","text":"admin@host:/config/> edit interface eth0\nadmin@host:/config/interface/eth0/>\nadmin@host:/config/interface/eth0/> set ipv4 address 192.168.2.200 prefix-length 24\n From anywhere in configure context you can see the changes you have made by typing diff:
admin@host:/config/interface/eth0/> diff\ninterfaces {\n interface eth0 {\n+ ipv4 {\n+ address 192.168.2.200 {\n+ prefix-length 24;\n+ }\n+ }\n }\n}\n "},{"location":"cli/configure/#saving-changes","title":"Saving Changes","text":"Apply the changes (from candidate to running-config):
admin@host:/config/interface/eth0/> leave\nadmin@host:/> show running-config\n...\ninterfaces {\n interface eth0 {\n type ethernetCsmacd;\n ipv4 {\n address 192.168.2.200 {\n prefix-length 24;\n }\n }\n }\n...\n Since we did not get any warnings we can save the running (RAM only) configuration to startup, so the changes are made persistent across reboots:
admin@host:/> copy running-config startup-config\n The startup-config can also be inspected with the show command to verify the changes are saved.
Note: all commands need to be spelled out, no short forms are allowed in the CLI. Use the TAB key to make your life easier.
"},{"location":"cli/configure/#changing-hostname","title":"Changing Hostname","text":"Settings like hostname are located in the ietf-system.yang model. Here is how it can be set.
admin@host:/config/> edit system\nadmin@host:/config/system/> set hostname example\nadmin@host:/config/system/> leave\nadmin@example:/> \n Notice how the hostname in the prompt does not change until the change is committed.
Note: critical services like syslog, mDNS, LLDP, and similar that advertise the hostname, are restarted when the hostname is changed.
"},{"location":"cli/configure/#changing-password","title":"Changing Password","text":"User management, including passwords, is also a part of ietf-system.
admin@host:/config/> edit system authentication user admin\nadmin@host:/config/system/authentication/user/admin/> change password\nNew password: \nRetype password: \nadmin@host:/config/system/authentication/user/admin/> leave\n The change password command starts an interactive dialogue that asks for the new password, with a confirmation, and then salts and encrypts the password with sha512crypt.
It is also possible to use the set password ... command. This allows setting an already hashed password. To manually hash a password, use the do password encrypt command. This launches the admin-exec command to hash, and optionally salt, your password. This encrypted string can then be used with set password ....
Tip: if you are having trouble thinking of a password, there is also do password generate, which generates random but readable strings using the UNIX command pwgen.
"},{"location":"cli/configure/#ssh-authorized-key","title":"SSH Authorized Key","text":"Logging in remotely with SSH is possible by adding a public key to a user. Here we add the authorized key to the admin user, multiple keys are supported.
With SSH keys in place it is possible to disable password login, just remember to verify SSH login and network connectivity before doing so.
admin@host:/config/> edit system authentication user admin \nadmin@host:/config/system/authentication/user/admin/> edit authorized-key example@host\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> show\nalgorithm ssh-rsa;\nkey-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=;\nadmin@host:/config/system/authentication/user/admin/authorized-key/example@host/> leave\n Note: the ssh-keygen program already base64 encodes the public key data, so there is no need to use the text-editor command, set does the job.
"},{"location":"cli/configure/#creating-a-veth-pair","title":"Creating a VETH Pair","text":"The following example creates a veth0a <--> veth0b virtual Ethernet pair which is useful for connecting, e.g., a container to the physical world. Here we also add an IPv4 address to one end of the pair.
admin@host:/config/> edit interface veth0a\nadmin@host:/config/interface/veth0a/> set veth peer veth0b\nadmin@host:/config/interface/veth0a/> set ipv4 address 192.168.0.1 prefix-length 24\nadmin@host:/config/interface/veth0a/> up\nadmin@host:/config/> diff\ninterfaces {\n+ interface veth0a {\n+ type veth;\n+ ipv4 {\n+ address 192.168.0.1 {\n+ prefix-length 24;\n+ }\n+ }\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ }\n}\nadmin@host:/config/> leave\n See the bridging example below for more.
Note: in the CLI you do not have to create the veth0b interface. The system infers this for you. When setting up a VETH pair using NETCONF, however, you must include the veth0b interface.
"},{"location":"cli/configure/#creating-a-bridge","title":"Creating a Bridge","text":"Building on the previous example, we now create a non-VLAN filtering bridge (br0) that forwards any, normally link-local, LLDP traffic between both its bridge ports: eth0 and vet0b.
admin@host:/> configure\nadmin@host:/config/> edit interface br0\nadmin@host:/config/interface/br0/> set bridge ieee-group-forward lldp\nadmin@host:/config/interface/br0/> up\nadmin@host:/config/> set interface eth0 bridge-port bridge br0\nadmin@host:/config/> set interface veth0b bridge-port bridge br0\nadmin@host:/config/> diff\ninterfaces {\n+ interface br0 {\n+ type bridge;\n+ bridge {\n+ ieee-group-forward lldp;\n+ }\n+ }\n interface eth0 {\n+ bridge-port {\n+ bridge br0;\n+ }\n }\n+ interface veth0a {\n+ type veth;\n+ ipv4 {\n+ address 192.168.0.1 {\n+ prefix-length 24;\n+ }\n+ }\n+ veth {\n+ peer veth0b;\n+ }\n+ }\n+ interface veth0b {\n+ type veth;\n+ veth {\n+ peer veth0a;\n+ }\n+ bridge-port {\n+ bridge br0;\n+ }\n+ }\n}\n Both a physical port eth0 and a virtual port veth0b (bridge side of the VETH pair from the previous example) are now bridged. Any traffic ingressing one port will egress the other. Only reserved IEEE multicast is filtered, except LLDP frames as shown above.
Note: the bridge can be named anything, provided the interface name is not already taken. However, for any name outside the pattern br[0-9]+, you have to set the interface type manually to bridge.
"},{"location":"cli/introduction/","title":"Introduction","text":""},{"location":"cli/introduction/#cli-introduction","title":"CLI Introduction","text":"The command line interface (CLI, see-ell-aye) implements a CISCO-like, or Juniper Networks JunOS-like, CLI. It is the traditional way of interacting with single network equipment like switches and routers. Today more advanced graphical NETCONF-based tools are available that allows for managing entire fleets of installed equipment.
Nevertheless, when it comes to initial deployment and debugging, it is very useful to know how to navigate and use the CLI. This very short guide intends to help you with that.
New users usually get the CLI as the default \"shell\" when logging in, but the default admin user logs in to bash. To access the CLI, type:
admin@host-12-34-56:~$ cli\n\nSee the 'help' command for an introduction to the system\n\nadmin@host-12-34-56:/>\n The prompt (beginning of the line) changes slightly. Key commands available in any context are:
admin@host-12-34-56:/> help # Try: Tab or ?\n...\nadmin@host-12-34-56:/> show # Try: Tab or ?\nadmin@host-12-34-56:/> # Try: Tab or ?\n Note: even on an empty command line, you can tap the Tab or ? keys. See help keybindings for more tips!
"},{"location":"cli/introduction/#key-concepts","title":"Key Concepts","text":"The two modes in the CLI are the admin-exec and the configure context.
The top-level context after logging in and starting the CLI is the admin-exec or \"main\" context. It is used for querying system status, managing configuration files/profiles and doing advanced debugging.
Available commands can be seen by pressing ? at the prompt:
admin@host:/>\n configure Create new candidate-config based on running-config\n copy Copy file or configuration, e.g., copy running-config startup-config\n dir List available configuration files\n exit Exit from CLI (log out)\n factory-reset Restore the system to factory default state\n follow Monitor a log file, use Ctrl-C to abort\n help Help system (try also the '?' key)\n logout Alias to exit\n netcalc IP subnet calculator, with subnetting\n password Password tools\n ping Ping a network host or multicast group\n poweroff Poweroff system (system policy may yield reboot)\n reboot Reboot system\n remove Remove a configuration file\n set Set operations, e.g., current date/time\n show Show system status and configuration files\n tcpdump Capture network traffic\n upgrade Install a software update bundle from remote or local file\n The system has three main datastores (or files): factory, startup, and running that can be managed and inspected using the copy, show, and configure commands. The traditional names used in the CLI for these are listed below:
factory-config the default configuration from factory for the device, i.e., what the system returns to after a factory-reset startup-config created from factory-config at first boot after factory reset. Loaded as the system configuration on each boot running-config what is actively running on the system. If no changes have been made since boot, it is the same as startup-config candidate-config is created from running-config when entering the configure context. Any changes made here can be discarded (abort, rollback) or committed (commit, leave) to running-config Edit the running configuration using the configure command. This copies running to candidate, a temporary datastore, where changes are made:
admin@host-12-34-56:/> configure\nadmin@host-12-34-56:/config/> ... # Try: Tab or ?\nadmin@host-12-34-56:/config/> leave\n The leave command activates the changes by issuing a transaction to, essentially, copy the candidate back to running. Depending on the changes made, this can take a few seconds. If the changes are invalid, i.e., not correct according to the underlying YANG models, a warning is shown and the session remains in configure context. Use the abort command to cancel your changes, or investigate further with the diff command (see more below).
To save configuration changes made to the running-config to persistent store, so the system will use them for consecutive reboots, use the copy command:
admin@host-12-34-56:/> copy running-config startup-config\n In configure context the following commands are available:
Command Description set foo bar val Set bar leaf node in foo subcontext to val no foo bar Clear/delete configuration made to bar in foo edit foo baz Enter baz sub-sub-context in foo subcontext change password Start password dialogue to change a user's password text-editor foo Open a text editor to edit binary setting foo abort Abort changes in configuration, return to admin-exec exit Exit one level sub-context, or abort from top-level leave Save changes to running-config, return to admin-exec show [foo] Show configured values (optionally in subcontext) diff [foo] Show uncommitted changes in candidate do command Call admin-exec command: do show log commit"},{"location":"cli/introduction/#example-session","title":"Example Session","text":"Remember to use the TAB and ? keys to speed up your navigation. See help keybindings for more tips!
In this example we enter configure context to add an IPv4 address to interface eth0, then we apply the changes using the leave command.
We inspect the system status to ensure the change took effect. Then we save the changes for the next reboot.
admin@host-12-34-56:/> configure\nadmin@host-12-34-56:/config/> edit interface eth0\nadmin@host-12-34-56:/config/interface/eth0/> set ipv4 <TAB>\n address autoconf bind-ni-name enabled\n forwarding mtu neighbor\nadmin@host-12-34-56:/config/interface/eth0/> set ipv4 address 192.168.2.200 prefix-length 24\nadmin@host-12-34-56:/config/interface/eth0/> show\ntype ethernetCsmacd;\nipv4 address 192.168.2.200 prefix-length 24;\nipv6 enabled true;\nadmin@host-12-34-56:/config/interface/eth0/> diff\ninterfaces {\n interface eth0 {\n+ ipv4 {\n+ address 192.168.2.200 {\n+ prefix-length 24;\n+ }\n+ }\n }\n}\nadmin@host-12-34-56:/config/interface/eth0/> leave\nadmin@host-12-34-56:/> show interfaces\nINTERFACE PROTOCOL STATE DATA\nlo loopback UNKNOWN 00:00:00:00:00:00\n ipv4 127.0.0.1/8 (static)\n ipv6 ::1/128 (static)\neth0 ethernet UP 52:54:00:12:34:56\n ipv4 192.168.2.200/24 (static)\n ipv6 fe80::5054:ff:fe12:3456/64 (link-layer)\nadmin@host-12-34-56:/> show ip brief\nlo UNKNOWN 127.0.0.1/8 ::1/128\neth0 UP 192.168.2.200/24 fe80::5054:ff:fe12:3456/64\nadmin@host-12-34-56:/> copy running-config startup-config\n One of the ideas behind a separate running and startup configuration is to be able to verify a configuration change. In case of an inadvertent change that, e.g., breaks networking, it is trivial to revert back by:
admin@host-12-34-56:/> copy startup-config running-config\n Or restart the device, for example if the change to the configuration caused you to lose contact with the system (it happens to the best of us). The system will start up from the last \"save gave\".
Tip: when restoring a backup of a configuration, or having manually edited a config file, you can validate it using system's YANG models, it is not applied if validation is successful:
copy /media/backup/old.cfg running-config validate
"},{"location":"cli/keybindings/","title":"Keybindings","text":""},{"location":"cli/keybindings/#cli-keybindings","title":"CLI Keybindings","text":"Writing CLI commands by hand is very tedious. To make things easier the CLI has several keybindings, most significant first:
Key Cmd/Key Description TAB Complete current command, see below for example ? Show available commands, or arguments, with help Ctrl-c Cancel everything on current line Ctrl-d abort/exit Delete character, or abort/exit on empty line Ctrl-z leave Leave and activate changes in configure context Ctrl-f Right arrow Move cursor forward one character Ctrl-b Left arrow Move cursor back one character Meta-f Ctrl-Right Move cursor forward one word Meta-b Ctrl-Left Move cursor back one word Ctrl-e End Move cursor to end of line Ctrl-a Home Move cursor to beginning of line Ctrl-k Kill (cut) text from cursor to end of line Ctrl-u Delete (cut) entire line Ctrl-y Yank (paste) from kill buffer to cursor Meta-. Yank (paste) last argument from previous line Ctrl-w Meta-Backspace Delete (cut) word to the left Meta-Delete Delete (cut) word to the right Ctrl-l Clear screen and refresh current line Ctrl-p Up arrow History, previous command Ctrl-n Down arrow History, next command Ctrl-r History, reversed interactive search (i-search)"},{"location":"cli/keybindings/#what-is-meta","title":"What is Meta?","text":"The Meta key is called Alt on most modern keyboards. If you have neither, first tap the Esc key instead of holding down Alt/Meta.
"},{"location":"cli/keybindings/#usage","title":"Usage","text":"Complete a word. Start by typing a few characters, then tap the TAB key on your keyboard:
conf<TAB> --> configure\n See possible arguments, with brief help text, to a command:
show ?\nbridge Show bridge (ports/fdb/mdb/vlans)\ndatetime Show current date and time, default RFC2822 format\n...\n Type the command, then tap the ? key.
"},{"location":"cli/netcalc/","title":"Network Calculator","text":""},{"location":"cli/netcalc/#network-calculator","title":"Network Calculator","text":"netcalc <ADDRESS/LEN | NETWORK NETMASK> [split <1-32 | 64-128>]\n "},{"location":"cli/netcalc/#description","title":"Description","text":"netcalc is a network calculator that takes an IP address and a subnet mask, or an IP address and prefix mask in CIDR notation, and then returns information about the subnet. Both IPv4 and IPv6 is supported.
A subnet can be entered in two ways:
192.168.2.0 255.255.255.0: traditional IPv4 'address netmask' style 192.168.2.0/24: modern prefix length, same also for IPv6 An optional split LEN can be given as argument, the new length value must be bigger than the current prefix length. See example below.
"},{"location":"cli/netcalc/#examples","title":"Examples","text":"Its most commonly used features are to understand how many addresses an IP subnet has, what the broadcast address is, first and last usable address.
admin@example:/> netcalc 192.168.2.0/24\nAddress : 192.168.2.0 11000000.10101000.00000010. 00000000\nNetmask : 255.255.255.0 = 24 11111111.11111111.11111111. 00000000\nWildcard : 0.0.0.255 00000000.00000000.00000000. 11111111\n=>\nNetwork : 192.168.2.0/24 11000000.10101000.00000010. 00000000\nHostMin : 192.168.2.1 11000000.10101000.00000010. 00000001\nHostMax : 192.168.2.254 11000000.10101000.00000010. 11111110\nBroadcast: 192.168.2.255 11000000.10101000.00000010. 11111111\nHosts/Net: 254 Class C, Private network (RFC1918)\n Another common use-case is for IP subnetting, i.e., using only as many addresses for an IP subnet as needed. Example, to split the above /24 in four:
admin@example:/> netcalc 192.168.2.0/24 split 26\nAddress : 192.168.2.0 11000000.10101000.00000010. 00000000\nNetmask : 255.255.255.0 = 24 11111111.11111111.11111111. 00000000\nWildcard : 0.0.0.255 00000000.00000000.00000000. 11111111\n=>\nNetwork : 192.168.2.0/24 11000000.10101000.00000010. 00000000\nHostMin : 192.168.2.1 11000000.10101000.00000010. 00000001\nHostMax : 192.168.2.254 11000000.10101000.00000010. 11111110\nBroadcast: 192.168.2.255 11000000.10101000.00000010. 11111111\nHosts/Net: 254 Class C, Private network (RFC1918)\n\n[Split network/26]\nNetwork : 192.168.2.0 - 192.168.2.63 Netmask : 255.255.255.192\nNetwork : 192.168.2.64 - 192.168.2.127 Netmask : 255.255.255.192\nNetwork : 192.168.2.128 - 192.168.2.191 Netmask : 255.255.255.192\nNetwork : 192.168.2.192 - 192.168.2.255 Netmask : 255.255.255.192\n "},{"location":"cli/quick/","title":"Quickstart Guide","text":""},{"location":"cli/quick/#cli-quickstart-guide","title":"CLI Quickstart Guide","text":"The question mark ? key along with the Tab key are your best friends in the command line interface. They provide context help and completion of commands you input. See the table below for a handful of examples.
Command Description help Overview of topics/settings in the current context ? Lists commands + brief help in the current context <Tab> Lists commands available in the current context partial<Tab> Completes a partial command name command <Tab> Lists keywords/arguments associated with a command command keyword ? Lists arguments associated with a keyword + help Explore the following topics for more information. Note, the keybindings are really useful to learn!
Command Description help introduction An introduction to the CLI help configure How to use configure context help text-editor Help with the built-in text-editor command help keybindings Lists available keybindings & other helpful tricks In configure context the help setting command shows the YANG description text for each node and container. To reach the admin exec help from configure context, e.g., do help text-editor
"},{"location":"cli/tcpdump/","title":"Network Monitoring","text":""},{"location":"cli/tcpdump/#network-monitoring","title":"Network Monitoring","text":"tcpdump is useful tool for analyzing and diagnosing network problems. This document presents the limited feature set that exposed is in the CLI. Administrator level users with shell access can use the full feature set, and is not described here.
The following section is useful for understanding how to use the tool. A section called Examples follows that, which may be what you want to scroll down to.
"},{"location":"cli/tcpdump/#hardware-overview","title":"Hardware Overview","text":"Using tcpdump effectively requires an understanding of how the underlying hardware works. For a standard PC, or common single-board computers (SBC), the network cards (NICs) are usually connected directly to the CPU.
.---------.\n | +-- eth0\n | CPU |\n | +-- eth1\n '---------'\n In this setup it is evident that traffic coming in on either eth0 or eth1 reach the CPU, i.e., running tcpdump eth0 captures all traffic.
However, on other types of networking hardware, e.g., dedicated switch core setups depicted below, the flow of network traffic will likely not pass through the CPU. This depends of course on how the switch is set up, for instance if routing between all ports is enabled, each flow will reach the CPU, but in a plain switching setup it will not.
.---------.\n | |\n | CPU |\n | |\n '----+----'\n |\n |\n .----------+---------.\n | |\n ---+ +---\n E1 | SC | E3\n ---+ +---\n E2 | | E4\n '--+--+--+--+--+--+--'\n | | | | | |\n E5 E6 E7 E8 E9 E10\n So, running tcpdump e1 in a switching setup, inside the CPU, the only traffic that will be captured is traffic ingressing port E1 destined for the CPU itself. To analyze traffic going through the switch, you need something called port mirroring, or port monitoring, i.e., setting up the switch core to mirror traffic ingressing and/or egressing a set of ports to another port. On this monitor port you can then run your tcpdump, which means you need an external device (laptop).
A planned feature is to support mirroring traffic to the CPU port, which would be an effective way to log and monitor traffic over a longer period of time. Highly effective for diagnosing intermittent and other rare network issues.
If only \"proof of life\" is required, then sometimes port counters, also called RMON counters, can be very useful too. Seeing counters of a particular type increment means traffic is ingressing or egressing.
"},{"location":"cli/tcpdump/#examples","title":"Examples","text":"Listen to all traffic on an interface:
admin@example:/> tcpdump e1\n Listen to only ping traffic:
admin@example:/> tcpdump e1 expression icmp\n Listen to traffic on a given port:
admin@example:/> tcpdump e1 expression \"port 80\"\n Wait for one ping only:
admin@example:/> tcpdump e1 count 1 expression icmp\n Very verbose output:
admin@example:/> tcpdump e1 verbose\n "},{"location":"cli/text-editor/","title":"Text Editor","text":""},{"location":"cli/text-editor/#text-editor","title":"Text Editor","text":"The CLI built-in text-editor command can be used to edit type binary settings in configure context.
The default editor is a Micro Emacs clone. Users not familiar with terminal based editors may benefit from this introduction.
"},{"location":"cli/text-editor/#escape-metaalt-control-shift","title":"Escape Meta/Alt Control Shift","text":"When starting up, the editor status field at the bottom shows the following shorthand:
C-h q quick help | C-h t tutorial | C-h b key bindings | C = Ctrl | M = Alt\n Key combinations with a - (dash) mean holding down the modifier key. Combinations without a - (dash) mean without any modifier key.
"},{"location":"cli/text-editor/#quick-help-c-h-q","title":"Quick help C-h q","text":" hold down the Ctrl key on tap the h key release Ctrl tap the q key The bottom part of the terminal now shows a \"buffer\" called *quick*:
FILE BUFFER WINDOW MARK/KILL MISC\nC-x C-c exit C-x b switch C-x 0 only other C-space mark C-_ undo\nC-x C-f find C-x k close C-x 1 only this C-w kill-rg C-s search\nC-x C-s save C-x C-b list C-x 2 split two C-k kill-ln C-r r-search\nC-x s save-all C-x h mark C-x ^ enlarge C-y yank M-% replace\nC-x i insert C-x g goto-ln C-x o other win C-x C-x swap M-q reformat\n "},{"location":"cli/text-editor/#save-exit-c-x-c-c","title":"Save & Exit C-x C-c","text":" Hold down the Ctrl key tap X tap c release Ctrl The status field at the bottom asks if you are really sure, and/or if you want to add a final Enter/newline to the file. For binary content that final newline may be important.
"},{"location":"cli/text-editor/#changing-the-editor","title":"Changing the Editor","text":"The system has three different built-in editors:
emacs (Micro Emacs) nano (GNU Nano) vi (Visual Editor) Changing editor is done in configure context, in the system container:
admin@host:/> configure\nadmin@host:/config/> edit system\nadmin@host:/config/system/> set text-editor <TAB>\nemacs nano vi\nadmin@host:/config/system/> set text-editor nano\nadmin@host:/config/system/> leave\nadmin@host:/>\n "},{"location":"cli/upgrade/","title":"Upgrading","text":""},{"location":"cli/upgrade/#upgrading-the-system","title":"Upgrading the System","text":"The admin-exec command upgrade can be used to install software images, or bundles. A bundle is a signed and self-contained package that carries all the information necessary to determine if it holds a bootloader, a Linux image, or even both.
To install a new software image to the currently inactive partition1 , we use the upgrade command and a URI to a ftp/tftp/sftp or http/https server that hosts the file:
admin@host:/> upgrade tftp://192.168.122.1/firmware-x86_64-v23.11.pkg\ninstalling\n 0% Installing\n 0% Determining slot states\n 20% Determining slot states done.\n 20% Checking bundle\n 20% Verifying signature\n 40% Verifying signature done.\n 40% Checking bundle done.\n 40% Checking manifest contents\n 60% Checking manifest contents done.\n 60% Determining target install group\n 80% Determining target install group done.\n 80% Updating slots\n 80% Checking slot rootfs.1\n 90% Checking slot rootfs.1 done.\n 90% Copying image to rootfs.1\n 99% Copying image to rootfs.1 done.\n 99% Updating slots done.\n100% Installing done.\nInstalling `tftp://192.168.122.1/firmware-x86_64-v23.11.pkg` succeeded\nadmin@host:/>\n The secondary partition (rootfs.1) has now been upgraded and will be used as the active partition on the next boot. Leaving the primary partition, with the version we are currently running, intact in case of trouble.
See Upgrade & Boot Order for more information on upgrading.
It is not possible to upgrade the partition we booted from. Thankfully the underlying \"rauc\" subsystem keeps track of this. Hence, to upgrade both partitions you must reboot to the new version (to verify it works) and then repeat the same command.\u00a0\u21a9
"}]}
\ No newline at end of file
diff --git a/dev/sitemap.xml b/dev/sitemap.xml
index 6af5052e..725446c2 100644
--- a/dev/sitemap.xml
+++ b/dev/sitemap.xml
@@ -2,154 +2,158 @@
https://kernelkit.github.io/infix/dev/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/boot/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/branding/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/container/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/developers-guide/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/dhcp/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/discovery/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/eth-counters/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/firewall/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/hardware/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/license/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/management/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/netboot/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/networking/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/override-package/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/qos/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/scripting-netconf/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/scripting-prod/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/scripting-restconf/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/scripting-sysrepocfg/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/scripting/
- 2025-12-03
+ 2025-12-06
+
+
+ https://kernelkit.github.io/infix/dev/support/
+ 2025-12-06
https://kernelkit.github.io/infix/dev/syslog/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/system/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/test-arch/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/testing/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/tunnels/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/upgrade/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/virtual/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/vpd/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/wifi/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/configure/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/introduction/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/keybindings/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/netcalc/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/quick/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/tcpdump/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/text-editor/
- 2025-12-03
+ 2025-12-06
https://kernelkit.github.io/infix/dev/cli/upgrade/
- 2025-12-03
+ 2025-12-06
\ No newline at end of file
diff --git a/dev/sitemap.xml.gz b/dev/sitemap.xml.gz
index 28fb69c2..a326673d 100644
Binary files a/dev/sitemap.xml.gz and b/dev/sitemap.xml.gz differ
diff --git a/dev/support/index.html b/dev/support/index.html
new file mode 100644
index 00000000..70f0f77d
--- /dev/null
+++ b/dev/support/index.html
@@ -0,0 +1,2110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Support Data - User's Guide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Introduction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Support Data Collection
+When troubleshooting issues or seeking support, the support command
+provides a convenient way to collect comprehensive system diagnostics.
+This command gathers configuration files, logs, network state, and other
+system information into a single compressed archive.
+Collecting Support Data
+To collect support data and save it to a file:
+ admin@host:~$ support collect > support-data.tar.gz
+( admin@host) Password: ***********
+ Starting support data collection from host...
+ This may take up to a minute. Please wait...
+ Tailing /var/log/messages for 30 seconds ( please wait ) ...
+ Log tail complete.
+ Collection complete. Creating archive...
+ admin@host:~$ ls -l support-data.tar.gz
+ -rw-rw-r-- 1 admin admin 508362 nov 30 13 :05 support-data.tar.gz
+
+The command can also be run remotely via SSH from your workstation:
+ $ ssh admin@host support collect > support-data.tar.gz
+ ...
+
+The collection process may take up to a minute depending on system load
+and the amount of logging data. Progress messages are shown during the
+collection process.
+Encrypted Collection
+For secure transmission of support data, the archive can be encrypted
+with GPG using a password:
+ admin@host:~$ support collect -p mypassword > support-data.tar.gz.gpg
+ Starting support data collection from host...
+ This may take up to a minute. Please wait...
+ ...
+ Collection complete. Creating archive...
+ Encrypting with GPG...
+
+The support collect command even supports omitting mypassword and
+will then prompt interactively for the password. This works over SSH too,
+but the local ssh client may then echo the password.
+
+
Tip
+
To hide the encryption password for an SSH session, the script supports
+reading from stdin:
+echo "$MYSECRET" | ssh user@device support collect -p >
+file.tar.gz.gpg
+
+After transferring the resulting file to your workstation, decrypt it
+with the password:
+ $ gpg -d support-data.tar.gz.gpg > support-data.tar.gz
+ $ tar xzf support-data.tar.gz
+ ...
+
+or
+ $ gpg -d support-data.tar.gz.gpg | tar xz
+ ...
+
+
+
Important
+
Make sure to share mypassword out-of-band from the encrypted data
+with the recipient of the data. I.e., avoid sending both in the same
+plain-text email for example.
+
+What is Collected
+The support archive includes:
+
+System identification (hostname, uptime, kernel version)
+Running and operational configuration (sysrepo datastores)
+System logs (/var/log directory and live tail of messages log)
+Network configuration and state (interfaces, routes, neighbors, bridges)
+FRRouting information (OSPF, BFD status)
+Container information (podman containers and their configuration)
+System resource usage (CPU, memory, disk, processes)
+Hardware information (PCI, USB devices, network interfaces)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dev/syslog/index.html b/dev/syslog/index.html
index 7a9aa915..464e5395 100644
--- a/dev/syslog/index.html
+++ b/dev/syslog/index.html
@@ -13,7 +13,7 @@
-
+
@@ -992,6 +992,8 @@
+
+
@@ -1355,6 +1357,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/system/index.html b/dev/system/index.html
index 4e9b8977..3f8c4d47 100644
--- a/dev/system/index.html
+++ b/dev/system/index.html
@@ -992,6 +992,8 @@
+
+
@@ -1288,56 +1290,6 @@
-
-
-
-
-
-
- Support Data Collection
-
-
-
-
-
-
-
-
@@ -1439,6 +1391,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2182,56 +2162,6 @@
-
-
-
-
-
-
- Support Data Collection
-
-
-
-
-
-
-
-
@@ -2559,74 +2489,6 @@ reference ID, stratum, time offsets, frequency, and root delay.
The system uses chronyd Network Time Protocol (NTP) daemon. The
output shown here is best explained in the Chrony documentation .
-Support Data Collection
-When troubleshooting issues or seeking support, the support command
-provides a convenient way to collect comprehensive system diagnostics.
-This command gathers configuration files, logs, network state, and other
-system information into a single compressed archive.
-Collecting Support Data
-To collect support data and save it to a file:
- admin@host:~$ support collect > support-data.tar.gz
-( admin@host) Password: ***********
- Starting support data collection from host...
- This may take up to a minute. Please wait...
- Tailing /var/log/messages for 30 seconds ( please wait ) ...
- Log tail complete.
- Collection complete. Creating archive...
- admin@host:~$ ls -l support-data.tar.gz
- -rw-rw-r-- 1 admin admin 508362 nov 30 13 :05 support-data.tar.gz
-
-The command can also be run remotely via SSH from your workstation:
- $ ssh admin@host support collect > support-data.tar.gz
- ...
-
-The collection process may take up to a minute depending on system load
-and the amount of logging data. Progress messages are shown during the
-collection process.
-Encrypted Collection
-For secure transmission of support data, the archive can be encrypted
-with GPG using a password:
- admin@host:~$ support collect -p mypassword > support-data.tar.gz.gpg
- Starting support data collection from host...
- This may take up to a minute. Please wait...
- ...
- Collection complete. Creating archive...
- Encrypting with GPG...
-
-The support collect command even supports omitting mypassword and
-will then prompt interactively for the password. This works over SSH too,
-but the local ssh client may then echo the password.
-
-
Tip
-
To hide the encryption password for an SSH session, the script supports reading from stdin:
-echo "$MYSECRET" | ssh user@device support collect -p > file.tar.gz.gpg
-
-After transferring the resulting file to your workstation, decrypt it
-with the password:
- $ gpg -d support-data.tar.gz.gpg > support-data.tar.gz
- $ tar xzf support-data.tar.gz
-
-or
- $ gpg -d support-data.tar.gz.gpg | tar xz
-
-
-
Important
-
Make sure to share mypassword out-of-band from the encrypted data
-with the recipient of the data. I.e., avoid sending both in the same
-plain-text email for example.
-
-What is Collected
-The support archive includes:
-
-System identification (hostname, uptime, kernel version)
-Running and operational configuration (sysrepo datastores)
-System logs (/var/log directory and live tail of messages log)
-Network configuration and state (interfaces, routes, neighbors, bridges)
-FRRouting information (OSPF, BFD status)
-Container information (podman containers and their configuration)
-System resource usage (CPU, memory, disk, processes)
-Hardware information (PCI, USB devices, network interfaces)
-
diff --git a/dev/test-arch/index.html b/dev/test-arch/index.html
index 8b4169cd..b82be6c0 100644
--- a/dev/test-arch/index.html
+++ b/dev/test-arch/index.html
@@ -990,6 +990,8 @@
+
+
@@ -1179,6 +1181,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/testing/index.html b/dev/testing/index.html
index d12fd6a5..e9a38d96 100644
--- a/dev/testing/index.html
+++ b/dev/testing/index.html
@@ -990,6 +990,8 @@
+
+
@@ -1179,6 +1181,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tunnels/index.html b/dev/tunnels/index.html
index 7516a7ab..c6c8cfc6 100644
--- a/dev/tunnels/index.html
+++ b/dev/tunnels/index.html
@@ -1178,6 +1178,8 @@
+
+
@@ -1367,6 +1369,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/upgrade/index.html b/dev/upgrade/index.html
index 6710f3e0..4f381a8e 100644
--- a/dev/upgrade/index.html
+++ b/dev/upgrade/index.html
@@ -10,7 +10,7 @@
-
+
@@ -992,6 +992,8 @@
+
+
@@ -1179,6 +1181,34 @@
+
+
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/virtual/index.html b/dev/virtual/index.html
index c19a3dd6..53261a14 100644
--- a/dev/virtual/index.html
+++ b/dev/virtual/index.html
@@ -990,6 +990,8 @@
+
+
@@ -1179,6 +1181,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/vpd/index.html b/dev/vpd/index.html
index fd717f5c..5dcd0ff8 100644
--- a/dev/vpd/index.html
+++ b/dev/vpd/index.html
@@ -990,6 +990,8 @@
+
+
@@ -1179,6 +1181,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/wifi/index.html b/dev/wifi/index.html
index 19374b81..6fa7d112 100644
--- a/dev/wifi/index.html
+++ b/dev/wifi/index.html
@@ -1100,6 +1100,8 @@
+
+
@@ -1289,6 +1291,34 @@
+
+
+
+
+
+
+
+
+ Support Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+