Skip to main content

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 IPv4
  • ipdb_v6 - Path for IPDB IPv6
  • nat_interfaces - Interface for internal network (from this interfaces will be nat)
  • tunnel_ip - Tunnel IP, would be ignore to software router
  • allow_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 domain
  • ignore_list - Ignore source CIDR for software router (such as internal router)
  • proxy - Line List
  • rules - Rules array for process (array for priority)
    • from - match by source ip (highest priority)
    • any - match any traffic
    • resolve - match by resolved domain name
    • cidr - match by target ip CIDR
    • country_name - match by country name
    • region_name - match by region name (such as ALIDNS.COM)
    • city_name - match by city
    • owner_domain - match by owner domain (such as github.com, twitter.com)
    • isp_domain - match by ISP (such as 阿里云, 阿里云/电信/联通/移动/教育网)
    • country_code - match by 2 char country code (such as CN)
    • anycast - match by is anycast ip (only or ANYCAST)
    • idc - match by is idc ip (only or IDC)
    • base_station - match by is base_station ip (only or 基站)

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) or user + auth_key (+priv_key) for v3 (HMAC192SHA256 / AES128), enabled: false to 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)

Source distribution for nftable-router 0.2.0
File Size Uploaded Provenance
nftable_router-0.2.0.tar.gz 167.4 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for nftable-router 0.2.0
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page