Configuring Policy Routing with nftables on Openwrt


If you have configured a VPN on an Openwrt device or have multiple internet connections and wish to use different lines for different destination IPs, you can follow the plan below. The overall idea of the plan is as follows:

  1. Create separate routing tables for each internet access interface and set up default routes in the Openwrt Luci interface.
  2. Apply marks to specific traffic.
  3. Configure IP rules according to requirements, so that traffic with different marks uses different routing tables.

Routing config

Prerequisite: To ensure proper routing functionality, it needs to have the 'ip-full' package installed.

opkg install ip-full

create routing table

Add a line in the file/etc/iproute2/rt_tables to create a new routing table.

200     VPN

add static route

The path for operations in the Luci interface:network->routing

Firstly, add a static IPv4 route; IPv6 is almost the same, just modify the part '' to '::/0'.

You can use the following command to check if the routing table is normal.

ip route list table gfw

use nftables to mark the traffic

create file /usr/share/nftables.d/vpn-ip-list.nft,and define the destinate IP in the file

These are some examples; you can describe the IP list according to your own needs.

define google_v4 ={
define google_v6 ={
define base_service_v4 = {
define base_service_v6 = {

You can refer to the following link for IP ranges of some commonly used services:

nftset config

"Note: After updating OpenWRT, the following files will be lost and need to be recreated."

Create the directory /usr/share/nftables.d/ruleset-post, and within this directory, create a file named create-set.nft (you can customize the file name).

vi  /usr/share/nftables.d/ruleset-post/create-set.nft

Enter the following content into the file:

include "/usr/share/nftables.d/vpn-ip-list.nft"
table inet fw4 {
	set vpn_ipv4_p {
	type ipv4_addr
	flags interval
	elements = { $base_service_v4 }
	set vpn_ipv6_p {
  type ipv6_addr        
  flags interval
	elements = { $base_service_v6 }
	set vpn_ipv4 {
	type ipv4_addr
	flags interval
	set vpn_ipv6 {
  type ipv6_addr        
  flags interval

	chain prerouting {
		ip daddr @vpn_ipv4_p counter  mark set 390
		ip6 daddr @vpn_ipv6_p counter  mark set 390
		ip daddr @vpn_ipv4 counter  mark set 390
		ip6 daddr @vpn_ipv6 counter  mark set 390
Pay attention to the number: 390. This can be customized and will be used in the following Luci interface operations. It needs to be converted to hexadecimal."

After configuring, restart the firewall service:

/etc/init.d/firewall restart

create ip rules

you can use luci interface to create the IPv4 rules & IPv6 rules

some useful scripts

add IP to nft set

nft add element inet fw4 vpn_ipv4_p { }

check status of nft set

nft list set inet fw4 vpn_ipv4

flush set

nft flush set inet fw4 vpn_ipv4
nft flush set inet fw4 vpn_ipv6