https://blog.fernvenue.com/archives/using-pbr-to-route-traffic-on-openwrt/ * Archives * Tags * Me * Zhong /EN Using PBR to Route Traffic on OpenWrt 2025.03.16 Playing around with OpenWrt often involves multiple outbounds, maybe multiple ISPs or VPN outbounds like WireGuard. We always need to configure and route the traffic. Configuring routing can actually be a headache because as the needs become more complex, the difficulty of configuration tends to increase exponentially, also considering potential conflicts. Using ipset is certainly good, but the workload doesn't diminish, it becomes greater sometimes. To address this need, I chose PBR, aka Policy-Based Routing, simplifying this complex problem with some simple configuration scripts. PBR (Policy Based Routing) Before discussing policy routing, it might be useful to first look at the ipset. We need to invoke ipset in the firewall to mark the traffic, then match this mark in the ipv4/ipv6 rules to perform other operations. Isn't it already giving you a headache? Figuring out how to update these ipset lists is also a problem. The PBR on OpenWrt provides a method called Custom User Files, which conveniently solves the issue of updating and configuring the list together. Furthermore, PBR on OpenWrt works with nft, so there's not much to worry about performance. Therefore, after considering it, I decided to use PBR. As for installation, it's quite simple, we only need the pbr and luci-app-pbr packages. Once installed, we can control it through the graphical interface under Services - Policy Routing. Route Traffic Assuming we have a WireGuard outbound interface named wg0 on OpenWrt, and our default route dictates that all our traffic goes out through wan and wan6, how should we configure it to ensure that all traffic going to Google using this WireGuard interface? #!/bin/sh # shellcheck disable=SC2015,SC3003,SC3060 TARGET_URL='https://www.gstatic.com/ipranges/goog.json' TARGET_DL_FILE='/var/pbr_tmp_google_ip_ranges.gz' TARGET_TABLE='inet fw4' TARGET_INTERFACE='wg0' _ret=1 mkdir -p "${TARGET_DL_FILE%/*}" [ -s "$TARGET_DL_FILE" ] || \ uclient-fetch -qO- "$TARGET_URL" | \ gzip > "$TARGET_DL_FILE" [ -s "$TARGET_DL_FILE" ] || return 1 params4="$(zcat "$TARGET_DL_FILE" | jsonfilter -e "@.prefixes[*].ipv4Prefix")" params6="$(zcat "$TARGET_DL_FILE" | jsonfilter -e "@.prefixes[*].ipv6Prefix")" [ "$(uci get pbr.config.ipv6_enabled)" = "1" ] && vers="4 6" || vers="4" for ver in $vers;do case "$ver" in 4) params="$params4";; 6) params="$params6";; esac [ -n "$params" ] && _ret=0 || continue nftset="pbr_${TARGET_INTERFACE}_${ver}_dst_ip_user" nft "add element $TARGET_TABLE $nftset { ${params//$'\n'/, } }" || _ret=1 done Here's a simple rule file, get the list of Google's CIDR ranges using Google's API, format them, and add them to the nft rules. Just placed this script in the location /usr/share/pbr/pbr.user.google, then on OpenWrt panel, go to Services - Policy Routing at the bottom, add / usr/share/pbr/pbr.user.google, enable it, and then Save & Apply: image After that, if we SSH into OpenWrt, we should now can see the corresponding nft list: # nft list ruleset | awk '/set pbr_wg0_[46]_dst_ip_user \{/,/\}/' set pbr_wg0_4_dst_ip_user { type ipv4_addr flags interval auto-merge comment "" elements = { 8.8.4.0/24, 8.8.8.0/24, 8.34.208.0/20, 8.35.192.0/20, 23.236.48.0/20, 23.251.128.0/19, ... 208.117.224.0/19, 209.85.128.0/17, 216.58.192.0/19, 216.73.80.0/20, 216.239.32.0/19 } set pbr_wg0_6_dst_ip_user { type ipv6_addr flags interval auto-merge comment "" elements = { 2001:4860::/32, 2404:6800::/32, 2404:f340::/32, 2600:1900::/28, ... 2800:3f0::/32, 2a00:1450::/32, 2c0f:fb50::/32 } At this time, when we try traceroute google.com again, we will find that the route has already gone out over WireGuard, simple right? More In the project PBR Rules Collection, I have provided many examples that can be freely used, and contributions through PRs are also welcome :) Link * Policy-Based Routing OpenWrt Package Documentation: Custom User Files OpenWrt PBR Routing Traffic VPN WireGuard Configuration Scripting Copyright (c) 2019-2025 fernvenue. All rights reserved.