Mình đã từng mất gần 3 ngày để trace lại một vụ intermittent packet loss chỉ xuất hiện vào giờ cao điểm — khoảng 6 đến 8 giờ tối. Ban ngày mọi thứ hoàn toàn bình thường, nhưng cứ đến khung giờ đó là user complain liên tục. Vấn đề là không thể chạy tcpdump liên tục trên production server vì ảnh hưởng CPU và disk. Mình cần một cách passive hơn — capture traffic mà server không hay biết.
Đó là lúc mình tìm đến Traffic Mirroring với tc-mirred. Thay vì can thiệp trực tiếp vào luồng traffic, ta sao chép toàn bộ packets sang một interface riêng rồi đẩy sang máy IDS. Server production không bị tác động gì — giống hệt cách SPAN port hoạt động trên switch, nhưng chạy hoàn toàn trên software.
tc-mirred là gì và hoạt động như thế nào?
tc (Traffic Control) là công cụ cốt lõi của Linux kernel để quản lý queue, shaping và filter packets. Module mirred (Mirror and Redirect) là một action của tc cung cấp hai chế độ hoạt động:
- mirror: Sao chép packet sang interface khác, packet gốc tiếp tục đi bình thường — đây là mode ta dùng cho IDS/IPS
- redirect: Chuyển hướng hoàn toàn packet sang interface khác, packet gốc bị drop
Kiến trúc tổng quan của giải pháp:
[Client] ──→ [eth0: Production server] ──→ [Internet/Backend]
│
│ (mirror copy, không ảnh hưởng traffic gốc)
↓
[eth1: Monitoring interface]
│
↓
[IDS/IPS: Suricata / Zeek / Wireshark]
Điểm khác biệt quan trọng so với tcpdump hay Wireshark chạy trực tiếp trên server: mirroring hoạt động ở kernel level, overhead rất thấp, và IDS nhận được packet ở dạng raw — bao gồm cả những packet bị drop bởi iptables.
Chuẩn bị môi trường
Server cần ít nhất 2 network interface:
eth0: Interface chính đang xử lý traffic thậteth1: Interface kết nối với máy IDS/IPS — không cần IP, chỉ cần trạng thái UP
Đưa monitoring interface lên:
ip link set eth1 up
ip link show eth1 # Kiểm tra state: UP
Xác nhận kernel module act_mirred đã có:
lsmod | grep mirred
# Nếu không có output, load thủ công:
modprobe act_mirred
Cấu hình Traffic Mirroring từng bước
Mirror ingress traffic (chiều vào)
Phần hay bị nhầm nhất: với ingress traffic, Linux yêu cầu phải thêm một ingress qdisc đặc biệt (handle ffff:) — đây là pseudo-qdisc của kernel dành cho việc xử lý packet đến trước khi routing decision.
# Bước 1: Thêm ingress qdisc vào eth0
tc qdisc add dev eth0 ingress
# Bước 2: Thêm filter mirror toàn bộ IP traffic chiều vào
tc filter add dev eth0 parent ffff: \
protocol ip \
u32 match u32 0 0 \
action mirred egress mirror dev eth1
Giải thích các tham số:
parent ffff:: Gắn filter vào ingress qdiscu32 match u32 0 0: Match tất cả packets (bitmask 0 trên offset 0 = match everything)action mirred egress mirror dev eth1: Sao chép bản copy ra eth1
Mirror egress traffic (chiều ra)
Egress cần một bước thêm — phải tạo qdisc cha trước vì Linux không gắn filter được lên noqueue hay pfifo_fast mặc định:
# Bước 1: Thay thế default qdisc bằng prio
tc qdisc add dev eth0 root handle 1: prio
# Bước 2: Thêm filter mirror cho egress
tc filter add dev eth0 parent 1: \
protocol ip \
u32 match u32 0 0 \
action mirred egress mirror dev eth1
Script mirror cả hai chiều
IDS cần nhìn thấy full session — cả request lẫn response. Đây là script kết hợp cả ingress lẫn egress:
#!/bin/bash
# /usr/local/bin/setup-mirror.sh
PROD_IF="eth0"
MONITOR_IF="eth1"
# Đảm bảo monitoring interface UP
ip link set $MONITOR_IF up
# === Ingress mirror (traffic đến) ===
tc qdisc add dev $PROD_IF ingress
tc filter add dev $PROD_IF parent ffff: \
protocol ip u32 match u32 0 0 \
action mirred egress mirror dev $MONITOR_IF
# === Egress mirror (traffic đi ra) ===
tc qdisc add dev $PROD_IF root handle 1: prio
tc filter add dev $PROD_IF parent 1: \
protocol ip u32 match u32 0 0 \
action mirred egress mirror dev $MONITOR_IF
echo "[OK] Traffic mirroring active: $PROD_IF → $MONITOR_IF"
Xác nhận mirroring đang chạy
# Xem tất cả qdisc trên eth0
tc qdisc show dev eth0
# Xem filter ingress
tc filter show dev eth0 ingress
# Xem filter egress
tc filter show dev eth0
# Theo dõi packet counter (chạy vài lần, số Sent phải tăng)
tc -s filter show dev eth0 ingress
Trên máy IDS, dùng tcpdump để xác nhận đang nhận được packet:
tcpdump -i eth1 -n -c 50
# Phải thấy traffic tương tự như đang capture trực tiếp trên eth0
Mẹo thực chiến khi dùng tc-mirred
Mirror có chọn lọc theo port hoặc IP
Khi server có throughput lớn, mirror toàn bộ có thể làm quá tải monitoring interface. Lọc theo port hoặc IP nguồn giúp giảm tải đáng kể:
# Chỉ mirror TCP port 443 (ingress)
tc filter add dev eth0 parent ffff: \
protocol ip \
u32 match ip dport 443 0xffff \
action mirred egress mirror dev eth1
# Chỉ mirror traffic từ một IP cụ thể
tc filter add dev eth0 parent ffff: \
protocol ip \
u32 match ip src 10.0.0.100/32 \
action mirred egress mirror dev eth1
Dọn dẹp khi tắt mirroring
#!/bin/bash
# /usr/local/bin/cleanup-mirror.sh
PROD_IF="eth0"
# Xóa ingress qdisc (kéo theo tất cả filter bên trong)
tc qdisc del dev $PROD_IF ingress 2>/dev/null
# Xóa root qdisc, kernel tự restore về default
tc qdisc del dev $PROD_IF root 2>/dev/null
echo "[OK] Mirroring stopped, defaults restored"
Tự động khởi động lại sau reboot với systemd
Cấu hình tc không persist qua reboot — đây là điều mình bị dính lần đầu tiên dùng. Tạo systemd service để tự động hóa:
# /etc/systemd/system/traffic-mirror.service
[Unit]
Description=Traffic Mirroring via tc-mirred
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup-mirror.sh
ExecStop=/usr/local/bin/cleanup-mirror.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
chmod +x /usr/local/bin/setup-mirror.sh /usr/local/bin/cleanup-mirror.sh
systemctl daemon-reload
systemctl enable --now traffic-mirror
systemctl status traffic-mirror
Kết luận
tc-mirred là một trong những tool ít được nhắc đến nhưng giải quyết được bài toán thực tế mà các phương pháp khác không làm được: giám sát passive, không can thiệp vào traffic thật, không cần phần cứng switch hỗ trợ SPAN port.
Sau lần debug vụ packet loss kể trên, mình đã setup Suricata chạy ở chế độ IDS passive trên eth1 — nhận mirror traffic, phân tích và alert, nhưng không chạm vào production. Kết quả: phát hiện ra pattern rõ ràng là một số client đang retransmit quá nhiều đúng giờ cao điểm do một switch cũ bị overload ở tầng access layer. Không có mirroring thì không thể thấy được pattern đó.
Một lưu ý thực tế: mirroring có overhead CPU nhỏ — thường dưới 2% trên server hiện đại. Tuy nhiên nếu interface đang xử lý throughput rất cao (10Gbps+), nên benchmark trước khi áp dụng production để tránh bất ngờ.

