nftable-router
Software Policy Router for nftables — GeoIP/domain based policy routing via NFQUEUE, with a built-in web admin, transparent-proxy process supervision, PowerDNS Recursor management and SNMP switch ARP/MAC collectors.
Install
Requirements
| Python | >= 3.8 (3.13 tested; see note on pysnmp below) |
| Kernel | nftables + NFQUEUE (nfnetlink_queue) |
| Services | redis (the UI stream, ARP/MAC cache and cross-process state board) |
| Build deps | build-essential, libnetfilter-queue-dev (for NetfilterQueue) |
One dependency cannot come from pip: the nftables Python binding ships
with the nftables project itself, not PyPI (the PyPI name nftables is an
unrelated project — do not install it).
apt install python3-nftables build-essential libnetfilter-queue-dev
Install the package
pip install .
# or from a built wheel
pip install dist/nftable_router-*.whl
If you install into a venv created without --system-site-packages, link
the distro-provided binding in, otherwise import nftables fails:
ln -s /usr/lib/python3/dist-packages/nftables \
/path/to/venv/lib/python3.*/site-packages/
nft-router checks this at startup and prints the exact command if missing.
Console scripts
| command | what it runs |
|---|---|
nft-router |
the router itself (reads nft_route.json from CWD, or $NFT_ROUTE_CONFIG) |
nft-router-webadmin |
the web admin standalone (normally supervised by the router) |
nft-router-arp |
one switch's SNMP collector (normally supervised by the router) |
A note on ipdb
The GeoIP reader imported as ipdb comes from ipip-ipdb-hp (IPIP.net
database format, C extension). Do not pip install ipdb — that is the
IPython debugger and will shadow it with a package that has no City class.
Requires >= 0.1.2. Earlier versions do not compile on GCC 14+ (Debian
trixie, Ubuntu 24.04+), which turned -Wincompatible-pointer-types and
-Wimplicit-function-declaration into errors.
The pure-Python ipip-ipdb exposes the same City / find_map / is_ipv6
API and works as a fallback if the extension cannot be built — but it is
~11x slower (40k vs 450k lookups/s, measured against a 175 MB database),
and this lookup runs for every new connection, so prefer the C reader.
A note on pysnmp
The switch collectors need pysnmp, and there are two incompatible generations in the wild:
- pysnmp >= 7 (lextudio, actively maintained, asyncio API) — required on Python >= 3.12.
- pysnmp 4.4.x (2019, unmaintained, sync API) — imports
asyncore, which was removed in Python 3.12, so it only works on older interpreters.
arp_snmp.py speaks both and picks automatically, and the dependency
markers install the right one for the running interpreter. The collectors are
separate child processes, so they can run under a different interpreter
than the router via switches.python in the config — useful when only that
interpreter has a working pysnmp.
Replaced dependencies
Two long-unmaintained packages were dropped in favour of compat.py:
| was | last release | now |
|---|---|---|
netifaces |
2021-05 | psutil (already required); only interfaces() was used |
python-prctl |
2020-11, needs libcap headers | setproctitle + a ctypes prctl(PR_SET_NAME) call |
pytput |
2020-05, imports the removed pkg_resources |
compat.TputFormatter, same {x:spec,style} syntax |
pytput is not merely stale — it raises ModuleNotFoundError on import
under a Python 3.13 venv, since setuptools 81 dropped pkg_resources and
new venvs do not ship setuptools at all.
Running
cd /etc/network # wherever nft_route.json lives
nft-router # needs root: nftables, NFQUEUE, SO_MARK
The web admin starts automatically as a supervised child (see the
webadmin config section) — by default on http://127.0.0.1:8788.
Reload after editing config: kill -USR1 $(cat /run/nft_route.pid), or the
「重载主进程」button on the UI's 状态 page.
Tests
Offline, no root / kernel / switches / redis needed:
cd nftable_router
for t in test_*.py; do python3 "$t"; done
Icon Means
Status ICON
ALIVE:
🟩 - Global Lock Idle
🔴 - Process Dead
🟡 - Process Busying
🟩 - Process Idle > 30s
🟢 - Process Idle
Proxy Test Status:
⚫ for Line
⬛ for Proxy
⚫ - N/A
🔴 - Failed
🟢 - <= 100ms
🔵 - <= 200ms
🟣 - <= 400ms
🟡 - <= 600ms
🟠 - <= 800ms
🟤 - > 800ms
Config.json
ipdb_v4- Path for IPDB IPv4ipdb_v6- Path for IPDB IPv6nat_interfaces- Interface for internal network (from this interfaces will be nat)tunnel_ip- Tunnel IP, would be ignore to software routerallow_ecmp- Allow Equal Cost multi-path CIDR (TODO)allow_ecmp_port- Allow Equal Cost multi-path Ports (TODO)ignore_print_domain- No output for Print domainignore_list- Ignore source CIDR for software router (such as internal router)proxy- Line Listrules- Rules array for process (array for priority)from- match by source ip (highest priority)any- match any trafficresolve- match by resolved domain namecidr- match by target ip CIDRcountry_name- match by country nameregion_name- match by region name (such asALIDNS.COM)city_name- match by cityowner_domain- match by owner domain (such asgithub.com,twitter.com)isp_domain- match by ISP (such as阿里云,阿里云/电信/联通/移动/教育网)country_code- match by 2 char country code (such asCN)anycast- match by is anycast ip (onlyorANYCAST)idc- match by is idc ip (onlyorIDC)base_station- match by is base_station ip (onlyor基站)
webadmin section
"webadmin": {
"enabled": true, "host": "127.0.0.1", "port": 8788,
"redis_host": "127.0.0.1", "redis_port": 6379, "redis_db": 1,
"log": "/var/log/nft_webadmin.log",
"restart": {"max": 5, "window": 300},
"pdns_config": "/etc/powerdns/pdns-recursor.json",
"pdns_poison_list": "/etc/powerdns/dns_posion_list.txt",
"pdns_host": "user@192.168.30.2",
"rec_control": "rec_control"
}
pdns_host routes all PowerDNS file I/O and rec_control over ssh (keys /
~/.ssh/config of the invoking user; nothing is managed by this package).
Omit it when the recursor is local. Omit pdns_config to hide the DNS tab.
switches section — SNMP ARP/MAC collectors
One supervised child process per switch, polling ARP / MAC / interface tables
into redis (ARP::MAPPING, MAC::TABLE::<sysname>, SW::INT::<sysname>),
which is what the flow view's 源设备 column resolves against.
"switches": {
"enabled": true,
"python": "python3.9",
"log_dir": "/var/log/nft_route",
"poll_interval": 300,
"iface_interval": 1800,
"restart": {"max": 5, "window": 300},
"devices": [
{"name": "sw-core", "ip": "192.168.11.1",
"user": "monitor", "auth_key": "...", "priv_key": "..."},
{"name": "sw-acc", "ip": "192.168.11.4", "community": "public"}
]
}
python— interpreter for the collector children; omit to use the router's own. Set it when only another interpreter has a working pysnmp.- per device:
community(v2c) oruser+auth_key(+priv_key) for v3 (HMAC192SHA256 / AES128),enabled: falseto keep the entry but stop polling.
Both Huawei ARP MIBs are handled automatically: the standard
ipNetToPhysical table on VRP8/CloudEngine, and HUAWEI-ETHARP-MIB on VRP5
(S5700 family), whose index layout differs.
Metadata
Release files for nftable-router 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| nftable_router-0.2.0.tar.gz | 167.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nftable_router-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 346.3 kB
Release files / nftable_router-0.2.0.tar.gz
| Download URL | nftable_router-0.2.0.tar.gz |
|---|---|
| Size | 167.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9f83af1fb2e5f194d767034bf05c78a73d9e0d10bf5915fdc6461528a9fb5773
|
|
BLAKE2b-256 checksum How to use checksums |
2490aab3b9cf1a41e4861081bdae9f233d8d38f35fcc2176602d5bf9d82252a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Release files / nftable_router-0.2.0-py3-none-any.whl
| Download URL | nftable_router-0.2.0-py3-none-any.whl |
|---|---|
| Size | 178.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c43acdcddf56d1c96fe3bff8543224585a04fdd07cd5f19c2f74cc6a06815bb9
|
|
BLAKE2b-256 checksum How to use checksums |
9810d753ad5b93299fb539f6f82b6309b586ae1657e3d72e4ba1d3bf94fe1955
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.12
|
Security for nftable-router 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.