Bookmark

PBR với netifd trên OpenWrt: tách đường VPN, WAN và các ngoại lệ

Tôi dùng netifd để tách quyết định định tuyến thay vì nhồi nhiều default route vào một bảng. Bản hướng dẫn chính thức này bao phủ cả IPv4 và IPv6, vì vậy tôi giữ lại toàn bộ các tình huống mẫu thay vì chỉ chép một cấu hình VPN đơn giản.1

Mục tiêu và giới hạn

PBR là cách chọn đường đi dựa trên chính sách của quản trị viên. Với pbr_netifd, mỗi interface được gán một routing table riêng rồi các rule có priority cụ thể sẽ quyết định traffic đi vào table nào. Cách này phù hợp với interface, subnet, địa chỉ IP và port; nếu cần match theo domain thì nên xem PBR app. mwan3 lại dành cho load-balancing hoặc failover nhiều WAN, không phải thay thế trực tiếp cho từng rule bên dưới.1

Trước khi làm, tôi cần có interface lan, vpn, wan hoặc wan6 đã hoạt động; có quyền SSH; biết rõ tuyến tới endpoint VPN; và có một bản sao /etc/config/network cùng /etc/config/firewall. Mỗi đoạn dưới đây đều kết thúc bằng commit và restart, nên nên áp dụng từng mẫu một, kiểm tra rồi mới chuyển sang mẫu tiếp theo.

Nguyên tắc

  • Gán mỗi interface vào routing table riêng; để netifd tự tạo các route nền tảng.
  • Giữ default route trên từng upstream interface, nhưng không đặt các route tĩnh giống nhau vào nhiều table.
  • Đưa tunnel endpoint và mạng nội bộ vào các rule cụ thể trước khi đưa default route vào rule tổng quát.
  • Priority khoảng 30000 thường đứng trước main table; khoảng 4000080000 đứng sau main table. Hãy kiểm tra ip rule thực tế sau khi reload.
  • Nếu dùng IPv6, hãy cân nhắc NAT/NPT và source filter theo đúng topology; đừng coi cấu hình IPv4 là đủ để ngăn rò rỉ IPv6.1

Hướng dẫn

Tôi giữ nguyên cú pháp UCI của nguồn. Các tên lan, vpn, dmz, wan, wan6, wgclient và các routing table ID chỉ là các tên mà mẫu nguồn yêu cầu; hãy đổi chúng theo cấu hình thật của router, không đổi nửa chừng giữa các đoạn.

Route LAN vào VPN và DMZ vào WAN

Mẫu đầu tiên đặt lan vào table 1, vpn vào table 2, dmz vào table 3, rồi đưa traffic vào từ LAN tới lookup 2 với priority 30000. Default route của DMZ vẫn nằm trong table WAN theo setup đã có.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci set network.vpn.ip${IPV}table="2"
uci set network.dmz.ip${IPV}table="3"
uci -q delete network.lan_vpn${IPV%4}
uci set network.lan_vpn${IPV%4}="rule${IPV%4}"
uci set network.lan_vpn${IPV%4}.in="lan"
uci set network.lan_vpn${IPV%4}.lookup="2"
uci set network.lan_vpn${IPV%4}.priority="30000"
done
uci commit network
service network restart

Route LAN của VPN server qua VPN client

Mẫu này giả định WireGuard client đã có peer. Hai dòng allowed_ips để peer nhận default route IPv4 và IPv6, sau đó LAN được đưa vào table VPN.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
uci add_list network.wgclient.allowed_ips="0.0.0.0/0"
uci add_list network.wgclient.allowed_ips="::/0"
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci set network.vpn.ip${IPV}table="2"
uci -q delete network.lan_vpn${IPV%4}
uci set network.lan_vpn${IPV%4}="rule${IPV%4}"
uci set network.lan_vpn${IPV%4}.in="lan"
uci set network.lan_vpn${IPV%4}.lookup="2"
uci set network.lan_vpn${IPV%4}.priority="30000"
done
uci commit network
service network restart

Route LAN vào VPN, rớt VPN thì quay về WAN

