A small Linux daemon that turns network events into drop-in hook scripts. netevd listens to netlink (and opt-in eBPF) across systemd-networkd, NetworkManager and dhclient, runs your /etc/netevd/<event>.d/ scripts, fixes multi-homed return paths with automatic policy routing, and exposes a REST API and Prometheus metrics.
3 network backends · 17 hook directories · Zero polling · Automatic policy routing · Opt-in eBPF drops and resets
Version 0.4.1 (CHANGELOG.md):
| Observe-only eBPF | --features ebpf: kfree_skb → drops.d, tcp_retransmit_skb → tcp-retransmit.d, tcp_*_reset → tcp-reset.d, with ringbuf drain, CO-RE ports/L4, coalescing and interface/reason filters. |
| eBPF metrics | Prometheus netevd_ebpf_* on the API port (:9090/metrics). |
| Container images | ghcr.io/zyvorai/netevd:latest-ubuntu (Ubuntu 26.04) and :latest-alpine (Alpine 3.23). |
| Remote lab prove | scripts/deploy-remote.sh builds with eBPF, installs the BPF object and unit drop-in, and checks attach plus observe hooks. |
| You have… | netevd gives you… |
|---|---|
| Scripts that should run when the network changes | Drop a file in /etc/netevd/routable.d/ |
| Multi-homed return-path breakage | Automatic per-interface tables + policy rules |
NetworkManager dispatcher.d only |
One contract for networkd, NM, and dhclient |
Cron polling ip addr |
Real netlink multicast (<100 ms) |
Silent kfree_skb / TCP RST netlink never sees |
Opt-in eBPF → drops.d / tcp-reset.d |
| Hand-rolled netlink + debounce + safe exec | Already done — with validated $LINK / $JSON |
| netevd | NetworkManager dispatcher | |
|---|---|---|
| Where it works | systemd-networkd, NetworkManager and dhclient hosts | Devices managed by NetworkManager |
| Event source | Netlink multicast, backend D-Bus or dhclient leases, optional eBPF ringbuf | NetworkManager's own device and connection actions |
| Script contract | /etc/netevd/<event>.d/, 17 directories, $LINK, $STATE, $ADDRESSES, versioned $JSON |
/etc/NetworkManager/dispatcher.d/ with interface and action arguments plus environment |
| Multi-homed return path | Automatic per-interface tables (200 + ifindex) and from/to rules |
Write it in your scripts |
| Silent drops and TCP resets | Opt-in eBPF tracepoints → drops.d, tcp-retransmit.d, tcp-reset.d |
Not in scope |
| API and metrics | REST /api/v1/*, Prometheus /metrics on :9090 |
Not included |
| Choose NetworkManager dispatcher when | Every host runs NetworkManager and plain per-action scripts are all you need |
| Backends | systemd-networkd · NetworkManager · dhclient |
| Hooks | 17 directories (carrier, routable, link/address, eBPF drops…) |
| Latency | <100 ms netlink · zero polling |
| eBPF | Opt-in observe-only: drops / TCP retransmit / TCP reset |
| Ops | Policy routing · REST :9090 · Prometheus /metrics |
| License | Apache-2.0 |
flowchart LR
Kernel[Netlink_plus_eBPF] --> State[NetworkState]
State --> Hooks[Hook_scripts]
State --> Routing[Policy_routing]
State --> API[REST_and_metrics]
- Sources — netlink multicast, backend D-Bus (or dhclient leases), optional eBPF ringbuf
- State — one
NetworkStatebehindArc<RwLock>, updated by Tokio tasks - Actions — matching
/etc/netevd/<event>.d/scripts, policy rules, optional DNS/hostname via D-Bus
Linux with systemd-networkd, NetworkManager or dhclient. Release tarball (recommended):
curl -LO https://github.com/zyvorai/zyvor-netevd/releases/download/v0.4.1/netevd-0.4.1-linux-amd64.tar.gz
tar xzf netevd-*-linux-amd64.tar.gz && cd netevd-*-linux-amd64
sudo ./install.sh && sudo systemctl enable --now netevdsudo tee /etc/netevd/routable.d/01-notify.sh >/dev/null <<'EOF'
#!/bin/bash
logger -t netevd "$LINK is routable: $ADDRESSES"
EOF
sudo chmod +x /etc/netevd/routable.d/01-notify.shWhen the interface becomes fully routable, that script runs. Same idea for carrier, link add/remove, routes — and, with eBPF, packet drops and TCP resets.
git clone https://github.com/zyvorai/zyvor-netevd.git && cd netevd
cargo build --release
# optional: make -C ebpf && cargo build --release --features ebpf
sudo install -Dm755 target/release/netevd /usr/bin/netevd
sudo install -Dm644 systemd/netevd.service /lib/systemd/system/netevd.service
sudo install -Dm644 config/netevd.example.yaml /etc/netevd/netevd.yaml
sudo systemctl enable --now netevddocker pull ghcr.io/zyvorai/netevd:latest-ubuntu # or :latest-alpineImages ship on every main push. Hook scripts have bash and ip; D-Bus uses zbus (no dbus/systemd in the image).
./scripts/deploy-remote.sh <host> [user] # builds, installs, veth + eBPF attach checkNetlink covers link, address, and route. It does not see silent stack drops or TCP retransmit/RST. With --features ebpf, the same hook contract gets three more sources — no XDP/TC deny, no DNS/SNI, no process attribution.
| Kernel source | Hook directory |
|---|---|
skb:kfree_skb |
/etc/netevd/drops.d/ |
tcp:tcp_retransmit_skb |
/etc/netevd/tcp-retransmit.d/ |
tcp:tcp_*_reset |
/etc/netevd/tcp-reset.d/ |
make -C ebpf
cargo build --release --features ebpf
sudo install -Dm644 ebpf/netevd-ebpf.o /usr/lib/netevd/netevd-ebpf.o
sudo install -Dm644 systemd/netevd-ebpf.conf /etc/systemd/system/netevd.service.d/ebpf.confebpf:
enabled: true
drops: true
tcp_reset: true
min_count: 8
reasons_deny: ["NO_SOCKET"]Scripts get $DROP_REASON, $PROTOCOL, $SPORT/$DPORT, $COUNT, $JSON. Metrics: netevd_ebpf_*. Full guide: docs/user/ebpf.md.
| Directory | Fires when |
|---|---|
carrier.d/ / no-carrier.d/ |
Link up / down |
routable.d/ |
Full L3 connectivity |
link-added.d/ / link-removed.d/ |
Interface appears / disappears |
address-added.d/ |
Address configured |
drops.d/ / tcp-reset.d/ |
eBPF observe-only (opt-in) |
All 17 directories, env vars, and netevd.event.v1 JSON: docs/hooks-contract.md. Use 01- / 02- prefixes for order; non-zero exits are logged and do not block siblings.
Every script gets $LINK, $LINKINDEX, $STATE, $BACKEND, $ADDRESSES (plus $JSON / DHCP fields by backend).
List an interface under routing.policy_rules and netevd:
- Creates table
200 + ifindex - Adds
from <ip>/to <ip>lookup rules - Installs a default via that interface’s gateway
- Tears it down when addresses leave
$ ip rule list
32765: from 192.168.1.100 lookup 203
$ ip route show table 203
default via 192.168.1.1 dev eth1Minimal shape — full template: config/netevd.example.yaml.
system:
backend: "systemd-networkd" # or NetworkManager | dhclient
monitoring:
match_patterns: ["eth*", "wg*"]
exclude: ["lo", "docker*", "veth*"]
hooks:
debounce_ms: 50
routing:
policy_rules: ["eth1"]- Starts as root, drops to user
netevd CAP_NET_ADMINby default; eBPF builds also keepCAP_BPF,CAP_PERFMON,CAP_DAC_READ_SEARCH- Validated interface names / IPs / hostnames — no shell metacharacters
- Scripts exec’d directly (not
sh -c) - systemd hardening (
NoNewPrivileges,ProtectSystem=strict, …); eBPF re-allowsbpf/perf_event_open
Details: SECURITY.md.
| Metric | Typical |
|---|---|
| RSS idle | 3–5 MB |
| CPU idle | <1 % |
| Netlink event latency | <100 ms |
| Event → script | <10 ms |
| Throughput | 1000+ events/s |
Same port as Prometheus (default 9090):
curl -s localhost:9090/api/v1/status
curl -s localhost:9090/api/v1/interfaces
curl -s localhost:9090/api/v1/events
curl -s localhost:9090/metrics
curl -s localhost:9090/healthcargo build && cargo test && cargo clippy -- -D warnings
# eBPF unit tests (no CAP_BPF): cargo test --lib ebpf::| Community (this repo) | Enterprise | |
|---|---|---|
| Support | GitHub Issues | SLA · sales@zyvor.dev |
| Scope | Self-hosted hooks + policy routing | Production rollouts, platform integration |
| Platform | netevd | netctl, cloud-netconfig, Zyvor Platform |
Demo · Pricing · Contact · docs/enterprise.md
Book a demo · 30-day PoC · fallback: sales@zyvor.dev
Maintained by Susant Sahani · Zyvor AI Labs. Community help: Issues · SECURITY.md.
netevd is at 0.4.1 (CHANGELOG.md); release tarballs ship from tags and container images on every main push.
| Area | Status |
|---|---|
| Netlink watchers, systemd-networkd / NetworkManager / dhclient backends, hook directories | Default build |
| Automatic policy routing | Default build, per interface listed in routing.policy_rules |
| REST API and Prometheus metrics | Default build, :9090 |
| eBPF drops, TCP retransmit, TCP reset | Opt-in (--features ebpf), observe-only: no XDP/TC deny, no DNS/SNI, no process attribution |
What comes next: docs/ROADMAP.md.
| Product | Role next to netevd |
|---|---|
| netevd | Linux network event daemon: netlink and eBPF events into hooks, policy routing, REST and metrics |
| netctl | Pairs with netevd: systemctl-style network configuration CLI over netlink and systemd D-Bus |
| cloud-netconfig | Pairs with netevd on cloud VMs: secondary IPs and policy routing from cloud metadata |
| Netra | Next to netevd: eBPF network observability and leased emergency network control for Linux and Kubernetes |
Enterprise rollouts cover netevd together with netctl and cloud-netconfig.
netevd is free and open source under the Apache License 2.0 (see NOTICE): use, modify and run it in production. That does not change.
Zyvor Enterprise adds what production teams ask for: supported releases, deployment and upgrade guidance, priority incident triage, a named technical contact and 24x7 critical intake. Plans and terms: docs/SUBSCRIPTION-MODEL.md · Pricing · sales@zyvor.dev.
Report vulnerabilities per SECURITY.md.



