Note: This project is currently in active development.
Netconfig is a standalone Linux interface configurator for YANET deployments. It configures KNI, VLAN, loopback and dummy interfaces in the network namespace where it runs.
The implementation is extracted from the YANET2 netlink dataplane sidecar. It is an independent Go module: the executable does not depend on YANET2, DPDK, a gateway, the neighbour API, Netplan executables or a service manager.
- The dataplane creates
kni[0-9]+; the kernel supplieslo. Netconfig waits for them and creates only explicitly configured VLAN and dummy interfaces. - Configuration is read and validated once, before opening netlink sockets. Missing or malformed files fail startup. File edits, replacements and deletion have no effect on a running instance, including its pending retries.
- Every setup pass attempts available links even when another link is missing or fails. Partial progress is retained. Both phases retry with exponential backoff until all configured links succeed, including IPv6 duplicate-address detection.
- After success, netconfig closes its netlink handle and waits for SIGTERM/SIGINT
without further mutations.
-onceexits successfully instead;-checkonly validates the selected source and needs no network privileges. - Shutdown never tears down interfaces or flushes addresses. Restart reconciles observed kernel state, reuses compatible links and preserves unrelated addresses. An incompatible type, VLAN parent, ID or protocol is an error, not a migration.
- There is no continuous drift repair. Restart netconfig to apply a new config or restore a managed interface deleted after successful bootstrap.
- Netconfig is the only interface configurator in its network namespace: one worker owns interface creation, addresses, MTU, administrative state and per-interface IPv6 sysctls. Do not run another netconfig instance, Netplan apply, networkd, NetworkManager or another interface configurator in that namespace.
- The dataplane supplies KNI, possibly after startup. During bootstrap it and other processes must not delete, rename or recreate observed managed links, or change their addresses, MTU or IPv6 policy. Each creation/configuration phase uses one kernel link snapshot; configuration updates it with its own MTU changes. There is no per-operation identity revalidation or defence against concurrent replacement.
- Carrier changes, IPv6 DAD, automatic IPv6LL and kernel RA processing are normal asynchronous events. Bootstrap still waits for required IPv6 readiness and retries kernel errors. A failed pass retains partial progress.
- Input shape, retry bounds and topology are validated during configuration loading, before acquiring kernel resources. The worker owns that startup state; internal helpers consume it without repeated validation or defensive deep copies.
- Existing state from an earlier run is supported: compatible links are reused, incompatible link types/VLAN identities are rejected and unowned addresses are preserved, except the documented cleanup of unlisted IPv6LL.
Routing and neighbour discovery belong to separate processes: netconfig does not program Linux routes/neighbours or start DHCP clients. Kernel connected/local/RA routes and ordinary ARP/NDP are normal side effects of interface configuration.
The CLI defaults to /etc/netconfig/config.yaml. The file selects exactly one
source; all paths are resolved in the process filesystem, relative to its working
directory when not absolute. See Netplan and
native examples.
source: netplan
netplan_path: /etc/netplan/00-interfaces.yaml
retry:
initial_backoff: 250ms
max_backoff: 30ssource is required: netplan or native. For Netplan, an omitted path uses
/etc/netplan/00-interfaces.yaml; an explicitly empty path is rejected. Native
mode requires a native mapping and forbids netplan_path entirely. Retry defaults
are 250ms and 30s; both must be positive, with the initial delay no greater than
the maximum. Unknown root keys and multiple YAML documents are rejected.
Native input is embedded directly, without a network wrapper:
source: native
native:
ethernets:
kni0: {mtu: 9000, link-local: [], accept-ra: false}
lo: {addresses: [192.0.2.241/32]}
vlans:
kni0.100:
link: kni0
id: 100
mtu: 9000
addresses: [192.0.2.2/24, '2001:db8:100::2/64', 'fe80::f1/64']
link-local: []
accept-ra: false
dummy-devices:
dummy0: {addresses: [198.51.100.1/32], link-local: []}| Section or field | Contract |
|---|---|
ethernets |
Only declared kni[0-9]+ and existing lo are managed; neither is created. |
vlans |
Directly on a declared KNI; required id (0..4094) and link (parent name). |
dummy-devices |
Explicitly declared dummy interfaces. Reserved base-interface names are rejected. |
addresses |
Static IPv4/IPv6 prefix strings. Existing matching addresses are made permanent and preferred. An omitted or empty list does not remove other addresses. Unspecified, multicast and IPv4-mapped IPv6 addresses are rejected; ::1 requires lo. |
mtu |
Omitted or 0 preserves existing MTU; otherwise 1280..2147483647, subject to kernel and parent limits. |
link-local |
Omitted enables automatic IPv6LL. [] disables generation; [ipv6] enables it. IPv6 and NDP stay enabled. |
accept-ra |
Optional boolean; omission leaves kernel policy untouched. False writes 0, true writes 2 to permit RA with forwarding enabled. |
dhcp4, dhcp6 |
Optional booleans, false-only. Existing DHCP-assigned addresses are not removed. |
MTU increases on a parent precede child changes; parent decreases follow them. A new VLAN inherits the configured parent MTU, or the observed parent MTU if unspecified. Oversized existing children, including unmanaged children, block parent decreases instead of being silently clamped. Existing interfaces with an unspecified MTU keep their current value.
With automatic IPv6LL disabled, unlisted IPv6 link-local addresses are removed only from managed non-loopback links, after explicit addresses are ensured. Explicitly listed link-local addresses are retained. A configured IPv6 address with failed DAD is removed and re-added for retry; tentative addresses prevent bootstrap completion. A conflicting IPv6 prefix length is rejected.
With automatic IPv6LL enabled, non-loopback links must have a preferred link-local address that has completed DAD before bootstrap succeeds. Missing carrier or failed automatic DAD keeps bootstrap pending; unlisted failed addresses are not deleted. Resolve the link or duplicate-address problem externally. Unrelated tentative global addresses do not block bootstrap. Loopback does not require a link-local address.
Native input strictly rejects unknown sections/fields, duplicate keys, null
values and incorrect YAML types. Numeric fields require unquoted decimal integers;
avoid leading zeros. There are no routes, policy rules, bridges, bonds, tunnels,
renderer, version, arbitrary Ethernet names or administrative-state fields.
Both sources normalize directly into desired state through the shared field parser
in internal/config; native restrictions and Netplan compatibility are explicit.
The Netplan adapter reads a single version-2 document and preserves the extracted
parser's compatibility rules, including aliases and decimal strings for MTU/VLAN
IDs. Unrelated Ethernet interfaces, VLANs on them and known unrelated network
sections are ignored. routes and routing-policy are ignored on managed links.
Unsupported settings on managed interfaces fail validation. This is a parser for
the documented subset, not an implementation of all Netplan features.
Build with Go 1.27.1 or newer on Linux; CI and container builds use Go 1.27.1:
go build -trimpath -o build/netconfig ./cmd/netconfig
./build/netconfig -config examples/native.yaml -checkThe multi-stage image uses scratch for the runtime and contains only the
statically linked binary and license notices. It needs no shell or OS packages:
docker build -t netconfig:local .
docker run --rm --network none --cap-drop ALL --read-only \
--mount type=bind,src="$PWD/examples",dst=/config,readonly \
netconfig:local -config /config/native.yaml -checkThe image workflow publishes ghcr.io/yanet-platform/netconfig for Linux amd64
and arm64 on pushes to main and v* tags, with branch/tag and commit-SHA tags.
Use an immutable tag for deployment. The image contains no default topology;
mount the config and, when selected, the external Netplan file.
Run netconfig in the dataplane Pod's private namespace (hostNetwork: false).
It operates in its current namespace and never switches namespaces itself.
Kernel mutations require CAP_NET_ADMIN and writable per-interface IPv6 sysctls.
Adding that capability alone does not make a container's read-only /proc/sys
writable. The privileged container mode below supplies the required access;
deployment policy must provide equivalent isolated access if using a narrower
security context. Do not mount the host's /proc/sys into the container.
Merge this fragment into the existing dataplane Pod, providing the named ConfigMap volumes and replacing the image tag:
spec:
hostNetwork: false
initContainers:
- name: netconfig
image: ghcr.io/yanet-platform/netconfig:sha-REPLACE
restartPolicy: Always
args: ["-config", "/etc/netconfig/config.yaml"]
securityContext:
privileged: true
volumeMounts:
- {name: netconfig, mountPath: /etc/netconfig, readOnly: true}
- {name: netplan, mountPath: /etc/netplan, readOnly: true}Use a restartable init container as shown or an ordinary sidecar. A traditional
init container waiting for KNI would prevent the dataplane from starting and
creating KNI. Do not gate startup of the dataplane on completed configuration.
The default idle-after-success lifecycle is intended for restartPolicy: Always;
-once is for explicitly supervised one-shot execution.
go test -race -count=1 ./...
go vet ./...
docker build --target test -t netconfig-test .
docker run --rm netconfig-test
docker run --rm --privileged --network none \
-e NETCONFIG_NETNS_TESTS=1 netconfig-test \
go test -race -count=1 -p=1 ./...Kernel tests require a disposable network namespace, TAP support and writable
IPv6 sysctls. Never run them in the host network namespace. The Docker test target
builds the executable and sets NETCONFIG_BINARY so subprocess tests exercise the
real CLI; without that variable they explicitly skip. Kernel cases additionally
require NETCONFIG_NETNS_TESTS=1 and run serially across packages.
Coverage includes strict parsing, address-family validation, restart compatibility, MTU ordering, IPv6LL/DAD, delayed KNI, partial progress, cancellation, immutable input during retries, preservation of foreign addresses, SIGTERM without teardown, restart reconciliation, permanent static lifetimes and automatic link-local readiness.
Netconfig is licensed under the Apache License, Version 2.0. The original YANET2 copyright notice is preserved. See NOTICE for upstream attribution and the exact source revision.