Ở đây table 2 được gán cho wan/wan6 và priority 40000, nên có thể dùng WAN làm đường dự phòng sau khi rule VPN không còn áp dụng. Tôi vẫn phải kiểm tra trạng thái tunnel và bảng ip rule thay vì mặc định rằng failover đã hoạt động.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci set network.wan${IPV%4}.ip${IPV}table="2"
uci -q delete network.lan_wan${IPV%4}
uci set network.lan_wan${IPV%4}="rule${IPV%4}"
uci set network.lan_wan${IPV%4}.in="lan"
uci set network.lan_wan${IPV%4}.lookup="2"
uci set network.lan_wan${IPV%4}.priority="40000"
done
uci commit network
service network restart

Route LAN vào VPN theo IP set

Nguồn tạo ipset chứa các mạng của WAN, đánh dấu traffic LAN không khớp các mạng đó bằng 0x1, rồi lookup vào table VPN. Đây là nhánh dùng khi cần giữ một số đích ngoài VPN.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
for IPV in 4 6
do
uci -q delete firewall.wan_set${IPV%4}
uci set firewall.wan_set${IPV%4}="ipset"
uci set firewall.wan_set${IPV%4}.name="wan${IPV%4}"
uci set firewall.wan_set${IPV%4}.family="ipv${IPV}"
uci set firewall.wan_set${IPV%4}.match="net"
uci -q delete firewall.lan_mark${IPV%4}
uci set firewall.lan_mark${IPV%4}="rule"
uci set firewall.lan_mark${IPV%4}.name="Mark-LAN-VPN"
uci set firewall.lan_mark${IPV%4}.src="lan"
uci set firewall.lan_mark${IPV%4}.dest="*"
uci set firewall.lan_mark${IPV%4}.ipset="!wan${IPV%4} dest"
uci set firewall.lan_mark${IPV%4}.proto="all"
uci set firewall.lan_mark${IPV%4}.family="ipv${IPV}"
uci set firewall.lan_mark${IPV%4}.set_mark="0x1"
uci set firewall.lan_mark${IPV%4}.target="MARK"
uci set network.lan.ip${IPV}table="1"
uci set network.vpn.ip${IPV}table="2"
uci -q delete network.lan_vpn${IPV%4}
uci set network.lan_vpn${IPV%4}="rule${IPV%4}"
uci set network.lan_vpn${IPV%4}.in="lan"
uci set network.lan_vpn${IPV%4}.mark="1"
uci set network.lan_vpn${IPV%4}.lookup="2"
uci set network.lan_vpn${IPV%4}.priority="30000"
done
uci commit firewall
uci commit network
service firewall restart
service network restart

Route LAN vào VPN nhưng giữ port WAN forwarding

Mẫu này đánh dấu traffic HTTPS của một máy LAN theo MAC 00:11:22:33:44:55 và port 443, sau đó đẩy phần đã đánh dấu vào table WAN. MAC và port trong nguồn là ví dụ kỹ thuật; tôi phải thay bằng máy chủ thật, đồng thời kiểm tra rule forward/redirect riêng của firewall.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
uci -q delete firewall.lan_web
uci set firewall.lan_web="rule"
uci set firewall.lan_web.name="Mark-HTTPS"
uci set firewall.lan_web.src="lan"
uci set firewall.lan_web.src_mac="00:11:22:33:44:55"
uci set firewall.lan_web.src_port="443"
uci set firewall.lan_web.dest="*"
uci set firewall.lan_web.proto="tcp"
uci set firewall.lan_web.set_mark="0x1"
uci set firewall.lan_web.target="MARK"
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci set network.wan${IPV%4}.ip${IPV}table="2"
uci -q delete network.lan_web${IPV%4}
uci set network.lan_web${IPV%4}="rule${IPV%4}"
uci set network.lan_web${IPV%4}.in="lan"
uci set network.lan_web${IPV%4}.mark="1"
uci set network.lan_web${IPV%4}.lookup="2"
uci set network.lan_web${IPV%4}.priority="30000"
done
uci commit firewall
uci commit network
service firewall restart
service network restart

Route LAN sang OpenVPN

