Bối cảnh & Lý do
Cơ chế định tuyến tiêu chuẩn trên Linux chỉ kiểm tra địa chỉ IP đích. Trong một cấu hình cơ bản, kernel sẽ phân tích gói tin, tra cứu bảng định tuyến main và đẩy lưu lượng ra ngoài qua default gateway chính. Cách hoạt động này hoàn toàn ổn với router gia đình đơn giản hay một máy chủ ảo cloud chỉ có một NIC duy nhất. Tuy nhiên, mọi thứ sẽ nhanh chóng đổ vỡ khi hạ tầng mạng của bạn trở nên phức tạp hơn.
Hãy xem xét một server multi-homed kết nối đồng thời với hai đường truyền cáp quang 1Gbps từ hai ISP khác nhau. Hoặc hình dung một database server sử dụng cổng 10GbE riêng cho lưu lượng replication, trong khi card mạng 1GbE đảm nhận SSH và monitoring. Nếu một client kết nối tới interface phụ (eth1), Linux thường sẽ cố gắng định tuyến gói phản hồi qua interface chính (eth0). Sự bất đối xứng này làm gãy kết nối, bởi các firewall upstream và bộ lọc chống giả mạo (anti-spoofing) của ISP sẽ âm thầm loại bỏ (drop) các gói tin đó.
Policy-Based Routing (PBR) giải quyết triệt để vấn đề này. Thay vì chỉ nhìn vào địa chỉ đích, kernel có thể định tuyến các gói tin dựa trên source IP, giao thức, transport port, hoặc các đánh dấu gói tin tùy chỉnh (fwmark) do iptables hoặc nftables thiết lập. Kỹ thuật này mang lại khả năng kiểm soát chính xác từng luồng outbound, giúp bạn tách biệt các lệnh gọi API nhạy cảm với độ trễ (jitter) khỏi những luồng backup cơ sở dữ liệu nặng 500MB mà không cần bổ sung thêm thiết bị phần cứng chuyên dụng.
Cài đặt
Mọi bản phân phối Linux hiện đại đều được tích hợp sẵn bộ công cụ iproute2. Lệnh chủ lực ip thay thế hoàn toàn các công cụ cũ như route và ifconfig, cung cấp quyền truy cập trực tiếp vào cơ sở dữ liệu chính sách định tuyến (RPDB) của kernel.
Kiểm tra xem hệ thống của bạn đã cài đặt iproute2 và các công cụ tường lửa hay chưa:
# Debian, Ubuntu và các bản phân phối phái sinh
sudo apt update
sudo apt install -y iproute2 iptables
# RHEL, Rocky Linux và Fedora
sudo dnf install -y iproute iptables-services
Kiểm tra cơ sở dữ liệu policy hiện có bằng một lệnh duy nhất:
ip rule show
Bạn sẽ thấy ba rule mặc định với mức độ ưu tiên lần lượt là 0, 32766 và 32767. Các rule này trỏ tới các bảng định tuyến local, main và default.
Cấu hình
Để triển khai PBR hoạt động trơn tru cần thực hiện qua bốn bước: đặt tên cho các bảng định tuyến tùy chỉnh, thêm các mục định tuyến, viết các policy rule và điều chỉnh bộ lọc chống giả mạo gói tin của kernel.
1. Định nghĩa tên cho bảng định tuyến tùy chỉnh
Mặc dù Linux kernel hỗ trợ tới 232 – 1 bảng định tuyến, các quản trị viên thường sử dụng các ID từ 1 đến 252 cho các đường đi tùy chỉnh. Việc dùng các con số thô trong script sẽ rất nhanh gây nhầm lẫn. Thay vào đó, hãy đặt các bí danh (alias) mang tính gợi nhớ trong file /etc/iproute2/rt_tables.
Thêm tên bảng tùy chỉnh cùng các ID duy nhất vào file:
# Gán tên mang tính gợi nhớ cho table ID 100 và 200
sudo bash -c 'echo "100 isp2_traffic" >> /etc/iproute2/rt_tables'
sudo bash -c 'echo "200 vpn_traffic" >> /etc/iproute2/rt_tables'
Bây giờ bạn có thể truyền trực tiếp isp2_traffic và vpn_traffic vào các lệnh ip route và ip rule.
2. Định tuyến theo địa chỉ Source IP
Xét một ví dụ thực tế với máy chủ dual-homed. Interface eth0 sử dụng IP 192.168.1.50/24 với gateway 192.168.1.1. Interface eth1 sử dụng IP 10.0.0.50/24 với gateway 10.0.0.1. Bất kỳ kết nối nào đến địa chỉ 10.0.0.50 đều phải gửi phản hồi ngược lại qua 10.0.0.1.
Khai báo default gateway cho interface thứ hai trong bảng tùy chỉnh:
sudo ip route add default via 10.0.0.1 dev eth1 table isp2_traffic
Tiếp theo, chỉ định cho kernel biết thời điểm cần áp dụng bảng này:
sudo ip rule add from 10.0.0.50/32 table isp2_traffic priority 1000
Luôn đặt một mức độ ưu tiên (priority) rõ ràng. Số càng nhỏ thì độ ưu tiên càng cao, vì vậy priority 1000 sẽ được đánh giá trước rule mặc định ở mức 32766 rất nhiều.
3. Định tuyến theo Port dịch vụ và fwmark
Lệnh ip rule hiện đại có thể khớp trực tiếp các port L4 bằng cú pháp sport hoặc dport. Tuy nhiên, việc kết hợp đánh dấu gói tin (fwmark) với iptables hoặc nftables vẫn là giải pháp chuẩn mực khi bạn cần các logic khớp điều kiện phức tạp.
Giả sử lưu lượng HTTPS chiều đi (outbound) trên cổng TCP 443 cần đi qua tunnel WireGuard (tun0) với gateway 10.10.10.1 nhằm ẩn IP công khai của bạn.
Thêm gateway vào bảng vpn_traffic:
sudo ip route add default via 10.10.10.1 dev tun0 table vpn_traffic
Đánh dấu các gói tin HTTPS đi ra ngoài trong bảng mangle:
sudo iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 0x1
Yêu cầu kernel tra cứu các gói tin đã đánh dấu trong bảng riêng biệt vừa tạo:
sudo ip rule add fwmark 0x1 table vpn_traffic priority 1100
Mỗi khi một dịch vụ nội bộ curl đến một endpoint HTTPS bên ngoài, bảng mangle sẽ gắn nhãn cho gói tin với mark 0x1. RPDB sẽ bắt được mark này, bỏ qua bảng main và định tuyến tải dữ liệu qua tun0.
4. Điều chỉnh Reverse Path Filtering (rp_filter)
Định tuyến bất đối xứng (asymmetric routing) thường hay gặp sự cố ở chính bước này. Theo mặc định, Linux chạy cơ chế Reverse Path Filtering ở chế độ nghiêm ngặt (strict mode, rp_filter = 1). Nếu một gói tin đi vào qua eth1 nhưng default route của kernel lại trỏ chiều đi ra qua eth0, network stack sẽ âm thầm drop gói tin vào đó vì coi nó là lưu lượng giả mạo (spoofed traffic).
Chuyển các interface sang chế độ lỏng (loose mode, rp_filter = 2) trong file /etc/sysctl.d/99-pbr.conf:
sudo tee /etc/sysctl.d/99-pbr.conf <<EOF
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.eth0.rp_filter = 2
net.ipv4.conf.eth1.rp_filter = 2
EOF
sudo sysctl --system
Chế độ loose mode vẫn bảo vệ máy chủ khỏi các địa chỉ nguồn không hợp lệ, nhưng nó cho phép gói tin đi vào một interface miễn là địa chỉ nguồn đó có thể truy cập được qua bất kỳ interface nào trên máy.
5. Lưu cấu hình vĩnh viễn qua các lần reboot
Các rule được thêm trực tiếp qua CLI chỉ tồn tại trong bộ nhớ kernel và sẽ biến mất sau khi khởi động lại máy chủ. Bạn phải ghi chúng vào các file cấu hình của trình quản lý mạng để duy trì vĩnh viễn.
Netplan (Ubuntu 20.04/22.04/24.04): Đặt các rule trực tiếp vào file cấu hình YAML, ví dụ /etc/netplan/01-netcfg.yaml:
network:
version: 2
ethernets:
eth1:
addresses:
- 10.0.0.50/24
routing-policy:
- from: 10.0.0.50/32
table: 100
priority: 1000
routes:
- to: default
via: 10.0.0.1
table: 100
Áp dụng các thay đổi bằng lệnh sudo netplan apply.
NetworkManager (RHEL, Rocky Linux, AlmaLinux): Sử dụng nmcli để cấu hình kết nối thứ hai:
nmcli connection modify eth1 +ipv4.routing-rules "priority 1000 from 10.0.0.50/32 table 100"
nmcli connection modify eth1 +ipv4.routes "0.0.0.0/0 10.0.0.1 100"
nmcli connection up eth1
Kiểm tra & Giám sát
Đừng bao giờ mặc định rằng việc định tuyến đã hoạt động trơn tru chỉ vì câu lệnh trả về mã exit code 0. Hãy kiểm tra các luồng tuyến một cách rõ ràng.
1. Kiểm tra các Rule và Table đang hoạt động
Xem toàn bộ danh sách routing rule đang hoạt động cùng các độ ưu tiên đã gán:
ip rule show
Đảm bảo các route bên trong bảng phụ khớp với mong muốn:
ip route show table isp2_traffic
2. Mô phỏng tra cứu định tuyến
Lệnh ip route get truy vấn cơ sở dữ liệu FIB (Forwarding Information Base) của kernel mà không sinh ra lưu lượng mạng thực tế:
# Kiểm tra định tuyến theo source IP
ip route get 8.8.8.8 from 10.0.0.50
# Kiểm tra định tuyến theo policy với mark 0x1
ip route get 1.1.1.1 mark 0x1
Kernel sẽ trả về chính xác bảng định tuyến và interface egress được lựa chọn:
8.8.8.8 from 10.0.0.50 via 10.0.0.1 dev eth1 table isp2_traffic uid 0
cache
3. Giám sát bộ đếm gói tin
Kiểm tra xem lưu lượng có thực sự đi qua các rule tường lửa hay không bằng cách xem các bộ đếm byte và gói tin:
sudo iptables -t mangle -L OUTPUT -v -n
Bạn sẽ thấy bộ đếm packet tăng lên mỗi khi có một kết nối thỏa mãn điều kiện được khởi tạo. Để theo dõi các frame dữ liệu thô đi qua interface phụ theo thời gian thực, hãy chạy tcpdump:
sudo tcpdump -nn -i eth1 host 10.0.0.50
Nhờ các bảng tùy chỉnh, độ ưu tiên rule rõ ràng và cấu hình loose reverse-path filtering hợp lý, các máy chủ Linux nhiều card mạng hoàn toàn có thể xử lý lưu lượng dual-homed phức tạp một cách chuẩn xác và ổn định.

