autora is a small Linux IPv6 Router Advertisement daemon.
It watches selected network interfaces and advertises enough IPv6 information for hosts on those links to configure an address with SLAAC and install a default route through the local link-local router address. It is intentionally narrow: it does not run DHCPv6, publish DNS settings, manage addresses, proxy neighbors, or install routes.
The daemon is designed for links where every served interface has one IPv6
policy and one routed /64. A common use case is a host with one VM-facing
interface per guest, but the same model can apply to any isolated L2 segment
where the attached systems should receive the same Router Advertisement.
This project was written by Codex-5.5 for a specific use case. It implements the planned control loop, polling-based interface discovery, fail-closed eligibility checks, RA packet construction, RS parsing, jittered scheduling, per-interface multicast rate limiting, and a Linux raw ICMPv6 socket path.
The daemon serves interfaces that match the configured regular expression and meet all of these requirements:
- IPv6 must be enabled.
- The interface is up, multicast-capable, and non-loopback.
- A link-local IPv6 address exists.
- Exactly one usable non-link-local
/64is present. /proc/sys/net/ipv6/conf/<ifname>/forwardingis1.- No other RA daemon should advertise on the same interface.
autora sends only multicast Router Advertisements. Solicited replies are also
multicast, which avoids creating kernel neighbor-cache state for
client-provided or spoofed source addresses.
Every Router Advertisement uses:
- ICMPv6 type
134, code0; - IPv6 hop limit
255; - RA Current Hop Limit
0; - Managed Address Configuration flag
M = 0; - Other Configuration flag
O = 0; - Router Lifetime from configuration, default
180s; - Reachable Time
0; - Retrans Timer
0; - one Prefix Information Option for the selected
/64; - PIO Prefix Length
64; - PIO Autonomous Address-Configuration flag
A = 1; - PIO On-Link flag
L = 0; - PIO Valid Lifetime from configuration, default
86400s; - PIO Preferred Lifetime from configuration, default
14400s.
cargo run -- \
--interface-regex '^lan[0-9]+$' \
--min-ra-interval 20s \
--max-ra-interval 60s \
--router-lifetime 180sThe default interface regex is ^$, so no interface is served until you provide
one. Use anchored regexes that match only interfaces you intend to advertise on.
Dry-run mode shows discovery, eligibility, scheduling, and packet construction without opening a raw socket:
cargo run -- --dry-run --interface-regex '^eth[0-9]+$'Raw packet operation requires CAP_NET_RAW. For a built binary, run as root or
grant the binary that capability through your service manager.
The main options also have environment variable equivalents:
--interface-regex,AUTORA_INTERFACE_REGEX--min-ra-interval,AUTORA_MIN_RA_INTERVAL--max-ra-interval,AUTORA_MAX_RA_INTERVAL--router-lifetime,AUTORA_ROUTER_LIFETIME--pio-valid-lifetime,AUTORA_PIO_VALID_LIFETIME--pio-preferred-lifetime,AUTORA_PIO_PREFERRED_LIFETIME--forwarding-resync-interval,AUTORA_FORWARDING_RESYNC_INTERVAL--heartbeat-interval,AUTORA_HEARTBEAT_INTERVAL--log-level,AUTORA_LOG--dry-run,AUTORA_DRY_RUN
For a routed-prefix-per-VM setup, give each guest its own L2-facing host
interface, such as a tap or veth, and route one /64 to that interface.
Then match only those interfaces:
cargo run -- \
--interface-regex '^tap-vm-[0-9]+$' \
--min-ra-interval 20s \
--max-ra-interval 60s \
--router-lifetime 180sWith the default packet settings, guests can form SLAAC addresses from the
advertised /64 and install the host link-local address as their default
router. The advertised prefix is intentionally not marked on-link.
For each served interface, assign one routed /64, bring the interface up, and
enable per-interface IPv6 forwarding:
ip addr add 2001:db8:1200::1/64 dev tap-vm-1
ip link set tap-vm-1 up
sysctl -w net.ipv6.conf.tap-vm-1.forwarding=1Make sure firewall rules allow ICMPv6 Router Solicitation, Router Advertisement, and Neighbor Discovery traffic on the served interfaces.
An example systemd unit is available at contrib/autora.service.
Build a local binary package with Debian tooling:
dpkg-buildpackage -us -uc -bThe package installs:
/usr/bin/autora/lib/systemd/system/autora.service/etc/default/autora
The packaged service reads /etc/default/autora:
AUTORA_INTERFACE_REGEX=^tap-vm-[0-9]+$
AUTORA_MIN_RA_INTERVAL=20s
AUTORA_MAX_RA_INTERVAL=60s
AUTORA_ROUTER_LIFETIME=180sAfter installing the package, edit /etc/default/autora with a restrictive
interface regex, then enable the service:
systemctl enable --now autora.serviceThe Debian package version comes from the first entry in debian/changelog.
For GitHub releases, .github/workflows/release.yml derives the version from
the release tag, strips a leading v, rewrites the top changelog version, runs
dpkg-buildpackage -us -uc -b, and uploads the generated .deb files to the
release. For example, a release tag v0.2.0 produces Debian package version
0.2.0.
Router Solicitations are accepted only on eligible interfaces and only with IPv6
hop limit 255. Solicited replies are sent as multicast RAs to ff02::1 scoped
to the receiving interface, rather than as unicast replies to client-provided
source addresses. This avoids creating kernel neighbor-cache state for spoofed
addresses. Solicited replies use a random delay from 0 through 500ms, unless
an already scheduled multicast RA will happen earlier.
Multicast Router Advertisements are rate-limited per interface with at least
3s between sends.
Non-privileged checks:
cargo fmt --check
cargo test
cargo checkPacket-level verification should be done in a Linux namespace or VM test
environment with CAP_NET_RAW and packet capture. Confirm that RAs are sent
from the interface link-local address to ff02::1, hop limit is 255, the
Prefix Information Option has A=1 and L=0, and the attached guest installs a
default route without an on-link route for the advertised /64.
The namespace smoke test creates an isolated veth pair and checks both a non-matching regex case and a working advertisement case:
sudo tests/ns-smoke.sh