Nguồn giả định interface OpenVPN tên vpn. Default route 0.0.0.0/0::/0 được đặt trong table 2, rồi LAN lookup table đó.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci set network.vpn.ip${IPV}table="2"
uci -q delete network.vpn_rt${IPV%4}
uci set network.vpn_rt${IPV%4}="route${IPV%4}"
uci set network.vpn_rt${IPV%4}.interface="vpn"
uci -q delete network.lan_vpn${IPV%4}
uci set network.lan_vpn${IPV%4}="rule${IPV%4}"
uci set network.lan_vpn${IPV%4}.in="lan"
uci set network.lan_vpn${IPV%4}.lookup="2"
uci set network.lan_vpn${IPV%4}.priority="30000"
done
uci set network.vpn_rt.target="0.0.0.0/0"
uci set network.vpn_rt6.target="::/0"
uci commit network
service network restart

Route LAN sang Tailscale

Tailscale có thể tự tạo rule. Mẫu dưới đây tạo rule riêng ở priority 30000 và dùng goto="10000" để override rule tích hợp, vì vậy phải kiểm tra ip rule sau khi Tailscale khởi động.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
for IPV in 4 6
do
uci set network.lan.ip${IPV}table="1"
uci -q delete network.lan_vpn${IPV%4}
uci set network.lan_vpn${IPV%4}="rule"
uci set network.lan_vpn${IPV%4}.in="lan"
uci set network.lan_vpn${IPV%4}.lookup="52"
uci set network.lan_vpn${IPV%4}.priority="30000"
uci -q delete network.pbr${IPV%4}
uci set network.pbr${IPV%4}="rule"
uci set network.pbr${IPV%4}.goto="10000"
uci set network.pbr${IPV%4}.priority="1"
done
uci commit network
service network restart

Prohibitive routes

Thay vì để traffic rơi xuống một default route không mong muốn, mẫu này tạo route kiểu prohibit trong table VPN. Interface loopback được dùng làm interface luôn up theo giả định của nguồn; hãy kiểm tra kết quả với ip route trước khi áp dụng trên router đang phục vụ người dùng.1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
for IPV in 4 6
do
uci set network.vpn.ip${IPV}table="2"
uci -q delete network.vpn_ks${IPV%4}
uci set network.vpn_ks${IPV%4}="route${IPV%4}"
uci set network.vpn_ks${IPV%4}.interface="loopback"
uci set network.vpn_ks${IPV%4}.type="prohibit"
uci set network.vpn_ks${IPV%4}.metric="9000"
uci set network.vpn_ks${IPV%4}.table="2"
done
uci set network.vpn_ks.target="0.0.0.0/0"
uci set network.vpn_ks6.target="::/0"
uci commit network
service network restart

Prohibitive rules

Nhánh cuối tạo rule action="prohibit" cho traffic từ LAN ở priority 32000. Đây là ranh giới an toàn để tránh leak khi VPN không có tuyến; nhưng nếu đặt priority sai, rule có thể chặn cả các exception hợp lệ.1

Kiểm tra và rollback

Sau mỗi mẫu, chạy ip rule show, ip -6 rule show, ip route show table all, ip -6 route show table all và kiểm tra endpoint VPN. Test một client LAN, một đích phải đi VPN và một đích phải đi WAN. Nếu mất mạng, khôi phục bản sao /etc/config/network hoặc /etc/config/firewall qua console/SSH đã chuẩn bị rồi restart dịch vụ; không trộn nhiều mẫu trước khi biết mẫu nào gây lỗi.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for IPV in 4 6
do
uci -q delete network.lan_ks${IPV%4}
uci set network.lan_ks${IPV%4}="rule${IPV%4}"
uci set network.lan_ks${IPV%4}.in="lan"
uci set network.lan_ks${IPV%4}.action="prohibit"
uci set network.lan_ks${IPV%4}.priority="32000"
done
uci commit network
service network restart

Nguồn


  1. https://openwrt.org/docs/guide-user/network/routing/pbr_netifd — OpenWrt Wiki – PBR with netifd. Nội dung được biên tập độc lập từ snapshot ngày 2025-12-30; không phải bản dịch nguyên văn. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎


0 Bình luận

Góp Ý / Bình Luận / Đánh giá